150696a6eSStefan Eßer /* 250696a6eSStefan Eßer * ***************************************************************************** 350696a6eSStefan Eßer * 450696a6eSStefan Eßer * SPDX-License-Identifier: BSD-2-Clause 550696a6eSStefan Eßer * 6d101cdd6SStefan Eßer * Copyright (c) 2018-2023 Gavin D. Howard and contributors. 750696a6eSStefan Eßer * 850696a6eSStefan Eßer * Redistribution and use in source and binary forms, with or without 950696a6eSStefan Eßer * modification, are permitted provided that the following conditions are met: 1050696a6eSStefan Eßer * 1150696a6eSStefan Eßer * * Redistributions of source code must retain the above copyright notice, this 1250696a6eSStefan Eßer * list of conditions and the following disclaimer. 1350696a6eSStefan Eßer * 1450696a6eSStefan Eßer * * Redistributions in binary form must reproduce the above copyright notice, 1550696a6eSStefan Eßer * this list of conditions and the following disclaimer in the documentation 1650696a6eSStefan Eßer * and/or other materials provided with the distribution. 1750696a6eSStefan Eßer * 1850696a6eSStefan Eßer * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 1950696a6eSStefan Eßer * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2050696a6eSStefan Eßer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2150696a6eSStefan Eßer * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 2250696a6eSStefan Eßer * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2350696a6eSStefan Eßer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2450696a6eSStefan Eßer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2550696a6eSStefan Eßer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2650696a6eSStefan Eßer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2750696a6eSStefan Eßer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2850696a6eSStefan Eßer * POSSIBILITY OF SUCH DAMAGE. 2950696a6eSStefan Eßer * 3050696a6eSStefan Eßer * ***************************************************************************** 3150696a6eSStefan Eßer * 3250696a6eSStefan Eßer * The lexer for dc. 3350696a6eSStefan Eßer * 3450696a6eSStefan Eßer */ 3550696a6eSStefan Eßer 3650696a6eSStefan Eßer #if DC_ENABLED 3750696a6eSStefan Eßer 3850696a6eSStefan Eßer #include <ctype.h> 3950696a6eSStefan Eßer 4050696a6eSStefan Eßer #include <dc.h> 4150696a6eSStefan Eßer #include <vm.h> 4250696a6eSStefan Eßer 4378bc019dSStefan Eßer bool 4478bc019dSStefan Eßer dc_lex_negCommand(BcLex* l) 4578bc019dSStefan Eßer { 4650696a6eSStefan Eßer char c = l->buf[l->i]; 4750696a6eSStefan Eßer return !BC_LEX_NUM_CHAR(c, false, false); 4850696a6eSStefan Eßer } 4950696a6eSStefan Eßer 5044d4804dSStefan Eßer /** 5144d4804dSStefan Eßer * Processes a dc command that needs a register. This is where the 5244d4804dSStefan Eßer * extended-register extension is implemented. 5344d4804dSStefan Eßer * @param l The lexer. 5444d4804dSStefan Eßer */ 5578bc019dSStefan Eßer static void 5678bc019dSStefan Eßer dc_lex_register(BcLex* l) 5778bc019dSStefan Eßer { 5844d4804dSStefan Eßer // If extended register is enabled and the character is whitespace... 5978bc019dSStefan Eßer if (DC_X && isspace(l->buf[l->i - 1])) 6078bc019dSStefan Eßer { 6150696a6eSStefan Eßer char c; 6250696a6eSStefan Eßer 6344d4804dSStefan Eßer // Eat the whitespace. 6450696a6eSStefan Eßer bc_lex_whitespace(l); 6550696a6eSStefan Eßer c = l->buf[l->i]; 6650696a6eSStefan Eßer 6744d4804dSStefan Eßer // Check for a letter or underscore. 6844d4804dSStefan Eßer if (BC_ERR(!isalpha(c) && c != '_')) 6978bc019dSStefan Eßer { 7050696a6eSStefan Eßer bc_lex_verr(l, BC_ERR_PARSE_CHAR, c); 7178bc019dSStefan Eßer } 7250696a6eSStefan Eßer 7344d4804dSStefan Eßer // Parse a normal identifier. 7450696a6eSStefan Eßer l->i += 1; 7550696a6eSStefan Eßer bc_lex_name(l); 7650696a6eSStefan Eßer } 7778bc019dSStefan Eßer else 7878bc019dSStefan Eßer { 7944d4804dSStefan Eßer // I don't allow newlines because newlines are used for controlling when 8044d4804dSStefan Eßer // execution happens, and allowing newlines would just be complex. 8144d4804dSStefan Eßer if (BC_ERR(l->buf[l->i - 1] == '\n')) 8278bc019dSStefan Eßer { 8344d4804dSStefan Eßer bc_lex_verr(l, BC_ERR_PARSE_CHAR, l->buf[l->i - 1]); 8478bc019dSStefan Eßer } 8544d4804dSStefan Eßer 8644d4804dSStefan Eßer // Set the lexer string and token. 8710328f8bSStefan Eßer bc_vec_popAll(&l->str); 8850696a6eSStefan Eßer bc_vec_pushByte(&l->str, (uchar) l->buf[l->i - 1]); 8950696a6eSStefan Eßer bc_vec_pushByte(&l->str, '\0'); 9050696a6eSStefan Eßer l->t = BC_LEX_NAME; 9150696a6eSStefan Eßer } 9250696a6eSStefan Eßer } 9350696a6eSStefan Eßer 9444d4804dSStefan Eßer /** 9544d4804dSStefan Eßer * Parses a dc string. Since dc's strings need to check for balanced brackets, 9644d4804dSStefan Eßer * we can't just parse bc and dc strings with different start and end 9744d4804dSStefan Eßer * characters. Oh, and dc strings need to check for escaped brackets. 9844d4804dSStefan Eßer * @param l The lexer. 9944d4804dSStefan Eßer */ 10078bc019dSStefan Eßer static void 10178bc019dSStefan Eßer dc_lex_string(BcLex* l) 10278bc019dSStefan Eßer { 10344d4804dSStefan Eßer size_t depth, nls, i; 10450696a6eSStefan Eßer char c; 10544d4804dSStefan Eßer bool got_more; 10650696a6eSStefan Eßer 10744d4804dSStefan Eßer // Set the token and clear the string. 10850696a6eSStefan Eßer l->t = BC_LEX_STR; 10910328f8bSStefan Eßer bc_vec_popAll(&l->str); 11050696a6eSStefan Eßer 11178bc019dSStefan Eßer do 11278bc019dSStefan Eßer { 11344d4804dSStefan Eßer depth = 1; 11444d4804dSStefan Eßer nls = 0; 11544d4804dSStefan Eßer got_more = false; 11644d4804dSStefan Eßer 117d101cdd6SStefan Eßer assert(l->mode != BC_MODE_STDIN || l->buf == vm->buffer.v); 11844d4804dSStefan Eßer 11944d4804dSStefan Eßer // This is the meat. As long as we don't run into the NUL byte, and we 12044d4804dSStefan Eßer // have "depth", which means we haven't completely balanced brackets 12144d4804dSStefan Eßer // yet, we continue eating the string. 12278bc019dSStefan Eßer for (i = l->i; (c = l->buf[i]) && depth; ++i) 12378bc019dSStefan Eßer { 12444d4804dSStefan Eßer // Check for escaped brackets and set the depths as appropriate. 12578bc019dSStefan Eßer if (c == '\\') 12678bc019dSStefan Eßer { 12750696a6eSStefan Eßer c = l->buf[++i]; 12850696a6eSStefan Eßer if (!c) break; 12950696a6eSStefan Eßer } 13078bc019dSStefan Eßer else 13178bc019dSStefan Eßer { 13250696a6eSStefan Eßer depth += (c == '['); 13350696a6eSStefan Eßer depth -= (c == ']'); 13450696a6eSStefan Eßer } 13550696a6eSStefan Eßer 13644d4804dSStefan Eßer // We want to adjust the line in the lexer as necessary. 13750696a6eSStefan Eßer nls += (c == '\n'); 13850696a6eSStefan Eßer 13950696a6eSStefan Eßer if (depth) bc_vec_push(&l->str, &c); 14050696a6eSStefan Eßer } 14150696a6eSStefan Eßer 14278bc019dSStefan Eßer if (BC_ERR(c == '\0' && depth)) 14378bc019dSStefan Eßer { 144d101cdd6SStefan Eßer if (!vm->eof && l->mode != BC_MODE_FILE) 14578bc019dSStefan Eßer { 14623210c9fSStefan Eßer got_more = bc_lex_readLine(l); 14778bc019dSStefan Eßer } 148d101cdd6SStefan Eßer 149d101cdd6SStefan Eßer if (got_more) 150d101cdd6SStefan Eßer { 151d101cdd6SStefan Eßer bc_vec_popAll(&l->str); 152d101cdd6SStefan Eßer } 15344d4804dSStefan Eßer } 15478bc019dSStefan Eßer } 15578bc019dSStefan Eßer while (got_more && depth); 15644d4804dSStefan Eßer 15744d4804dSStefan Eßer // Obviously, if we didn't balance, that's an error. 15878bc019dSStefan Eßer if (BC_ERR(c == '\0' && depth)) 15978bc019dSStefan Eßer { 16050696a6eSStefan Eßer l->i = i; 16150696a6eSStefan Eßer bc_lex_err(l, BC_ERR_PARSE_STRING); 16250696a6eSStefan Eßer } 16350696a6eSStefan Eßer 16450696a6eSStefan Eßer bc_vec_pushByte(&l->str, '\0'); 16550696a6eSStefan Eßer 16650696a6eSStefan Eßer l->i = i; 16750696a6eSStefan Eßer l->line += nls; 16850696a6eSStefan Eßer } 16950696a6eSStefan Eßer 17044d4804dSStefan Eßer /** 17144d4804dSStefan Eßer * Lexes a dc token. This is the dc implementation of BcLexNext. 17244d4804dSStefan Eßer * @param l The lexer. 17344d4804dSStefan Eßer */ 17478bc019dSStefan Eßer void 17578bc019dSStefan Eßer dc_lex_token(BcLex* l) 17678bc019dSStefan Eßer { 17750696a6eSStefan Eßer char c = l->buf[l->i++], c2; 17850696a6eSStefan Eßer size_t i; 17950696a6eSStefan Eßer 18010041e99SStefan Eßer BC_SIG_ASSERT_LOCKED; 18110041e99SStefan Eßer 18244d4804dSStefan Eßer // If the last token was a command that needs a register, we need to parse a 18344d4804dSStefan Eßer // register, so do so. 18478bc019dSStefan Eßer for (i = 0; i < dc_lex_regs_len; ++i) 18578bc019dSStefan Eßer { 18644d4804dSStefan Eßer // If the token is a register token, take care of it and return. 18778bc019dSStefan Eßer if (l->last == dc_lex_regs[i]) 18878bc019dSStefan Eßer { 18950696a6eSStefan Eßer dc_lex_register(l); 19050696a6eSStefan Eßer return; 19150696a6eSStefan Eßer } 19250696a6eSStefan Eßer } 19350696a6eSStefan Eßer 19444d4804dSStefan Eßer // These lines are for tokens that easily correspond to one character. We 19544d4804dSStefan Eßer // just set the token. 19650696a6eSStefan Eßer if (c >= '"' && c <= '~' && 19750696a6eSStefan Eßer (l->t = dc_lex_tokens[(c - '"')]) != BC_LEX_INVALID) 19850696a6eSStefan Eßer { 19950696a6eSStefan Eßer return; 20050696a6eSStefan Eßer } 20150696a6eSStefan Eßer 20244d4804dSStefan Eßer // This is the workhorse of the lexer when more complicated things are 20344d4804dSStefan Eßer // needed. 20478bc019dSStefan Eßer switch (c) 20578bc019dSStefan Eßer { 20650696a6eSStefan Eßer case '\0': 20750696a6eSStefan Eßer case '\n': 20850696a6eSStefan Eßer case '\t': 20950696a6eSStefan Eßer case '\v': 21050696a6eSStefan Eßer case '\f': 21150696a6eSStefan Eßer case '\r': 21250696a6eSStefan Eßer case ' ': 21350696a6eSStefan Eßer { 21450696a6eSStefan Eßer bc_lex_commonTokens(l, c); 21550696a6eSStefan Eßer break; 21650696a6eSStefan Eßer } 21750696a6eSStefan Eßer 21844d4804dSStefan Eßer // We don't have the ! command, so we always expect certain things 21944d4804dSStefan Eßer // after the exclamation point. 22050696a6eSStefan Eßer case '!': 22150696a6eSStefan Eßer { 22250696a6eSStefan Eßer c2 = l->buf[l->i]; 22350696a6eSStefan Eßer 22450696a6eSStefan Eßer if (c2 == '=') l->t = BC_LEX_OP_REL_NE; 22550696a6eSStefan Eßer else if (c2 == '<') l->t = BC_LEX_OP_REL_LE; 22650696a6eSStefan Eßer else if (c2 == '>') l->t = BC_LEX_OP_REL_GE; 22750696a6eSStefan Eßer else bc_lex_invalidChar(l, c); 22850696a6eSStefan Eßer 22950696a6eSStefan Eßer l->i += 1; 23044d4804dSStefan Eßer 23150696a6eSStefan Eßer break; 23250696a6eSStefan Eßer } 23350696a6eSStefan Eßer 23450696a6eSStefan Eßer case '#': 23550696a6eSStefan Eßer { 23650696a6eSStefan Eßer bc_lex_lineComment(l); 23750696a6eSStefan Eßer break; 23850696a6eSStefan Eßer } 23950696a6eSStefan Eßer 24050696a6eSStefan Eßer case '.': 24150696a6eSStefan Eßer { 24250696a6eSStefan Eßer c2 = l->buf[l->i]; 24344d4804dSStefan Eßer 24444d4804dSStefan Eßer // If the character after is a number, this dot is part of a number. 24544d4804dSStefan Eßer // Otherwise, it's the BSD dot (equivalent to last). 24650696a6eSStefan Eßer if (BC_NO_ERR(BC_LEX_NUM_CHAR(c2, true, false))) 24778bc019dSStefan Eßer { 24850696a6eSStefan Eßer bc_lex_number(l, c); 24978bc019dSStefan Eßer } 25050696a6eSStefan Eßer else bc_lex_invalidChar(l, c); 25144d4804dSStefan Eßer 25250696a6eSStefan Eßer break; 25350696a6eSStefan Eßer } 25450696a6eSStefan Eßer 25550696a6eSStefan Eßer case '0': 25650696a6eSStefan Eßer case '1': 25750696a6eSStefan Eßer case '2': 25850696a6eSStefan Eßer case '3': 25950696a6eSStefan Eßer case '4': 26050696a6eSStefan Eßer case '5': 26150696a6eSStefan Eßer case '6': 26250696a6eSStefan Eßer case '7': 26350696a6eSStefan Eßer case '8': 26450696a6eSStefan Eßer case '9': 26550696a6eSStefan Eßer case 'A': 26650696a6eSStefan Eßer case 'B': 26750696a6eSStefan Eßer case 'C': 26850696a6eSStefan Eßer case 'D': 26950696a6eSStefan Eßer case 'E': 27050696a6eSStefan Eßer case 'F': 27150696a6eSStefan Eßer { 27250696a6eSStefan Eßer bc_lex_number(l, c); 27350696a6eSStefan Eßer break; 27450696a6eSStefan Eßer } 27550696a6eSStefan Eßer 276d43fa8efSStefan Eßer case 'g': 277d43fa8efSStefan Eßer { 278d43fa8efSStefan Eßer c2 = l->buf[l->i]; 279d43fa8efSStefan Eßer 280d43fa8efSStefan Eßer if (c2 == 'l') l->t = BC_LEX_KW_LINE_LENGTH; 281d43fa8efSStefan Eßer else if (c2 == 'z') l->t = BC_LEX_KW_LEADING_ZERO; 282d43fa8efSStefan Eßer else bc_lex_invalidChar(l, c2); 283d43fa8efSStefan Eßer 284d43fa8efSStefan Eßer l->i += 1; 285d43fa8efSStefan Eßer 286d43fa8efSStefan Eßer break; 287d43fa8efSStefan Eßer } 288d43fa8efSStefan Eßer 28950696a6eSStefan Eßer case '[': 29050696a6eSStefan Eßer { 29150696a6eSStefan Eßer dc_lex_string(l); 29250696a6eSStefan Eßer break; 29350696a6eSStefan Eßer } 29450696a6eSStefan Eßer 29550696a6eSStefan Eßer default: 29650696a6eSStefan Eßer { 29750696a6eSStefan Eßer bc_lex_invalidChar(l, c); 29850696a6eSStefan Eßer } 29950696a6eSStefan Eßer } 30050696a6eSStefan Eßer } 30150696a6eSStefan Eßer #endif // DC_ENABLED 302