1252884aeSStefan Eßer /* 2252884aeSStefan Eßer * ***************************************************************************** 3252884aeSStefan Eßer * 43aa99676SStefan Eßer * SPDX-License-Identifier: BSD-2-Clause 5252884aeSStefan Eßer * 6*a970610aSStefan Eßer * Copyright (c) 2018-2024 Gavin D. Howard and contributors. 7252884aeSStefan Eßer * 8252884aeSStefan Eßer * Redistribution and use in source and binary forms, with or without 9252884aeSStefan Eßer * modification, are permitted provided that the following conditions are met: 10252884aeSStefan Eßer * 11252884aeSStefan Eßer * * Redistributions of source code must retain the above copyright notice, this 12252884aeSStefan Eßer * list of conditions and the following disclaimer. 13252884aeSStefan Eßer * 14252884aeSStefan Eßer * * Redistributions in binary form must reproduce the above copyright notice, 15252884aeSStefan Eßer * this list of conditions and the following disclaimer in the documentation 16252884aeSStefan Eßer * and/or other materials provided with the distribution. 17252884aeSStefan Eßer * 18252884aeSStefan Eßer * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19252884aeSStefan Eßer * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20252884aeSStefan Eßer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21252884aeSStefan Eßer * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22252884aeSStefan Eßer * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23252884aeSStefan Eßer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24252884aeSStefan Eßer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25252884aeSStefan Eßer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26252884aeSStefan Eßer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27252884aeSStefan Eßer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28252884aeSStefan Eßer * POSSIBILITY OF SUCH DAMAGE. 29252884aeSStefan Eßer * 30252884aeSStefan Eßer * ***************************************************************************** 31252884aeSStefan Eßer * 32252884aeSStefan Eßer * Common code for the lexers. 33252884aeSStefan Eßer * 34252884aeSStefan Eßer */ 35252884aeSStefan Eßer 36252884aeSStefan Eßer #include <assert.h> 37252884aeSStefan Eßer #include <ctype.h> 38252884aeSStefan Eßer #include <stdbool.h> 39252884aeSStefan Eßer #include <string.h> 40252884aeSStefan Eßer 41252884aeSStefan Eßer #include <lex.h> 42252884aeSStefan Eßer #include <vm.h> 43252884aeSStefan Eßer #include <bc.h> 44252884aeSStefan Eßer 4578bc019dSStefan Eßer void 4678bc019dSStefan Eßer bc_lex_invalidChar(BcLex* l, char c) 4778bc019dSStefan Eßer { 48252884aeSStefan Eßer l->t = BC_LEX_INVALID; 4950696a6eSStefan Eßer bc_lex_verr(l, BC_ERR_PARSE_CHAR, c); 50252884aeSStefan Eßer } 51252884aeSStefan Eßer 5278bc019dSStefan Eßer void 5378bc019dSStefan Eßer bc_lex_lineComment(BcLex* l) 5478bc019dSStefan Eßer { 55252884aeSStefan Eßer l->t = BC_LEX_WHITESPACE; 5678bc019dSStefan Eßer while (l->i < l->len && l->buf[l->i] != '\n') 5778bc019dSStefan Eßer { 5878bc019dSStefan Eßer l->i += 1; 5978bc019dSStefan Eßer } 60252884aeSStefan Eßer } 61252884aeSStefan Eßer 6278bc019dSStefan Eßer void 6378bc019dSStefan Eßer bc_lex_comment(BcLex* l) 6478bc019dSStefan Eßer { 65252884aeSStefan Eßer size_t i, nlines = 0; 6644d4804dSStefan Eßer const char* buf; 6744d4804dSStefan Eßer bool end = false, got_more; 68252884aeSStefan Eßer char c; 69252884aeSStefan Eßer 70252884aeSStefan Eßer l->i += 1; 71252884aeSStefan Eßer l->t = BC_LEX_WHITESPACE; 72252884aeSStefan Eßer 7344d4804dSStefan Eßer // This loop is complex because it might need to request more data from 7444d4804dSStefan Eßer // stdin if the comment is not ended. This loop is taken until the comment 7544d4804dSStefan Eßer // is finished or we have EOF. 7678bc019dSStefan Eßer do 7778bc019dSStefan Eßer { 7844d4804dSStefan Eßer buf = l->buf; 7944d4804dSStefan Eßer got_more = false; 8044d4804dSStefan Eßer 8144d4804dSStefan Eßer // If we are in stdin mode, the buffer must be the one used for stdin. 82d101cdd6SStefan Eßer assert(vm->mode != BC_MODE_STDIN || buf == vm->buffer.v); 8344d4804dSStefan Eßer 8444d4804dSStefan Eßer // Find the end of the comment. 8578bc019dSStefan Eßer for (i = l->i; !end; i += !end) 8678bc019dSStefan Eßer { 8744d4804dSStefan Eßer // While we don't have an asterisk, eat, but increment nlines. 8878bc019dSStefan Eßer for (; (c = buf[i]) && c != '*'; ++i) 8978bc019dSStefan Eßer { 9078bc019dSStefan Eßer nlines += (c == '\n'); 9178bc019dSStefan Eßer } 92252884aeSStefan Eßer 9344d4804dSStefan Eßer // If this is true, we need to request more data. 9478bc019dSStefan Eßer if (BC_ERR(!c || buf[i + 1] == '\0')) 9578bc019dSStefan Eßer { 9623210c9fSStefan Eßer // Read more, if possible. 97d101cdd6SStefan Eßer if (!vm->eof && l->mode != BC_MODE_FILE) 9878bc019dSStefan Eßer { 9923210c9fSStefan Eßer got_more = bc_lex_readLine(l); 10078bc019dSStefan Eßer } 10144d4804dSStefan Eßer 10244d4804dSStefan Eßer break; 103252884aeSStefan Eßer } 104252884aeSStefan Eßer 10544d4804dSStefan Eßer // If this turns true, we found the end. Yay! 10644d4804dSStefan Eßer end = (buf[i + 1] == '/'); 10744d4804dSStefan Eßer } 10878bc019dSStefan Eßer } 10978bc019dSStefan Eßer while (got_more && !end); 11044d4804dSStefan Eßer 11144d4804dSStefan Eßer // If we didn't find the end, barf. 11278bc019dSStefan Eßer if (!end) 11378bc019dSStefan Eßer { 11444d4804dSStefan Eßer l->i = i; 11544d4804dSStefan Eßer bc_lex_err(l, BC_ERR_PARSE_COMMENT); 116252884aeSStefan Eßer } 117252884aeSStefan Eßer 118252884aeSStefan Eßer l->i = i + 2; 119252884aeSStefan Eßer l->line += nlines; 120252884aeSStefan Eßer } 121252884aeSStefan Eßer 12278bc019dSStefan Eßer void 12378bc019dSStefan Eßer bc_lex_whitespace(BcLex* l) 12478bc019dSStefan Eßer { 125252884aeSStefan Eßer char c; 12644d4804dSStefan Eßer 127252884aeSStefan Eßer l->t = BC_LEX_WHITESPACE; 12844d4804dSStefan Eßer 12944d4804dSStefan Eßer // Eat. We don't eat newlines because they can be special. 13078bc019dSStefan Eßer for (c = l->buf[l->i]; c != '\n' && isspace(c); c = l->buf[++l->i]) 13178bc019dSStefan Eßer { 13278bc019dSStefan Eßer continue; 13378bc019dSStefan Eßer } 134252884aeSStefan Eßer } 135252884aeSStefan Eßer 13678bc019dSStefan Eßer void 13778bc019dSStefan Eßer bc_lex_commonTokens(BcLex* l, char c) 13878bc019dSStefan Eßer { 139252884aeSStefan Eßer if (!c) l->t = BC_LEX_EOF; 140252884aeSStefan Eßer else if (c == '\n') l->t = BC_LEX_NLINE; 141252884aeSStefan Eßer else bc_lex_whitespace(l); 142252884aeSStefan Eßer } 143252884aeSStefan Eßer 14444d4804dSStefan Eßer /** 14544d4804dSStefan Eßer * Parses a number. 14644d4804dSStefan Eßer * @param l The lexer. 14744d4804dSStefan Eßer * @param start The start character. 14844d4804dSStefan Eßer * @param int_only Whether this function should only look for an integer. This 14944d4804dSStefan Eßer * is used to implement the exponent of scientific notation. 15044d4804dSStefan Eßer */ 15178bc019dSStefan Eßer static size_t 15278bc019dSStefan Eßer bc_lex_num(BcLex* l, char start, bool int_only) 15378bc019dSStefan Eßer { 154252884aeSStefan Eßer const char* buf = l->buf + l->i; 155252884aeSStefan Eßer size_t i; 156252884aeSStefan Eßer char c; 157252884aeSStefan Eßer bool last_pt, pt = (start == '.'); 158252884aeSStefan Eßer 15944d4804dSStefan Eßer // This loop looks complex. It is not. It is asking if the character is not 16044d4804dSStefan Eßer // a nul byte and it if it a valid num character based on what we have found 16144d4804dSStefan Eßer // thus far, or whether it is a backslash followed by a newline. I can do 16244d4804dSStefan Eßer // i+1 on the buffer because the buffer must have a nul byte. 163252884aeSStefan Eßer for (i = 0; (c = buf[i]) && (BC_LEX_NUM_CHAR(c, pt, int_only) || 16478bc019dSStefan Eßer (c == '\\' && buf[i + 1] == '\n')); 16578bc019dSStefan Eßer ++i) 166252884aeSStefan Eßer { 16744d4804dSStefan Eßer // I don't need to test that the next character is a newline because 16844d4804dSStefan Eßer // the loop condition above ensures that. 16978bc019dSStefan Eßer if (c == '\\') 17078bc019dSStefan Eßer { 171252884aeSStefan Eßer i += 2; 172252884aeSStefan Eßer 173252884aeSStefan Eßer // Make sure to eat whitespace at the beginning of the line. 17478bc019dSStefan Eßer while (isspace(buf[i]) && buf[i] != '\n') 17578bc019dSStefan Eßer { 17678bc019dSStefan Eßer i += 1; 17778bc019dSStefan Eßer } 178252884aeSStefan Eßer 179252884aeSStefan Eßer c = buf[i]; 180252884aeSStefan Eßer 18144d4804dSStefan Eßer // If the next character is not a number character, bail. 182252884aeSStefan Eßer if (!BC_LEX_NUM_CHAR(c, pt, int_only)) break; 183252884aeSStefan Eßer } 184252884aeSStefan Eßer 18544d4804dSStefan Eßer // Did we find the radix point? 186252884aeSStefan Eßer last_pt = (c == '.'); 18744d4804dSStefan Eßer 18844d4804dSStefan Eßer // If we did, and we already have one, then break because it's not part 18944d4804dSStefan Eßer // of this number. 190252884aeSStefan Eßer if (pt && last_pt) break; 19144d4804dSStefan Eßer 19244d4804dSStefan Eßer // Set whether we have found a radix point. 193252884aeSStefan Eßer pt = pt || last_pt; 194252884aeSStefan Eßer 195252884aeSStefan Eßer bc_vec_push(&l->str, &c); 196252884aeSStefan Eßer } 197252884aeSStefan Eßer 198252884aeSStefan Eßer return i; 199252884aeSStefan Eßer } 200252884aeSStefan Eßer 20178bc019dSStefan Eßer void 20278bc019dSStefan Eßer bc_lex_number(BcLex* l, char start) 20378bc019dSStefan Eßer { 204252884aeSStefan Eßer l->t = BC_LEX_NUMBER; 205252884aeSStefan Eßer 20644d4804dSStefan Eßer // Make sure the string is clear. 20710328f8bSStefan Eßer bc_vec_popAll(&l->str); 208252884aeSStefan Eßer bc_vec_push(&l->str, &start); 209252884aeSStefan Eßer 21044d4804dSStefan Eßer // Parse the number. 211252884aeSStefan Eßer l->i += bc_lex_num(l, start, false); 212252884aeSStefan Eßer 213252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 214252884aeSStefan Eßer { 215252884aeSStefan Eßer char c = l->buf[l->i]; 216252884aeSStefan Eßer 21744d4804dSStefan Eßer // Do we have a number in scientific notation? 21878bc019dSStefan Eßer if (c == 'e') 21978bc019dSStefan Eßer { 220252884aeSStefan Eßer #if BC_ENABLED 22144d4804dSStefan Eßer // Barf for POSIX. 22250696a6eSStefan Eßer if (BC_IS_POSIX) bc_lex_err(l, BC_ERR_POSIX_EXP_NUM); 223252884aeSStefan Eßer #endif // BC_ENABLED 224252884aeSStefan Eßer 22544d4804dSStefan Eßer // Push the e. 226252884aeSStefan Eßer bc_vec_push(&l->str, &c); 227252884aeSStefan Eßer l->i += 1; 228252884aeSStefan Eßer c = l->buf[l->i]; 229252884aeSStefan Eßer 23044d4804dSStefan Eßer // Check for negative specifically because bc_lex_num() does not. 23178bc019dSStefan Eßer if (c == BC_LEX_NEG_CHAR) 23278bc019dSStefan Eßer { 233252884aeSStefan Eßer bc_vec_push(&l->str, &c); 234252884aeSStefan Eßer l->i += 1; 235252884aeSStefan Eßer c = l->buf[l->i]; 236252884aeSStefan Eßer } 237252884aeSStefan Eßer 23844d4804dSStefan Eßer // We must have a number character, so barf if not. 239252884aeSStefan Eßer if (BC_ERR(!BC_LEX_NUM_CHAR(c, false, true))) 24078bc019dSStefan Eßer { 24150696a6eSStefan Eßer bc_lex_verr(l, BC_ERR_PARSE_CHAR, c); 24278bc019dSStefan Eßer } 243252884aeSStefan Eßer 24444d4804dSStefan Eßer // Parse the exponent. 245252884aeSStefan Eßer l->i += bc_lex_num(l, 0, true); 246252884aeSStefan Eßer } 247252884aeSStefan Eßer } 248252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 249252884aeSStefan Eßer 250252884aeSStefan Eßer bc_vec_pushByte(&l->str, '\0'); 251252884aeSStefan Eßer } 252252884aeSStefan Eßer 25378bc019dSStefan Eßer void 25478bc019dSStefan Eßer bc_lex_name(BcLex* l) 25578bc019dSStefan Eßer { 256252884aeSStefan Eßer size_t i = 0; 257252884aeSStefan Eßer const char* buf = l->buf + l->i - 1; 258252884aeSStefan Eßer char c = buf[i]; 259252884aeSStefan Eßer 260252884aeSStefan Eßer l->t = BC_LEX_NAME; 261252884aeSStefan Eßer 26244d4804dSStefan Eßer // Should be obvious. It's looking for valid characters. 26378bc019dSStefan Eßer while ((c >= 'a' && c <= 'z') || isdigit(c) || c == '_') 26478bc019dSStefan Eßer { 26578bc019dSStefan Eßer c = buf[++i]; 26678bc019dSStefan Eßer } 267252884aeSStefan Eßer 26844d4804dSStefan Eßer // Set the string to the identifier. 269252884aeSStefan Eßer bc_vec_string(&l->str, i, buf); 270252884aeSStefan Eßer 271252884aeSStefan Eßer // Increment the index. We minus 1 because it has already been incremented. 272252884aeSStefan Eßer l->i += i - 1; 273252884aeSStefan Eßer } 274252884aeSStefan Eßer 27578bc019dSStefan Eßer void 27678bc019dSStefan Eßer bc_lex_init(BcLex* l) 27778bc019dSStefan Eßer { 278252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 279252884aeSStefan Eßer assert(l != NULL); 28044d4804dSStefan Eßer bc_vec_init(&l->str, sizeof(char), BC_DTOR_NONE); 281252884aeSStefan Eßer } 282252884aeSStefan Eßer 28378bc019dSStefan Eßer void 28478bc019dSStefan Eßer bc_lex_free(BcLex* l) 28578bc019dSStefan Eßer { 286252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 287252884aeSStefan Eßer assert(l != NULL); 288252884aeSStefan Eßer bc_vec_free(&l->str); 289252884aeSStefan Eßer } 290252884aeSStefan Eßer 29178bc019dSStefan Eßer void 29278bc019dSStefan Eßer bc_lex_file(BcLex* l, const char* file) 29378bc019dSStefan Eßer { 294252884aeSStefan Eßer assert(l != NULL && file != NULL); 295252884aeSStefan Eßer l->line = 1; 296d101cdd6SStefan Eßer vm->file = file; 297252884aeSStefan Eßer } 298252884aeSStefan Eßer 29978bc019dSStefan Eßer void 30078bc019dSStefan Eßer bc_lex_next(BcLex* l) 30178bc019dSStefan Eßer { 30210041e99SStefan Eßer BC_SIG_ASSERT_LOCKED; 30310041e99SStefan Eßer 304252884aeSStefan Eßer assert(l != NULL); 305252884aeSStefan Eßer 306252884aeSStefan Eßer l->last = l->t; 30744d4804dSStefan Eßer 30844d4804dSStefan Eßer // If this wasn't here, the line number would be off. 309252884aeSStefan Eßer l->line += (l->i != 0 && l->buf[l->i - 1] == '\n'); 310252884aeSStefan Eßer 31144d4804dSStefan Eßer // If the last token was EOF, someone called this one too many times. 31250696a6eSStefan Eßer if (BC_ERR(l->last == BC_LEX_EOF)) bc_lex_err(l, BC_ERR_PARSE_EOF); 313252884aeSStefan Eßer 314252884aeSStefan Eßer l->t = BC_LEX_EOF; 315252884aeSStefan Eßer 31644d4804dSStefan Eßer // We are done if this is true. 317252884aeSStefan Eßer if (l->i == l->len) return; 318252884aeSStefan Eßer 319252884aeSStefan Eßer // Loop until failure or we don't have whitespace. This 320252884aeSStefan Eßer // is so the parser doesn't get inundated with whitespace. 32178bc019dSStefan Eßer do 32278bc019dSStefan Eßer { 323d101cdd6SStefan Eßer vm->next(l); 32478bc019dSStefan Eßer } 32578bc019dSStefan Eßer while (l->t == BC_LEX_WHITESPACE); 326252884aeSStefan Eßer } 327252884aeSStefan Eßer 32844d4804dSStefan Eßer /** 32944d4804dSStefan Eßer * Updates the buffer and len so that they are not invalidated when the stdin 33044d4804dSStefan Eßer * buffer grows. 33144d4804dSStefan Eßer * @param l The lexer. 33244d4804dSStefan Eßer * @param text The text. 33344d4804dSStefan Eßer * @param len The length of the text. 33444d4804dSStefan Eßer */ 33578bc019dSStefan Eßer static void 33678bc019dSStefan Eßer bc_lex_fixText(BcLex* l, const char* text, size_t len) 33778bc019dSStefan Eßer { 338252884aeSStefan Eßer l->buf = text; 33944d4804dSStefan Eßer l->len = len; 34044d4804dSStefan Eßer } 34144d4804dSStefan Eßer 34278bc019dSStefan Eßer bool 34378bc019dSStefan Eßer bc_lex_readLine(BcLex* l) 34478bc019dSStefan Eßer { 34510041e99SStefan Eßer bool good; 34610041e99SStefan Eßer 34710041e99SStefan Eßer // These are reversed because they should be already locked, but 34810041e99SStefan Eßer // bc_vm_readLine() needs them to be unlocked. 34910041e99SStefan Eßer BC_SIG_UNLOCK; 35010041e99SStefan Eßer 35123210c9fSStefan Eßer // Make sure we read from the appropriate place. 352d101cdd6SStefan Eßer switch (l->mode) 35378bc019dSStefan Eßer { 354d101cdd6SStefan Eßer case BC_MODE_EXPRS: 355d101cdd6SStefan Eßer { 35623210c9fSStefan Eßer good = bc_vm_readBuf(false); 357d101cdd6SStefan Eßer break; 358d101cdd6SStefan Eßer } 359d101cdd6SStefan Eßer 360d101cdd6SStefan Eßer case BC_MODE_FILE: 361d101cdd6SStefan Eßer { 362d101cdd6SStefan Eßer good = false; 363d101cdd6SStefan Eßer break; 364d101cdd6SStefan Eßer } 365d101cdd6SStefan Eßer 366d101cdd6SStefan Eßer case BC_MODE_STDIN: 367d101cdd6SStefan Eßer { 368d101cdd6SStefan Eßer good = bc_vm_readLine(false); 369d101cdd6SStefan Eßer break; 370d101cdd6SStefan Eßer } 371d101cdd6SStefan Eßer 372d101cdd6SStefan Eßer #ifdef __GNUC__ 373d101cdd6SStefan Eßer #ifndef __clang__ 374d101cdd6SStefan Eßer default: 375d101cdd6SStefan Eßer { 376d101cdd6SStefan Eßer // We should never get here. 377d101cdd6SStefan Eßer abort(); 378d101cdd6SStefan Eßer } 379d101cdd6SStefan Eßer #endif // __clang__ 380d101cdd6SStefan Eßer #endif // __GNUC__ 38123210c9fSStefan Eßer } 38210041e99SStefan Eßer 38310041e99SStefan Eßer BC_SIG_LOCK; 38444d4804dSStefan Eßer 385d101cdd6SStefan Eßer bc_lex_fixText(l, vm->buffer.v, vm->buffer.len - 1); 38644d4804dSStefan Eßer 38744d4804dSStefan Eßer return good; 38844d4804dSStefan Eßer } 38944d4804dSStefan Eßer 39078bc019dSStefan Eßer void 391d101cdd6SStefan Eßer bc_lex_text(BcLex* l, const char* text, BcMode mode) 39278bc019dSStefan Eßer { 39310041e99SStefan Eßer BC_SIG_ASSERT_LOCKED; 39410041e99SStefan Eßer 39544d4804dSStefan Eßer assert(l != NULL && text != NULL); 39610041e99SStefan Eßer 39744d4804dSStefan Eßer bc_lex_fixText(l, text, strlen(text)); 398252884aeSStefan Eßer l->i = 0; 399252884aeSStefan Eßer l->t = l->last = BC_LEX_INVALID; 400d101cdd6SStefan Eßer l->mode = mode; 40110041e99SStefan Eßer 402252884aeSStefan Eßer bc_lex_next(l); 403252884aeSStefan Eßer } 404