150696a6eSStefan Eßer /* 250696a6eSStefan Eßer * ***************************************************************************** 350696a6eSStefan Eßer * 450696a6eSStefan Eßer * SPDX-License-Identifier: BSD-2-Clause 550696a6eSStefan Eßer * 610328f8bSStefan Eßer * Copyright (c) 2018-2021 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 parser for bc. 3350696a6eSStefan Eßer * 3450696a6eSStefan Eßer */ 3550696a6eSStefan Eßer 3650696a6eSStefan Eßer #if BC_ENABLED 3750696a6eSStefan Eßer 3850696a6eSStefan Eßer #include <assert.h> 3950696a6eSStefan Eßer #include <stdbool.h> 4050696a6eSStefan Eßer #include <stdlib.h> 4150696a6eSStefan Eßer #include <string.h> 4250696a6eSStefan Eßer 4350696a6eSStefan Eßer #include <setjmp.h> 4450696a6eSStefan Eßer 4550696a6eSStefan Eßer #include <bc.h> 4650696a6eSStefan Eßer #include <num.h> 4750696a6eSStefan Eßer #include <vm.h> 4850696a6eSStefan Eßer 4944d4804dSStefan Eßer // Before you embark on trying to understand this code, have you read the 5044d4804dSStefan Eßer // Development manual (manuals/development.md) and the comment in include/bc.h 5144d4804dSStefan Eßer // yet? No? Do that first. I'm serious. 5244d4804dSStefan Eßer // 5344d4804dSStefan Eßer // The reason is because this file holds the most sensitive and finicky code in 5444d4804dSStefan Eßer // the entire codebase. Even getting history to work on Windows was nothing 5544d4804dSStefan Eßer // compared to this. This is where dreams go to die, where dragons live, and 5644d4804dSStefan Eßer // from which Ken Thompson himself would flee. 5744d4804dSStefan Eßer 5850696a6eSStefan Eßer static void bc_parse_else(BcParse *p); 5950696a6eSStefan Eßer static void bc_parse_stmt(BcParse *p); 6050696a6eSStefan Eßer static BcParseStatus bc_parse_expr_err(BcParse *p, uint8_t flags, 6150696a6eSStefan Eßer BcParseNext next); 6244d4804dSStefan Eßer static void bc_parse_expr_status(BcParse *p, uint8_t flags, BcParseNext next); 6350696a6eSStefan Eßer 6444d4804dSStefan Eßer /** 6544d4804dSStefan Eßer * Returns true if an instruction could only have come from a "leaf" expression. 6644d4804dSStefan Eßer * For more on what leaf expressions are, read the comment for BC_PARSE_LEAF(). 6744d4804dSStefan Eßer * @param t The instruction to test. 6844d4804dSStefan Eßer */ 6950696a6eSStefan Eßer static bool bc_parse_inst_isLeaf(BcInst t) { 7050696a6eSStefan Eßer return (t >= BC_INST_NUM && t <= BC_INST_MAXSCALE) || 7150696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH 7250696a6eSStefan Eßer t == BC_INST_TRUNC || 7350696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 7450696a6eSStefan Eßer t <= BC_INST_DEC; 7550696a6eSStefan Eßer } 7650696a6eSStefan Eßer 7744d4804dSStefan Eßer /** 7844d4804dSStefan Eßer * Returns true if the *previous* token was a delimiter. A delimiter is anything 7944d4804dSStefan Eßer * that can legally end a statement. In bc's case, it could be a newline, a 8044d4804dSStefan Eßer * semicolon, and a brace in certain cases. 8144d4804dSStefan Eßer * @param p The parser. 8244d4804dSStefan Eßer */ 8350696a6eSStefan Eßer static bool bc_parse_isDelimiter(const BcParse *p) { 8450696a6eSStefan Eßer 8550696a6eSStefan Eßer BcLexType t = p->l.t; 8644d4804dSStefan Eßer bool good; 8750696a6eSStefan Eßer 8844d4804dSStefan Eßer // If it's an obvious delimiter, say so. 8950696a6eSStefan Eßer if (BC_PARSE_DELIMITER(t)) return true; 9050696a6eSStefan Eßer 9144d4804dSStefan Eßer good = false; 9244d4804dSStefan Eßer 9344d4804dSStefan Eßer // If the current token is a keyword, then...beware. That means that we need 9444d4804dSStefan Eßer // to check for a "dangling" else, where there was no brace-delimited block 9544d4804dSStefan Eßer // on the previous if. 9650696a6eSStefan Eßer if (t == BC_LEX_KW_ELSE) { 9750696a6eSStefan Eßer 9850696a6eSStefan Eßer size_t i; 9950696a6eSStefan Eßer uint16_t *fptr = NULL, flags = BC_PARSE_FLAG_ELSE; 10050696a6eSStefan Eßer 10144d4804dSStefan Eßer // As long as going up the stack is valid for a dangling else, keep on. 10250696a6eSStefan Eßer for (i = 0; i < p->flags.len && BC_PARSE_BLOCK_STMT(flags); ++i) { 10350696a6eSStefan Eßer 10450696a6eSStefan Eßer fptr = bc_vec_item_rev(&p->flags, i); 10550696a6eSStefan Eßer flags = *fptr; 10650696a6eSStefan Eßer 10744d4804dSStefan Eßer // If we need a brace and don't have one, then we don't have a 10844d4804dSStefan Eßer // delimiter. 10950696a6eSStefan Eßer if ((flags & BC_PARSE_FLAG_BRACE) && p->l.last != BC_LEX_RBRACE) 11050696a6eSStefan Eßer return false; 11150696a6eSStefan Eßer } 11250696a6eSStefan Eßer 11344d4804dSStefan Eßer // Oh, and we had also better have an if statement somewhere. 11450696a6eSStefan Eßer good = ((flags & BC_PARSE_FLAG_IF) != 0); 11550696a6eSStefan Eßer } 11650696a6eSStefan Eßer else if (t == BC_LEX_RBRACE) { 11750696a6eSStefan Eßer 11850696a6eSStefan Eßer size_t i; 11950696a6eSStefan Eßer 12044d4804dSStefan Eßer // Since we have a brace, we need to just check if a brace was needed. 12150696a6eSStefan Eßer for (i = 0; !good && i < p->flags.len; ++i) { 12250696a6eSStefan Eßer uint16_t *fptr = bc_vec_item_rev(&p->flags, i); 12350696a6eSStefan Eßer good = (((*fptr) & BC_PARSE_FLAG_BRACE) != 0); 12450696a6eSStefan Eßer } 12550696a6eSStefan Eßer } 12650696a6eSStefan Eßer 12750696a6eSStefan Eßer return good; 12850696a6eSStefan Eßer } 12950696a6eSStefan Eßer 13044d4804dSStefan Eßer /** 13144d4804dSStefan Eßer * Sets a previously defined exit label. What are labels? See the bc Parsing 13244d4804dSStefan Eßer * section of the Development manual (manuals/development.md). 13344d4804dSStefan Eßer * @param p The parser. 13444d4804dSStefan Eßer */ 13550696a6eSStefan Eßer static void bc_parse_setLabel(BcParse *p) { 13650696a6eSStefan Eßer 13750696a6eSStefan Eßer BcFunc *func = p->func; 13850696a6eSStefan Eßer BcInstPtr *ip = bc_vec_top(&p->exits); 13950696a6eSStefan Eßer size_t *label; 14050696a6eSStefan Eßer 14150696a6eSStefan Eßer assert(func == bc_vec_item(&p->prog->fns, p->fidx)); 14250696a6eSStefan Eßer 14344d4804dSStefan Eßer // Set the preallocated label to the correct index. 14450696a6eSStefan Eßer label = bc_vec_item(&func->labels, ip->idx); 14550696a6eSStefan Eßer *label = func->code.len; 14650696a6eSStefan Eßer 14744d4804dSStefan Eßer // Now, we don't need the exit label; it is done. 14850696a6eSStefan Eßer bc_vec_pop(&p->exits); 14950696a6eSStefan Eßer } 15050696a6eSStefan Eßer 15144d4804dSStefan Eßer /** 15244d4804dSStefan Eßer * Creates a label and sets it to idx. If this is an exit label, then idx is 15344d4804dSStefan Eßer * actually invalid, but it doesn't matter because it will be fixed by 15444d4804dSStefan Eßer * bc_parse_setLabel() later. 15544d4804dSStefan Eßer * @param p The parser. 15644d4804dSStefan Eßer * @param idx The index of the label. 15744d4804dSStefan Eßer */ 15850696a6eSStefan Eßer static void bc_parse_createLabel(BcParse *p, size_t idx) { 15950696a6eSStefan Eßer bc_vec_push(&p->func->labels, &idx); 16050696a6eSStefan Eßer } 16150696a6eSStefan Eßer 16244d4804dSStefan Eßer /** 16344d4804dSStefan Eßer * Creates a conditional label. Unlike an exit label, this label is set at 16444d4804dSStefan Eßer * creation time because it comes *before* the code that will target it. 16544d4804dSStefan Eßer * @param p The parser. 16644d4804dSStefan Eßer * @param idx The index of the label. 16744d4804dSStefan Eßer */ 16850696a6eSStefan Eßer static void bc_parse_createCondLabel(BcParse *p, size_t idx) { 16950696a6eSStefan Eßer bc_parse_createLabel(p, p->func->code.len); 17050696a6eSStefan Eßer bc_vec_push(&p->conds, &idx); 17150696a6eSStefan Eßer } 17250696a6eSStefan Eßer 17344d4804dSStefan Eßer /* 17444d4804dSStefan Eßer * Creates an exit label to be filled in later by bc_parse_setLabel(). Also, why 17544d4804dSStefan Eßer * create a label to be filled in later? Because exit labels are meant to be 17644d4804dSStefan Eßer * targeted by code that comes *before* the label. Since we have to parse that 17744d4804dSStefan Eßer * code first, and don't know how long it will be, we need to just make sure to 17844d4804dSStefan Eßer * reserve a slot to be filled in later when we know. 17944d4804dSStefan Eßer * 18044d4804dSStefan Eßer * By the way, this uses BcInstPtr because it was convenient. The field idx 18144d4804dSStefan Eßer * holds the index, and the field func holds the loop boolean. 18244d4804dSStefan Eßer * 18344d4804dSStefan Eßer * @param p The parser. 18444d4804dSStefan Eßer * @param idx The index of the label's position. 18544d4804dSStefan Eßer * @param loop True if the exit label is for a loop or not. 18644d4804dSStefan Eßer */ 18750696a6eSStefan Eßer static void bc_parse_createExitLabel(BcParse *p, size_t idx, bool loop) { 18850696a6eSStefan Eßer 18950696a6eSStefan Eßer BcInstPtr ip; 19050696a6eSStefan Eßer 19150696a6eSStefan Eßer assert(p->func == bc_vec_item(&p->prog->fns, p->fidx)); 19250696a6eSStefan Eßer 19350696a6eSStefan Eßer ip.func = loop; 19450696a6eSStefan Eßer ip.idx = idx; 19550696a6eSStefan Eßer ip.len = 0; 19650696a6eSStefan Eßer 19750696a6eSStefan Eßer bc_vec_push(&p->exits, &ip); 19850696a6eSStefan Eßer bc_parse_createLabel(p, SIZE_MAX); 19950696a6eSStefan Eßer } 20050696a6eSStefan Eßer 20144d4804dSStefan Eßer /** 20244d4804dSStefan Eßer * Pops the correct operators off of the operator stack based on the current 20344d4804dSStefan Eßer * operator. This is because of the Shunting-Yard algorithm. Lower prec means 20444d4804dSStefan Eßer * higher precedence. 20544d4804dSStefan Eßer * @param p The parser. 20644d4804dSStefan Eßer * @param type The operator. 20744d4804dSStefan Eßer * @param start The previous start of the operator stack. For more 20844d4804dSStefan Eßer * information, see the bc Parsing section of the Development 20944d4804dSStefan Eßer * manual (manuals/development.md). 21044d4804dSStefan Eßer * @param nexprs A pointer to the current number of expressions that have not 21144d4804dSStefan Eßer * been consumed yet. This is an IN and OUT parameter. 21244d4804dSStefan Eßer */ 21350696a6eSStefan Eßer static void bc_parse_operator(BcParse *p, BcLexType type, 21450696a6eSStefan Eßer size_t start, size_t *nexprs) 21550696a6eSStefan Eßer { 21650696a6eSStefan Eßer BcLexType t; 21750696a6eSStefan Eßer uchar l, r = BC_PARSE_OP_PREC(type); 21850696a6eSStefan Eßer uchar left = BC_PARSE_OP_LEFT(type); 21950696a6eSStefan Eßer 22044d4804dSStefan Eßer // While we haven't hit the stop point yet. 22150696a6eSStefan Eßer while (p->ops.len > start) { 22250696a6eSStefan Eßer 22344d4804dSStefan Eßer // Get the top operator. 22450696a6eSStefan Eßer t = BC_PARSE_TOP_OP(p); 22544d4804dSStefan Eßer 22644d4804dSStefan Eßer // If it's a right paren, we have reached the end of whatever expression 22744d4804dSStefan Eßer // this is no matter what. 22850696a6eSStefan Eßer if (t == BC_LEX_LPAREN) break; 22950696a6eSStefan Eßer 23044d4804dSStefan Eßer // Break for precedence. Precedence operates differently on left and 23144d4804dSStefan Eßer // right associativity, by the way. A left associative operator that 23244d4804dSStefan Eßer // matches the current precedence should take priority, but a right 23344d4804dSStefan Eßer // associative operator should not. 23450696a6eSStefan Eßer l = BC_PARSE_OP_PREC(t); 23550696a6eSStefan Eßer if (l >= r && (l != r || !left)) break; 23650696a6eSStefan Eßer 23744d4804dSStefan Eßer // Do the housekeeping. In particular, make sure to note that one 23844d4804dSStefan Eßer // expression was consumed. (Two were, but another was added.) 23950696a6eSStefan Eßer bc_parse_push(p, BC_PARSE_TOKEN_INST(t)); 24050696a6eSStefan Eßer bc_vec_pop(&p->ops); 24150696a6eSStefan Eßer *nexprs -= !BC_PARSE_OP_PREFIX(t); 24250696a6eSStefan Eßer } 24350696a6eSStefan Eßer 24450696a6eSStefan Eßer bc_vec_push(&p->ops, &type); 24550696a6eSStefan Eßer } 24650696a6eSStefan Eßer 24744d4804dSStefan Eßer /** 24844d4804dSStefan Eßer * Parses a right paren. In the Shunting-Yard algorithm, it needs to be put on 24944d4804dSStefan Eßer * the operator stack. But before that, it needs to consume whatever operators 25044d4804dSStefan Eßer * there are until it hits a left paren. 25144d4804dSStefan Eßer * @param p The parser. 25244d4804dSStefan Eßer * @param nexprs A pointer to the current number of expressions that have not 25344d4804dSStefan Eßer * been consumed yet. This is an IN and OUT parameter. 25444d4804dSStefan Eßer */ 25544d4804dSStefan Eßer static void bc_parse_rightParen(BcParse *p, size_t *nexprs) { 25650696a6eSStefan Eßer 25750696a6eSStefan Eßer BcLexType top; 25850696a6eSStefan Eßer 25944d4804dSStefan Eßer // Consume operators until a left paren. 26050696a6eSStefan Eßer while ((top = BC_PARSE_TOP_OP(p)) != BC_LEX_LPAREN) { 26150696a6eSStefan Eßer bc_parse_push(p, BC_PARSE_TOKEN_INST(top)); 26250696a6eSStefan Eßer bc_vec_pop(&p->ops); 26344d4804dSStefan Eßer *nexprs -= !BC_PARSE_OP_PREFIX(top); 26450696a6eSStefan Eßer } 26550696a6eSStefan Eßer 26644d4804dSStefan Eßer // We need to pop the left paren as well. 26750696a6eSStefan Eßer bc_vec_pop(&p->ops); 26850696a6eSStefan Eßer 26944d4804dSStefan Eßer // Oh, and we also want the next token. 27050696a6eSStefan Eßer bc_lex_next(&p->l); 27150696a6eSStefan Eßer } 27250696a6eSStefan Eßer 27344d4804dSStefan Eßer /** 27444d4804dSStefan Eßer * Parses function arguments. 27544d4804dSStefan Eßer * @param p The parser. 27644d4804dSStefan Eßer * @param flags Flags restricting what kind of expressions the arguments can 27744d4804dSStefan Eßer * be. 27844d4804dSStefan Eßer */ 27944d4804dSStefan Eßer static void bc_parse_args(BcParse *p, uint8_t flags) { 28050696a6eSStefan Eßer 28150696a6eSStefan Eßer bool comma = false; 28244d4804dSStefan Eßer size_t nargs; 28350696a6eSStefan Eßer 28450696a6eSStefan Eßer bc_lex_next(&p->l); 28550696a6eSStefan Eßer 28644d4804dSStefan Eßer // Print and comparison operators not allowed. Well, comparison operators 28744d4804dSStefan Eßer // only for POSIX. But we do allow arrays, and we *must* get a value. 28850696a6eSStefan Eßer flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); 28950696a6eSStefan Eßer flags |= (BC_PARSE_ARRAY | BC_PARSE_NEEDVAL); 29050696a6eSStefan Eßer 29144d4804dSStefan Eßer // Count the arguments and parse them. 29244d4804dSStefan Eßer for (nargs = 0; p->l.t != BC_LEX_RPAREN; ++nargs) { 29350696a6eSStefan Eßer 29444d4804dSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_arg); 29550696a6eSStefan Eßer 29650696a6eSStefan Eßer comma = (p->l.t == BC_LEX_COMMA); 29750696a6eSStefan Eßer if (comma) bc_lex_next(&p->l); 29850696a6eSStefan Eßer } 29950696a6eSStefan Eßer 30044d4804dSStefan Eßer // An ending comma is FAIL. 30150696a6eSStefan Eßer if (BC_ERR(comma)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 30244d4804dSStefan Eßer 30344d4804dSStefan Eßer // Now do the call with the number of arguments. 30450696a6eSStefan Eßer bc_parse_push(p, BC_INST_CALL); 30544d4804dSStefan Eßer bc_parse_pushIndex(p, nargs); 30650696a6eSStefan Eßer } 30750696a6eSStefan Eßer 30844d4804dSStefan Eßer /** 30944d4804dSStefan Eßer * Parses a function call. 31044d4804dSStefan Eßer * @param p The parser. 31144d4804dSStefan Eßer * @param flags Flags restricting what kind of expressions the arguments can 31244d4804dSStefan Eßer * be. 31344d4804dSStefan Eßer */ 31450696a6eSStefan Eßer static void bc_parse_call(BcParse *p, const char *name, uint8_t flags) { 31550696a6eSStefan Eßer 31650696a6eSStefan Eßer size_t idx; 31750696a6eSStefan Eßer 31844d4804dSStefan Eßer bc_parse_args(p, flags); 31950696a6eSStefan Eßer 32044d4804dSStefan Eßer // We just assert this because bc_parse_args() should 32150696a6eSStefan Eßer // ensure that the next token is what it should be. 32250696a6eSStefan Eßer assert(p->l.t == BC_LEX_RPAREN); 32350696a6eSStefan Eßer 32450696a6eSStefan Eßer // We cannot use bc_program_insertFunc() here 32550696a6eSStefan Eßer // because it will overwrite an existing function. 32650696a6eSStefan Eßer idx = bc_map_index(&p->prog->fn_map, name); 32750696a6eSStefan Eßer 32844d4804dSStefan Eßer // The function does not exist yet. Create a space for it. If the user does 32944d4804dSStefan Eßer // not define it, it's a *runtime* error, not a parse error. 33050696a6eSStefan Eßer if (idx == BC_VEC_INVALID_IDX) { 33150696a6eSStefan Eßer 33250696a6eSStefan Eßer BC_SIG_LOCK; 33350696a6eSStefan Eßer 33450696a6eSStefan Eßer idx = bc_program_insertFunc(p->prog, name); 33550696a6eSStefan Eßer 33650696a6eSStefan Eßer BC_SIG_UNLOCK; 33750696a6eSStefan Eßer 33850696a6eSStefan Eßer assert(idx != BC_VEC_INVALID_IDX); 33950696a6eSStefan Eßer 34050696a6eSStefan Eßer // Make sure that this pointer was not invalidated. 34150696a6eSStefan Eßer p->func = bc_vec_item(&p->prog->fns, p->fidx); 34250696a6eSStefan Eßer } 34344d4804dSStefan Eßer // The function exists, so set the right function index. 34450696a6eSStefan Eßer else idx = ((BcId*) bc_vec_item(&p->prog->fn_map, idx))->idx; 34550696a6eSStefan Eßer 34650696a6eSStefan Eßer bc_parse_pushIndex(p, idx); 34750696a6eSStefan Eßer 34844d4804dSStefan Eßer // Make sure to get the next token. 34950696a6eSStefan Eßer bc_lex_next(&p->l); 35050696a6eSStefan Eßer } 35150696a6eSStefan Eßer 35244d4804dSStefan Eßer /** 35344d4804dSStefan Eßer * Parses a name/identifier-based expression. It could be a variable, an array 35444d4804dSStefan Eßer * element, an array itself (for function arguments), a function call, etc. 35544d4804dSStefan Eßer * 35644d4804dSStefan Eßer */ 35750696a6eSStefan Eßer static void bc_parse_name(BcParse *p, BcInst *type, 35850696a6eSStefan Eßer bool *can_assign, uint8_t flags) 35950696a6eSStefan Eßer { 36050696a6eSStefan Eßer char *name; 36150696a6eSStefan Eßer 36250696a6eSStefan Eßer BC_SIG_LOCK; 36350696a6eSStefan Eßer 36444d4804dSStefan Eßer // We want a copy of the name since the lexer might overwrite its copy. 36550696a6eSStefan Eßer name = bc_vm_strdup(p->l.str.v); 36650696a6eSStefan Eßer 36750696a6eSStefan Eßer BC_SETJMP_LOCKED(err); 36850696a6eSStefan Eßer 36950696a6eSStefan Eßer BC_SIG_UNLOCK; 37050696a6eSStefan Eßer 37144d4804dSStefan Eßer // We need the next token to see if it's just a variable or something more. 37250696a6eSStefan Eßer bc_lex_next(&p->l); 37350696a6eSStefan Eßer 37444d4804dSStefan Eßer // Array element or array. 37550696a6eSStefan Eßer if (p->l.t == BC_LEX_LBRACKET) { 37650696a6eSStefan Eßer 37750696a6eSStefan Eßer bc_lex_next(&p->l); 37850696a6eSStefan Eßer 37944d4804dSStefan Eßer // Array only. This has to be a function parameter. 38050696a6eSStefan Eßer if (p->l.t == BC_LEX_RBRACKET) { 38150696a6eSStefan Eßer 38244d4804dSStefan Eßer // Error if arrays are not allowed. 38350696a6eSStefan Eßer if (BC_ERR(!(flags & BC_PARSE_ARRAY))) 38450696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 38550696a6eSStefan Eßer 38650696a6eSStefan Eßer *type = BC_INST_ARRAY; 38750696a6eSStefan Eßer *can_assign = false; 38850696a6eSStefan Eßer } 38950696a6eSStefan Eßer else { 39050696a6eSStefan Eßer 39144d4804dSStefan Eßer // If we are here, we have an array element. We need to set the 39244d4804dSStefan Eßer // expression parsing flags. 39350696a6eSStefan Eßer uint8_t flags2 = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) | 39450696a6eSStefan Eßer BC_PARSE_NEEDVAL; 39550696a6eSStefan Eßer 39650696a6eSStefan Eßer bc_parse_expr_status(p, flags2, bc_parse_next_elem); 39750696a6eSStefan Eßer 39844d4804dSStefan Eßer // The next token *must* be a right bracket. 39950696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RBRACKET)) 40050696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 40150696a6eSStefan Eßer 40250696a6eSStefan Eßer *type = BC_INST_ARRAY_ELEM; 40350696a6eSStefan Eßer *can_assign = true; 40450696a6eSStefan Eßer } 40550696a6eSStefan Eßer 40644d4804dSStefan Eßer // Make sure to get the next token. 40750696a6eSStefan Eßer bc_lex_next(&p->l); 40850696a6eSStefan Eßer 40944d4804dSStefan Eßer // Push the instruction and the name of the identifier. 41050696a6eSStefan Eßer bc_parse_push(p, *type); 41150696a6eSStefan Eßer bc_parse_pushName(p, name, false); 41250696a6eSStefan Eßer } 41350696a6eSStefan Eßer else if (p->l.t == BC_LEX_LPAREN) { 41450696a6eSStefan Eßer 41544d4804dSStefan Eßer // We are parsing a function call; error if not allowed. 41650696a6eSStefan Eßer if (BC_ERR(flags & BC_PARSE_NOCALL)) 41750696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 41850696a6eSStefan Eßer 41950696a6eSStefan Eßer *type = BC_INST_CALL; 42050696a6eSStefan Eßer *can_assign = false; 42150696a6eSStefan Eßer 42250696a6eSStefan Eßer bc_parse_call(p, name, flags); 42350696a6eSStefan Eßer } 42450696a6eSStefan Eßer else { 42544d4804dSStefan Eßer // Just a variable. 42650696a6eSStefan Eßer *type = BC_INST_VAR; 42750696a6eSStefan Eßer *can_assign = true; 42850696a6eSStefan Eßer bc_parse_push(p, BC_INST_VAR); 42950696a6eSStefan Eßer bc_parse_pushName(p, name, true); 43050696a6eSStefan Eßer } 43150696a6eSStefan Eßer 43250696a6eSStefan Eßer err: 43344d4804dSStefan Eßer // Need to make sure to unallocate the name. 43450696a6eSStefan Eßer BC_SIG_MAYLOCK; 43550696a6eSStefan Eßer free(name); 43650696a6eSStefan Eßer BC_LONGJMP_CONT; 43750696a6eSStefan Eßer } 43850696a6eSStefan Eßer 43944d4804dSStefan Eßer /** 44044d4804dSStefan Eßer * Parses a builtin function that takes no arguments. This includes read(), 44144d4804dSStefan Eßer * rand(), maxibase(), maxobase(), maxscale(), and maxrand(). 44244d4804dSStefan Eßer * @param p The parser. 44344d4804dSStefan Eßer * @param inst The instruction corresponding to the builtin. 44444d4804dSStefan Eßer */ 44550696a6eSStefan Eßer static void bc_parse_noArgBuiltin(BcParse *p, BcInst inst) { 44650696a6eSStefan Eßer 44744d4804dSStefan Eßer // Must have a left paren. 44850696a6eSStefan Eßer bc_lex_next(&p->l); 44950696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 45050696a6eSStefan Eßer 45144d4804dSStefan Eßer // Must have a right paren. 45250696a6eSStefan Eßer bc_lex_next(&p->l); 45350696a6eSStefan Eßer if ((p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 45450696a6eSStefan Eßer 45550696a6eSStefan Eßer bc_parse_push(p, inst); 45650696a6eSStefan Eßer 45750696a6eSStefan Eßer bc_lex_next(&p->l); 45850696a6eSStefan Eßer } 45950696a6eSStefan Eßer 46044d4804dSStefan Eßer /** 46144d4804dSStefan Eßer * Parses a builtin function that takes 1 argument. This includes length(), 46244d4804dSStefan Eßer * sqrt(), abs(), scale(), and irand(). 46344d4804dSStefan Eßer * @param p The parser. 46444d4804dSStefan Eßer * @param type The lex token. 46544d4804dSStefan Eßer * @param flags The expression parsing flags for parsing the argument. 46644d4804dSStefan Eßer * @param prev An out parameter; the previous instruction pointer. 46744d4804dSStefan Eßer */ 46850696a6eSStefan Eßer static void bc_parse_builtin(BcParse *p, BcLexType type, 46950696a6eSStefan Eßer uint8_t flags, BcInst *prev) 47050696a6eSStefan Eßer { 47144d4804dSStefan Eßer // Must have a left paren. 47250696a6eSStefan Eßer bc_lex_next(&p->l); 47350696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) 47450696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 47550696a6eSStefan Eßer 47650696a6eSStefan Eßer bc_lex_next(&p->l); 47750696a6eSStefan Eßer 47844d4804dSStefan Eßer // Change the flags as needed for parsing the argument. 47950696a6eSStefan Eßer flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); 48050696a6eSStefan Eßer flags |= BC_PARSE_NEEDVAL; 48144d4804dSStefan Eßer 48244d4804dSStefan Eßer // Since length can take arrays, we need to specially add that flag. 48350696a6eSStefan Eßer if (type == BC_LEX_KW_LENGTH) flags |= BC_PARSE_ARRAY; 48450696a6eSStefan Eßer 48550696a6eSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_rel); 48650696a6eSStefan Eßer 48744d4804dSStefan Eßer // Must have a right paren. 48850696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RPAREN)) 48950696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 49050696a6eSStefan Eßer 49144d4804dSStefan Eßer // Adjust previous based on the token and push it. 49250696a6eSStefan Eßer *prev = type - BC_LEX_KW_LENGTH + BC_INST_LENGTH; 49350696a6eSStefan Eßer bc_parse_push(p, *prev); 49450696a6eSStefan Eßer 49550696a6eSStefan Eßer bc_lex_next(&p->l); 49650696a6eSStefan Eßer } 49750696a6eSStefan Eßer 49844d4804dSStefan Eßer /** 49944d4804dSStefan Eßer * Parses a builtin function that takes 3 arguments. This includes modexp() and 50044d4804dSStefan Eßer * divmod(). 50144d4804dSStefan Eßer */ 50244d4804dSStefan Eßer static void bc_parse_builtin3(BcParse *p, BcLexType type, 50344d4804dSStefan Eßer uint8_t flags, BcInst *prev) 50444d4804dSStefan Eßer { 50544d4804dSStefan Eßer assert(type == BC_LEX_KW_MODEXP || type == BC_LEX_KW_DIVMOD); 50644d4804dSStefan Eßer 50744d4804dSStefan Eßer // Must have a left paren. 50844d4804dSStefan Eßer bc_lex_next(&p->l); 50944d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) 51044d4804dSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 51144d4804dSStefan Eßer 51244d4804dSStefan Eßer bc_lex_next(&p->l); 51344d4804dSStefan Eßer 51444d4804dSStefan Eßer // Change the flags as needed for parsing the argument. 51544d4804dSStefan Eßer flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); 51644d4804dSStefan Eßer flags |= BC_PARSE_NEEDVAL; 51744d4804dSStefan Eßer 51844d4804dSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_builtin); 51944d4804dSStefan Eßer 52044d4804dSStefan Eßer // Must have a comma. 52144d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_COMMA)) 52244d4804dSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 52344d4804dSStefan Eßer 52444d4804dSStefan Eßer bc_lex_next(&p->l); 52544d4804dSStefan Eßer 52644d4804dSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_builtin); 52744d4804dSStefan Eßer 52844d4804dSStefan Eßer // Must have a comma. 52944d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_COMMA)) 53044d4804dSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 53144d4804dSStefan Eßer 53244d4804dSStefan Eßer bc_lex_next(&p->l); 53344d4804dSStefan Eßer 53444d4804dSStefan Eßer // If it is a divmod, parse an array name. Otherwise, just parse another 53544d4804dSStefan Eßer // expression. 53644d4804dSStefan Eßer if (type == BC_LEX_KW_DIVMOD) { 53744d4804dSStefan Eßer 53844d4804dSStefan Eßer // Must have a name. 53944d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_NAME)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 54044d4804dSStefan Eßer 54144d4804dSStefan Eßer // This is safe because the next token should not overwrite the name. 54244d4804dSStefan Eßer bc_lex_next(&p->l); 54344d4804dSStefan Eßer 54444d4804dSStefan Eßer // Must have a left bracket. 54544d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LBRACKET)) 54644d4804dSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 54744d4804dSStefan Eßer 54844d4804dSStefan Eßer // This is safe because the next token should not overwrite the name. 54944d4804dSStefan Eßer bc_lex_next(&p->l); 55044d4804dSStefan Eßer 55144d4804dSStefan Eßer // Must have a right bracket. 55244d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RBRACKET)) 55344d4804dSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 55444d4804dSStefan Eßer 55544d4804dSStefan Eßer // This is safe because the next token should not overwrite the name. 55644d4804dSStefan Eßer bc_lex_next(&p->l); 55744d4804dSStefan Eßer } 55844d4804dSStefan Eßer else bc_parse_expr_status(p, flags, bc_parse_next_rel); 55944d4804dSStefan Eßer 56044d4804dSStefan Eßer // Must have a right paren. 56144d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RPAREN)) 56244d4804dSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 56344d4804dSStefan Eßer 56444d4804dSStefan Eßer // Adjust previous based on the token and push it. 56544d4804dSStefan Eßer *prev = type - BC_LEX_KW_MODEXP + BC_INST_MODEXP; 56644d4804dSStefan Eßer bc_parse_push(p, *prev); 56744d4804dSStefan Eßer 56844d4804dSStefan Eßer // If we have divmod, we need to assign the modulus to the array element, so 56944d4804dSStefan Eßer // we need to push the instructions for doing so. 57044d4804dSStefan Eßer if (type == BC_LEX_KW_DIVMOD) { 57144d4804dSStefan Eßer 57244d4804dSStefan Eßer // The zeroth element. 57344d4804dSStefan Eßer bc_parse_push(p, BC_INST_ZERO); 57444d4804dSStefan Eßer bc_parse_push(p, BC_INST_ARRAY_ELEM); 57544d4804dSStefan Eßer 57644d4804dSStefan Eßer // Push the array. 57744d4804dSStefan Eßer bc_parse_pushName(p, p->l.str.v, false); 57844d4804dSStefan Eßer 57944d4804dSStefan Eßer // Swap them and assign. After this, the top item on the stack should 58044d4804dSStefan Eßer // be the quotient. 58144d4804dSStefan Eßer bc_parse_push(p, BC_INST_SWAP); 58244d4804dSStefan Eßer bc_parse_push(p, BC_INST_ASSIGN_NO_VAL); 58344d4804dSStefan Eßer } 58444d4804dSStefan Eßer 58544d4804dSStefan Eßer bc_lex_next(&p->l); 58644d4804dSStefan Eßer } 58744d4804dSStefan Eßer 58844d4804dSStefan Eßer /** 58944d4804dSStefan Eßer * Parses the scale keyword. This is special because scale can be a value or a 59044d4804dSStefan Eßer * builtin function. 59144d4804dSStefan Eßer * @param p The parser. 59244d4804dSStefan Eßer * @param type An out parameter; the instruction for the parse. 59344d4804dSStefan Eßer * @param can_assign An out parameter; whether the expression can be assigned 59444d4804dSStefan Eßer * to. 59544d4804dSStefan Eßer * @param flags The expression parsing flags for parsing a scale() arg. 59644d4804dSStefan Eßer */ 59750696a6eSStefan Eßer static void bc_parse_scale(BcParse *p, BcInst *type, 59850696a6eSStefan Eßer bool *can_assign, uint8_t flags) 59950696a6eSStefan Eßer { 60050696a6eSStefan Eßer bc_lex_next(&p->l); 60150696a6eSStefan Eßer 60244d4804dSStefan Eßer // Without the left paren, it's just the keyword. 60350696a6eSStefan Eßer if (p->l.t != BC_LEX_LPAREN) { 60444d4804dSStefan Eßer 60544d4804dSStefan Eßer // Set, push, and return. 60650696a6eSStefan Eßer *type = BC_INST_SCALE; 60750696a6eSStefan Eßer *can_assign = true; 60850696a6eSStefan Eßer bc_parse_push(p, BC_INST_SCALE); 60950696a6eSStefan Eßer return; 61050696a6eSStefan Eßer } 61150696a6eSStefan Eßer 61244d4804dSStefan Eßer // Handle the scale function. 61350696a6eSStefan Eßer *type = BC_INST_SCALE_FUNC; 61450696a6eSStefan Eßer *can_assign = false; 61544d4804dSStefan Eßer 61644d4804dSStefan Eßer // Once again, adjust the flags. 61750696a6eSStefan Eßer flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); 61850696a6eSStefan Eßer flags |= BC_PARSE_NEEDVAL; 61950696a6eSStefan Eßer 62050696a6eSStefan Eßer bc_lex_next(&p->l); 62150696a6eSStefan Eßer 62250696a6eSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_rel); 62344d4804dSStefan Eßer 62444d4804dSStefan Eßer // Must have a right paren. 62550696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RPAREN)) 62650696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 62750696a6eSStefan Eßer 62850696a6eSStefan Eßer bc_parse_push(p, BC_INST_SCALE_FUNC); 62950696a6eSStefan Eßer 63050696a6eSStefan Eßer bc_lex_next(&p->l); 63150696a6eSStefan Eßer } 63250696a6eSStefan Eßer 63344d4804dSStefan Eßer /** 63444d4804dSStefan Eßer * Parses and increment or decrement operator. This is a bit complex. 63544d4804dSStefan Eßer * @param p The parser. 63644d4804dSStefan Eßer * @param prev An out parameter; the previous instruction pointer. 63744d4804dSStefan Eßer * @param can_assign An out parameter; whether the expression can be assigned 63844d4804dSStefan Eßer * to. 63944d4804dSStefan Eßer * @param nexs An in/out parameter; the number of expressions in the 64044d4804dSStefan Eßer * parse tree that are not used. 64144d4804dSStefan Eßer * @param flags The expression parsing flags for parsing a scale() arg. 64244d4804dSStefan Eßer */ 64350696a6eSStefan Eßer static void bc_parse_incdec(BcParse *p, BcInst *prev, bool *can_assign, 64450696a6eSStefan Eßer size_t *nexs, uint8_t flags) 64550696a6eSStefan Eßer { 64650696a6eSStefan Eßer BcLexType type; 64750696a6eSStefan Eßer uchar inst; 64850696a6eSStefan Eßer BcInst etype = *prev; 64950696a6eSStefan Eßer BcLexType last = p->l.last; 65050696a6eSStefan Eßer 65150696a6eSStefan Eßer assert(prev != NULL && can_assign != NULL); 65250696a6eSStefan Eßer 65344d4804dSStefan Eßer // If we can't assign to the previous token, then we have an error. 65450696a6eSStefan Eßer if (BC_ERR(last == BC_LEX_OP_INC || last == BC_LEX_OP_DEC || 65550696a6eSStefan Eßer last == BC_LEX_RPAREN)) 65650696a6eSStefan Eßer { 65750696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_ASSIGN); 65850696a6eSStefan Eßer } 65950696a6eSStefan Eßer 66044d4804dSStefan Eßer // Is the previous instruction for a variable? 66150696a6eSStefan Eßer if (BC_PARSE_INST_VAR(etype)) { 66250696a6eSStefan Eßer 66344d4804dSStefan Eßer // If so, this is a postfix operator. 66450696a6eSStefan Eßer if (!*can_assign) bc_parse_err(p, BC_ERR_PARSE_ASSIGN); 66550696a6eSStefan Eßer 66644d4804dSStefan Eßer // Only postfix uses BC_INST_INC and BC_INST_DEC. 66750696a6eSStefan Eßer *prev = inst = BC_INST_INC + (p->l.t != BC_LEX_OP_INC); 66850696a6eSStefan Eßer bc_parse_push(p, inst); 66950696a6eSStefan Eßer bc_lex_next(&p->l); 67050696a6eSStefan Eßer *can_assign = false; 67150696a6eSStefan Eßer } 67250696a6eSStefan Eßer else { 67350696a6eSStefan Eßer 67444d4804dSStefan Eßer // This is a prefix operator. In that case, we just convert it to 67544d4804dSStefan Eßer // an assignment instruction. 67650696a6eSStefan Eßer *prev = inst = BC_INST_ASSIGN_PLUS + (p->l.t != BC_LEX_OP_INC); 67750696a6eSStefan Eßer 67850696a6eSStefan Eßer bc_lex_next(&p->l); 67950696a6eSStefan Eßer type = p->l.t; 68050696a6eSStefan Eßer 68150696a6eSStefan Eßer // Because we parse the next part of the expression 68250696a6eSStefan Eßer // right here, we need to increment this. 68350696a6eSStefan Eßer *nexs = *nexs + 1; 68450696a6eSStefan Eßer 68544d4804dSStefan Eßer // Is the next token a normal identifier? 68650696a6eSStefan Eßer if (type == BC_LEX_NAME) { 68744d4804dSStefan Eßer 68844d4804dSStefan Eßer // Parse the name. 68950696a6eSStefan Eßer uint8_t flags2 = flags & ~BC_PARSE_ARRAY; 69050696a6eSStefan Eßer bc_parse_name(p, prev, can_assign, flags2 | BC_PARSE_NOCALL); 69150696a6eSStefan Eßer } 69244d4804dSStefan Eßer // Is the next token a global? 69350696a6eSStefan Eßer else if (type >= BC_LEX_KW_LAST && type <= BC_LEX_KW_OBASE) { 69450696a6eSStefan Eßer bc_parse_push(p, type - BC_LEX_KW_LAST + BC_INST_LAST); 69550696a6eSStefan Eßer bc_lex_next(&p->l); 69650696a6eSStefan Eßer } 69744d4804dSStefan Eßer // Is the next token specifically scale, which needs special treatment? 69850696a6eSStefan Eßer else if (BC_NO_ERR(type == BC_LEX_KW_SCALE)) { 69950696a6eSStefan Eßer 70050696a6eSStefan Eßer bc_lex_next(&p->l); 70150696a6eSStefan Eßer 70244d4804dSStefan Eßer // Check that scale() was not used. 70350696a6eSStefan Eßer if (BC_ERR(p->l.t == BC_LEX_LPAREN)) 70450696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 70550696a6eSStefan Eßer else bc_parse_push(p, BC_INST_SCALE); 70650696a6eSStefan Eßer } 70744d4804dSStefan Eßer // Now we know we have an error. 70850696a6eSStefan Eßer else bc_parse_err(p, BC_ERR_PARSE_TOKEN); 70950696a6eSStefan Eßer 71050696a6eSStefan Eßer *can_assign = false; 71150696a6eSStefan Eßer 71250696a6eSStefan Eßer bc_parse_push(p, BC_INST_ONE); 71350696a6eSStefan Eßer bc_parse_push(p, inst); 71450696a6eSStefan Eßer } 71550696a6eSStefan Eßer } 71650696a6eSStefan Eßer 71744d4804dSStefan Eßer /** 71844d4804dSStefan Eßer * Parses the minus operator. This needs special treatment because it is either 71944d4804dSStefan Eßer * subtract or negation. 72044d4804dSStefan Eßer * @param p The parser. 72144d4804dSStefan Eßer * @param prev An in/out parameter; the previous instruction. 72244d4804dSStefan Eßer * @param ops_bgn The size of the operator stack. 72344d4804dSStefan Eßer * @param rparen True if the last token was a right paren. 72444d4804dSStefan Eßer * @param binlast True if the last token was a binary operator. 72544d4804dSStefan Eßer * @param nexprs An in/out parameter; the number of unused expressions. 72644d4804dSStefan Eßer */ 72750696a6eSStefan Eßer static void bc_parse_minus(BcParse *p, BcInst *prev, size_t ops_bgn, 72850696a6eSStefan Eßer bool rparen, bool binlast, size_t *nexprs) 72950696a6eSStefan Eßer { 73050696a6eSStefan Eßer BcLexType type; 73150696a6eSStefan Eßer 73250696a6eSStefan Eßer bc_lex_next(&p->l); 73350696a6eSStefan Eßer 73444d4804dSStefan Eßer // Figure out if it's a minus or a negation. 73550696a6eSStefan Eßer type = BC_PARSE_LEAF(*prev, binlast, rparen) ? BC_LEX_OP_MINUS : BC_LEX_NEG; 73650696a6eSStefan Eßer *prev = BC_PARSE_TOKEN_INST(type); 73750696a6eSStefan Eßer 73850696a6eSStefan Eßer // We can just push onto the op stack because this is the largest 73950696a6eSStefan Eßer // precedence operator that gets pushed. Inc/dec does not. 74050696a6eSStefan Eßer if (type != BC_LEX_OP_MINUS) bc_vec_push(&p->ops, &type); 74150696a6eSStefan Eßer else bc_parse_operator(p, type, ops_bgn, nexprs); 74250696a6eSStefan Eßer } 74350696a6eSStefan Eßer 74444d4804dSStefan Eßer /** 74544d4804dSStefan Eßer * Parses a string. 74644d4804dSStefan Eßer * @param p The parser. 74744d4804dSStefan Eßer * @param inst The instruction corresponding to how the string was found and 74844d4804dSStefan Eßer * how it should be printed. 74944d4804dSStefan Eßer */ 75044d4804dSStefan Eßer static void bc_parse_str(BcParse *p, BcInst inst) { 75150696a6eSStefan Eßer bc_parse_addString(p); 75250696a6eSStefan Eßer bc_parse_push(p, inst); 75350696a6eSStefan Eßer bc_lex_next(&p->l); 75450696a6eSStefan Eßer } 75550696a6eSStefan Eßer 75644d4804dSStefan Eßer /** 75744d4804dSStefan Eßer * Parses a print statement. 75844d4804dSStefan Eßer * @param p The parser. 75944d4804dSStefan Eßer */ 76044d4804dSStefan Eßer static void bc_parse_print(BcParse *p, BcLexType type) { 76150696a6eSStefan Eßer 76250696a6eSStefan Eßer BcLexType t; 76350696a6eSStefan Eßer bool comma = false; 76444d4804dSStefan Eßer BcInst inst = type == BC_LEX_KW_STREAM ? 76544d4804dSStefan Eßer BC_INST_PRINT_STREAM : BC_INST_PRINT_POP; 76650696a6eSStefan Eßer 76750696a6eSStefan Eßer bc_lex_next(&p->l); 76850696a6eSStefan Eßer 76950696a6eSStefan Eßer t = p->l.t; 77050696a6eSStefan Eßer 77144d4804dSStefan Eßer // A print or stream statement has to have *something*. 77250696a6eSStefan Eßer if (bc_parse_isDelimiter(p)) bc_parse_err(p, BC_ERR_PARSE_PRINT); 77350696a6eSStefan Eßer 77450696a6eSStefan Eßer do { 77544d4804dSStefan Eßer 77644d4804dSStefan Eßer // If the token is a string, then print it with escapes. 77744d4804dSStefan Eßer // BC_INST_PRINT_POP plays that role for bc. 77844d4804dSStefan Eßer if (t == BC_LEX_STR) bc_parse_str(p, inst); 77950696a6eSStefan Eßer else { 78044d4804dSStefan Eßer // We have an actual number; parse and add a print instruction. 78150696a6eSStefan Eßer bc_parse_expr_status(p, BC_PARSE_NEEDVAL, bc_parse_next_print); 78244d4804dSStefan Eßer bc_parse_push(p, inst); 78350696a6eSStefan Eßer } 78450696a6eSStefan Eßer 78544d4804dSStefan Eßer // Is the next token a comma? 78650696a6eSStefan Eßer comma = (p->l.t == BC_LEX_COMMA); 78750696a6eSStefan Eßer 78844d4804dSStefan Eßer // Get the next token if we have a comma. 78950696a6eSStefan Eßer if (comma) bc_lex_next(&p->l); 79050696a6eSStefan Eßer else { 79144d4804dSStefan Eßer 79244d4804dSStefan Eßer // If we don't have a comma, the statement needs to end. 79350696a6eSStefan Eßer if (!bc_parse_isDelimiter(p)) 79450696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 79550696a6eSStefan Eßer else break; 79650696a6eSStefan Eßer } 79750696a6eSStefan Eßer 79850696a6eSStefan Eßer t = p->l.t; 79944d4804dSStefan Eßer 80050696a6eSStefan Eßer } while (true); 80150696a6eSStefan Eßer 80244d4804dSStefan Eßer // If we have a comma but no token, that's bad. 80350696a6eSStefan Eßer if (BC_ERR(comma)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 80450696a6eSStefan Eßer } 80550696a6eSStefan Eßer 80644d4804dSStefan Eßer /** 80744d4804dSStefan Eßer * Parses a return statement. 80844d4804dSStefan Eßer * @param p The parser. 80944d4804dSStefan Eßer */ 81050696a6eSStefan Eßer static void bc_parse_return(BcParse *p) { 81150696a6eSStefan Eßer 81250696a6eSStefan Eßer BcLexType t; 81350696a6eSStefan Eßer bool paren; 81450696a6eSStefan Eßer uchar inst = BC_INST_RET0; 81550696a6eSStefan Eßer 81644d4804dSStefan Eßer // If we are not in a function, that's an error. 81750696a6eSStefan Eßer if (BC_ERR(!BC_PARSE_FUNC(p))) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 81850696a6eSStefan Eßer 81944d4804dSStefan Eßer // If we are in a void function, make sure to return void. 82050696a6eSStefan Eßer if (p->func->voidfn) inst = BC_INST_RET_VOID; 82150696a6eSStefan Eßer 82250696a6eSStefan Eßer bc_lex_next(&p->l); 82350696a6eSStefan Eßer 82450696a6eSStefan Eßer t = p->l.t; 82544d4804dSStefan Eßer paren = (t == BC_LEX_LPAREN); 82650696a6eSStefan Eßer 82744d4804dSStefan Eßer // An empty return statement just needs to push the selected instruction. 82850696a6eSStefan Eßer if (bc_parse_isDelimiter(p)) bc_parse_push(p, inst); 82950696a6eSStefan Eßer else { 83050696a6eSStefan Eßer 83150696a6eSStefan Eßer BcParseStatus s; 83250696a6eSStefan Eßer 83344d4804dSStefan Eßer // Need to parse the expression whose value will be returned. 83450696a6eSStefan Eßer s = bc_parse_expr_err(p, BC_PARSE_NEEDVAL, bc_parse_next_expr); 83550696a6eSStefan Eßer 83644d4804dSStefan Eßer // If the expression was empty, just push the selected instruction. 83750696a6eSStefan Eßer if (s == BC_PARSE_STATUS_EMPTY_EXPR) { 83850696a6eSStefan Eßer bc_parse_push(p, inst); 83950696a6eSStefan Eßer bc_lex_next(&p->l); 84050696a6eSStefan Eßer } 84150696a6eSStefan Eßer 84244d4804dSStefan Eßer // POSIX requires parentheses. 84350696a6eSStefan Eßer if (!paren || p->l.last != BC_LEX_RPAREN) { 84450696a6eSStefan Eßer bc_parse_err(p, BC_ERR_POSIX_RET); 84550696a6eSStefan Eßer } 84644d4804dSStefan Eßer 84744d4804dSStefan Eßer // Void functions require an empty expression. 84844d4804dSStefan Eßer if (BC_ERR(p->func->voidfn)) { 84944d4804dSStefan Eßer if (s != BC_PARSE_STATUS_EMPTY_EXPR) 85050696a6eSStefan Eßer bc_parse_verr(p, BC_ERR_PARSE_RET_VOID, p->func->name); 85144d4804dSStefan Eßer } 85244d4804dSStefan Eßer // If we got here, we want to be sure to end the function with a real 85344d4804dSStefan Eßer // return instruction, just in case. 85444d4804dSStefan Eßer else bc_parse_push(p, BC_INST_RET); 85550696a6eSStefan Eßer } 85650696a6eSStefan Eßer } 85750696a6eSStefan Eßer 85844d4804dSStefan Eßer /** 85944d4804dSStefan Eßer * Clears flags that indicate the end of an if statement and its block and sets 86044d4804dSStefan Eßer * the jump location. 86144d4804dSStefan Eßer * @param p The parser. 86244d4804dSStefan Eßer */ 86350696a6eSStefan Eßer static void bc_parse_noElse(BcParse *p) { 86450696a6eSStefan Eßer uint16_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p); 86550696a6eSStefan Eßer *flag_ptr = (*flag_ptr & ~(BC_PARSE_FLAG_IF_END)); 86650696a6eSStefan Eßer bc_parse_setLabel(p); 86750696a6eSStefan Eßer } 86850696a6eSStefan Eßer 86944d4804dSStefan Eßer /** 87044d4804dSStefan Eßer * Ends (finishes parsing) the body of a control statement or a function. 87144d4804dSStefan Eßer * @param p The parser. 87244d4804dSStefan Eßer * @param brace True if the body was ended by a brace, false otherwise. 87344d4804dSStefan Eßer */ 87450696a6eSStefan Eßer static void bc_parse_endBody(BcParse *p, bool brace) { 87550696a6eSStefan Eßer 87650696a6eSStefan Eßer bool has_brace, new_else = false; 87750696a6eSStefan Eßer 87844d4804dSStefan Eßer // We cannot be ending a body if there are no bodies to end. 87950696a6eSStefan Eßer if (BC_ERR(p->flags.len <= 1)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 88050696a6eSStefan Eßer 88150696a6eSStefan Eßer if (brace) { 88250696a6eSStefan Eßer 88344d4804dSStefan Eßer // The brace was already gotten; make sure that the caller did not lie. 88444d4804dSStefan Eßer // We check for the requirement of braces later. 88550696a6eSStefan Eßer assert(p->l.t == BC_LEX_RBRACE); 88650696a6eSStefan Eßer 88750696a6eSStefan Eßer bc_lex_next(&p->l); 88844d4804dSStefan Eßer 88944d4804dSStefan Eßer // If the next token is not a delimiter, that is a problem. 89050696a6eSStefan Eßer if (BC_ERR(!bc_parse_isDelimiter(p))) 89150696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 89250696a6eSStefan Eßer } 89350696a6eSStefan Eßer 89444d4804dSStefan Eßer // Do we have a brace flag? 89550696a6eSStefan Eßer has_brace = (BC_PARSE_BRACE(p) != 0); 89650696a6eSStefan Eßer 89750696a6eSStefan Eßer do { 89850696a6eSStefan Eßer size_t len = p->flags.len; 89950696a6eSStefan Eßer bool loop; 90050696a6eSStefan Eßer 90144d4804dSStefan Eßer // If we have a brace flag but not a brace, that's a problem. 90250696a6eSStefan Eßer if (has_brace && !brace) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 90350696a6eSStefan Eßer 90444d4804dSStefan Eßer // Are we inside a loop? 90550696a6eSStefan Eßer loop = (BC_PARSE_LOOP_INNER(p) != 0); 90650696a6eSStefan Eßer 90744d4804dSStefan Eßer // If we are ending a loop or an else... 90850696a6eSStefan Eßer if (loop || BC_PARSE_ELSE(p)) { 90950696a6eSStefan Eßer 91044d4804dSStefan Eßer // Loops have condition labels that we have to take care of as well. 91150696a6eSStefan Eßer if (loop) { 91250696a6eSStefan Eßer 91350696a6eSStefan Eßer size_t *label = bc_vec_top(&p->conds); 91450696a6eSStefan Eßer 91550696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP); 91650696a6eSStefan Eßer bc_parse_pushIndex(p, *label); 91750696a6eSStefan Eßer 91850696a6eSStefan Eßer bc_vec_pop(&p->conds); 91950696a6eSStefan Eßer } 92050696a6eSStefan Eßer 92150696a6eSStefan Eßer bc_parse_setLabel(p); 92250696a6eSStefan Eßer bc_vec_pop(&p->flags); 92350696a6eSStefan Eßer } 92444d4804dSStefan Eßer // If we are ending a function... 92550696a6eSStefan Eßer else if (BC_PARSE_FUNC_INNER(p)) { 92650696a6eSStefan Eßer BcInst inst = (p->func->voidfn ? BC_INST_RET_VOID : BC_INST_RET0); 92750696a6eSStefan Eßer bc_parse_push(p, inst); 92850696a6eSStefan Eßer bc_parse_updateFunc(p, BC_PROG_MAIN); 92950696a6eSStefan Eßer bc_vec_pop(&p->flags); 93050696a6eSStefan Eßer } 93144d4804dSStefan Eßer // If we have a brace flag and not an if statement, we can pop the top 93244d4804dSStefan Eßer // of the flags stack because they have been taken care of above. 93344d4804dSStefan Eßer else if (has_brace && !BC_PARSE_IF(p)) bc_vec_pop(&p->flags); 93450696a6eSStefan Eßer 93550696a6eSStefan Eßer // This needs to be last to parse nested if's properly. 93650696a6eSStefan Eßer if (BC_PARSE_IF(p) && (len == p->flags.len || !BC_PARSE_BRACE(p))) { 93750696a6eSStefan Eßer 93844d4804dSStefan Eßer // Eat newlines. 93950696a6eSStefan Eßer while (p->l.t == BC_LEX_NLINE) bc_lex_next(&p->l); 94050696a6eSStefan Eßer 94144d4804dSStefan Eßer // *Now* we can pop the flags. 94250696a6eSStefan Eßer bc_vec_pop(&p->flags); 94350696a6eSStefan Eßer 94444d4804dSStefan Eßer // If we are allowed non-POSIX stuff... 94550696a6eSStefan Eßer if (!BC_S) { 94650696a6eSStefan Eßer 94744d4804dSStefan Eßer // Have we found yet another dangling else? 94850696a6eSStefan Eßer *(BC_PARSE_TOP_FLAG_PTR(p)) |= BC_PARSE_FLAG_IF_END; 94950696a6eSStefan Eßer new_else = (p->l.t == BC_LEX_KW_ELSE); 95050696a6eSStefan Eßer 95144d4804dSStefan Eßer // Parse the else or end the if statement body. 95250696a6eSStefan Eßer if (new_else) bc_parse_else(p); 95350696a6eSStefan Eßer else if (!has_brace && (!BC_PARSE_IF_END(p) || brace)) 95450696a6eSStefan Eßer bc_parse_noElse(p); 95550696a6eSStefan Eßer } 95644d4804dSStefan Eßer // POSIX requires us to do the bare minimum only. 95750696a6eSStefan Eßer else bc_parse_noElse(p); 95850696a6eSStefan Eßer } 95950696a6eSStefan Eßer 96044d4804dSStefan Eßer // If these are both true, we have "used" the braces that we found. 96150696a6eSStefan Eßer if (brace && has_brace) brace = false; 96250696a6eSStefan Eßer 96344d4804dSStefan Eßer // This condition was perhaps the hardest single part of the parser. If the 96444d4804dSStefan Eßer // flags stack does not have enough, we should stop. If we have a new else 96544d4804dSStefan Eßer // statement, we should stop. If we do have the end of an if statement and 96644d4804dSStefan Eßer // we have eaten the brace, we should stop. If we do have a brace flag, we 96744d4804dSStefan Eßer // should stop. 96850696a6eSStefan Eßer } while (p->flags.len > 1 && !new_else && (!BC_PARSE_IF_END(p) || brace) && 96950696a6eSStefan Eßer !(has_brace = (BC_PARSE_BRACE(p) != 0))); 97050696a6eSStefan Eßer 97144d4804dSStefan Eßer // If we have a brace, yet no body for it, that's a problem. 97250696a6eSStefan Eßer if (BC_ERR(p->flags.len == 1 && brace)) 97350696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 97450696a6eSStefan Eßer else if (brace && BC_PARSE_BRACE(p)) { 97550696a6eSStefan Eßer 97644d4804dSStefan Eßer // If we make it here, we have a brace and a flag for it. 97750696a6eSStefan Eßer uint16_t flags = BC_PARSE_TOP_FLAG(p); 97850696a6eSStefan Eßer 97944d4804dSStefan Eßer // This condition ensure that the *last* body is correctly finished by 98044d4804dSStefan Eßer // popping its flags. 98150696a6eSStefan Eßer if (!(flags & (BC_PARSE_FLAG_FUNC_INNER | BC_PARSE_FLAG_LOOP_INNER)) && 98250696a6eSStefan Eßer !(flags & (BC_PARSE_FLAG_IF | BC_PARSE_FLAG_ELSE)) && 98350696a6eSStefan Eßer !(flags & (BC_PARSE_FLAG_IF_END))) 98450696a6eSStefan Eßer { 98550696a6eSStefan Eßer bc_vec_pop(&p->flags); 98650696a6eSStefan Eßer } 98750696a6eSStefan Eßer } 98850696a6eSStefan Eßer } 98950696a6eSStefan Eßer 99044d4804dSStefan Eßer /** 99144d4804dSStefan Eßer * Starts the body of a control statement or function. 99244d4804dSStefan Eßer * @param p The parser. 99344d4804dSStefan Eßer * @param flags The current flags (will be edited). 99444d4804dSStefan Eßer */ 99550696a6eSStefan Eßer static void bc_parse_startBody(BcParse *p, uint16_t flags) { 99650696a6eSStefan Eßer assert(flags); 99750696a6eSStefan Eßer flags |= (BC_PARSE_TOP_FLAG(p) & (BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_LOOP)); 99850696a6eSStefan Eßer flags |= BC_PARSE_FLAG_BODY; 99950696a6eSStefan Eßer bc_vec_push(&p->flags, &flags); 100050696a6eSStefan Eßer } 100150696a6eSStefan Eßer 1002*a30efc5cSStefan Eßer void bc_parse_endif(BcParse *p) { 1003*a30efc5cSStefan Eßer 1004*a30efc5cSStefan Eßer size_t i; 1005*a30efc5cSStefan Eßer bool good; 1006*a30efc5cSStefan Eßer 1007*a30efc5cSStefan Eßer // Not a problem if this is true. 1008*a30efc5cSStefan Eßer if (BC_NO_ERR(!BC_PARSE_NO_EXEC(p))) return; 1009*a30efc5cSStefan Eßer 1010*a30efc5cSStefan Eßer good = true; 1011*a30efc5cSStefan Eßer 1012*a30efc5cSStefan Eßer // Find an instance of a body that needs closing, i.e., a statement that did 1013*a30efc5cSStefan Eßer // not have a right brace when it should have. 1014*a30efc5cSStefan Eßer for (i = 0; good && i < p->flags.len; ++i) { 1015*a30efc5cSStefan Eßer uint16_t flag = *((uint16_t*) bc_vec_item(&p->flags, i)); 1016*a30efc5cSStefan Eßer good = ((flag & BC_PARSE_FLAG_BRACE) != BC_PARSE_FLAG_BRACE); 1017*a30efc5cSStefan Eßer } 1018*a30efc5cSStefan Eßer 1019*a30efc5cSStefan Eßer // If we did not find such an instance... 1020*a30efc5cSStefan Eßer if (good) { 1021*a30efc5cSStefan Eßer 1022*a30efc5cSStefan Eßer // We set this to restore it later. We don't want the parser thinking 1023*a30efc5cSStefan Eßer // that we are on stdin for this one because it will want more. 1024*a30efc5cSStefan Eßer bool is_stdin = vm.is_stdin; 1025*a30efc5cSStefan Eßer 1026*a30efc5cSStefan Eßer vm.is_stdin = false; 1027*a30efc5cSStefan Eßer 1028*a30efc5cSStefan Eßer // End all of the if statements and loops. 1029*a30efc5cSStefan Eßer while (p->flags.len > 1 || BC_PARSE_IF_END(p)) { 1030*a30efc5cSStefan Eßer if (BC_PARSE_IF_END(p)) bc_parse_noElse(p); 1031*a30efc5cSStefan Eßer if (p->flags.len > 1) bc_parse_endBody(p, false); 1032*a30efc5cSStefan Eßer } 1033*a30efc5cSStefan Eßer 1034*a30efc5cSStefan Eßer vm.is_stdin = is_stdin; 1035*a30efc5cSStefan Eßer } 1036*a30efc5cSStefan Eßer // If we reach here, a block was not properly closed, and we should error. 1037*a30efc5cSStefan Eßer else bc_parse_err(&vm.prs, BC_ERR_PARSE_BLOCK); 1038*a30efc5cSStefan Eßer } 1039*a30efc5cSStefan Eßer 104044d4804dSStefan Eßer /** 104144d4804dSStefan Eßer * Parses an if statement. 104244d4804dSStefan Eßer * @param p The parser. 104344d4804dSStefan Eßer */ 104450696a6eSStefan Eßer static void bc_parse_if(BcParse *p) { 104550696a6eSStefan Eßer 104644d4804dSStefan Eßer // We are allowed relational operators, and we must have a value. 104750696a6eSStefan Eßer size_t idx; 104850696a6eSStefan Eßer uint8_t flags = (BC_PARSE_REL | BC_PARSE_NEEDVAL); 104950696a6eSStefan Eßer 105044d4804dSStefan Eßer // Get the left paren and barf if necessary. 105150696a6eSStefan Eßer bc_lex_next(&p->l); 105250696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) 105350696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 105450696a6eSStefan Eßer 105544d4804dSStefan Eßer // Parse the condition. 105650696a6eSStefan Eßer bc_lex_next(&p->l); 105750696a6eSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_rel); 105844d4804dSStefan Eßer 105944d4804dSStefan Eßer // Must have a right paren. 106050696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RPAREN)) 106150696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 106250696a6eSStefan Eßer 106350696a6eSStefan Eßer bc_lex_next(&p->l); 106444d4804dSStefan Eßer 106544d4804dSStefan Eßer // Insert the conditional jump instruction. 106650696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP_ZERO); 106750696a6eSStefan Eßer 106850696a6eSStefan Eßer idx = p->func->labels.len; 106950696a6eSStefan Eßer 107044d4804dSStefan Eßer // Push the index for the instruction and create an exit label for an else 107144d4804dSStefan Eßer // statement. 107250696a6eSStefan Eßer bc_parse_pushIndex(p, idx); 107350696a6eSStefan Eßer bc_parse_createExitLabel(p, idx, false); 107444d4804dSStefan Eßer 107550696a6eSStefan Eßer bc_parse_startBody(p, BC_PARSE_FLAG_IF); 107650696a6eSStefan Eßer } 107750696a6eSStefan Eßer 107844d4804dSStefan Eßer /** 107944d4804dSStefan Eßer * Parses an else statement. 108044d4804dSStefan Eßer * @param p The parser. 108144d4804dSStefan Eßer */ 108250696a6eSStefan Eßer static void bc_parse_else(BcParse *p) { 108350696a6eSStefan Eßer 108450696a6eSStefan Eßer size_t idx = p->func->labels.len; 108550696a6eSStefan Eßer 108644d4804dSStefan Eßer // We must be at the end of an if statement. 108750696a6eSStefan Eßer if (BC_ERR(!BC_PARSE_IF_END(p))) 108850696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 108950696a6eSStefan Eßer 109044d4804dSStefan Eßer // Push an unconditional jump to make bc jump over the else statement if it 109144d4804dSStefan Eßer // executed the original if statement. 109250696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP); 109350696a6eSStefan Eßer bc_parse_pushIndex(p, idx); 109450696a6eSStefan Eßer 109544d4804dSStefan Eßer // Clear the else stuff. Yes, that function is misnamed for its use here, 109644d4804dSStefan Eßer // but deal with it. 109750696a6eSStefan Eßer bc_parse_noElse(p); 109850696a6eSStefan Eßer 109944d4804dSStefan Eßer // Create the exit label and parse the body. 110050696a6eSStefan Eßer bc_parse_createExitLabel(p, idx, false); 110150696a6eSStefan Eßer bc_parse_startBody(p, BC_PARSE_FLAG_ELSE); 110250696a6eSStefan Eßer 110350696a6eSStefan Eßer bc_lex_next(&p->l); 110450696a6eSStefan Eßer } 110550696a6eSStefan Eßer 110644d4804dSStefan Eßer /** 110744d4804dSStefan Eßer * Parse a while loop. 110844d4804dSStefan Eßer * @param p The parser. 110944d4804dSStefan Eßer */ 111050696a6eSStefan Eßer static void bc_parse_while(BcParse *p) { 111150696a6eSStefan Eßer 111244d4804dSStefan Eßer // We are allowed relational operators, and we must have a value. 111350696a6eSStefan Eßer size_t idx; 111450696a6eSStefan Eßer uint8_t flags = (BC_PARSE_REL | BC_PARSE_NEEDVAL); 111550696a6eSStefan Eßer 111644d4804dSStefan Eßer // Get the left paren and barf if necessary. 111750696a6eSStefan Eßer bc_lex_next(&p->l); 111850696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) 111950696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 112050696a6eSStefan Eßer bc_lex_next(&p->l); 112150696a6eSStefan Eßer 112244d4804dSStefan Eßer // Create the labels. Loops need both. 112350696a6eSStefan Eßer bc_parse_createCondLabel(p, p->func->labels.len); 112450696a6eSStefan Eßer idx = p->func->labels.len; 112550696a6eSStefan Eßer bc_parse_createExitLabel(p, idx, true); 112650696a6eSStefan Eßer 112744d4804dSStefan Eßer // Parse the actual condition and barf on non-right paren. 112850696a6eSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_rel); 112950696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RPAREN)) 113050696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 113150696a6eSStefan Eßer bc_lex_next(&p->l); 113250696a6eSStefan Eßer 113344d4804dSStefan Eßer // Now we can push the conditional jump and start the body. 113450696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP_ZERO); 113550696a6eSStefan Eßer bc_parse_pushIndex(p, idx); 113650696a6eSStefan Eßer bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER); 113750696a6eSStefan Eßer } 113850696a6eSStefan Eßer 113944d4804dSStefan Eßer /** 114044d4804dSStefan Eßer * Parse a for loop. 114144d4804dSStefan Eßer * @param p The parser. 114244d4804dSStefan Eßer */ 114350696a6eSStefan Eßer static void bc_parse_for(BcParse *p) { 114450696a6eSStefan Eßer 114550696a6eSStefan Eßer size_t cond_idx, exit_idx, body_idx, update_idx; 114650696a6eSStefan Eßer 114744d4804dSStefan Eßer // Barf on the missing left paren. 114850696a6eSStefan Eßer bc_lex_next(&p->l); 114950696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) 115050696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 115150696a6eSStefan Eßer bc_lex_next(&p->l); 115250696a6eSStefan Eßer 115344d4804dSStefan Eßer // The first statement can be empty, but if it is, check for error in POSIX 115444d4804dSStefan Eßer // mode. Otherwise, parse it. 115550696a6eSStefan Eßer if (p->l.t != BC_LEX_SCOLON) 115650696a6eSStefan Eßer bc_parse_expr_status(p, 0, bc_parse_next_for); 115750696a6eSStefan Eßer else bc_parse_err(p, BC_ERR_POSIX_FOR); 115850696a6eSStefan Eßer 115944d4804dSStefan Eßer // Must have a semicolon. 116044d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_SCOLON)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 116150696a6eSStefan Eßer bc_lex_next(&p->l); 116250696a6eSStefan Eßer 116344d4804dSStefan Eßer // These are indices for labels. There are so many of them because the end 116444d4804dSStefan Eßer // of the loop must unconditionally jump to the update code. Then the update 116544d4804dSStefan Eßer // code must unconditionally jump to the condition code. Then the condition 116644d4804dSStefan Eßer // code must *conditionally* jump to the exit. 116750696a6eSStefan Eßer cond_idx = p->func->labels.len; 116850696a6eSStefan Eßer update_idx = cond_idx + 1; 116950696a6eSStefan Eßer body_idx = update_idx + 1; 117050696a6eSStefan Eßer exit_idx = body_idx + 1; 117150696a6eSStefan Eßer 117244d4804dSStefan Eßer // This creates the condition label. 117350696a6eSStefan Eßer bc_parse_createLabel(p, p->func->code.len); 117450696a6eSStefan Eßer 117544d4804dSStefan Eßer // Parse an expression if it exists. 117650696a6eSStefan Eßer if (p->l.t != BC_LEX_SCOLON) { 117750696a6eSStefan Eßer uint8_t flags = (BC_PARSE_REL | BC_PARSE_NEEDVAL); 117850696a6eSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_for); 117950696a6eSStefan Eßer } 118050696a6eSStefan Eßer else { 118150696a6eSStefan Eßer 118244d4804dSStefan Eßer // Set this for the next call to bc_parse_number because an empty 118344d4804dSStefan Eßer // condition means that it is an infinite loop, so the condition must be 118444d4804dSStefan Eßer // non-zero. This is safe to set because the current token is a 118544d4804dSStefan Eßer // semicolon, which has no string requirement. 118650696a6eSStefan Eßer bc_vec_string(&p->l.str, sizeof(bc_parse_one) - 1, bc_parse_one); 118750696a6eSStefan Eßer bc_parse_number(p); 118850696a6eSStefan Eßer 118944d4804dSStefan Eßer // An empty condition makes POSIX mad. 119050696a6eSStefan Eßer bc_parse_err(p, BC_ERR_POSIX_FOR); 119150696a6eSStefan Eßer } 119250696a6eSStefan Eßer 119344d4804dSStefan Eßer // Must have a semicolon. 119450696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_SCOLON)) 119550696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 119650696a6eSStefan Eßer bc_lex_next(&p->l); 119750696a6eSStefan Eßer 119844d4804dSStefan Eßer // Now we can set up the conditional jump to the exit and an unconditional 119944d4804dSStefan Eßer // jump to the body right after. The unconditional jump to the body is 120044d4804dSStefan Eßer // because there is update code coming right after the condition, so we need 120144d4804dSStefan Eßer // to skip it to get to the body. 120250696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP_ZERO); 120350696a6eSStefan Eßer bc_parse_pushIndex(p, exit_idx); 120450696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP); 120550696a6eSStefan Eßer bc_parse_pushIndex(p, body_idx); 120650696a6eSStefan Eßer 120744d4804dSStefan Eßer // Now create the label for the update code. 120850696a6eSStefan Eßer bc_parse_createCondLabel(p, update_idx); 120950696a6eSStefan Eßer 121044d4804dSStefan Eßer // Parse if not empty, and if it is, let POSIX yell if necessary. 121150696a6eSStefan Eßer if (p->l.t != BC_LEX_RPAREN) 121250696a6eSStefan Eßer bc_parse_expr_status(p, 0, bc_parse_next_rel); 121350696a6eSStefan Eßer else bc_parse_err(p, BC_ERR_POSIX_FOR); 121450696a6eSStefan Eßer 121544d4804dSStefan Eßer // Must have a right paren. 121650696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RPAREN)) 121750696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 121844d4804dSStefan Eßer 121944d4804dSStefan Eßer // Set up a jump to the condition right after the update code. 122050696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP); 122150696a6eSStefan Eßer bc_parse_pushIndex(p, cond_idx); 122250696a6eSStefan Eßer bc_parse_createLabel(p, p->func->code.len); 122350696a6eSStefan Eßer 122444d4804dSStefan Eßer // Create an exit label for the body and start the body. 122550696a6eSStefan Eßer bc_parse_createExitLabel(p, exit_idx, true); 122650696a6eSStefan Eßer bc_lex_next(&p->l); 122750696a6eSStefan Eßer bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER); 122850696a6eSStefan Eßer } 122950696a6eSStefan Eßer 123044d4804dSStefan Eßer /** 123144d4804dSStefan Eßer * Parse a statement or token that indicates a loop exit. This includes an 123244d4804dSStefan Eßer * actual loop exit, the break keyword, or the continue keyword. 123344d4804dSStefan Eßer * @param p The parser. 123444d4804dSStefan Eßer * @param type The type of exit. 123544d4804dSStefan Eßer */ 123650696a6eSStefan Eßer static void bc_parse_loopExit(BcParse *p, BcLexType type) { 123750696a6eSStefan Eßer 123850696a6eSStefan Eßer size_t i; 123950696a6eSStefan Eßer BcInstPtr *ip; 124050696a6eSStefan Eßer 124144d4804dSStefan Eßer // Must have a loop. If we don't, that's an error. 124250696a6eSStefan Eßer if (BC_ERR(!BC_PARSE_LOOP(p))) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 124350696a6eSStefan Eßer 124444d4804dSStefan Eßer // If we have a break statement... 124550696a6eSStefan Eßer if (type == BC_LEX_KW_BREAK) { 124650696a6eSStefan Eßer 124744d4804dSStefan Eßer // If there are no exits, something went wrong somewhere. 124850696a6eSStefan Eßer if (BC_ERR(!p->exits.len)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 124950696a6eSStefan Eßer 125044d4804dSStefan Eßer // Get the exit. 125150696a6eSStefan Eßer i = p->exits.len - 1; 125250696a6eSStefan Eßer ip = bc_vec_item(&p->exits, i); 125350696a6eSStefan Eßer 125444d4804dSStefan Eßer // The condition !ip->func is true if the exit is not for a loop, so we 125544d4804dSStefan Eßer // need to find the first actual loop exit. 125650696a6eSStefan Eßer while (!ip->func && i < p->exits.len) ip = bc_vec_item(&p->exits, i--); 125744d4804dSStefan Eßer 125844d4804dSStefan Eßer // Make sure everything is hunky dory. 125950696a6eSStefan Eßer assert(ip != NULL && (i < p->exits.len || ip->func)); 126044d4804dSStefan Eßer 126144d4804dSStefan Eßer // Set the index for the exit. 126250696a6eSStefan Eßer i = ip->idx; 126350696a6eSStefan Eßer } 126444d4804dSStefan Eßer // If we have a continue statement or just the loop end, jump to the 126544d4804dSStefan Eßer // condition (or update for a foor loop). 126650696a6eSStefan Eßer else i = *((size_t*) bc_vec_top(&p->conds)); 126750696a6eSStefan Eßer 126844d4804dSStefan Eßer // Add the unconditional jump. 126950696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP); 127050696a6eSStefan Eßer bc_parse_pushIndex(p, i); 127150696a6eSStefan Eßer 127250696a6eSStefan Eßer bc_lex_next(&p->l); 127350696a6eSStefan Eßer } 127450696a6eSStefan Eßer 127544d4804dSStefan Eßer /** 127644d4804dSStefan Eßer * Parse a function (header). 127744d4804dSStefan Eßer * @param p The parser. 127844d4804dSStefan Eßer */ 127950696a6eSStefan Eßer static void bc_parse_func(BcParse *p) { 128050696a6eSStefan Eßer 128150696a6eSStefan Eßer bool comma = false, voidfn; 128250696a6eSStefan Eßer uint16_t flags; 128350696a6eSStefan Eßer size_t idx; 128450696a6eSStefan Eßer 128550696a6eSStefan Eßer bc_lex_next(&p->l); 128650696a6eSStefan Eßer 128744d4804dSStefan Eßer // Must have a name. 128844d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_NAME)) bc_parse_err(p, BC_ERR_PARSE_FUNC); 128950696a6eSStefan Eßer 129044d4804dSStefan Eßer // If the name is "void", and POSIX is not on, mark as void. 129150696a6eSStefan Eßer voidfn = (!BC_IS_POSIX && p->l.t == BC_LEX_NAME && 129250696a6eSStefan Eßer !strcmp(p->l.str.v, "void")); 129350696a6eSStefan Eßer 129444d4804dSStefan Eßer // We can safely do this because the expected token should not overwrite the 129544d4804dSStefan Eßer // function name. 129650696a6eSStefan Eßer bc_lex_next(&p->l); 129750696a6eSStefan Eßer 129844d4804dSStefan Eßer // If we *don't* have another name, then void is the name of the function. 129950696a6eSStefan Eßer voidfn = (voidfn && p->l.t == BC_LEX_NAME); 130050696a6eSStefan Eßer 130144d4804dSStefan Eßer // With a void function, allow POSIX to complain and get a new token. 130250696a6eSStefan Eßer if (voidfn) { 130344d4804dSStefan Eßer 130450696a6eSStefan Eßer bc_parse_err(p, BC_ERR_POSIX_VOID); 130544d4804dSStefan Eßer 130644d4804dSStefan Eßer // We can safely do this because the expected token should not overwrite 130744d4804dSStefan Eßer // the function name. 130850696a6eSStefan Eßer bc_lex_next(&p->l); 130950696a6eSStefan Eßer } 131050696a6eSStefan Eßer 131144d4804dSStefan Eßer // Must have a left paren. 131250696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) 131350696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_FUNC); 131450696a6eSStefan Eßer 131544d4804dSStefan Eßer // Make sure the functions map and vector are synchronized. 131650696a6eSStefan Eßer assert(p->prog->fns.len == p->prog->fn_map.len); 131750696a6eSStefan Eßer 131844d4804dSStefan Eßer // Must lock signals because vectors are changed, and the vector functions 131944d4804dSStefan Eßer // expect signals to be locked. 132050696a6eSStefan Eßer BC_SIG_LOCK; 132150696a6eSStefan Eßer 132244d4804dSStefan Eßer // Insert the function by name into the map and vector. 132350696a6eSStefan Eßer idx = bc_program_insertFunc(p->prog, p->l.str.v); 132450696a6eSStefan Eßer 132550696a6eSStefan Eßer BC_SIG_UNLOCK; 132650696a6eSStefan Eßer 132744d4804dSStefan Eßer // Make sure the insert worked. 132850696a6eSStefan Eßer assert(idx); 132944d4804dSStefan Eßer 133044d4804dSStefan Eßer // Update the function pointer and stuff in the parser and set its void. 133150696a6eSStefan Eßer bc_parse_updateFunc(p, idx); 133250696a6eSStefan Eßer p->func->voidfn = voidfn; 133350696a6eSStefan Eßer 133450696a6eSStefan Eßer bc_lex_next(&p->l); 133550696a6eSStefan Eßer 133644d4804dSStefan Eßer // While we do not have a right paren, we are still parsing arguments. 133750696a6eSStefan Eßer while (p->l.t != BC_LEX_RPAREN) { 133850696a6eSStefan Eßer 133950696a6eSStefan Eßer BcType t = BC_TYPE_VAR; 134050696a6eSStefan Eßer 134144d4804dSStefan Eßer // If we have an asterisk, we are parsing a reference argument. 134250696a6eSStefan Eßer if (p->l.t == BC_LEX_OP_MULTIPLY) { 134344d4804dSStefan Eßer 134450696a6eSStefan Eßer t = BC_TYPE_REF; 134550696a6eSStefan Eßer bc_lex_next(&p->l); 134644d4804dSStefan Eßer 134744d4804dSStefan Eßer // Let POSIX complain if necessary. 134850696a6eSStefan Eßer bc_parse_err(p, BC_ERR_POSIX_REF); 134950696a6eSStefan Eßer } 135050696a6eSStefan Eßer 135144d4804dSStefan Eßer // If we don't have a name, the argument will not have a name. Barf. 135250696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_NAME)) 135350696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_FUNC); 135450696a6eSStefan Eßer 135544d4804dSStefan Eßer // Increment the number of parameters. 135650696a6eSStefan Eßer p->func->nparams += 1; 135750696a6eSStefan Eßer 135844d4804dSStefan Eßer // Copy the string in the lexer so that we can use the lexer again. 135950696a6eSStefan Eßer bc_vec_string(&p->buf, p->l.str.len, p->l.str.v); 136050696a6eSStefan Eßer 136150696a6eSStefan Eßer bc_lex_next(&p->l); 136250696a6eSStefan Eßer 136344d4804dSStefan Eßer // We are parsing an array parameter if this is true. 136450696a6eSStefan Eßer if (p->l.t == BC_LEX_LBRACKET) { 136550696a6eSStefan Eßer 136644d4804dSStefan Eßer // Set the array type, unless we are already parsing a reference. 136750696a6eSStefan Eßer if (t == BC_TYPE_VAR) t = BC_TYPE_ARRAY; 136850696a6eSStefan Eßer 136950696a6eSStefan Eßer bc_lex_next(&p->l); 137050696a6eSStefan Eßer 137144d4804dSStefan Eßer // The brackets *must* be empty. 137250696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RBRACKET)) 137350696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_FUNC); 137450696a6eSStefan Eßer 137550696a6eSStefan Eßer bc_lex_next(&p->l); 137650696a6eSStefan Eßer } 137744d4804dSStefan Eßer // If we did *not* get a bracket, but we are expecting a reference, we 137844d4804dSStefan Eßer // have a problem. 137950696a6eSStefan Eßer else if (BC_ERR(t == BC_TYPE_REF)) 138050696a6eSStefan Eßer bc_parse_verr(p, BC_ERR_PARSE_REF_VAR, p->buf.v); 138150696a6eSStefan Eßer 138244d4804dSStefan Eßer // Test for comma and get the next token if it exists. 138350696a6eSStefan Eßer comma = (p->l.t == BC_LEX_COMMA); 138444d4804dSStefan Eßer if (comma) bc_lex_next(&p->l); 138550696a6eSStefan Eßer 138644d4804dSStefan Eßer // Insert the parameter into the function. 138750696a6eSStefan Eßer bc_func_insert(p->func, p->prog, p->buf.v, t, p->l.line); 138850696a6eSStefan Eßer } 138950696a6eSStefan Eßer 139044d4804dSStefan Eßer // If we have a comma, but no parameter, barf. 139150696a6eSStefan Eßer if (BC_ERR(comma)) bc_parse_err(p, BC_ERR_PARSE_FUNC); 139250696a6eSStefan Eßer 139344d4804dSStefan Eßer // Start the body. 139450696a6eSStefan Eßer flags = BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_FUNC_INNER; 139550696a6eSStefan Eßer bc_parse_startBody(p, flags); 139650696a6eSStefan Eßer 139750696a6eSStefan Eßer bc_lex_next(&p->l); 139850696a6eSStefan Eßer 139944d4804dSStefan Eßer // POSIX requires that a brace be on the same line as the function header. 140044d4804dSStefan Eßer // If we don't have a brace, let POSIX throw an error. 140150696a6eSStefan Eßer if (p->l.t != BC_LEX_LBRACE) bc_parse_err(p, BC_ERR_POSIX_BRACE); 140250696a6eSStefan Eßer } 140350696a6eSStefan Eßer 140444d4804dSStefan Eßer /** 140544d4804dSStefan Eßer * Parse an auto list. 140644d4804dSStefan Eßer * @param p The parser. 140744d4804dSStefan Eßer */ 140850696a6eSStefan Eßer static void bc_parse_auto(BcParse *p) { 140950696a6eSStefan Eßer 141050696a6eSStefan Eßer bool comma, one; 141150696a6eSStefan Eßer 141244d4804dSStefan Eßer // Error if the auto keyword appeared in the wrong place. 141350696a6eSStefan Eßer if (BC_ERR(!p->auto_part)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 141450696a6eSStefan Eßer bc_lex_next(&p->l); 141550696a6eSStefan Eßer 141650696a6eSStefan Eßer p->auto_part = comma = false; 141750696a6eSStefan Eßer 141844d4804dSStefan Eßer // We need at least one variable or array. 141944d4804dSStefan Eßer one = (p->l.t == BC_LEX_NAME); 142044d4804dSStefan Eßer 142144d4804dSStefan Eßer // While we have a variable or array. 142250696a6eSStefan Eßer while (p->l.t == BC_LEX_NAME) { 142350696a6eSStefan Eßer 142450696a6eSStefan Eßer BcType t; 142550696a6eSStefan Eßer 142644d4804dSStefan Eßer // Copy the name from the lexer, so we can use it again. 142750696a6eSStefan Eßer bc_vec_string(&p->buf, p->l.str.len - 1, p->l.str.v); 142850696a6eSStefan Eßer 142950696a6eSStefan Eßer bc_lex_next(&p->l); 143050696a6eSStefan Eßer 143144d4804dSStefan Eßer // If we are parsing an array... 143250696a6eSStefan Eßer if (p->l.t == BC_LEX_LBRACKET) { 143350696a6eSStefan Eßer 143450696a6eSStefan Eßer t = BC_TYPE_ARRAY; 143550696a6eSStefan Eßer 143650696a6eSStefan Eßer bc_lex_next(&p->l); 143750696a6eSStefan Eßer 143844d4804dSStefan Eßer // The brackets *must* be empty. 143950696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RBRACKET)) 144050696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_FUNC); 144150696a6eSStefan Eßer 144250696a6eSStefan Eßer bc_lex_next(&p->l); 144350696a6eSStefan Eßer } 144450696a6eSStefan Eßer else t = BC_TYPE_VAR; 144550696a6eSStefan Eßer 144644d4804dSStefan Eßer // Test for comma and get the next token if it exists. 144750696a6eSStefan Eßer comma = (p->l.t == BC_LEX_COMMA); 144850696a6eSStefan Eßer if (comma) bc_lex_next(&p->l); 144950696a6eSStefan Eßer 145044d4804dSStefan Eßer // Insert the auto into the function. 145150696a6eSStefan Eßer bc_func_insert(p->func, p->prog, p->buf.v, t, p->l.line); 145250696a6eSStefan Eßer } 145350696a6eSStefan Eßer 145444d4804dSStefan Eßer // If we have a comma, but no auto, barf. 145550696a6eSStefan Eßer if (BC_ERR(comma)) bc_parse_err(p, BC_ERR_PARSE_FUNC); 145644d4804dSStefan Eßer 145744d4804dSStefan Eßer // If we don't have any variables or arrays, barf. 145850696a6eSStefan Eßer if (BC_ERR(!one)) bc_parse_err(p, BC_ERR_PARSE_NO_AUTO); 145944d4804dSStefan Eßer 146044d4804dSStefan Eßer // The auto statement should be all that's in the statement. 146150696a6eSStefan Eßer if (BC_ERR(!bc_parse_isDelimiter(p))) 146250696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 146350696a6eSStefan Eßer } 146450696a6eSStefan Eßer 146544d4804dSStefan Eßer /** 146644d4804dSStefan Eßer * Parses a body. 146744d4804dSStefan Eßer * @param p The parser. 146844d4804dSStefan Eßer * @param brace True if a brace was encountered, false otherwise. 146944d4804dSStefan Eßer */ 147050696a6eSStefan Eßer static void bc_parse_body(BcParse *p, bool brace) { 147150696a6eSStefan Eßer 147250696a6eSStefan Eßer uint16_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p); 147350696a6eSStefan Eßer 147450696a6eSStefan Eßer assert(flag_ptr != NULL); 147550696a6eSStefan Eßer assert(p->flags.len >= 2); 147650696a6eSStefan Eßer 147744d4804dSStefan Eßer // The body flag is for when we expect a body. We got a body, so clear the 147844d4804dSStefan Eßer // flag. 147950696a6eSStefan Eßer *flag_ptr &= ~(BC_PARSE_FLAG_BODY); 148050696a6eSStefan Eßer 148144d4804dSStefan Eßer // If we are inside a function, that means we just barely entered it, and 148244d4804dSStefan Eßer // we can expect an auto list. 148350696a6eSStefan Eßer if (*flag_ptr & BC_PARSE_FLAG_FUNC_INNER) { 148450696a6eSStefan Eßer 148544d4804dSStefan Eßer // We *must* have a brace in this case. 148650696a6eSStefan Eßer if (BC_ERR(!brace)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); 148750696a6eSStefan Eßer 148850696a6eSStefan Eßer p->auto_part = (p->l.t != BC_LEX_KW_AUTO); 148950696a6eSStefan Eßer 149050696a6eSStefan Eßer if (!p->auto_part) { 149150696a6eSStefan Eßer 149250696a6eSStefan Eßer // Make sure this is true to not get a parse error. 149350696a6eSStefan Eßer p->auto_part = true; 149450696a6eSStefan Eßer 149544d4804dSStefan Eßer // Since we already have the auto keyword, parse. 149650696a6eSStefan Eßer bc_parse_auto(p); 149750696a6eSStefan Eßer } 149850696a6eSStefan Eßer 149944d4804dSStefan Eßer // Eat a newline. 150050696a6eSStefan Eßer if (p->l.t == BC_LEX_NLINE) bc_lex_next(&p->l); 150150696a6eSStefan Eßer } 150250696a6eSStefan Eßer else { 150350696a6eSStefan Eßer 150444d4804dSStefan Eßer // This is the easy part. 150550696a6eSStefan Eßer size_t len = p->flags.len; 150650696a6eSStefan Eßer 150750696a6eSStefan Eßer assert(*flag_ptr); 150850696a6eSStefan Eßer 150944d4804dSStefan Eßer // Parse a statement. 151050696a6eSStefan Eßer bc_parse_stmt(p); 151150696a6eSStefan Eßer 151244d4804dSStefan Eßer // This is a very important condition to get right. If there is no 151344d4804dSStefan Eßer // brace, and no body flag, and the flags len hasn't shrunk, then we 151444d4804dSStefan Eßer // have a body that was not delimited by braces, so we need to end it 151544d4804dSStefan Eßer // now, after just one statement. 151650696a6eSStefan Eßer if (!brace && !BC_PARSE_BODY(p) && len <= p->flags.len) 151750696a6eSStefan Eßer bc_parse_endBody(p, false); 151850696a6eSStefan Eßer } 151950696a6eSStefan Eßer } 152050696a6eSStefan Eßer 152144d4804dSStefan Eßer /** 152244d4804dSStefan Eßer * Parses a statement. This is the entry point for just about everything, except 152344d4804dSStefan Eßer * function definitions. 152444d4804dSStefan Eßer * @param p The parser. 152544d4804dSStefan Eßer */ 152650696a6eSStefan Eßer static void bc_parse_stmt(BcParse *p) { 152750696a6eSStefan Eßer 152850696a6eSStefan Eßer size_t len; 152950696a6eSStefan Eßer uint16_t flags; 153050696a6eSStefan Eßer BcLexType type = p->l.t; 153150696a6eSStefan Eßer 153244d4804dSStefan Eßer // Eat newline. 153350696a6eSStefan Eßer if (type == BC_LEX_NLINE) { 153450696a6eSStefan Eßer bc_lex_next(&p->l); 153550696a6eSStefan Eßer return; 153650696a6eSStefan Eßer } 153744d4804dSStefan Eßer 153844d4804dSStefan Eßer // Eat auto list. 153950696a6eSStefan Eßer if (type == BC_LEX_KW_AUTO) { 154050696a6eSStefan Eßer bc_parse_auto(p); 154150696a6eSStefan Eßer return; 154250696a6eSStefan Eßer } 154350696a6eSStefan Eßer 154444d4804dSStefan Eßer // If we reach this point, no auto list is allowed. 154550696a6eSStefan Eßer p->auto_part = false; 154650696a6eSStefan Eßer 154744d4804dSStefan Eßer // Everything but an else needs to be taken care of here, but else is 154844d4804dSStefan Eßer // special. 154950696a6eSStefan Eßer if (type != BC_LEX_KW_ELSE) { 155050696a6eSStefan Eßer 155144d4804dSStefan Eßer // After an if, no else found. 155250696a6eSStefan Eßer if (BC_PARSE_IF_END(p)) { 155344d4804dSStefan Eßer 155444d4804dSStefan Eßer // Clear the expectation for else, end body, and return. Returning 155544d4804dSStefan Eßer // gives us a clean slate for parsing again. 155650696a6eSStefan Eßer bc_parse_noElse(p); 155750696a6eSStefan Eßer if (p->flags.len > 1 && !BC_PARSE_BRACE(p)) 155850696a6eSStefan Eßer bc_parse_endBody(p, false); 155950696a6eSStefan Eßer return; 156050696a6eSStefan Eßer } 156144d4804dSStefan Eßer // With a left brace, we are parsing a body. 156250696a6eSStefan Eßer else if (type == BC_LEX_LBRACE) { 156350696a6eSStefan Eßer 156444d4804dSStefan Eßer // We need to start a body if we are not expecting one yet. 156550696a6eSStefan Eßer if (!BC_PARSE_BODY(p)) { 156650696a6eSStefan Eßer bc_parse_startBody(p, BC_PARSE_FLAG_BRACE); 156750696a6eSStefan Eßer bc_lex_next(&p->l); 156850696a6eSStefan Eßer } 156944d4804dSStefan Eßer // If we *are* expecting a body, that body should get a brace. This 157044d4804dSStefan Eßer // takes care of braces being on a different line than if and loop 157144d4804dSStefan Eßer // headers. 157250696a6eSStefan Eßer else { 157350696a6eSStefan Eßer *(BC_PARSE_TOP_FLAG_PTR(p)) |= BC_PARSE_FLAG_BRACE; 157450696a6eSStefan Eßer bc_lex_next(&p->l); 157550696a6eSStefan Eßer bc_parse_body(p, true); 157650696a6eSStefan Eßer } 157750696a6eSStefan Eßer 157844d4804dSStefan Eßer // If we have reached this point, we need to return for a clean 157944d4804dSStefan Eßer // slate. 158050696a6eSStefan Eßer return; 158150696a6eSStefan Eßer } 158244d4804dSStefan Eßer // This happens when we are expecting a body and get a single statement, 158344d4804dSStefan Eßer // i.e., a body with no braces surrounding it. Returns after for a clean 158444d4804dSStefan Eßer // slate. 158550696a6eSStefan Eßer else if (BC_PARSE_BODY(p) && !BC_PARSE_BRACE(p)) { 158650696a6eSStefan Eßer bc_parse_body(p, false); 158750696a6eSStefan Eßer return; 158850696a6eSStefan Eßer } 158950696a6eSStefan Eßer } 159050696a6eSStefan Eßer 159150696a6eSStefan Eßer len = p->flags.len; 159250696a6eSStefan Eßer flags = BC_PARSE_TOP_FLAG(p); 159350696a6eSStefan Eßer 159450696a6eSStefan Eßer switch (type) { 159550696a6eSStefan Eßer 159644d4804dSStefan Eßer // All of these are valid for expressions. 159750696a6eSStefan Eßer case BC_LEX_OP_INC: 159850696a6eSStefan Eßer case BC_LEX_OP_DEC: 159950696a6eSStefan Eßer case BC_LEX_OP_MINUS: 160050696a6eSStefan Eßer case BC_LEX_OP_BOOL_NOT: 160150696a6eSStefan Eßer case BC_LEX_LPAREN: 160250696a6eSStefan Eßer case BC_LEX_NAME: 160350696a6eSStefan Eßer case BC_LEX_NUMBER: 160450696a6eSStefan Eßer case BC_LEX_KW_IBASE: 160550696a6eSStefan Eßer case BC_LEX_KW_LAST: 160650696a6eSStefan Eßer case BC_LEX_KW_LENGTH: 160750696a6eSStefan Eßer case BC_LEX_KW_OBASE: 160850696a6eSStefan Eßer case BC_LEX_KW_SCALE: 160944d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 161050696a6eSStefan Eßer case BC_LEX_KW_SEED: 161144d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 161250696a6eSStefan Eßer case BC_LEX_KW_SQRT: 161350696a6eSStefan Eßer case BC_LEX_KW_ABS: 161444d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 161550696a6eSStefan Eßer case BC_LEX_KW_IRAND: 161644d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 161744d4804dSStefan Eßer case BC_LEX_KW_ASCIIFY: 161844d4804dSStefan Eßer case BC_LEX_KW_MODEXP: 161944d4804dSStefan Eßer case BC_LEX_KW_DIVMOD: 162050696a6eSStefan Eßer case BC_LEX_KW_READ: 162144d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 162250696a6eSStefan Eßer case BC_LEX_KW_RAND: 162344d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 162450696a6eSStefan Eßer case BC_LEX_KW_MAXIBASE: 162550696a6eSStefan Eßer case BC_LEX_KW_MAXOBASE: 162650696a6eSStefan Eßer case BC_LEX_KW_MAXSCALE: 162744d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 162850696a6eSStefan Eßer case BC_LEX_KW_MAXRAND: 162944d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 1630d43fa8efSStefan Eßer case BC_LEX_KW_LINE_LENGTH: 1631d43fa8efSStefan Eßer case BC_LEX_KW_GLOBAL_STACKS: 1632d43fa8efSStefan Eßer case BC_LEX_KW_LEADING_ZERO: 163350696a6eSStefan Eßer { 163450696a6eSStefan Eßer bc_parse_expr_status(p, BC_PARSE_PRINT, bc_parse_next_expr); 163550696a6eSStefan Eßer break; 163650696a6eSStefan Eßer } 163750696a6eSStefan Eßer 163850696a6eSStefan Eßer case BC_LEX_KW_ELSE: 163950696a6eSStefan Eßer { 164050696a6eSStefan Eßer bc_parse_else(p); 164150696a6eSStefan Eßer break; 164250696a6eSStefan Eßer } 164350696a6eSStefan Eßer 164444d4804dSStefan Eßer // Just eat. 164550696a6eSStefan Eßer case BC_LEX_SCOLON: 164650696a6eSStefan Eßer { 164750696a6eSStefan Eßer // Do nothing. 164850696a6eSStefan Eßer break; 164950696a6eSStefan Eßer } 165050696a6eSStefan Eßer 165150696a6eSStefan Eßer case BC_LEX_RBRACE: 165250696a6eSStefan Eßer { 165350696a6eSStefan Eßer bc_parse_endBody(p, true); 165450696a6eSStefan Eßer break; 165550696a6eSStefan Eßer } 165650696a6eSStefan Eßer 165750696a6eSStefan Eßer case BC_LEX_STR: 165850696a6eSStefan Eßer { 165950696a6eSStefan Eßer bc_parse_str(p, BC_INST_PRINT_STR); 166050696a6eSStefan Eßer break; 166150696a6eSStefan Eßer } 166250696a6eSStefan Eßer 166350696a6eSStefan Eßer case BC_LEX_KW_BREAK: 166450696a6eSStefan Eßer case BC_LEX_KW_CONTINUE: 166550696a6eSStefan Eßer { 166650696a6eSStefan Eßer bc_parse_loopExit(p, p->l.t); 166750696a6eSStefan Eßer break; 166850696a6eSStefan Eßer } 166950696a6eSStefan Eßer 167050696a6eSStefan Eßer case BC_LEX_KW_FOR: 167150696a6eSStefan Eßer { 167250696a6eSStefan Eßer bc_parse_for(p); 167350696a6eSStefan Eßer break; 167450696a6eSStefan Eßer } 167550696a6eSStefan Eßer 167650696a6eSStefan Eßer case BC_LEX_KW_HALT: 167750696a6eSStefan Eßer { 167850696a6eSStefan Eßer bc_parse_push(p, BC_INST_HALT); 167950696a6eSStefan Eßer bc_lex_next(&p->l); 168050696a6eSStefan Eßer break; 168150696a6eSStefan Eßer } 168250696a6eSStefan Eßer 168350696a6eSStefan Eßer case BC_LEX_KW_IF: 168450696a6eSStefan Eßer { 168550696a6eSStefan Eßer bc_parse_if(p); 168650696a6eSStefan Eßer break; 168750696a6eSStefan Eßer } 168850696a6eSStefan Eßer 168950696a6eSStefan Eßer case BC_LEX_KW_LIMITS: 169050696a6eSStefan Eßer { 169144d4804dSStefan Eßer // `limits` is a compile-time command, so execute it right away. 169250696a6eSStefan Eßer bc_vm_printf("BC_LONG_BIT = %lu\n", (ulong) BC_LONG_BIT); 169350696a6eSStefan Eßer bc_vm_printf("BC_BASE_DIGS = %lu\n", (ulong) BC_BASE_DIGS); 169450696a6eSStefan Eßer bc_vm_printf("BC_BASE_POW = %lu\n", (ulong) BC_BASE_POW); 169550696a6eSStefan Eßer bc_vm_printf("BC_OVERFLOW_MAX = %lu\n", (ulong) BC_NUM_BIGDIG_MAX); 169650696a6eSStefan Eßer bc_vm_printf("\n"); 169750696a6eSStefan Eßer bc_vm_printf("BC_BASE_MAX = %lu\n", BC_MAX_OBASE); 169850696a6eSStefan Eßer bc_vm_printf("BC_DIM_MAX = %lu\n", BC_MAX_DIM); 169950696a6eSStefan Eßer bc_vm_printf("BC_SCALE_MAX = %lu\n", BC_MAX_SCALE); 170050696a6eSStefan Eßer bc_vm_printf("BC_STRING_MAX = %lu\n", BC_MAX_STRING); 170150696a6eSStefan Eßer bc_vm_printf("BC_NAME_MAX = %lu\n", BC_MAX_NAME); 170250696a6eSStefan Eßer bc_vm_printf("BC_NUM_MAX = %lu\n", BC_MAX_NUM); 170344d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 170450696a6eSStefan Eßer bc_vm_printf("BC_RAND_MAX = %lu\n", BC_MAX_RAND); 170544d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 170650696a6eSStefan Eßer bc_vm_printf("MAX Exponent = %lu\n", BC_MAX_EXP); 170750696a6eSStefan Eßer bc_vm_printf("Number of vars = %lu\n", BC_MAX_VARS); 170850696a6eSStefan Eßer 170950696a6eSStefan Eßer bc_lex_next(&p->l); 171050696a6eSStefan Eßer 171150696a6eSStefan Eßer break; 171250696a6eSStefan Eßer } 171350696a6eSStefan Eßer 171444d4804dSStefan Eßer case BC_LEX_KW_STREAM: 171550696a6eSStefan Eßer case BC_LEX_KW_PRINT: 171650696a6eSStefan Eßer { 171744d4804dSStefan Eßer bc_parse_print(p, type); 171850696a6eSStefan Eßer break; 171950696a6eSStefan Eßer } 172050696a6eSStefan Eßer 172150696a6eSStefan Eßer case BC_LEX_KW_QUIT: 172250696a6eSStefan Eßer { 172344d4804dSStefan Eßer // Quit is a compile-time command. We don't exit directly, so the vm 172444d4804dSStefan Eßer // can clean up. 172550696a6eSStefan Eßer vm.status = BC_STATUS_QUIT; 172644d4804dSStefan Eßer BC_JMP; 172750696a6eSStefan Eßer break; 172850696a6eSStefan Eßer } 172950696a6eSStefan Eßer 173050696a6eSStefan Eßer case BC_LEX_KW_RETURN: 173150696a6eSStefan Eßer { 173250696a6eSStefan Eßer bc_parse_return(p); 173350696a6eSStefan Eßer break; 173450696a6eSStefan Eßer } 173550696a6eSStefan Eßer 173650696a6eSStefan Eßer case BC_LEX_KW_WHILE: 173750696a6eSStefan Eßer { 173850696a6eSStefan Eßer bc_parse_while(p); 173950696a6eSStefan Eßer break; 174050696a6eSStefan Eßer } 174150696a6eSStefan Eßer 174250696a6eSStefan Eßer default: 174350696a6eSStefan Eßer { 174450696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 174550696a6eSStefan Eßer } 174650696a6eSStefan Eßer } 174750696a6eSStefan Eßer 174844d4804dSStefan Eßer // If the flags did not change, we expect a delimiter. 174950696a6eSStefan Eßer if (len == p->flags.len && flags == BC_PARSE_TOP_FLAG(p)) { 175050696a6eSStefan Eßer if (BC_ERR(!bc_parse_isDelimiter(p))) 175150696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 175250696a6eSStefan Eßer } 175350696a6eSStefan Eßer 175450696a6eSStefan Eßer // Make sure semicolons are eaten. 175550696a6eSStefan Eßer while (p->l.t == BC_LEX_SCOLON) bc_lex_next(&p->l); 175650696a6eSStefan Eßer } 175750696a6eSStefan Eßer 175850696a6eSStefan Eßer void bc_parse_parse(BcParse *p) { 175950696a6eSStefan Eßer 176050696a6eSStefan Eßer assert(p); 176150696a6eSStefan Eßer 176250696a6eSStefan Eßer BC_SETJMP(exit); 176350696a6eSStefan Eßer 176444d4804dSStefan Eßer // We should not let an EOF get here unless some partial parse was not 176544d4804dSStefan Eßer // completed, in which case, it's the user's fault. 176650696a6eSStefan Eßer if (BC_ERR(p->l.t == BC_LEX_EOF)) bc_parse_err(p, BC_ERR_PARSE_EOF); 176744d4804dSStefan Eßer 176844d4804dSStefan Eßer // Functions need special parsing. 176950696a6eSStefan Eßer else if (p->l.t == BC_LEX_KW_DEFINE) { 1770d43fa8efSStefan Eßer if (BC_ERR(BC_PARSE_NO_EXEC(p))) { 1771*a30efc5cSStefan Eßer bc_parse_endif(p); 1772*a30efc5cSStefan Eßer if (BC_ERR(BC_PARSE_NO_EXEC(p))) 1773*a30efc5cSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 1774d43fa8efSStefan Eßer } 177550696a6eSStefan Eßer bc_parse_func(p); 177650696a6eSStefan Eßer } 177744d4804dSStefan Eßer 177844d4804dSStefan Eßer // Otherwise, parse a normal statement. 177950696a6eSStefan Eßer else bc_parse_stmt(p); 178050696a6eSStefan Eßer 178150696a6eSStefan Eßer exit: 178244d4804dSStefan Eßer 178350696a6eSStefan Eßer BC_SIG_MAYLOCK; 178444d4804dSStefan Eßer 178544d4804dSStefan Eßer // We need to reset on error. 178650696a6eSStefan Eßer if (BC_ERR(((vm.status && vm.status != BC_STATUS_QUIT) || vm.sig))) 178750696a6eSStefan Eßer bc_parse_reset(p); 178844d4804dSStefan Eßer 178950696a6eSStefan Eßer BC_LONGJMP_CONT; 179050696a6eSStefan Eßer } 179150696a6eSStefan Eßer 179244d4804dSStefan Eßer /** 179344d4804dSStefan Eßer * Parse an expression. This is the actual implementation of the Shunting-Yard 179444d4804dSStefan Eßer * Algorithm. 179544d4804dSStefan Eßer * @param p The parser. 179644d4804dSStefan Eßer * @param flags The flags for what is valid in the expression. 179744d4804dSStefan Eßer * @param next A set of tokens for what is valid *after* the expression. 179844d4804dSStefan Eßer * @return A parse status. In some places, an empty expression is an 179944d4804dSStefan Eßer * error, and sometimes, it is required. This allows this function 180044d4804dSStefan Eßer * to tell the caller if the expression was empty and let the 180144d4804dSStefan Eßer * caller handle it. 180244d4804dSStefan Eßer */ 180350696a6eSStefan Eßer static BcParseStatus bc_parse_expr_err(BcParse *p, uint8_t flags, 180450696a6eSStefan Eßer BcParseNext next) 180550696a6eSStefan Eßer { 180650696a6eSStefan Eßer BcInst prev = BC_INST_PRINT; 180750696a6eSStefan Eßer uchar inst = BC_INST_INVALID; 180844d4804dSStefan Eßer BcLexType top, t; 180944d4804dSStefan Eßer size_t nexprs, ops_bgn; 181050696a6eSStefan Eßer uint32_t i, nparens, nrelops; 181150696a6eSStefan Eßer bool pfirst, rprn, done, get_token, assign, bin_last, incdec, can_assign; 181250696a6eSStefan Eßer 181344d4804dSStefan Eßer // One of these *must* be true. 181450696a6eSStefan Eßer assert(!(flags & BC_PARSE_PRINT) || !(flags & BC_PARSE_NEEDVAL)); 181550696a6eSStefan Eßer 181644d4804dSStefan Eßer // These are set very carefully. In fact, controlling the values of these 181744d4804dSStefan Eßer // locals is the biggest part of making this work. ops_bgn especially is 181844d4804dSStefan Eßer // important because it marks where the operator stack begins for *this* 181944d4804dSStefan Eßer // invocation of this function. That's because bc_parse_expr_err() is 182044d4804dSStefan Eßer // recursive (the Shunting-Yard Algorithm is most easily expressed 182144d4804dSStefan Eßer // recursively when parsing subexpressions), and each invocation needs to 182244d4804dSStefan Eßer // know where to stop. 182344d4804dSStefan Eßer // 182444d4804dSStefan Eßer // - nparens is the number of left parens without matches. 182544d4804dSStefan Eßer // - nrelops is the number of relational operators that appear in the expr. 182644d4804dSStefan Eßer // - nexprs is the number of unused expressions. 182744d4804dSStefan Eßer // - rprn is a right paren encountered last. 182844d4804dSStefan Eßer // - done means the expression has been fully parsed. 182944d4804dSStefan Eßer // - get_token is true when a token is needed at the end of an iteration. 183044d4804dSStefan Eßer // - assign is true when an assignment statement was parsed last. 183144d4804dSStefan Eßer // - incdec is true when the previous operator was an inc or dec operator. 183244d4804dSStefan Eßer // - can_assign is true when an assignemnt is valid. 183344d4804dSStefan Eßer // - bin_last is true when the previous instruction was a binary operator. 183444d4804dSStefan Eßer t = p->l.t; 183550696a6eSStefan Eßer pfirst = (p->l.t == BC_LEX_LPAREN); 183650696a6eSStefan Eßer nparens = nrelops = 0; 183744d4804dSStefan Eßer nexprs = 0; 183844d4804dSStefan Eßer ops_bgn = p->ops.len; 183950696a6eSStefan Eßer rprn = done = get_token = assign = incdec = can_assign = false; 184050696a6eSStefan Eßer bin_last = true; 184150696a6eSStefan Eßer 184250696a6eSStefan Eßer // We want to eat newlines if newlines are not a valid ending token. 184350696a6eSStefan Eßer // This is for spacing in things like for loop headers. 184450696a6eSStefan Eßer if (!(flags & BC_PARSE_NOREAD)) { 184550696a6eSStefan Eßer while ((t = p->l.t) == BC_LEX_NLINE) bc_lex_next(&p->l); 184650696a6eSStefan Eßer } 184750696a6eSStefan Eßer 184844d4804dSStefan Eßer // This is the Shunting-Yard algorithm loop. 184950696a6eSStefan Eßer for (; !done && BC_PARSE_EXPR(t); t = p->l.t) 185050696a6eSStefan Eßer { 185150696a6eSStefan Eßer switch (t) { 185250696a6eSStefan Eßer 185350696a6eSStefan Eßer case BC_LEX_OP_INC: 185450696a6eSStefan Eßer case BC_LEX_OP_DEC: 185550696a6eSStefan Eßer { 185644d4804dSStefan Eßer // These operators can only be used with items that can be 185744d4804dSStefan Eßer // assigned to. 185850696a6eSStefan Eßer if (BC_ERR(incdec)) bc_parse_err(p, BC_ERR_PARSE_ASSIGN); 185944d4804dSStefan Eßer 186050696a6eSStefan Eßer bc_parse_incdec(p, &prev, &can_assign, &nexprs, flags); 186144d4804dSStefan Eßer 186250696a6eSStefan Eßer rprn = get_token = bin_last = false; 186350696a6eSStefan Eßer incdec = true; 186450696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY); 186544d4804dSStefan Eßer 186650696a6eSStefan Eßer break; 186750696a6eSStefan Eßer } 186850696a6eSStefan Eßer 186950696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH 187050696a6eSStefan Eßer case BC_LEX_OP_TRUNC: 187150696a6eSStefan Eßer { 187244d4804dSStefan Eßer // The previous token must have been a leaf expression, or the 187344d4804dSStefan Eßer // operator is in the wrong place. 187450696a6eSStefan Eßer if (BC_ERR(!BC_PARSE_LEAF(prev, bin_last, rprn))) 187550696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 187650696a6eSStefan Eßer 187750696a6eSStefan Eßer // I can just add the instruction because 187850696a6eSStefan Eßer // negative will already be taken care of. 187950696a6eSStefan Eßer bc_parse_push(p, BC_INST_TRUNC); 188044d4804dSStefan Eßer 188150696a6eSStefan Eßer rprn = can_assign = incdec = false; 188250696a6eSStefan Eßer get_token = true; 188350696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY); 188444d4804dSStefan Eßer 188550696a6eSStefan Eßer break; 188650696a6eSStefan Eßer } 188750696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 188850696a6eSStefan Eßer 188950696a6eSStefan Eßer case BC_LEX_OP_MINUS: 189050696a6eSStefan Eßer { 189150696a6eSStefan Eßer bc_parse_minus(p, &prev, ops_bgn, rprn, bin_last, &nexprs); 189244d4804dSStefan Eßer 189350696a6eSStefan Eßer rprn = get_token = can_assign = false; 189444d4804dSStefan Eßer 189544d4804dSStefan Eßer // This is true if it was a binary operator last. 189650696a6eSStefan Eßer bin_last = (prev == BC_INST_MINUS); 189750696a6eSStefan Eßer if (bin_last) incdec = false; 189844d4804dSStefan Eßer 189950696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY); 190044d4804dSStefan Eßer 190150696a6eSStefan Eßer break; 190250696a6eSStefan Eßer } 190350696a6eSStefan Eßer 190444d4804dSStefan Eßer // All of this group, including the fallthrough, is to parse binary 190544d4804dSStefan Eßer // operators. 190650696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_POWER: 190750696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_MULTIPLY: 190850696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_DIVIDE: 190950696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_MODULUS: 191050696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_PLUS: 191150696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_MINUS: 191250696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH 191350696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_PLACES: 191450696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_LSHIFT: 191550696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_RSHIFT: 191650696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 191750696a6eSStefan Eßer case BC_LEX_OP_ASSIGN: 191850696a6eSStefan Eßer { 191944d4804dSStefan Eßer // We need to make sure the assignment is valid. 192050696a6eSStefan Eßer if (!BC_PARSE_INST_VAR(prev)) 192150696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_ASSIGN); 192250696a6eSStefan Eßer } 192350696a6eSStefan Eßer // Fallthrough. 192450696a6eSStefan Eßer BC_FALLTHROUGH 192550696a6eSStefan Eßer 192650696a6eSStefan Eßer case BC_LEX_OP_POWER: 192750696a6eSStefan Eßer case BC_LEX_OP_MULTIPLY: 192850696a6eSStefan Eßer case BC_LEX_OP_DIVIDE: 192950696a6eSStefan Eßer case BC_LEX_OP_MODULUS: 193050696a6eSStefan Eßer case BC_LEX_OP_PLUS: 193150696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH 193250696a6eSStefan Eßer case BC_LEX_OP_PLACES: 193350696a6eSStefan Eßer case BC_LEX_OP_LSHIFT: 193450696a6eSStefan Eßer case BC_LEX_OP_RSHIFT: 193550696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 193650696a6eSStefan Eßer case BC_LEX_OP_REL_EQ: 193750696a6eSStefan Eßer case BC_LEX_OP_REL_LE: 193850696a6eSStefan Eßer case BC_LEX_OP_REL_GE: 193950696a6eSStefan Eßer case BC_LEX_OP_REL_NE: 194050696a6eSStefan Eßer case BC_LEX_OP_REL_LT: 194150696a6eSStefan Eßer case BC_LEX_OP_REL_GT: 194250696a6eSStefan Eßer case BC_LEX_OP_BOOL_NOT: 194350696a6eSStefan Eßer case BC_LEX_OP_BOOL_OR: 194450696a6eSStefan Eßer case BC_LEX_OP_BOOL_AND: 194550696a6eSStefan Eßer { 194644d4804dSStefan Eßer // This is true if the operator if the token is a prefix 194744d4804dSStefan Eßer // operator. This is only for boolean not. 194850696a6eSStefan Eßer if (BC_PARSE_OP_PREFIX(t)) { 194944d4804dSStefan Eßer 195044d4804dSStefan Eßer // Prefix operators are only allowed after binary operators 195144d4804dSStefan Eßer // or prefix operators. 195250696a6eSStefan Eßer if (BC_ERR(!bin_last && !BC_PARSE_OP_PREFIX(p->l.last))) 195350696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 195450696a6eSStefan Eßer } 195544d4804dSStefan Eßer // If we execute the else, that means we have a binary operator. 195644d4804dSStefan Eßer // If the previous operator was a prefix or a binary operator, 195744d4804dSStefan Eßer // then a binary operator is not allowed. 195850696a6eSStefan Eßer else if (BC_ERR(BC_PARSE_PREV_PREFIX(prev) || bin_last)) 195950696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 196050696a6eSStefan Eßer 196150696a6eSStefan Eßer nrelops += (t >= BC_LEX_OP_REL_EQ && t <= BC_LEX_OP_REL_GT); 196250696a6eSStefan Eßer prev = BC_PARSE_TOKEN_INST(t); 196344d4804dSStefan Eßer 196450696a6eSStefan Eßer bc_parse_operator(p, t, ops_bgn, &nexprs); 196544d4804dSStefan Eßer 196650696a6eSStefan Eßer rprn = incdec = can_assign = false; 196750696a6eSStefan Eßer get_token = true; 196850696a6eSStefan Eßer bin_last = !BC_PARSE_OP_PREFIX(t); 196950696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY); 197050696a6eSStefan Eßer 197150696a6eSStefan Eßer break; 197250696a6eSStefan Eßer } 197350696a6eSStefan Eßer 197450696a6eSStefan Eßer case BC_LEX_LPAREN: 197550696a6eSStefan Eßer { 197644d4804dSStefan Eßer // A left paren is *not* allowed right after a leaf expr. 197750696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) 197850696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 197950696a6eSStefan Eßer 198050696a6eSStefan Eßer nparens += 1; 198150696a6eSStefan Eßer rprn = incdec = can_assign = false; 198250696a6eSStefan Eßer get_token = true; 198344d4804dSStefan Eßer 198444d4804dSStefan Eßer // Push the paren onto the operator stack. 198550696a6eSStefan Eßer bc_vec_push(&p->ops, &t); 198650696a6eSStefan Eßer 198750696a6eSStefan Eßer break; 198850696a6eSStefan Eßer } 198950696a6eSStefan Eßer 199050696a6eSStefan Eßer case BC_LEX_RPAREN: 199150696a6eSStefan Eßer { 199244d4804dSStefan Eßer // This needs to be a status. The error is handled in 199344d4804dSStefan Eßer // bc_parse_expr_status(). 199450696a6eSStefan Eßer if (BC_ERR(p->l.last == BC_LEX_LPAREN)) 199550696a6eSStefan Eßer return BC_PARSE_STATUS_EMPTY_EXPR; 199650696a6eSStefan Eßer 199744d4804dSStefan Eßer // The right paren must not come after a prefix or binary 199844d4804dSStefan Eßer // operator. 199950696a6eSStefan Eßer if (BC_ERR(bin_last || BC_PARSE_PREV_PREFIX(prev))) 200050696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 200150696a6eSStefan Eßer 200244d4804dSStefan Eßer // If there are no parens left, we are done, but we need another 200344d4804dSStefan Eßer // token. 200450696a6eSStefan Eßer if (!nparens) { 200550696a6eSStefan Eßer done = true; 200650696a6eSStefan Eßer get_token = false; 200750696a6eSStefan Eßer break; 200850696a6eSStefan Eßer } 200950696a6eSStefan Eßer 201050696a6eSStefan Eßer nparens -= 1; 201150696a6eSStefan Eßer rprn = true; 201250696a6eSStefan Eßer get_token = bin_last = incdec = false; 201350696a6eSStefan Eßer 201450696a6eSStefan Eßer bc_parse_rightParen(p, &nexprs); 201550696a6eSStefan Eßer 201650696a6eSStefan Eßer break; 201750696a6eSStefan Eßer } 201850696a6eSStefan Eßer 201944d4804dSStefan Eßer case BC_LEX_STR: 202044d4804dSStefan Eßer { 202144d4804dSStefan Eßer // POSIX only allows strings alone. 202244d4804dSStefan Eßer if (BC_IS_POSIX) bc_parse_err(p, BC_ERR_POSIX_EXPR_STRING); 202344d4804dSStefan Eßer 202444d4804dSStefan Eßer // A string is a leaf and cannot come right after a leaf. 202544d4804dSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) 202644d4804dSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 202744d4804dSStefan Eßer 202844d4804dSStefan Eßer bc_parse_addString(p); 202944d4804dSStefan Eßer 203044d4804dSStefan Eßer get_token = true; 203144d4804dSStefan Eßer bin_last = rprn = false; 203244d4804dSStefan Eßer nexprs += 1; 203344d4804dSStefan Eßer 203444d4804dSStefan Eßer break; 203544d4804dSStefan Eßer } 203644d4804dSStefan Eßer 203750696a6eSStefan Eßer case BC_LEX_NAME: 203850696a6eSStefan Eßer { 203944d4804dSStefan Eßer // A name is a leaf and cannot come right after a leaf. 204050696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) 204150696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 204250696a6eSStefan Eßer 204350696a6eSStefan Eßer get_token = bin_last = false; 204444d4804dSStefan Eßer 204544d4804dSStefan Eßer bc_parse_name(p, &prev, &can_assign, flags & ~BC_PARSE_NOCALL); 204644d4804dSStefan Eßer 204750696a6eSStefan Eßer rprn = (prev == BC_INST_CALL); 204850696a6eSStefan Eßer nexprs += 1; 204950696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY); 205050696a6eSStefan Eßer 205150696a6eSStefan Eßer break; 205250696a6eSStefan Eßer } 205350696a6eSStefan Eßer 205450696a6eSStefan Eßer case BC_LEX_NUMBER: 205550696a6eSStefan Eßer { 205644d4804dSStefan Eßer // A number is a leaf and cannot come right after a leaf. 205750696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) 205850696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 205950696a6eSStefan Eßer 206044d4804dSStefan Eßer // The number instruction is pushed in here. 206150696a6eSStefan Eßer bc_parse_number(p); 206244d4804dSStefan Eßer 206350696a6eSStefan Eßer nexprs += 1; 206450696a6eSStefan Eßer prev = BC_INST_NUM; 206550696a6eSStefan Eßer get_token = true; 206650696a6eSStefan Eßer rprn = bin_last = can_assign = false; 206750696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY); 206850696a6eSStefan Eßer 206950696a6eSStefan Eßer break; 207050696a6eSStefan Eßer } 207150696a6eSStefan Eßer 207250696a6eSStefan Eßer case BC_LEX_KW_IBASE: 207350696a6eSStefan Eßer case BC_LEX_KW_LAST: 207450696a6eSStefan Eßer case BC_LEX_KW_OBASE: 207544d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 207650696a6eSStefan Eßer case BC_LEX_KW_SEED: 207744d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 207850696a6eSStefan Eßer { 207944d4804dSStefan Eßer // All of these are leaves and cannot come right after a leaf. 208050696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) 208150696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 208250696a6eSStefan Eßer 208350696a6eSStefan Eßer prev = t - BC_LEX_KW_LAST + BC_INST_LAST; 208450696a6eSStefan Eßer bc_parse_push(p, prev); 208550696a6eSStefan Eßer 208650696a6eSStefan Eßer get_token = can_assign = true; 208750696a6eSStefan Eßer rprn = bin_last = false; 208850696a6eSStefan Eßer nexprs += 1; 208950696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY); 209050696a6eSStefan Eßer 209150696a6eSStefan Eßer break; 209250696a6eSStefan Eßer } 209350696a6eSStefan Eßer 209450696a6eSStefan Eßer case BC_LEX_KW_LENGTH: 209550696a6eSStefan Eßer case BC_LEX_KW_SQRT: 209650696a6eSStefan Eßer case BC_LEX_KW_ABS: 209744d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 209850696a6eSStefan Eßer case BC_LEX_KW_IRAND: 209944d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 210044d4804dSStefan Eßer case BC_LEX_KW_ASCIIFY: 210150696a6eSStefan Eßer { 210244d4804dSStefan Eßer // All of these are leaves and cannot come right after a leaf. 210350696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) 210450696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 210550696a6eSStefan Eßer 210650696a6eSStefan Eßer bc_parse_builtin(p, t, flags, &prev); 210744d4804dSStefan Eßer 210850696a6eSStefan Eßer rprn = get_token = bin_last = incdec = can_assign = false; 210950696a6eSStefan Eßer nexprs += 1; 211050696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY); 211150696a6eSStefan Eßer 211250696a6eSStefan Eßer break; 211350696a6eSStefan Eßer } 211450696a6eSStefan Eßer 211550696a6eSStefan Eßer case BC_LEX_KW_READ: 211644d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 211750696a6eSStefan Eßer case BC_LEX_KW_RAND: 211844d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 211950696a6eSStefan Eßer case BC_LEX_KW_MAXIBASE: 212050696a6eSStefan Eßer case BC_LEX_KW_MAXOBASE: 212150696a6eSStefan Eßer case BC_LEX_KW_MAXSCALE: 212244d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH 212350696a6eSStefan Eßer case BC_LEX_KW_MAXRAND: 212444d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH 2125d43fa8efSStefan Eßer case BC_LEX_KW_LINE_LENGTH: 2126d43fa8efSStefan Eßer case BC_LEX_KW_GLOBAL_STACKS: 2127d43fa8efSStefan Eßer case BC_LEX_KW_LEADING_ZERO: 212850696a6eSStefan Eßer { 212944d4804dSStefan Eßer // All of these are leaves and cannot come right after a leaf. 213050696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) 213150696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 213244d4804dSStefan Eßer 213344d4804dSStefan Eßer // Error if we have read and it's not allowed. 213450696a6eSStefan Eßer else if (t == BC_LEX_KW_READ && BC_ERR(flags & BC_PARSE_NOREAD)) 213550696a6eSStefan Eßer bc_parse_err(p, BC_ERR_EXEC_REC_READ); 213644d4804dSStefan Eßer 213750696a6eSStefan Eßer prev = t - BC_LEX_KW_READ + BC_INST_READ; 213850696a6eSStefan Eßer bc_parse_noArgBuiltin(p, prev); 213950696a6eSStefan Eßer 214050696a6eSStefan Eßer rprn = get_token = bin_last = incdec = can_assign = false; 214150696a6eSStefan Eßer nexprs += 1; 214250696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY); 214350696a6eSStefan Eßer 214450696a6eSStefan Eßer break; 214550696a6eSStefan Eßer } 214650696a6eSStefan Eßer 214750696a6eSStefan Eßer case BC_LEX_KW_SCALE: 214850696a6eSStefan Eßer { 214944d4804dSStefan Eßer // This is a leaf and cannot come right after a leaf. 215050696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) 215150696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 215250696a6eSStefan Eßer 215344d4804dSStefan Eßer // Scale needs special work because it can be a variable *or* a 215444d4804dSStefan Eßer // function. 215550696a6eSStefan Eßer bc_parse_scale(p, &prev, &can_assign, flags); 215644d4804dSStefan Eßer 215750696a6eSStefan Eßer rprn = get_token = bin_last = false; 215850696a6eSStefan Eßer nexprs += 1; 215950696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY); 216050696a6eSStefan Eßer 216150696a6eSStefan Eßer break; 216250696a6eSStefan Eßer } 216350696a6eSStefan Eßer 216444d4804dSStefan Eßer case BC_LEX_KW_MODEXP: 216544d4804dSStefan Eßer case BC_LEX_KW_DIVMOD: 216644d4804dSStefan Eßer { 216744d4804dSStefan Eßer // This is a leaf and cannot come right after a leaf. 216844d4804dSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) 216944d4804dSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 217044d4804dSStefan Eßer 217144d4804dSStefan Eßer bc_parse_builtin3(p, t, flags, &prev); 217244d4804dSStefan Eßer 217344d4804dSStefan Eßer rprn = get_token = bin_last = incdec = can_assign = false; 217444d4804dSStefan Eßer nexprs += 1; 217544d4804dSStefan Eßer flags &= ~(BC_PARSE_ARRAY); 217644d4804dSStefan Eßer 217744d4804dSStefan Eßer break; 217844d4804dSStefan Eßer } 217944d4804dSStefan Eßer 218050696a6eSStefan Eßer default: 218150696a6eSStefan Eßer { 218250696a6eSStefan Eßer #ifndef NDEBUG 218344d4804dSStefan Eßer // We should never get here, even in debug builds. 218450696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN); 218550696a6eSStefan Eßer break; 218650696a6eSStefan Eßer #endif // NDEBUG 218750696a6eSStefan Eßer } 218850696a6eSStefan Eßer } 218950696a6eSStefan Eßer 219050696a6eSStefan Eßer if (get_token) bc_lex_next(&p->l); 219150696a6eSStefan Eßer } 219250696a6eSStefan Eßer 219344d4804dSStefan Eßer // Now that we have parsed the expression, we need to empty the operator 219444d4804dSStefan Eßer // stack. 219550696a6eSStefan Eßer while (p->ops.len > ops_bgn) { 219650696a6eSStefan Eßer 219750696a6eSStefan Eßer top = BC_PARSE_TOP_OP(p); 219850696a6eSStefan Eßer assign = top >= BC_LEX_OP_ASSIGN_POWER && top <= BC_LEX_OP_ASSIGN; 219950696a6eSStefan Eßer 220044d4804dSStefan Eßer // There should not be *any* parens on the stack anymore. 220150696a6eSStefan Eßer if (BC_ERR(top == BC_LEX_LPAREN || top == BC_LEX_RPAREN)) 220250696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 220350696a6eSStefan Eßer 220450696a6eSStefan Eßer bc_parse_push(p, BC_PARSE_TOKEN_INST(top)); 220550696a6eSStefan Eßer 220644d4804dSStefan Eßer // Adjust the number of unused expressions. 220750696a6eSStefan Eßer nexprs -= !BC_PARSE_OP_PREFIX(top); 220850696a6eSStefan Eßer bc_vec_pop(&p->ops); 220950696a6eSStefan Eßer 221050696a6eSStefan Eßer incdec = false; 221150696a6eSStefan Eßer } 221250696a6eSStefan Eßer 221344d4804dSStefan Eßer // There must be only one expression at the top. 221450696a6eSStefan Eßer if (BC_ERR(nexprs != 1)) bc_parse_err(p, BC_ERR_PARSE_EXPR); 221550696a6eSStefan Eßer 221644d4804dSStefan Eßer // Check that the next token is correct. 221750696a6eSStefan Eßer for (i = 0; i < next.len && t != next.tokens[i]; ++i); 221850696a6eSStefan Eßer if (BC_ERR(i == next.len && !bc_parse_isDelimiter(p))) 221950696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR); 222050696a6eSStefan Eßer 222144d4804dSStefan Eßer // Check that POSIX would be happy with the number of relational operators. 222250696a6eSStefan Eßer if (!(flags & BC_PARSE_REL) && nrelops) 222350696a6eSStefan Eßer bc_parse_err(p, BC_ERR_POSIX_REL_POS); 222450696a6eSStefan Eßer else if ((flags & BC_PARSE_REL) && nrelops > 1) 222550696a6eSStefan Eßer bc_parse_err(p, BC_ERR_POSIX_MULTIREL); 222650696a6eSStefan Eßer 222744d4804dSStefan Eßer // If this is true, then we might be in a situation where we don't print. 222844d4804dSStefan Eßer // We would want to have the increment/decrement operator not make an extra 222944d4804dSStefan Eßer // copy if it's not necessary. 223050696a6eSStefan Eßer if (!(flags & BC_PARSE_NEEDVAL) && !pfirst) { 223150696a6eSStefan Eßer 223244d4804dSStefan Eßer // We have the easy case if the last operator was an assignment 223344d4804dSStefan Eßer // operator. 223450696a6eSStefan Eßer if (assign) { 223550696a6eSStefan Eßer inst = *((uchar*) bc_vec_top(&p->func->code)); 223650696a6eSStefan Eßer inst += (BC_INST_ASSIGN_POWER_NO_VAL - BC_INST_ASSIGN_POWER); 223750696a6eSStefan Eßer incdec = false; 223850696a6eSStefan Eßer } 223944d4804dSStefan Eßer // If we have an inc/dec operator and we are *not* printing, implement 224044d4804dSStefan Eßer // the optimization to get rid of the extra copy. 224150696a6eSStefan Eßer else if (incdec && !(flags & BC_PARSE_PRINT)) { 224250696a6eSStefan Eßer inst = *((uchar*) bc_vec_top(&p->func->code)); 224350696a6eSStefan Eßer incdec = (inst <= BC_INST_DEC); 224450696a6eSStefan Eßer inst = BC_INST_ASSIGN_PLUS_NO_VAL + (inst != BC_INST_INC && 224550696a6eSStefan Eßer inst != BC_INST_ASSIGN_PLUS); 224650696a6eSStefan Eßer } 224750696a6eSStefan Eßer 224844d4804dSStefan Eßer // This condition allows us to change the previous assignment 224944d4804dSStefan Eßer // instruction (which does a copy) for a NO_VAL version, which does not. 225044d4804dSStefan Eßer // This condition is set if either of the above if statements ends up 225144d4804dSStefan Eßer // being true. 225250696a6eSStefan Eßer if (inst >= BC_INST_ASSIGN_POWER_NO_VAL && 225350696a6eSStefan Eßer inst <= BC_INST_ASSIGN_NO_VAL) 225450696a6eSStefan Eßer { 225544d4804dSStefan Eßer // Pop the previous assignment instruction and push a new one. 225644d4804dSStefan Eßer // Inc/dec needs the extra instruction because it is now a binary 225744d4804dSStefan Eßer // operator and needs a second operand. 225850696a6eSStefan Eßer bc_vec_pop(&p->func->code); 225950696a6eSStefan Eßer if (incdec) bc_parse_push(p, BC_INST_ONE); 226050696a6eSStefan Eßer bc_parse_push(p, inst); 226150696a6eSStefan Eßer } 226250696a6eSStefan Eßer } 226350696a6eSStefan Eßer 226444d4804dSStefan Eßer // If we might have to print... 226550696a6eSStefan Eßer if ((flags & BC_PARSE_PRINT)) { 226644d4804dSStefan Eßer 226744d4804dSStefan Eßer // With a paren first or the last operator not being an assignment, we 226844d4804dSStefan Eßer // *do* want to print. 226950696a6eSStefan Eßer if (pfirst || !assign) bc_parse_push(p, BC_INST_PRINT); 227050696a6eSStefan Eßer } 227144d4804dSStefan Eßer // We need to make sure to push a pop instruction for assignment statements 227244d4804dSStefan Eßer // that will not print. The print will pop, but without it, we need to pop. 227350696a6eSStefan Eßer else if (!(flags & BC_PARSE_NEEDVAL) && 227450696a6eSStefan Eßer (inst < BC_INST_ASSIGN_POWER_NO_VAL || 227550696a6eSStefan Eßer inst > BC_INST_ASSIGN_NO_VAL)) 227650696a6eSStefan Eßer { 227750696a6eSStefan Eßer bc_parse_push(p, BC_INST_POP); 227850696a6eSStefan Eßer } 227950696a6eSStefan Eßer 228050696a6eSStefan Eßer // We want to eat newlines if newlines are not a valid ending token. 228150696a6eSStefan Eßer // This is for spacing in things like for loop headers. 228244d4804dSStefan Eßer // 228344d4804dSStefan Eßer // Yes, this is one case where I reuse a variable for a different purpose; 228444d4804dSStefan Eßer // in this case, incdec being true now means that newlines are not valid. 228550696a6eSStefan Eßer for (incdec = true, i = 0; i < next.len && incdec; ++i) 228650696a6eSStefan Eßer incdec = (next.tokens[i] != BC_LEX_NLINE); 228750696a6eSStefan Eßer if (incdec) { 228850696a6eSStefan Eßer while (p->l.t == BC_LEX_NLINE) bc_lex_next(&p->l); 228950696a6eSStefan Eßer } 229050696a6eSStefan Eßer 229150696a6eSStefan Eßer return BC_PARSE_STATUS_SUCCESS; 229250696a6eSStefan Eßer } 229350696a6eSStefan Eßer 229444d4804dSStefan Eßer /** 229544d4804dSStefan Eßer * Parses an expression with bc_parse_expr_err(), but throws an error if it gets 229644d4804dSStefan Eßer * an empty expression. 229744d4804dSStefan Eßer * @param p The parser. 229844d4804dSStefan Eßer * @param flags The flags for what is valid in the expression. 229944d4804dSStefan Eßer * @param next A set of tokens for what is valid *after* the expression. 230044d4804dSStefan Eßer */ 230144d4804dSStefan Eßer static void bc_parse_expr_status(BcParse *p, uint8_t flags, BcParseNext next) { 230250696a6eSStefan Eßer 230350696a6eSStefan Eßer BcParseStatus s = bc_parse_expr_err(p, flags, next); 230450696a6eSStefan Eßer 230550696a6eSStefan Eßer if (BC_ERR(s == BC_PARSE_STATUS_EMPTY_EXPR)) 230650696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EMPTY_EXPR); 230750696a6eSStefan Eßer } 230850696a6eSStefan Eßer 230950696a6eSStefan Eßer void bc_parse_expr(BcParse *p, uint8_t flags) { 231050696a6eSStefan Eßer assert(p); 231150696a6eSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_read); 231250696a6eSStefan Eßer } 231350696a6eSStefan Eßer #endif // BC_ENABLED 2314