1252884aeSStefan Eßer /* 2252884aeSStefan Eßer * ***************************************************************************** 3252884aeSStefan Eßer * 43aa99676SStefan Eßer * SPDX-License-Identifier: BSD-2-Clause 5252884aeSStefan Eßer * 610328f8bSStefan Eßer * Copyright (c) 2018-2021 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 45252884aeSStefan Eßer void bc_lex_invalidChar(BcLex *l, char c) { 46252884aeSStefan Eßer l->t = BC_LEX_INVALID; 4750696a6eSStefan Eßer bc_lex_verr(l, BC_ERR_PARSE_CHAR, c); 48252884aeSStefan Eßer } 49252884aeSStefan Eßer 50252884aeSStefan Eßer void bc_lex_lineComment(BcLex *l) { 51252884aeSStefan Eßer l->t = BC_LEX_WHITESPACE; 52252884aeSStefan Eßer while (l->i < l->len && l->buf[l->i] != '\n') l->i += 1; 53252884aeSStefan Eßer } 54252884aeSStefan Eßer 55252884aeSStefan Eßer void bc_lex_comment(BcLex *l) { 56252884aeSStefan Eßer 57252884aeSStefan Eßer size_t i, nlines = 0; 5844d4804dSStefan Eßer const char *buf; 5944d4804dSStefan Eßer bool end = false, got_more; 60252884aeSStefan Eßer char c; 61252884aeSStefan Eßer 62252884aeSStefan Eßer l->i += 1; 63252884aeSStefan Eßer l->t = BC_LEX_WHITESPACE; 64252884aeSStefan Eßer 6544d4804dSStefan Eßer // This loop is complex because it might need to request more data from 6644d4804dSStefan Eßer // stdin if the comment is not ended. This loop is taken until the comment 6744d4804dSStefan Eßer // is finished or we have EOF. 6844d4804dSStefan Eßer do { 6944d4804dSStefan Eßer 7044d4804dSStefan Eßer buf = l->buf; 7144d4804dSStefan Eßer got_more = false; 7244d4804dSStefan Eßer 7344d4804dSStefan Eßer // If we are in stdin mode, the buffer must be the one used for stdin. 7444d4804dSStefan Eßer assert(!vm.is_stdin || buf == vm.buffer.v); 7544d4804dSStefan Eßer 7644d4804dSStefan Eßer // Find the end of the comment. 77252884aeSStefan Eßer for (i = l->i; !end; i += !end) { 78252884aeSStefan Eßer 7944d4804dSStefan Eßer // While we don't have an asterisk, eat, but increment nlines. 80252884aeSStefan Eßer for (; (c = buf[i]) && c != '*'; ++i) nlines += (c == '\n'); 81252884aeSStefan Eßer 8244d4804dSStefan Eßer // If this is true, we need to request more data. 83252884aeSStefan Eßer if (BC_ERR(!c || buf[i + 1] == '\0')) { 8444d4804dSStefan Eßer 8544d4804dSStefan Eßer // Read more. 8644d4804dSStefan Eßer if (!vm.eof && l->is_stdin) got_more = bc_lex_readLine(l); 8744d4804dSStefan Eßer 8844d4804dSStefan Eßer break; 89252884aeSStefan Eßer } 90252884aeSStefan Eßer 9144d4804dSStefan Eßer // If this turns true, we found the end. Yay! 9244d4804dSStefan Eßer end = (buf[i + 1] == '/'); 9344d4804dSStefan Eßer } 9444d4804dSStefan Eßer 9544d4804dSStefan Eßer } while (got_more && !end); 9644d4804dSStefan Eßer 9744d4804dSStefan Eßer // If we didn't find the end, barf. 9844d4804dSStefan Eßer if (!end) { 9944d4804dSStefan Eßer l->i = i; 10044d4804dSStefan Eßer bc_lex_err(l, BC_ERR_PARSE_COMMENT); 101252884aeSStefan Eßer } 102252884aeSStefan Eßer 103252884aeSStefan Eßer l->i = i + 2; 104252884aeSStefan Eßer l->line += nlines; 105252884aeSStefan Eßer } 106252884aeSStefan Eßer 107252884aeSStefan Eßer void bc_lex_whitespace(BcLex *l) { 10844d4804dSStefan Eßer 109252884aeSStefan Eßer char c; 11044d4804dSStefan Eßer 111252884aeSStefan Eßer l->t = BC_LEX_WHITESPACE; 11244d4804dSStefan Eßer 11344d4804dSStefan Eßer // Eat. We don't eat newlines because they can be special. 114252884aeSStefan Eßer for (c = l->buf[l->i]; c != '\n' && isspace(c); c = l->buf[++l->i]); 115252884aeSStefan Eßer } 116252884aeSStefan Eßer 117252884aeSStefan Eßer void bc_lex_commonTokens(BcLex *l, char c) { 118252884aeSStefan Eßer if (!c) l->t = BC_LEX_EOF; 119252884aeSStefan Eßer else if (c == '\n') l->t = BC_LEX_NLINE; 120252884aeSStefan Eßer else bc_lex_whitespace(l); 121252884aeSStefan Eßer } 122252884aeSStefan Eßer 12344d4804dSStefan Eßer /** 12444d4804dSStefan Eßer * Parses a number. 12544d4804dSStefan Eßer * @param l The lexer. 12644d4804dSStefan Eßer * @param start The start character. 12744d4804dSStefan Eßer * @param int_only Whether this function should only look for an integer. This 12844d4804dSStefan Eßer * is used to implement the exponent of scientific notation. 12944d4804dSStefan Eßer */ 130252884aeSStefan Eßer static size_t bc_lex_num(BcLex *l, char start, bool int_only) { 131252884aeSStefan Eßer 132252884aeSStefan Eßer const char *buf = l->buf + l->i; 133252884aeSStefan Eßer size_t i; 134252884aeSStefan Eßer char c; 135252884aeSStefan Eßer bool last_pt, pt = (start == '.'); 136252884aeSStefan Eßer 13744d4804dSStefan Eßer // This loop looks complex. It is not. It is asking if the character is not 13844d4804dSStefan Eßer // a nul byte and it if it a valid num character based on what we have found 13944d4804dSStefan Eßer // thus far, or whether it is a backslash followed by a newline. I can do 14044d4804dSStefan Eßer // i+1 on the buffer because the buffer must have a nul byte. 141252884aeSStefan Eßer for (i = 0; (c = buf[i]) && (BC_LEX_NUM_CHAR(c, pt, int_only) || 142252884aeSStefan Eßer (c == '\\' && buf[i + 1] == '\n')); ++i) 143252884aeSStefan Eßer { 14444d4804dSStefan Eßer // I don't need to test that the next character is a newline because 14544d4804dSStefan Eßer // the loop condition above ensures that. 146252884aeSStefan Eßer if (c == '\\') { 147252884aeSStefan Eßer 148252884aeSStefan Eßer i += 2; 149252884aeSStefan Eßer 150252884aeSStefan Eßer // Make sure to eat whitespace at the beginning of the line. 151252884aeSStefan Eßer while(isspace(buf[i]) && buf[i] != '\n') i += 1; 152252884aeSStefan Eßer 153252884aeSStefan Eßer c = buf[i]; 154252884aeSStefan Eßer 15544d4804dSStefan Eßer // If the next character is not a number character, bail. 156252884aeSStefan Eßer if (!BC_LEX_NUM_CHAR(c, pt, int_only)) break; 157252884aeSStefan Eßer } 158252884aeSStefan Eßer 15944d4804dSStefan Eßer // Did we find the radix point? 160252884aeSStefan Eßer last_pt = (c == '.'); 16144d4804dSStefan Eßer 16244d4804dSStefan Eßer // If we did, and we already have one, then break because it's not part 16344d4804dSStefan Eßer // of this number. 164252884aeSStefan Eßer if (pt && last_pt) break; 16544d4804dSStefan Eßer 16644d4804dSStefan Eßer // Set whether we have found a radix point. 167252884aeSStefan Eßer pt = pt || last_pt; 168252884aeSStefan Eßer 169252884aeSStefan Eßer bc_vec_push(&l->str, &c); 170252884aeSStefan Eßer } 171252884aeSStefan Eßer 172252884aeSStefan Eßer return i; 173252884aeSStefan Eßer } 174252884aeSStefan Eßer 175252884aeSStefan Eßer void bc_lex_number(BcLex *l, char start) { 176252884aeSStefan Eßer 177252884aeSStefan Eßer l->t = BC_LEX_NUMBER; 178252884aeSStefan Eßer 17944d4804dSStefan Eßer // Make sure the string is clear. 18010328f8bSStefan Eßer bc_vec_popAll(&l->str); 181252884aeSStefan Eßer bc_vec_push(&l->str, &start); 182252884aeSStefan Eßer 18344d4804dSStefan Eßer // Parse the number. 184252884aeSStefan Eßer l->i += bc_lex_num(l, start, false); 185252884aeSStefan Eßer 186252884aeSStefan Eßer #if BC_ENABLE_EXTRA_MATH 187252884aeSStefan Eßer { 188252884aeSStefan Eßer char c = l->buf[l->i]; 189252884aeSStefan Eßer 19044d4804dSStefan Eßer // Do we have a number in scientific notation? 191252884aeSStefan Eßer if (c == 'e') { 192252884aeSStefan Eßer 193252884aeSStefan Eßer #if BC_ENABLED 19444d4804dSStefan Eßer // Barf for POSIX. 19550696a6eSStefan Eßer if (BC_IS_POSIX) bc_lex_err(l, BC_ERR_POSIX_EXP_NUM); 196252884aeSStefan Eßer #endif // BC_ENABLED 197252884aeSStefan Eßer 19844d4804dSStefan Eßer // Push the e. 199252884aeSStefan Eßer bc_vec_push(&l->str, &c); 200252884aeSStefan Eßer l->i += 1; 201252884aeSStefan Eßer c = l->buf[l->i]; 202252884aeSStefan Eßer 20344d4804dSStefan Eßer // Check for negative specifically because bc_lex_num() does not. 204252884aeSStefan Eßer if (c == BC_LEX_NEG_CHAR) { 205252884aeSStefan Eßer bc_vec_push(&l->str, &c); 206252884aeSStefan Eßer l->i += 1; 207252884aeSStefan Eßer c = l->buf[l->i]; 208252884aeSStefan Eßer } 209252884aeSStefan Eßer 21044d4804dSStefan Eßer // We must have a number character, so barf if not. 211252884aeSStefan Eßer if (BC_ERR(!BC_LEX_NUM_CHAR(c, false, true))) 21250696a6eSStefan Eßer bc_lex_verr(l, BC_ERR_PARSE_CHAR, c); 213252884aeSStefan Eßer 21444d4804dSStefan Eßer // Parse the exponent. 215252884aeSStefan Eßer l->i += bc_lex_num(l, 0, true); 216252884aeSStefan Eßer } 217252884aeSStefan Eßer } 218252884aeSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 219252884aeSStefan Eßer 220252884aeSStefan Eßer bc_vec_pushByte(&l->str, '\0'); 221252884aeSStefan Eßer } 222252884aeSStefan Eßer 223252884aeSStefan Eßer void bc_lex_name(BcLex *l) { 224252884aeSStefan Eßer 225252884aeSStefan Eßer size_t i = 0; 226252884aeSStefan Eßer const char *buf = l->buf + l->i - 1; 227252884aeSStefan Eßer char c = buf[i]; 228252884aeSStefan Eßer 229252884aeSStefan Eßer l->t = BC_LEX_NAME; 230252884aeSStefan Eßer 23144d4804dSStefan Eßer // Should be obvious. It's looking for valid characters. 232252884aeSStefan Eßer while ((c >= 'a' && c <= 'z') || isdigit(c) || c == '_') c = buf[++i]; 233252884aeSStefan Eßer 23444d4804dSStefan Eßer // Set the string to the identifier. 235252884aeSStefan Eßer bc_vec_string(&l->str, i, buf); 236252884aeSStefan Eßer 237252884aeSStefan Eßer // Increment the index. We minus 1 because it has already been incremented. 238252884aeSStefan Eßer l->i += i - 1; 239252884aeSStefan Eßer } 240252884aeSStefan Eßer 241252884aeSStefan Eßer void bc_lex_init(BcLex *l) { 242252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 243252884aeSStefan Eßer assert(l != NULL); 24444d4804dSStefan Eßer bc_vec_init(&l->str, sizeof(char), BC_DTOR_NONE); 245252884aeSStefan Eßer } 246252884aeSStefan Eßer 247252884aeSStefan Eßer void bc_lex_free(BcLex *l) { 248252884aeSStefan Eßer BC_SIG_ASSERT_LOCKED; 249252884aeSStefan Eßer assert(l != NULL); 250252884aeSStefan Eßer bc_vec_free(&l->str); 251252884aeSStefan Eßer } 252252884aeSStefan Eßer 253252884aeSStefan Eßer void bc_lex_file(BcLex *l, const char *file) { 254252884aeSStefan Eßer assert(l != NULL && file != NULL); 255252884aeSStefan Eßer l->line = 1; 256252884aeSStefan Eßer vm.file = file; 257252884aeSStefan Eßer } 258252884aeSStefan Eßer 259252884aeSStefan Eßer void bc_lex_next(BcLex *l) { 260252884aeSStefan Eßer 261*10041e99SStefan Eßer BC_SIG_ASSERT_LOCKED; 262*10041e99SStefan Eßer 263252884aeSStefan Eßer assert(l != NULL); 264252884aeSStefan Eßer 265252884aeSStefan Eßer l->last = l->t; 26644d4804dSStefan Eßer 26744d4804dSStefan Eßer // If this wasn't here, the line number would be off. 268252884aeSStefan Eßer l->line += (l->i != 0 && l->buf[l->i - 1] == '\n'); 269252884aeSStefan Eßer 27044d4804dSStefan Eßer // If the last token was EOF, someone called this one too many times. 27150696a6eSStefan Eßer if (BC_ERR(l->last == BC_LEX_EOF)) bc_lex_err(l, BC_ERR_PARSE_EOF); 272252884aeSStefan Eßer 273252884aeSStefan Eßer l->t = BC_LEX_EOF; 274252884aeSStefan Eßer 27544d4804dSStefan Eßer // We are done if this is true. 276252884aeSStefan Eßer if (l->i == l->len) return; 277252884aeSStefan Eßer 278252884aeSStefan Eßer // Loop until failure or we don't have whitespace. This 279252884aeSStefan Eßer // is so the parser doesn't get inundated with whitespace. 280252884aeSStefan Eßer do { 281252884aeSStefan Eßer vm.next(l); 282252884aeSStefan Eßer } while (l->t == BC_LEX_WHITESPACE); 283252884aeSStefan Eßer } 284252884aeSStefan Eßer 28544d4804dSStefan Eßer /** 28644d4804dSStefan Eßer * Updates the buffer and len so that they are not invalidated when the stdin 28744d4804dSStefan Eßer * buffer grows. 28844d4804dSStefan Eßer * @param l The lexer. 28944d4804dSStefan Eßer * @param text The text. 29044d4804dSStefan Eßer * @param len The length of the text. 29144d4804dSStefan Eßer */ 29244d4804dSStefan Eßer static void bc_lex_fixText(BcLex *l, const char *text, size_t len) { 293252884aeSStefan Eßer l->buf = text; 29444d4804dSStefan Eßer l->len = len; 29544d4804dSStefan Eßer } 29644d4804dSStefan Eßer 29744d4804dSStefan Eßer bool bc_lex_readLine(BcLex *l) { 29844d4804dSStefan Eßer 299*10041e99SStefan Eßer bool good; 300*10041e99SStefan Eßer 301*10041e99SStefan Eßer // These are reversed because they should be already locked, but 302*10041e99SStefan Eßer // bc_vm_readLine() needs them to be unlocked. 303*10041e99SStefan Eßer BC_SIG_UNLOCK; 304*10041e99SStefan Eßer 305*10041e99SStefan Eßer good = bc_vm_readLine(false); 306*10041e99SStefan Eßer 307*10041e99SStefan Eßer BC_SIG_LOCK; 30844d4804dSStefan Eßer 30944d4804dSStefan Eßer bc_lex_fixText(l, vm.buffer.v, vm.buffer.len - 1); 31044d4804dSStefan Eßer 31144d4804dSStefan Eßer return good; 31244d4804dSStefan Eßer } 31344d4804dSStefan Eßer 31444d4804dSStefan Eßer void bc_lex_text(BcLex *l, const char *text, bool is_stdin) { 315*10041e99SStefan Eßer 316*10041e99SStefan Eßer BC_SIG_ASSERT_LOCKED; 317*10041e99SStefan Eßer 31844d4804dSStefan Eßer assert(l != NULL && text != NULL); 319*10041e99SStefan Eßer 32044d4804dSStefan Eßer bc_lex_fixText(l, text, strlen(text)); 321252884aeSStefan Eßer l->i = 0; 322252884aeSStefan Eßer l->t = l->last = BC_LEX_INVALID; 32344d4804dSStefan Eßer l->is_stdin = is_stdin; 324*10041e99SStefan Eßer 325252884aeSStefan Eßer bc_lex_next(l); 326252884aeSStefan Eßer } 327