150696a6eSStefan Eßer /*
250696a6eSStefan Eßer * *****************************************************************************
350696a6eSStefan Eßer *
450696a6eSStefan Eßer * SPDX-License-Identifier: BSD-2-Clause
550696a6eSStefan Eßer *
6a970610aSStefan Eßer * Copyright (c) 2018-2024 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
5878bc019dSStefan Eßer static void
5978bc019dSStefan Eßer bc_parse_else(BcParse* p);
6078bc019dSStefan Eßer
6178bc019dSStefan Eßer static void
6278bc019dSStefan Eßer bc_parse_stmt(BcParse* p);
6378bc019dSStefan Eßer
6478bc019dSStefan Eßer static BcParseStatus
6578bc019dSStefan Eßer bc_parse_expr_err(BcParse* p, uint8_t flags, BcParseNext next);
6678bc019dSStefan Eßer
6778bc019dSStefan Eßer static void
6878bc019dSStefan Eßer bc_parse_expr_status(BcParse* p, uint8_t flags, BcParseNext next);
6950696a6eSStefan Eßer
7044d4804dSStefan Eßer /**
7144d4804dSStefan Eßer * Returns true if an instruction could only have come from a "leaf" expression.
7244d4804dSStefan Eßer * For more on what leaf expressions are, read the comment for BC_PARSE_LEAF().
7344d4804dSStefan Eßer * @param t The instruction to test.
74d101cdd6SStefan Eßer * @return True if the instruction is a from a leaf expression.
7544d4804dSStefan Eßer */
7678bc019dSStefan Eßer static bool
bc_parse_inst_isLeaf(BcInst t)7778bc019dSStefan Eßer bc_parse_inst_isLeaf(BcInst t)
7878bc019dSStefan Eßer {
79d101cdd6SStefan Eßer return (t >= BC_INST_NUM && t <= BC_INST_LEADING_ZERO) ||
8050696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH
8150696a6eSStefan Eßer t == BC_INST_TRUNC ||
8250696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
8350696a6eSStefan Eßer t <= BC_INST_DEC;
8450696a6eSStefan Eßer }
8550696a6eSStefan Eßer
8644d4804dSStefan Eßer /**
8744d4804dSStefan Eßer * Returns true if the *previous* token was a delimiter. A delimiter is anything
8844d4804dSStefan Eßer * that can legally end a statement. In bc's case, it could be a newline, a
8944d4804dSStefan Eßer * semicolon, and a brace in certain cases.
9044d4804dSStefan Eßer * @param p The parser.
9110041e99SStefan Eßer * @return True if the token is a legal delimiter.
9244d4804dSStefan Eßer */
9378bc019dSStefan Eßer static bool
bc_parse_isDelimiter(const BcParse * p)9478bc019dSStefan Eßer bc_parse_isDelimiter(const BcParse* p)
9578bc019dSStefan Eßer {
9650696a6eSStefan Eßer BcLexType t = p->l.t;
9744d4804dSStefan Eßer bool good;
9850696a6eSStefan Eßer
9944d4804dSStefan Eßer // If it's an obvious delimiter, say so.
10050696a6eSStefan Eßer if (BC_PARSE_DELIMITER(t)) return true;
10150696a6eSStefan Eßer
10244d4804dSStefan Eßer good = false;
10344d4804dSStefan Eßer
10444d4804dSStefan Eßer // If the current token is a keyword, then...beware. That means that we need
10544d4804dSStefan Eßer // to check for a "dangling" else, where there was no brace-delimited block
10644d4804dSStefan Eßer // on the previous if.
10778bc019dSStefan Eßer if (t == BC_LEX_KW_ELSE)
10878bc019dSStefan Eßer {
10950696a6eSStefan Eßer size_t i;
11050696a6eSStefan Eßer uint16_t *fptr = NULL, flags = BC_PARSE_FLAG_ELSE;
11150696a6eSStefan Eßer
11244d4804dSStefan Eßer // As long as going up the stack is valid for a dangling else, keep on.
11378bc019dSStefan Eßer for (i = 0; i < p->flags.len && BC_PARSE_BLOCK_STMT(flags); ++i)
11478bc019dSStefan Eßer {
11550696a6eSStefan Eßer fptr = bc_vec_item_rev(&p->flags, i);
11650696a6eSStefan Eßer flags = *fptr;
11750696a6eSStefan Eßer
11844d4804dSStefan Eßer // If we need a brace and don't have one, then we don't have a
11944d4804dSStefan Eßer // delimiter.
12050696a6eSStefan Eßer if ((flags & BC_PARSE_FLAG_BRACE) && p->l.last != BC_LEX_RBRACE)
12178bc019dSStefan Eßer {
12250696a6eSStefan Eßer return false;
12350696a6eSStefan Eßer }
12478bc019dSStefan Eßer }
12550696a6eSStefan Eßer
12644d4804dSStefan Eßer // Oh, and we had also better have an if statement somewhere.
12750696a6eSStefan Eßer good = ((flags & BC_PARSE_FLAG_IF) != 0);
12850696a6eSStefan Eßer }
12978bc019dSStefan Eßer else if (t == BC_LEX_RBRACE)
13078bc019dSStefan Eßer {
13150696a6eSStefan Eßer size_t i;
13250696a6eSStefan Eßer
13344d4804dSStefan Eßer // Since we have a brace, we need to just check if a brace was needed.
13478bc019dSStefan Eßer for (i = 0; !good && i < p->flags.len; ++i)
13578bc019dSStefan Eßer {
13650696a6eSStefan Eßer uint16_t* fptr = bc_vec_item_rev(&p->flags, i);
13750696a6eSStefan Eßer good = (((*fptr) & BC_PARSE_FLAG_BRACE) != 0);
13850696a6eSStefan Eßer }
13950696a6eSStefan Eßer }
14050696a6eSStefan Eßer
14150696a6eSStefan Eßer return good;
14250696a6eSStefan Eßer }
14350696a6eSStefan Eßer
14444d4804dSStefan Eßer /**
14510041e99SStefan Eßer * Returns true if we are in top level of a function body. The POSIX grammar
14610041e99SStefan Eßer * is defined such that anything is allowed after a function body, so we must
14710041e99SStefan Eßer * use this function to detect that case when ending a function body.
14810041e99SStefan Eßer * @param p The parser.
14910041e99SStefan Eßer * @return True if we are in the top level of parsing a function body.
15010041e99SStefan Eßer */
15178bc019dSStefan Eßer static bool
bc_parse_TopFunc(const BcParse * p)15278bc019dSStefan Eßer bc_parse_TopFunc(const BcParse* p)
15378bc019dSStefan Eßer {
15410041e99SStefan Eßer bool good = p->flags.len == 2;
15510041e99SStefan Eßer
15610041e99SStefan Eßer uint16_t val = BC_PARSE_FLAG_BRACE | BC_PARSE_FLAG_FUNC_INNER;
15710041e99SStefan Eßer val |= BC_PARSE_FLAG_FUNC;
15810041e99SStefan Eßer
15910041e99SStefan Eßer return good && BC_PARSE_TOP_FLAG(p) == val;
16010041e99SStefan Eßer }
16110041e99SStefan Eßer
16210041e99SStefan Eßer /**
16344d4804dSStefan Eßer * Sets a previously defined exit label. What are labels? See the bc Parsing
16444d4804dSStefan Eßer * section of the Development manual (manuals/development.md).
16544d4804dSStefan Eßer * @param p The parser.
16644d4804dSStefan Eßer */
16778bc019dSStefan Eßer static void
bc_parse_setLabel(BcParse * p)16878bc019dSStefan Eßer bc_parse_setLabel(BcParse* p)
16978bc019dSStefan Eßer {
17050696a6eSStefan Eßer BcFunc* func = p->func;
17150696a6eSStefan Eßer BcInstPtr* ip = bc_vec_top(&p->exits);
17250696a6eSStefan Eßer size_t* label;
17350696a6eSStefan Eßer
17450696a6eSStefan Eßer assert(func == bc_vec_item(&p->prog->fns, p->fidx));
17550696a6eSStefan Eßer
17644d4804dSStefan Eßer // Set the preallocated label to the correct index.
17750696a6eSStefan Eßer label = bc_vec_item(&func->labels, ip->idx);
17850696a6eSStefan Eßer *label = func->code.len;
17950696a6eSStefan Eßer
18044d4804dSStefan Eßer // Now, we don't need the exit label; it is done.
18150696a6eSStefan Eßer bc_vec_pop(&p->exits);
18250696a6eSStefan Eßer }
18350696a6eSStefan Eßer
18444d4804dSStefan Eßer /**
18544d4804dSStefan Eßer * Creates a label and sets it to idx. If this is an exit label, then idx is
18644d4804dSStefan Eßer * actually invalid, but it doesn't matter because it will be fixed by
18744d4804dSStefan Eßer * bc_parse_setLabel() later.
18844d4804dSStefan Eßer * @param p The parser.
18944d4804dSStefan Eßer * @param idx The index of the label.
19044d4804dSStefan Eßer */
19178bc019dSStefan Eßer static void
bc_parse_createLabel(BcParse * p,size_t idx)19278bc019dSStefan Eßer bc_parse_createLabel(BcParse* p, size_t idx)
19378bc019dSStefan Eßer {
19450696a6eSStefan Eßer bc_vec_push(&p->func->labels, &idx);
19550696a6eSStefan Eßer }
19650696a6eSStefan Eßer
19744d4804dSStefan Eßer /**
19844d4804dSStefan Eßer * Creates a conditional label. Unlike an exit label, this label is set at
19944d4804dSStefan Eßer * creation time because it comes *before* the code that will target it.
20044d4804dSStefan Eßer * @param p The parser.
20144d4804dSStefan Eßer * @param idx The index of the label.
20244d4804dSStefan Eßer */
20378bc019dSStefan Eßer static void
bc_parse_createCondLabel(BcParse * p,size_t idx)20478bc019dSStefan Eßer bc_parse_createCondLabel(BcParse* p, size_t idx)
20578bc019dSStefan Eßer {
20650696a6eSStefan Eßer bc_parse_createLabel(p, p->func->code.len);
20750696a6eSStefan Eßer bc_vec_push(&p->conds, &idx);
20850696a6eSStefan Eßer }
20950696a6eSStefan Eßer
210d101cdd6SStefan Eßer /**
21144d4804dSStefan Eßer * Creates an exit label to be filled in later by bc_parse_setLabel(). Also, why
21244d4804dSStefan Eßer * create a label to be filled in later? Because exit labels are meant to be
21344d4804dSStefan Eßer * targeted by code that comes *before* the label. Since we have to parse that
21444d4804dSStefan Eßer * code first, and don't know how long it will be, we need to just make sure to
21544d4804dSStefan Eßer * reserve a slot to be filled in later when we know.
21644d4804dSStefan Eßer *
21744d4804dSStefan Eßer * By the way, this uses BcInstPtr because it was convenient. The field idx
21844d4804dSStefan Eßer * holds the index, and the field func holds the loop boolean.
21944d4804dSStefan Eßer *
22044d4804dSStefan Eßer * @param p The parser.
22144d4804dSStefan Eßer * @param idx The index of the label's position.
22244d4804dSStefan Eßer * @param loop True if the exit label is for a loop or not.
22344d4804dSStefan Eßer */
22478bc019dSStefan Eßer static void
bc_parse_createExitLabel(BcParse * p,size_t idx,bool loop)22578bc019dSStefan Eßer bc_parse_createExitLabel(BcParse* p, size_t idx, bool loop)
22678bc019dSStefan Eßer {
22750696a6eSStefan Eßer BcInstPtr ip;
22850696a6eSStefan Eßer
22950696a6eSStefan Eßer assert(p->func == bc_vec_item(&p->prog->fns, p->fidx));
23050696a6eSStefan Eßer
23150696a6eSStefan Eßer ip.func = loop;
23250696a6eSStefan Eßer ip.idx = idx;
23350696a6eSStefan Eßer ip.len = 0;
23450696a6eSStefan Eßer
23550696a6eSStefan Eßer bc_vec_push(&p->exits, &ip);
23650696a6eSStefan Eßer bc_parse_createLabel(p, SIZE_MAX);
23750696a6eSStefan Eßer }
23850696a6eSStefan Eßer
23944d4804dSStefan Eßer /**
24044d4804dSStefan Eßer * Pops the correct operators off of the operator stack based on the current
24144d4804dSStefan Eßer * operator. This is because of the Shunting-Yard algorithm. Lower prec means
24244d4804dSStefan Eßer * higher precedence.
24344d4804dSStefan Eßer * @param p The parser.
24444d4804dSStefan Eßer * @param type The operator.
24544d4804dSStefan Eßer * @param start The previous start of the operator stack. For more
24644d4804dSStefan Eßer * information, see the bc Parsing section of the Development
24744d4804dSStefan Eßer * manual (manuals/development.md).
24844d4804dSStefan Eßer * @param nexprs A pointer to the current number of expressions that have not
24944d4804dSStefan Eßer * been consumed yet. This is an IN and OUT parameter.
25044d4804dSStefan Eßer */
25178bc019dSStefan Eßer static void
bc_parse_operator(BcParse * p,BcLexType type,size_t start,size_t * nexprs)25278bc019dSStefan Eßer bc_parse_operator(BcParse* p, BcLexType type, size_t start, size_t* nexprs)
25350696a6eSStefan Eßer {
25450696a6eSStefan Eßer BcLexType t;
25550696a6eSStefan Eßer uchar l, r = BC_PARSE_OP_PREC(type);
25650696a6eSStefan Eßer uchar left = BC_PARSE_OP_LEFT(type);
25750696a6eSStefan Eßer
258d101cdd6SStefan Eßer // While we haven't hit the stop point yet...
25978bc019dSStefan Eßer while (p->ops.len > start)
26078bc019dSStefan Eßer {
26144d4804dSStefan Eßer // Get the top operator.
26250696a6eSStefan Eßer t = BC_PARSE_TOP_OP(p);
26344d4804dSStefan Eßer
264d101cdd6SStefan Eßer // If it's a left paren, we have reached the end of whatever expression
265d101cdd6SStefan Eßer // this is no matter what. We also don't pop the left paren because it
266d101cdd6SStefan Eßer // will need to stay for the rest of the subexpression.
26750696a6eSStefan Eßer if (t == BC_LEX_LPAREN) break;
26850696a6eSStefan Eßer
26944d4804dSStefan Eßer // Break for precedence. Precedence operates differently on left and
27044d4804dSStefan Eßer // right associativity, by the way. A left associative operator that
27144d4804dSStefan Eßer // matches the current precedence should take priority, but a right
27244d4804dSStefan Eßer // associative operator should not.
273d101cdd6SStefan Eßer //
274d101cdd6SStefan Eßer // Also, a lower precedence value means a higher precedence.
27550696a6eSStefan Eßer l = BC_PARSE_OP_PREC(t);
27650696a6eSStefan Eßer if (l >= r && (l != r || !left)) break;
27750696a6eSStefan Eßer
27844d4804dSStefan Eßer // Do the housekeeping. In particular, make sure to note that one
279d101cdd6SStefan Eßer // expression was consumed (well, two were, but another was added) if
280d101cdd6SStefan Eßer // the operator was not a prefix operator. (Postfix operators are not
281d101cdd6SStefan Eßer // handled by this function at all.)
28250696a6eSStefan Eßer bc_parse_push(p, BC_PARSE_TOKEN_INST(t));
28350696a6eSStefan Eßer bc_vec_pop(&p->ops);
28450696a6eSStefan Eßer *nexprs -= !BC_PARSE_OP_PREFIX(t);
28550696a6eSStefan Eßer }
28650696a6eSStefan Eßer
28750696a6eSStefan Eßer bc_vec_push(&p->ops, &type);
28850696a6eSStefan Eßer }
28950696a6eSStefan Eßer
29044d4804dSStefan Eßer /**
29144d4804dSStefan Eßer * Parses a right paren. In the Shunting-Yard algorithm, it needs to be put on
29244d4804dSStefan Eßer * the operator stack. But before that, it needs to consume whatever operators
29344d4804dSStefan Eßer * there are until it hits a left paren.
29444d4804dSStefan Eßer * @param p The parser.
29544d4804dSStefan Eßer * @param nexprs A pointer to the current number of expressions that have not
29644d4804dSStefan Eßer * been consumed yet. This is an IN and OUT parameter.
29744d4804dSStefan Eßer */
29878bc019dSStefan Eßer static void
bc_parse_rightParen(BcParse * p,size_t * nexprs)29978bc019dSStefan Eßer bc_parse_rightParen(BcParse* p, size_t* nexprs)
30078bc019dSStefan Eßer {
30150696a6eSStefan Eßer BcLexType top;
30250696a6eSStefan Eßer
30344d4804dSStefan Eßer // Consume operators until a left paren.
30478bc019dSStefan Eßer while ((top = BC_PARSE_TOP_OP(p)) != BC_LEX_LPAREN)
30578bc019dSStefan Eßer {
30650696a6eSStefan Eßer bc_parse_push(p, BC_PARSE_TOKEN_INST(top));
30750696a6eSStefan Eßer bc_vec_pop(&p->ops);
30844d4804dSStefan Eßer *nexprs -= !BC_PARSE_OP_PREFIX(top);
30950696a6eSStefan Eßer }
31050696a6eSStefan Eßer
31144d4804dSStefan Eßer // We need to pop the left paren as well.
31250696a6eSStefan Eßer bc_vec_pop(&p->ops);
31350696a6eSStefan Eßer
31444d4804dSStefan Eßer // Oh, and we also want the next token.
31550696a6eSStefan Eßer bc_lex_next(&p->l);
31650696a6eSStefan Eßer }
31750696a6eSStefan Eßer
31844d4804dSStefan Eßer /**
31944d4804dSStefan Eßer * Parses function arguments.
32044d4804dSStefan Eßer * @param p The parser.
32144d4804dSStefan Eßer * @param flags Flags restricting what kind of expressions the arguments can
32244d4804dSStefan Eßer * be.
32344d4804dSStefan Eßer */
32478bc019dSStefan Eßer static void
bc_parse_args(BcParse * p,uint8_t flags)32578bc019dSStefan Eßer bc_parse_args(BcParse* p, uint8_t flags)
32678bc019dSStefan Eßer {
32750696a6eSStefan Eßer bool comma = false;
32844d4804dSStefan Eßer size_t nargs;
32950696a6eSStefan Eßer
33050696a6eSStefan Eßer bc_lex_next(&p->l);
33150696a6eSStefan Eßer
33244d4804dSStefan Eßer // Print and comparison operators not allowed. Well, comparison operators
33344d4804dSStefan Eßer // only for POSIX. But we do allow arrays, and we *must* get a value.
33450696a6eSStefan Eßer flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL);
33550696a6eSStefan Eßer flags |= (BC_PARSE_ARRAY | BC_PARSE_NEEDVAL);
33650696a6eSStefan Eßer
33744d4804dSStefan Eßer // Count the arguments and parse them.
33878bc019dSStefan Eßer for (nargs = 0; p->l.t != BC_LEX_RPAREN; ++nargs)
33978bc019dSStefan Eßer {
34044d4804dSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_arg);
34150696a6eSStefan Eßer
34250696a6eSStefan Eßer comma = (p->l.t == BC_LEX_COMMA);
34350696a6eSStefan Eßer if (comma) bc_lex_next(&p->l);
34450696a6eSStefan Eßer }
34550696a6eSStefan Eßer
34644d4804dSStefan Eßer // An ending comma is FAIL.
34750696a6eSStefan Eßer if (BC_ERR(comma)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
34844d4804dSStefan Eßer
34944d4804dSStefan Eßer // Now do the call with the number of arguments.
35050696a6eSStefan Eßer bc_parse_push(p, BC_INST_CALL);
35144d4804dSStefan Eßer bc_parse_pushIndex(p, nargs);
35250696a6eSStefan Eßer }
35350696a6eSStefan Eßer
35444d4804dSStefan Eßer /**
35544d4804dSStefan Eßer * Parses a function call.
35644d4804dSStefan Eßer * @param p The parser.
35744d4804dSStefan Eßer * @param flags Flags restricting what kind of expressions the arguments can
35844d4804dSStefan Eßer * be.
35944d4804dSStefan Eßer */
36078bc019dSStefan Eßer static void
bc_parse_call(BcParse * p,const char * name,uint8_t flags)36178bc019dSStefan Eßer bc_parse_call(BcParse* p, const char* name, uint8_t flags)
36278bc019dSStefan Eßer {
36350696a6eSStefan Eßer size_t idx;
36450696a6eSStefan Eßer
36544d4804dSStefan Eßer bc_parse_args(p, flags);
36650696a6eSStefan Eßer
36744d4804dSStefan Eßer // We just assert this because bc_parse_args() should
36850696a6eSStefan Eßer // ensure that the next token is what it should be.
36950696a6eSStefan Eßer assert(p->l.t == BC_LEX_RPAREN);
37050696a6eSStefan Eßer
37150696a6eSStefan Eßer // We cannot use bc_program_insertFunc() here
37250696a6eSStefan Eßer // because it will overwrite an existing function.
37350696a6eSStefan Eßer idx = bc_map_index(&p->prog->fn_map, name);
37450696a6eSStefan Eßer
37544d4804dSStefan Eßer // The function does not exist yet. Create a space for it. If the user does
37644d4804dSStefan Eßer // not define it, it's a *runtime* error, not a parse error.
37778bc019dSStefan Eßer if (idx == BC_VEC_INVALID_IDX)
37878bc019dSStefan Eßer {
37950696a6eSStefan Eßer idx = bc_program_insertFunc(p->prog, name);
38050696a6eSStefan Eßer
38150696a6eSStefan Eßer assert(idx != BC_VEC_INVALID_IDX);
38250696a6eSStefan Eßer
38350696a6eSStefan Eßer // Make sure that this pointer was not invalidated.
38450696a6eSStefan Eßer p->func = bc_vec_item(&p->prog->fns, p->fidx);
38550696a6eSStefan Eßer }
38644d4804dSStefan Eßer // The function exists, so set the right function index.
38750696a6eSStefan Eßer else idx = ((BcId*) bc_vec_item(&p->prog->fn_map, idx))->idx;
38850696a6eSStefan Eßer
38950696a6eSStefan Eßer bc_parse_pushIndex(p, idx);
39050696a6eSStefan Eßer
39144d4804dSStefan Eßer // Make sure to get the next token.
39250696a6eSStefan Eßer bc_lex_next(&p->l);
39350696a6eSStefan Eßer }
39450696a6eSStefan Eßer
39544d4804dSStefan Eßer /**
39644d4804dSStefan Eßer * Parses a name/identifier-based expression. It could be a variable, an array
39744d4804dSStefan Eßer * element, an array itself (for function arguments), a function call, etc.
398d101cdd6SStefan Eßer * @param p The parser.
399d101cdd6SStefan Eßer * @param type A pointer to return the resulting instruction.
400d101cdd6SStefan Eßer * @param can_assign A pointer to return true if the name can be assigned to,
401d101cdd6SStefan Eßer * false otherwise.
402d101cdd6SStefan Eßer * @param flags Flags restricting what kind of expression the name can be.
40344d4804dSStefan Eßer */
40478bc019dSStefan Eßer static void
bc_parse_name(BcParse * p,BcInst * type,bool * can_assign,uint8_t flags)40578bc019dSStefan Eßer bc_parse_name(BcParse* p, BcInst* type, bool* can_assign, uint8_t flags)
40650696a6eSStefan Eßer {
40750696a6eSStefan Eßer char* name;
40850696a6eSStefan Eßer
40910041e99SStefan Eßer BC_SIG_ASSERT_LOCKED;
41050696a6eSStefan Eßer
41144d4804dSStefan Eßer // We want a copy of the name since the lexer might overwrite its copy.
41250696a6eSStefan Eßer name = bc_vm_strdup(p->l.str.v);
41350696a6eSStefan Eßer
414d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, err);
41550696a6eSStefan Eßer
41644d4804dSStefan Eßer // We need the next token to see if it's just a variable or something more.
41750696a6eSStefan Eßer bc_lex_next(&p->l);
41850696a6eSStefan Eßer
41944d4804dSStefan Eßer // Array element or array.
42078bc019dSStefan Eßer if (p->l.t == BC_LEX_LBRACKET)
42178bc019dSStefan Eßer {
42250696a6eSStefan Eßer bc_lex_next(&p->l);
42350696a6eSStefan Eßer
42444d4804dSStefan Eßer // Array only. This has to be a function parameter.
42578bc019dSStefan Eßer if (p->l.t == BC_LEX_RBRACKET)
42678bc019dSStefan Eßer {
42744d4804dSStefan Eßer // Error if arrays are not allowed.
42850696a6eSStefan Eßer if (BC_ERR(!(flags & BC_PARSE_ARRAY)))
42978bc019dSStefan Eßer {
43050696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
43178bc019dSStefan Eßer }
43250696a6eSStefan Eßer
43350696a6eSStefan Eßer *type = BC_INST_ARRAY;
43450696a6eSStefan Eßer *can_assign = false;
43550696a6eSStefan Eßer }
43678bc019dSStefan Eßer else
43778bc019dSStefan Eßer {
43844d4804dSStefan Eßer // If we are here, we have an array element. We need to set the
43944d4804dSStefan Eßer // expression parsing flags.
44050696a6eSStefan Eßer uint8_t flags2 = (flags & ~(BC_PARSE_PRINT | BC_PARSE_REL)) |
44150696a6eSStefan Eßer BC_PARSE_NEEDVAL;
44250696a6eSStefan Eßer
44350696a6eSStefan Eßer bc_parse_expr_status(p, flags2, bc_parse_next_elem);
44450696a6eSStefan Eßer
44544d4804dSStefan Eßer // The next token *must* be a right bracket.
44650696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RBRACKET))
44778bc019dSStefan Eßer {
44850696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN);
44978bc019dSStefan Eßer }
45050696a6eSStefan Eßer
45150696a6eSStefan Eßer *type = BC_INST_ARRAY_ELEM;
45250696a6eSStefan Eßer *can_assign = true;
45350696a6eSStefan Eßer }
45450696a6eSStefan Eßer
45544d4804dSStefan Eßer // Make sure to get the next token.
45650696a6eSStefan Eßer bc_lex_next(&p->l);
45750696a6eSStefan Eßer
45844d4804dSStefan Eßer // Push the instruction and the name of the identifier.
45950696a6eSStefan Eßer bc_parse_push(p, *type);
46050696a6eSStefan Eßer bc_parse_pushName(p, name, false);
46150696a6eSStefan Eßer }
46278bc019dSStefan Eßer else if (p->l.t == BC_LEX_LPAREN)
46378bc019dSStefan Eßer {
46444d4804dSStefan Eßer // We are parsing a function call; error if not allowed.
46550696a6eSStefan Eßer if (BC_ERR(flags & BC_PARSE_NOCALL))
46678bc019dSStefan Eßer {
46750696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN);
46878bc019dSStefan Eßer }
46950696a6eSStefan Eßer
47050696a6eSStefan Eßer *type = BC_INST_CALL;
47150696a6eSStefan Eßer *can_assign = false;
47250696a6eSStefan Eßer
47350696a6eSStefan Eßer bc_parse_call(p, name, flags);
47450696a6eSStefan Eßer }
47578bc019dSStefan Eßer else
47678bc019dSStefan Eßer {
47744d4804dSStefan Eßer // Just a variable.
47850696a6eSStefan Eßer *type = BC_INST_VAR;
47950696a6eSStefan Eßer *can_assign = true;
48050696a6eSStefan Eßer bc_parse_push(p, BC_INST_VAR);
48150696a6eSStefan Eßer bc_parse_pushName(p, name, true);
48250696a6eSStefan Eßer }
48350696a6eSStefan Eßer
48450696a6eSStefan Eßer err:
48544d4804dSStefan Eßer // Need to make sure to unallocate the name.
48650696a6eSStefan Eßer free(name);
487d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm);
48810041e99SStefan Eßer BC_SIG_MAYLOCK;
48950696a6eSStefan Eßer }
49050696a6eSStefan Eßer
49144d4804dSStefan Eßer /**
49244d4804dSStefan Eßer * Parses a builtin function that takes no arguments. This includes read(),
49344d4804dSStefan Eßer * rand(), maxibase(), maxobase(), maxscale(), and maxrand().
49444d4804dSStefan Eßer * @param p The parser.
49544d4804dSStefan Eßer * @param inst The instruction corresponding to the builtin.
49644d4804dSStefan Eßer */
49778bc019dSStefan Eßer static void
bc_parse_noArgBuiltin(BcParse * p,BcInst inst)49878bc019dSStefan Eßer bc_parse_noArgBuiltin(BcParse* p, BcInst inst)
49978bc019dSStefan Eßer {
50044d4804dSStefan Eßer // Must have a left paren.
50150696a6eSStefan Eßer bc_lex_next(&p->l);
50250696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
50350696a6eSStefan Eßer
50444d4804dSStefan Eßer // Must have a right paren.
50550696a6eSStefan Eßer bc_lex_next(&p->l);
50650696a6eSStefan Eßer if ((p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
50750696a6eSStefan Eßer
50850696a6eSStefan Eßer bc_parse_push(p, inst);
50950696a6eSStefan Eßer
51050696a6eSStefan Eßer bc_lex_next(&p->l);
51150696a6eSStefan Eßer }
51250696a6eSStefan Eßer
51344d4804dSStefan Eßer /**
51444d4804dSStefan Eßer * Parses a builtin function that takes 1 argument. This includes length(),
51544d4804dSStefan Eßer * sqrt(), abs(), scale(), and irand().
51644d4804dSStefan Eßer * @param p The parser.
51744d4804dSStefan Eßer * @param type The lex token.
51844d4804dSStefan Eßer * @param flags The expression parsing flags for parsing the argument.
51944d4804dSStefan Eßer * @param prev An out parameter; the previous instruction pointer.
52044d4804dSStefan Eßer */
52178bc019dSStefan Eßer static void
bc_parse_builtin(BcParse * p,BcLexType type,uint8_t flags,BcInst * prev)52278bc019dSStefan Eßer bc_parse_builtin(BcParse* p, BcLexType type, uint8_t flags, BcInst* prev)
52350696a6eSStefan Eßer {
52444d4804dSStefan Eßer // Must have a left paren.
52550696a6eSStefan Eßer bc_lex_next(&p->l);
52678bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
52750696a6eSStefan Eßer
52850696a6eSStefan Eßer bc_lex_next(&p->l);
52950696a6eSStefan Eßer
53044d4804dSStefan Eßer // Change the flags as needed for parsing the argument.
53150696a6eSStefan Eßer flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL);
53250696a6eSStefan Eßer flags |= BC_PARSE_NEEDVAL;
53344d4804dSStefan Eßer
53444d4804dSStefan Eßer // Since length can take arrays, we need to specially add that flag.
535d101cdd6SStefan Eßer if (type == BC_LEX_KW_LENGTH || type == BC_LEX_KW_ASCIIFY)
536d101cdd6SStefan Eßer {
537d101cdd6SStefan Eßer flags |= BC_PARSE_ARRAY;
538d101cdd6SStefan Eßer }
539d101cdd6SStefan Eßer
540d101cdd6SStefan Eßer // Otherwise, we need to clear it because it could be set.
541d101cdd6SStefan Eßer else flags &= ~(BC_PARSE_ARRAY);
54250696a6eSStefan Eßer
54350696a6eSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_rel);
54450696a6eSStefan Eßer
54544d4804dSStefan Eßer // Must have a right paren.
54678bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
54750696a6eSStefan Eßer
54844d4804dSStefan Eßer // Adjust previous based on the token and push it.
54950696a6eSStefan Eßer *prev = type - BC_LEX_KW_LENGTH + BC_INST_LENGTH;
55050696a6eSStefan Eßer bc_parse_push(p, *prev);
55150696a6eSStefan Eßer
55250696a6eSStefan Eßer bc_lex_next(&p->l);
55350696a6eSStefan Eßer }
55450696a6eSStefan Eßer
55544d4804dSStefan Eßer /**
55644d4804dSStefan Eßer * Parses a builtin function that takes 3 arguments. This includes modexp() and
55744d4804dSStefan Eßer * divmod().
558d101cdd6SStefan Eßer * @param p The parser.
559d101cdd6SStefan Eßer * @param type The lex token.
560d101cdd6SStefan Eßer * @param flags The expression parsing flags for parsing the argument.
561d101cdd6SStefan Eßer * @param prev An out parameter; the previous instruction pointer.
56244d4804dSStefan Eßer */
56378bc019dSStefan Eßer static void
bc_parse_builtin3(BcParse * p,BcLexType type,uint8_t flags,BcInst * prev)56478bc019dSStefan Eßer bc_parse_builtin3(BcParse* p, BcLexType type, uint8_t flags, BcInst* prev)
56544d4804dSStefan Eßer {
56644d4804dSStefan Eßer assert(type == BC_LEX_KW_MODEXP || type == BC_LEX_KW_DIVMOD);
56744d4804dSStefan Eßer
56844d4804dSStefan Eßer // Must have a left paren.
56944d4804dSStefan Eßer bc_lex_next(&p->l);
57078bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
57144d4804dSStefan Eßer
57244d4804dSStefan Eßer bc_lex_next(&p->l);
57344d4804dSStefan Eßer
57444d4804dSStefan Eßer // Change the flags as needed for parsing the argument.
57544d4804dSStefan Eßer flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL);
57644d4804dSStefan Eßer flags |= BC_PARSE_NEEDVAL;
57744d4804dSStefan Eßer
57844d4804dSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_builtin);
57944d4804dSStefan Eßer
58044d4804dSStefan Eßer // Must have a comma.
58178bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_COMMA)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
58244d4804dSStefan Eßer
58344d4804dSStefan Eßer bc_lex_next(&p->l);
58444d4804dSStefan Eßer
58544d4804dSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_builtin);
58644d4804dSStefan Eßer
58744d4804dSStefan Eßer // Must have a comma.
58878bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_COMMA)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
58944d4804dSStefan Eßer
59044d4804dSStefan Eßer bc_lex_next(&p->l);
59144d4804dSStefan Eßer
59244d4804dSStefan Eßer // If it is a divmod, parse an array name. Otherwise, just parse another
59344d4804dSStefan Eßer // expression.
59478bc019dSStefan Eßer if (type == BC_LEX_KW_DIVMOD)
59578bc019dSStefan Eßer {
59644d4804dSStefan Eßer // Must have a name.
59744d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_NAME)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
59844d4804dSStefan Eßer
59944d4804dSStefan Eßer // This is safe because the next token should not overwrite the name.
60044d4804dSStefan Eßer bc_lex_next(&p->l);
60144d4804dSStefan Eßer
60244d4804dSStefan Eßer // Must have a left bracket.
60344d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LBRACKET))
60478bc019dSStefan Eßer {
60544d4804dSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN);
60678bc019dSStefan Eßer }
60744d4804dSStefan Eßer
60844d4804dSStefan Eßer // This is safe because the next token should not overwrite the name.
60944d4804dSStefan Eßer bc_lex_next(&p->l);
61044d4804dSStefan Eßer
61144d4804dSStefan Eßer // Must have a right bracket.
61244d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RBRACKET))
61378bc019dSStefan Eßer {
61444d4804dSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN);
61578bc019dSStefan Eßer }
61644d4804dSStefan Eßer
61744d4804dSStefan Eßer // This is safe because the next token should not overwrite the name.
61844d4804dSStefan Eßer bc_lex_next(&p->l);
61944d4804dSStefan Eßer }
62044d4804dSStefan Eßer else bc_parse_expr_status(p, flags, bc_parse_next_rel);
62144d4804dSStefan Eßer
62244d4804dSStefan Eßer // Must have a right paren.
62378bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
62444d4804dSStefan Eßer
62544d4804dSStefan Eßer // Adjust previous based on the token and push it.
62644d4804dSStefan Eßer *prev = type - BC_LEX_KW_MODEXP + BC_INST_MODEXP;
62744d4804dSStefan Eßer bc_parse_push(p, *prev);
62844d4804dSStefan Eßer
62944d4804dSStefan Eßer // If we have divmod, we need to assign the modulus to the array element, so
63044d4804dSStefan Eßer // we need to push the instructions for doing so.
63178bc019dSStefan Eßer if (type == BC_LEX_KW_DIVMOD)
63278bc019dSStefan Eßer {
63344d4804dSStefan Eßer // The zeroth element.
63444d4804dSStefan Eßer bc_parse_push(p, BC_INST_ZERO);
63544d4804dSStefan Eßer bc_parse_push(p, BC_INST_ARRAY_ELEM);
63644d4804dSStefan Eßer
63744d4804dSStefan Eßer // Push the array.
63844d4804dSStefan Eßer bc_parse_pushName(p, p->l.str.v, false);
63944d4804dSStefan Eßer
64044d4804dSStefan Eßer // Swap them and assign. After this, the top item on the stack should
64144d4804dSStefan Eßer // be the quotient.
64244d4804dSStefan Eßer bc_parse_push(p, BC_INST_SWAP);
64344d4804dSStefan Eßer bc_parse_push(p, BC_INST_ASSIGN_NO_VAL);
64444d4804dSStefan Eßer }
64544d4804dSStefan Eßer
64644d4804dSStefan Eßer bc_lex_next(&p->l);
64744d4804dSStefan Eßer }
64844d4804dSStefan Eßer
64944d4804dSStefan Eßer /**
65044d4804dSStefan Eßer * Parses the scale keyword. This is special because scale can be a value or a
65144d4804dSStefan Eßer * builtin function.
65244d4804dSStefan Eßer * @param p The parser.
65344d4804dSStefan Eßer * @param type An out parameter; the instruction for the parse.
65444d4804dSStefan Eßer * @param can_assign An out parameter; whether the expression can be assigned
65544d4804dSStefan Eßer * to.
65644d4804dSStefan Eßer * @param flags The expression parsing flags for parsing a scale() arg.
65744d4804dSStefan Eßer */
65878bc019dSStefan Eßer static void
bc_parse_scale(BcParse * p,BcInst * type,bool * can_assign,uint8_t flags)65978bc019dSStefan Eßer bc_parse_scale(BcParse* p, BcInst* type, bool* can_assign, uint8_t flags)
66050696a6eSStefan Eßer {
66150696a6eSStefan Eßer bc_lex_next(&p->l);
66250696a6eSStefan Eßer
66344d4804dSStefan Eßer // Without the left paren, it's just the keyword.
66478bc019dSStefan Eßer if (p->l.t != BC_LEX_LPAREN)
66578bc019dSStefan Eßer {
66644d4804dSStefan Eßer // Set, push, and return.
66750696a6eSStefan Eßer *type = BC_INST_SCALE;
66850696a6eSStefan Eßer *can_assign = true;
66950696a6eSStefan Eßer bc_parse_push(p, BC_INST_SCALE);
67050696a6eSStefan Eßer return;
67150696a6eSStefan Eßer }
67250696a6eSStefan Eßer
67344d4804dSStefan Eßer // Handle the scale function.
67450696a6eSStefan Eßer *type = BC_INST_SCALE_FUNC;
67550696a6eSStefan Eßer *can_assign = false;
67644d4804dSStefan Eßer
67744d4804dSStefan Eßer // Once again, adjust the flags.
67850696a6eSStefan Eßer flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL);
67950696a6eSStefan Eßer flags |= BC_PARSE_NEEDVAL;
68050696a6eSStefan Eßer
68150696a6eSStefan Eßer bc_lex_next(&p->l);
68250696a6eSStefan Eßer
68350696a6eSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_rel);
68444d4804dSStefan Eßer
68544d4804dSStefan Eßer // Must have a right paren.
68678bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
68750696a6eSStefan Eßer
68850696a6eSStefan Eßer bc_parse_push(p, BC_INST_SCALE_FUNC);
68950696a6eSStefan Eßer
69050696a6eSStefan Eßer bc_lex_next(&p->l);
69150696a6eSStefan Eßer }
69250696a6eSStefan Eßer
69344d4804dSStefan Eßer /**
69444d4804dSStefan Eßer * Parses and increment or decrement operator. This is a bit complex.
69544d4804dSStefan Eßer * @param p The parser.
69644d4804dSStefan Eßer * @param prev An out parameter; the previous instruction pointer.
69744d4804dSStefan Eßer * @param can_assign An out parameter; whether the expression can be assigned
69844d4804dSStefan Eßer * to.
69944d4804dSStefan Eßer * @param nexs An in/out parameter; the number of expressions in the
70044d4804dSStefan Eßer * parse tree that are not used.
70144d4804dSStefan Eßer * @param flags The expression parsing flags for parsing a scale() arg.
70244d4804dSStefan Eßer */
70378bc019dSStefan Eßer static void
bc_parse_incdec(BcParse * p,BcInst * prev,bool * can_assign,size_t * nexs,uint8_t flags)70478bc019dSStefan Eßer bc_parse_incdec(BcParse* p, BcInst* prev, bool* can_assign, size_t* nexs,
70578bc019dSStefan Eßer uint8_t flags)
70650696a6eSStefan Eßer {
70750696a6eSStefan Eßer BcLexType type;
70850696a6eSStefan Eßer uchar inst;
70950696a6eSStefan Eßer BcInst etype = *prev;
71050696a6eSStefan Eßer BcLexType last = p->l.last;
71150696a6eSStefan Eßer
71250696a6eSStefan Eßer assert(prev != NULL && can_assign != NULL);
71350696a6eSStefan Eßer
71444d4804dSStefan Eßer // If we can't assign to the previous token, then we have an error.
71550696a6eSStefan Eßer if (BC_ERR(last == BC_LEX_OP_INC || last == BC_LEX_OP_DEC ||
71650696a6eSStefan Eßer last == BC_LEX_RPAREN))
71750696a6eSStefan Eßer {
71850696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_ASSIGN);
71950696a6eSStefan Eßer }
72050696a6eSStefan Eßer
72144d4804dSStefan Eßer // Is the previous instruction for a variable?
72278bc019dSStefan Eßer if (BC_PARSE_INST_VAR(etype))
72378bc019dSStefan Eßer {
72444d4804dSStefan Eßer // If so, this is a postfix operator.
72550696a6eSStefan Eßer if (!*can_assign) bc_parse_err(p, BC_ERR_PARSE_ASSIGN);
72650696a6eSStefan Eßer
72744d4804dSStefan Eßer // Only postfix uses BC_INST_INC and BC_INST_DEC.
72850696a6eSStefan Eßer *prev = inst = BC_INST_INC + (p->l.t != BC_LEX_OP_INC);
72950696a6eSStefan Eßer bc_parse_push(p, inst);
73050696a6eSStefan Eßer bc_lex_next(&p->l);
73150696a6eSStefan Eßer *can_assign = false;
73250696a6eSStefan Eßer }
73378bc019dSStefan Eßer else
73478bc019dSStefan Eßer {
73544d4804dSStefan Eßer // This is a prefix operator. In that case, we just convert it to
73644d4804dSStefan Eßer // an assignment instruction.
73750696a6eSStefan Eßer *prev = inst = BC_INST_ASSIGN_PLUS + (p->l.t != BC_LEX_OP_INC);
73850696a6eSStefan Eßer
73950696a6eSStefan Eßer bc_lex_next(&p->l);
74050696a6eSStefan Eßer type = p->l.t;
74150696a6eSStefan Eßer
74250696a6eSStefan Eßer // Because we parse the next part of the expression
74350696a6eSStefan Eßer // right here, we need to increment this.
74450696a6eSStefan Eßer *nexs = *nexs + 1;
74550696a6eSStefan Eßer
74644d4804dSStefan Eßer // Is the next token a normal identifier?
74778bc019dSStefan Eßer if (type == BC_LEX_NAME)
74878bc019dSStefan Eßer {
74944d4804dSStefan Eßer // Parse the name.
750d101cdd6SStefan Eßer uint8_t flags2 = flags & ~(BC_PARSE_ARRAY);
75150696a6eSStefan Eßer bc_parse_name(p, prev, can_assign, flags2 | BC_PARSE_NOCALL);
75250696a6eSStefan Eßer }
75344d4804dSStefan Eßer // Is the next token a global?
75478bc019dSStefan Eßer else if (type >= BC_LEX_KW_LAST && type <= BC_LEX_KW_OBASE)
75578bc019dSStefan Eßer {
75650696a6eSStefan Eßer bc_parse_push(p, type - BC_LEX_KW_LAST + BC_INST_LAST);
75750696a6eSStefan Eßer bc_lex_next(&p->l);
75850696a6eSStefan Eßer }
75944d4804dSStefan Eßer // Is the next token specifically scale, which needs special treatment?
76078bc019dSStefan Eßer else if (BC_NO_ERR(type == BC_LEX_KW_SCALE))
76178bc019dSStefan Eßer {
76250696a6eSStefan Eßer bc_lex_next(&p->l);
76350696a6eSStefan Eßer
76444d4804dSStefan Eßer // Check that scale() was not used.
76550696a6eSStefan Eßer if (BC_ERR(p->l.t == BC_LEX_LPAREN))
76678bc019dSStefan Eßer {
76750696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN);
76878bc019dSStefan Eßer }
76950696a6eSStefan Eßer else bc_parse_push(p, BC_INST_SCALE);
77050696a6eSStefan Eßer }
77144d4804dSStefan Eßer // Now we know we have an error.
77250696a6eSStefan Eßer else bc_parse_err(p, BC_ERR_PARSE_TOKEN);
77350696a6eSStefan Eßer
77450696a6eSStefan Eßer *can_assign = false;
77550696a6eSStefan Eßer
77650696a6eSStefan Eßer bc_parse_push(p, BC_INST_ONE);
77750696a6eSStefan Eßer bc_parse_push(p, inst);
77850696a6eSStefan Eßer }
77950696a6eSStefan Eßer }
78050696a6eSStefan Eßer
78144d4804dSStefan Eßer /**
78244d4804dSStefan Eßer * Parses the minus operator. This needs special treatment because it is either
78344d4804dSStefan Eßer * subtract or negation.
78444d4804dSStefan Eßer * @param p The parser.
78544d4804dSStefan Eßer * @param prev An in/out parameter; the previous instruction.
78644d4804dSStefan Eßer * @param ops_bgn The size of the operator stack.
78744d4804dSStefan Eßer * @param rparen True if the last token was a right paren.
78844d4804dSStefan Eßer * @param binlast True if the last token was a binary operator.
78944d4804dSStefan Eßer * @param nexprs An in/out parameter; the number of unused expressions.
79044d4804dSStefan Eßer */
79178bc019dSStefan Eßer static void
bc_parse_minus(BcParse * p,BcInst * prev,size_t ops_bgn,bool rparen,bool binlast,size_t * nexprs)79278bc019dSStefan Eßer bc_parse_minus(BcParse* p, BcInst* prev, size_t ops_bgn, bool rparen,
79378bc019dSStefan Eßer bool binlast, size_t* nexprs)
79450696a6eSStefan Eßer {
79550696a6eSStefan Eßer BcLexType type;
79650696a6eSStefan Eßer
79750696a6eSStefan Eßer bc_lex_next(&p->l);
79850696a6eSStefan Eßer
79944d4804dSStefan Eßer // Figure out if it's a minus or a negation.
80050696a6eSStefan Eßer type = BC_PARSE_LEAF(*prev, binlast, rparen) ? BC_LEX_OP_MINUS : BC_LEX_NEG;
80150696a6eSStefan Eßer *prev = BC_PARSE_TOKEN_INST(type);
80250696a6eSStefan Eßer
80350696a6eSStefan Eßer // We can just push onto the op stack because this is the largest
80450696a6eSStefan Eßer // precedence operator that gets pushed. Inc/dec does not.
80550696a6eSStefan Eßer if (type != BC_LEX_OP_MINUS) bc_vec_push(&p->ops, &type);
80650696a6eSStefan Eßer else bc_parse_operator(p, type, ops_bgn, nexprs);
80750696a6eSStefan Eßer }
80850696a6eSStefan Eßer
80944d4804dSStefan Eßer /**
81044d4804dSStefan Eßer * Parses a string.
81144d4804dSStefan Eßer * @param p The parser.
81244d4804dSStefan Eßer * @param inst The instruction corresponding to how the string was found and
81344d4804dSStefan Eßer * how it should be printed.
81444d4804dSStefan Eßer */
81578bc019dSStefan Eßer static void
bc_parse_str(BcParse * p,BcInst inst)81678bc019dSStefan Eßer bc_parse_str(BcParse* p, BcInst inst)
81778bc019dSStefan Eßer {
81850696a6eSStefan Eßer bc_parse_addString(p);
81950696a6eSStefan Eßer bc_parse_push(p, inst);
82050696a6eSStefan Eßer bc_lex_next(&p->l);
82150696a6eSStefan Eßer }
82250696a6eSStefan Eßer
82344d4804dSStefan Eßer /**
82444d4804dSStefan Eßer * Parses a print statement.
82544d4804dSStefan Eßer * @param p The parser.
82644d4804dSStefan Eßer */
82778bc019dSStefan Eßer static void
bc_parse_print(BcParse * p,BcLexType type)82878bc019dSStefan Eßer bc_parse_print(BcParse* p, BcLexType type)
82978bc019dSStefan Eßer {
83050696a6eSStefan Eßer BcLexType t;
83150696a6eSStefan Eßer bool comma = false;
83278bc019dSStefan Eßer BcInst inst = type == BC_LEX_KW_STREAM ? BC_INST_PRINT_STREAM :
83378bc019dSStefan Eßer BC_INST_PRINT_POP;
83450696a6eSStefan Eßer
83550696a6eSStefan Eßer bc_lex_next(&p->l);
83650696a6eSStefan Eßer
83750696a6eSStefan Eßer t = p->l.t;
83850696a6eSStefan Eßer
83944d4804dSStefan Eßer // A print or stream statement has to have *something*.
84050696a6eSStefan Eßer if (bc_parse_isDelimiter(p)) bc_parse_err(p, BC_ERR_PARSE_PRINT);
84150696a6eSStefan Eßer
84278bc019dSStefan Eßer do
84378bc019dSStefan Eßer {
84444d4804dSStefan Eßer // If the token is a string, then print it with escapes.
84544d4804dSStefan Eßer // BC_INST_PRINT_POP plays that role for bc.
84644d4804dSStefan Eßer if (t == BC_LEX_STR) bc_parse_str(p, inst);
84778bc019dSStefan Eßer else
84878bc019dSStefan Eßer {
84944d4804dSStefan Eßer // We have an actual number; parse and add a print instruction.
85050696a6eSStefan Eßer bc_parse_expr_status(p, BC_PARSE_NEEDVAL, bc_parse_next_print);
85144d4804dSStefan Eßer bc_parse_push(p, inst);
85250696a6eSStefan Eßer }
85350696a6eSStefan Eßer
85444d4804dSStefan Eßer // Is the next token a comma?
85550696a6eSStefan Eßer comma = (p->l.t == BC_LEX_COMMA);
85650696a6eSStefan Eßer
85744d4804dSStefan Eßer // Get the next token if we have a comma.
85850696a6eSStefan Eßer if (comma) bc_lex_next(&p->l);
85978bc019dSStefan Eßer else
86078bc019dSStefan Eßer {
86144d4804dSStefan Eßer // If we don't have a comma, the statement needs to end.
86278bc019dSStefan Eßer if (!bc_parse_isDelimiter(p)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
86350696a6eSStefan Eßer else break;
86450696a6eSStefan Eßer }
86550696a6eSStefan Eßer
86650696a6eSStefan Eßer t = p->l.t;
86778bc019dSStefan Eßer }
86878bc019dSStefan Eßer while (true);
86950696a6eSStefan Eßer
87044d4804dSStefan Eßer // If we have a comma but no token, that's bad.
87150696a6eSStefan Eßer if (BC_ERR(comma)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
87250696a6eSStefan Eßer }
87350696a6eSStefan Eßer
87444d4804dSStefan Eßer /**
87544d4804dSStefan Eßer * Parses a return statement.
87644d4804dSStefan Eßer * @param p The parser.
87744d4804dSStefan Eßer */
87878bc019dSStefan Eßer static void
bc_parse_return(BcParse * p)87978bc019dSStefan Eßer bc_parse_return(BcParse* p)
88078bc019dSStefan Eßer {
88150696a6eSStefan Eßer BcLexType t;
88250696a6eSStefan Eßer bool paren;
88350696a6eSStefan Eßer uchar inst = BC_INST_RET0;
88450696a6eSStefan Eßer
88544d4804dSStefan Eßer // If we are not in a function, that's an error.
88650696a6eSStefan Eßer if (BC_ERR(!BC_PARSE_FUNC(p))) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
88750696a6eSStefan Eßer
88844d4804dSStefan Eßer // If we are in a void function, make sure to return void.
88950696a6eSStefan Eßer if (p->func->voidfn) inst = BC_INST_RET_VOID;
89050696a6eSStefan Eßer
89150696a6eSStefan Eßer bc_lex_next(&p->l);
89250696a6eSStefan Eßer
89350696a6eSStefan Eßer t = p->l.t;
89444d4804dSStefan Eßer paren = (t == BC_LEX_LPAREN);
89550696a6eSStefan Eßer
89644d4804dSStefan Eßer // An empty return statement just needs to push the selected instruction.
89750696a6eSStefan Eßer if (bc_parse_isDelimiter(p)) bc_parse_push(p, inst);
89878bc019dSStefan Eßer else
89978bc019dSStefan Eßer {
90050696a6eSStefan Eßer BcParseStatus s;
90150696a6eSStefan Eßer
90244d4804dSStefan Eßer // Need to parse the expression whose value will be returned.
90350696a6eSStefan Eßer s = bc_parse_expr_err(p, BC_PARSE_NEEDVAL, bc_parse_next_expr);
90450696a6eSStefan Eßer
90544d4804dSStefan Eßer // If the expression was empty, just push the selected instruction.
90678bc019dSStefan Eßer if (s == BC_PARSE_STATUS_EMPTY_EXPR)
90778bc019dSStefan Eßer {
90850696a6eSStefan Eßer bc_parse_push(p, inst);
90950696a6eSStefan Eßer bc_lex_next(&p->l);
91050696a6eSStefan Eßer }
91150696a6eSStefan Eßer
91244d4804dSStefan Eßer // POSIX requires parentheses.
91378bc019dSStefan Eßer if (!paren || p->l.last != BC_LEX_RPAREN)
91478bc019dSStefan Eßer {
91550696a6eSStefan Eßer bc_parse_err(p, BC_ERR_POSIX_RET);
91650696a6eSStefan Eßer }
91744d4804dSStefan Eßer
91844d4804dSStefan Eßer // Void functions require an empty expression.
91978bc019dSStefan Eßer if (BC_ERR(p->func->voidfn))
92078bc019dSStefan Eßer {
92144d4804dSStefan Eßer if (s != BC_PARSE_STATUS_EMPTY_EXPR)
92278bc019dSStefan Eßer {
92350696a6eSStefan Eßer bc_parse_verr(p, BC_ERR_PARSE_RET_VOID, p->func->name);
92444d4804dSStefan Eßer }
92578bc019dSStefan Eßer }
92644d4804dSStefan Eßer // If we got here, we want to be sure to end the function with a real
92744d4804dSStefan Eßer // return instruction, just in case.
92844d4804dSStefan Eßer else bc_parse_push(p, BC_INST_RET);
92950696a6eSStefan Eßer }
93050696a6eSStefan Eßer }
93150696a6eSStefan Eßer
93244d4804dSStefan Eßer /**
93344d4804dSStefan Eßer * Clears flags that indicate the end of an if statement and its block and sets
93444d4804dSStefan Eßer * the jump location.
93544d4804dSStefan Eßer * @param p The parser.
93644d4804dSStefan Eßer */
93778bc019dSStefan Eßer static void
bc_parse_noElse(BcParse * p)93878bc019dSStefan Eßer bc_parse_noElse(BcParse* p)
93978bc019dSStefan Eßer {
94050696a6eSStefan Eßer uint16_t* flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
94150696a6eSStefan Eßer *flag_ptr = (*flag_ptr & ~(BC_PARSE_FLAG_IF_END));
94250696a6eSStefan Eßer bc_parse_setLabel(p);
94350696a6eSStefan Eßer }
94450696a6eSStefan Eßer
94544d4804dSStefan Eßer /**
94644d4804dSStefan Eßer * Ends (finishes parsing) the body of a control statement or a function.
94744d4804dSStefan Eßer * @param p The parser.
94844d4804dSStefan Eßer * @param brace True if the body was ended by a brace, false otherwise.
94944d4804dSStefan Eßer */
95078bc019dSStefan Eßer static void
bc_parse_endBody(BcParse * p,bool brace)95178bc019dSStefan Eßer bc_parse_endBody(BcParse* p, bool brace)
95278bc019dSStefan Eßer {
95350696a6eSStefan Eßer bool has_brace, new_else = false;
95450696a6eSStefan Eßer
95544d4804dSStefan Eßer // We cannot be ending a body if there are no bodies to end.
95650696a6eSStefan Eßer if (BC_ERR(p->flags.len <= 1)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
95750696a6eSStefan Eßer
95878bc019dSStefan Eßer if (brace)
95978bc019dSStefan Eßer {
96044d4804dSStefan Eßer // The brace was already gotten; make sure that the caller did not lie.
96144d4804dSStefan Eßer // We check for the requirement of braces later.
96250696a6eSStefan Eßer assert(p->l.t == BC_LEX_RBRACE);
96350696a6eSStefan Eßer
96450696a6eSStefan Eßer bc_lex_next(&p->l);
96544d4804dSStefan Eßer
96644d4804dSStefan Eßer // If the next token is not a delimiter, that is a problem.
96710041e99SStefan Eßer if (BC_ERR(!bc_parse_isDelimiter(p) && !bc_parse_TopFunc(p)))
96878bc019dSStefan Eßer {
96950696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN);
97050696a6eSStefan Eßer }
97178bc019dSStefan Eßer }
97250696a6eSStefan Eßer
97344d4804dSStefan Eßer // Do we have a brace flag?
97450696a6eSStefan Eßer has_brace = (BC_PARSE_BRACE(p) != 0);
97550696a6eSStefan Eßer
97678bc019dSStefan Eßer do
97778bc019dSStefan Eßer {
97850696a6eSStefan Eßer size_t len = p->flags.len;
97950696a6eSStefan Eßer bool loop;
98050696a6eSStefan Eßer
98144d4804dSStefan Eßer // If we have a brace flag but not a brace, that's a problem.
98250696a6eSStefan Eßer if (has_brace && !brace) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
98350696a6eSStefan Eßer
98444d4804dSStefan Eßer // Are we inside a loop?
98550696a6eSStefan Eßer loop = (BC_PARSE_LOOP_INNER(p) != 0);
98650696a6eSStefan Eßer
98744d4804dSStefan Eßer // If we are ending a loop or an else...
98878bc019dSStefan Eßer if (loop || BC_PARSE_ELSE(p))
98978bc019dSStefan Eßer {
99044d4804dSStefan Eßer // Loops have condition labels that we have to take care of as well.
99178bc019dSStefan Eßer if (loop)
99278bc019dSStefan Eßer {
99350696a6eSStefan Eßer size_t* label = bc_vec_top(&p->conds);
99450696a6eSStefan Eßer
99550696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP);
99650696a6eSStefan Eßer bc_parse_pushIndex(p, *label);
99750696a6eSStefan Eßer
99850696a6eSStefan Eßer bc_vec_pop(&p->conds);
99950696a6eSStefan Eßer }
100050696a6eSStefan Eßer
100150696a6eSStefan Eßer bc_parse_setLabel(p);
100250696a6eSStefan Eßer bc_vec_pop(&p->flags);
100350696a6eSStefan Eßer }
100444d4804dSStefan Eßer // If we are ending a function...
100578bc019dSStefan Eßer else if (BC_PARSE_FUNC_INNER(p))
100678bc019dSStefan Eßer {
100750696a6eSStefan Eßer BcInst inst = (p->func->voidfn ? BC_INST_RET_VOID : BC_INST_RET0);
100850696a6eSStefan Eßer bc_parse_push(p, inst);
100950696a6eSStefan Eßer bc_parse_updateFunc(p, BC_PROG_MAIN);
101050696a6eSStefan Eßer bc_vec_pop(&p->flags);
101150696a6eSStefan Eßer }
101244d4804dSStefan Eßer // If we have a brace flag and not an if statement, we can pop the top
101344d4804dSStefan Eßer // of the flags stack because they have been taken care of above.
101444d4804dSStefan Eßer else if (has_brace && !BC_PARSE_IF(p)) bc_vec_pop(&p->flags);
101550696a6eSStefan Eßer
101650696a6eSStefan Eßer // This needs to be last to parse nested if's properly.
101778bc019dSStefan Eßer if (BC_PARSE_IF(p) && (len == p->flags.len || !BC_PARSE_BRACE(p)))
101878bc019dSStefan Eßer {
101944d4804dSStefan Eßer // Eat newlines.
102078bc019dSStefan Eßer while (p->l.t == BC_LEX_NLINE)
102178bc019dSStefan Eßer {
102278bc019dSStefan Eßer bc_lex_next(&p->l);
102378bc019dSStefan Eßer }
102450696a6eSStefan Eßer
102544d4804dSStefan Eßer // *Now* we can pop the flags.
102650696a6eSStefan Eßer bc_vec_pop(&p->flags);
102750696a6eSStefan Eßer
102844d4804dSStefan Eßer // If we are allowed non-POSIX stuff...
102978bc019dSStefan Eßer if (!BC_S)
103078bc019dSStefan Eßer {
103144d4804dSStefan Eßer // Have we found yet another dangling else?
103250696a6eSStefan Eßer *(BC_PARSE_TOP_FLAG_PTR(p)) |= BC_PARSE_FLAG_IF_END;
103350696a6eSStefan Eßer new_else = (p->l.t == BC_LEX_KW_ELSE);
103450696a6eSStefan Eßer
103544d4804dSStefan Eßer // Parse the else or end the if statement body.
103650696a6eSStefan Eßer if (new_else) bc_parse_else(p);
103750696a6eSStefan Eßer else if (!has_brace && (!BC_PARSE_IF_END(p) || brace))
103878bc019dSStefan Eßer {
103950696a6eSStefan Eßer bc_parse_noElse(p);
104050696a6eSStefan Eßer }
104178bc019dSStefan Eßer }
104244d4804dSStefan Eßer // POSIX requires us to do the bare minimum only.
104350696a6eSStefan Eßer else bc_parse_noElse(p);
104450696a6eSStefan Eßer }
104550696a6eSStefan Eßer
104644d4804dSStefan Eßer // If these are both true, we have "used" the braces that we found.
104750696a6eSStefan Eßer if (brace && has_brace) brace = false;
104878bc019dSStefan Eßer }
104978bc019dSStefan Eßer // This condition was perhaps the hardest single part of the parser. If
105078bc019dSStefan Eßer // the flags stack does not have enough, we should stop. If we have a
105178bc019dSStefan Eßer // new else statement, we should stop. If we do have the end of an if
105278bc019dSStefan Eßer // statement and we have eaten the brace, we should stop. If we do have
105378bc019dSStefan Eßer // a brace flag, we should stop.
105478bc019dSStefan Eßer while (p->flags.len > 1 && !new_else && (!BC_PARSE_IF_END(p) || brace) &&
105550696a6eSStefan Eßer !(has_brace = (BC_PARSE_BRACE(p) != 0)));
105650696a6eSStefan Eßer
105744d4804dSStefan Eßer // If we have a brace, yet no body for it, that's a problem.
105878bc019dSStefan Eßer if (BC_ERR(p->flags.len == 1 && brace)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
105978bc019dSStefan Eßer else if (brace && BC_PARSE_BRACE(p))
106078bc019dSStefan Eßer {
106144d4804dSStefan Eßer // If we make it here, we have a brace and a flag for it.
106250696a6eSStefan Eßer uint16_t flags = BC_PARSE_TOP_FLAG(p);
106350696a6eSStefan Eßer
106444d4804dSStefan Eßer // This condition ensure that the *last* body is correctly finished by
106544d4804dSStefan Eßer // popping its flags.
106650696a6eSStefan Eßer if (!(flags & (BC_PARSE_FLAG_FUNC_INNER | BC_PARSE_FLAG_LOOP_INNER)) &&
106750696a6eSStefan Eßer !(flags & (BC_PARSE_FLAG_IF | BC_PARSE_FLAG_ELSE)) &&
106850696a6eSStefan Eßer !(flags & (BC_PARSE_FLAG_IF_END)))
106950696a6eSStefan Eßer {
107050696a6eSStefan Eßer bc_vec_pop(&p->flags);
107150696a6eSStefan Eßer }
107250696a6eSStefan Eßer }
107350696a6eSStefan Eßer }
107450696a6eSStefan Eßer
107544d4804dSStefan Eßer /**
107644d4804dSStefan Eßer * Starts the body of a control statement or function.
107744d4804dSStefan Eßer * @param p The parser.
107844d4804dSStefan Eßer * @param flags The current flags (will be edited).
107944d4804dSStefan Eßer */
108078bc019dSStefan Eßer static void
bc_parse_startBody(BcParse * p,uint16_t flags)108178bc019dSStefan Eßer bc_parse_startBody(BcParse* p, uint16_t flags)
108278bc019dSStefan Eßer {
108350696a6eSStefan Eßer assert(flags);
108450696a6eSStefan Eßer flags |= (BC_PARSE_TOP_FLAG(p) & (BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_LOOP));
108550696a6eSStefan Eßer flags |= BC_PARSE_FLAG_BODY;
108650696a6eSStefan Eßer bc_vec_push(&p->flags, &flags);
108750696a6eSStefan Eßer }
108850696a6eSStefan Eßer
108978bc019dSStefan Eßer void
bc_parse_endif(BcParse * p)109078bc019dSStefan Eßer bc_parse_endif(BcParse* p)
109178bc019dSStefan Eßer {
1092a30efc5cSStefan Eßer size_t i;
1093a30efc5cSStefan Eßer bool good;
1094a30efc5cSStefan Eßer
1095a30efc5cSStefan Eßer // Not a problem if this is true.
1096a30efc5cSStefan Eßer if (BC_NO_ERR(!BC_PARSE_NO_EXEC(p))) return;
1097a30efc5cSStefan Eßer
1098a30efc5cSStefan Eßer good = true;
1099a30efc5cSStefan Eßer
1100a30efc5cSStefan Eßer // Find an instance of a body that needs closing, i.e., a statement that did
1101a30efc5cSStefan Eßer // not have a right brace when it should have.
110278bc019dSStefan Eßer for (i = 0; good && i < p->flags.len; ++i)
110378bc019dSStefan Eßer {
1104a30efc5cSStefan Eßer uint16_t flag = *((uint16_t*) bc_vec_item(&p->flags, i));
1105a30efc5cSStefan Eßer good = ((flag & BC_PARSE_FLAG_BRACE) != BC_PARSE_FLAG_BRACE);
1106a30efc5cSStefan Eßer }
1107a30efc5cSStefan Eßer
1108a30efc5cSStefan Eßer // If we did not find such an instance...
110978bc019dSStefan Eßer if (good)
111078bc019dSStefan Eßer {
1111a30efc5cSStefan Eßer // We set this to restore it later. We don't want the parser thinking
1112a30efc5cSStefan Eßer // that we are on stdin for this one because it will want more.
1113d101cdd6SStefan Eßer BcMode mode = vm->mode;
1114a30efc5cSStefan Eßer
1115d101cdd6SStefan Eßer vm->mode = BC_MODE_FILE;
1116a30efc5cSStefan Eßer
1117a30efc5cSStefan Eßer // End all of the if statements and loops.
111878bc019dSStefan Eßer while (p->flags.len > 1 || BC_PARSE_IF_END(p))
111978bc019dSStefan Eßer {
1120a30efc5cSStefan Eßer if (BC_PARSE_IF_END(p)) bc_parse_noElse(p);
1121a30efc5cSStefan Eßer if (p->flags.len > 1) bc_parse_endBody(p, false);
1122a30efc5cSStefan Eßer }
1123a30efc5cSStefan Eßer
1124d101cdd6SStefan Eßer vm->mode = (uchar) mode;
1125a30efc5cSStefan Eßer }
1126a30efc5cSStefan Eßer // If we reach here, a block was not properly closed, and we should error.
1127d101cdd6SStefan Eßer else bc_parse_err(&vm->prs, BC_ERR_PARSE_BLOCK);
1128a30efc5cSStefan Eßer }
1129a30efc5cSStefan Eßer
113044d4804dSStefan Eßer /**
113144d4804dSStefan Eßer * Parses an if statement.
113244d4804dSStefan Eßer * @param p The parser.
113344d4804dSStefan Eßer */
113478bc019dSStefan Eßer static void
bc_parse_if(BcParse * p)113578bc019dSStefan Eßer bc_parse_if(BcParse* p)
113678bc019dSStefan Eßer {
113744d4804dSStefan Eßer // We are allowed relational operators, and we must have a value.
113850696a6eSStefan Eßer size_t idx;
113950696a6eSStefan Eßer uint8_t flags = (BC_PARSE_REL | BC_PARSE_NEEDVAL);
114050696a6eSStefan Eßer
114144d4804dSStefan Eßer // Get the left paren and barf if necessary.
114250696a6eSStefan Eßer bc_lex_next(&p->l);
114378bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
114450696a6eSStefan Eßer
114544d4804dSStefan Eßer // Parse the condition.
114650696a6eSStefan Eßer bc_lex_next(&p->l);
114750696a6eSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_rel);
114844d4804dSStefan Eßer
114944d4804dSStefan Eßer // Must have a right paren.
115078bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
115150696a6eSStefan Eßer
115250696a6eSStefan Eßer bc_lex_next(&p->l);
115344d4804dSStefan Eßer
115444d4804dSStefan Eßer // Insert the conditional jump instruction.
115550696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP_ZERO);
115650696a6eSStefan Eßer
115750696a6eSStefan Eßer idx = p->func->labels.len;
115850696a6eSStefan Eßer
115944d4804dSStefan Eßer // Push the index for the instruction and create an exit label for an else
116044d4804dSStefan Eßer // statement.
116150696a6eSStefan Eßer bc_parse_pushIndex(p, idx);
116250696a6eSStefan Eßer bc_parse_createExitLabel(p, idx, false);
116344d4804dSStefan Eßer
116450696a6eSStefan Eßer bc_parse_startBody(p, BC_PARSE_FLAG_IF);
116550696a6eSStefan Eßer }
116650696a6eSStefan Eßer
116744d4804dSStefan Eßer /**
116844d4804dSStefan Eßer * Parses an else statement.
116944d4804dSStefan Eßer * @param p The parser.
117044d4804dSStefan Eßer */
117178bc019dSStefan Eßer static void
bc_parse_else(BcParse * p)117278bc019dSStefan Eßer bc_parse_else(BcParse* p)
117378bc019dSStefan Eßer {
117450696a6eSStefan Eßer size_t idx = p->func->labels.len;
117550696a6eSStefan Eßer
117644d4804dSStefan Eßer // We must be at the end of an if statement.
117778bc019dSStefan Eßer if (BC_ERR(!BC_PARSE_IF_END(p))) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
117850696a6eSStefan Eßer
117944d4804dSStefan Eßer // Push an unconditional jump to make bc jump over the else statement if it
118044d4804dSStefan Eßer // executed the original if statement.
118150696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP);
118250696a6eSStefan Eßer bc_parse_pushIndex(p, idx);
118350696a6eSStefan Eßer
118444d4804dSStefan Eßer // Clear the else stuff. Yes, that function is misnamed for its use here,
118544d4804dSStefan Eßer // but deal with it.
118650696a6eSStefan Eßer bc_parse_noElse(p);
118750696a6eSStefan Eßer
118844d4804dSStefan Eßer // Create the exit label and parse the body.
118950696a6eSStefan Eßer bc_parse_createExitLabel(p, idx, false);
119050696a6eSStefan Eßer bc_parse_startBody(p, BC_PARSE_FLAG_ELSE);
119150696a6eSStefan Eßer
119250696a6eSStefan Eßer bc_lex_next(&p->l);
119350696a6eSStefan Eßer }
119450696a6eSStefan Eßer
119544d4804dSStefan Eßer /**
119644d4804dSStefan Eßer * Parse a while loop.
119744d4804dSStefan Eßer * @param p The parser.
119844d4804dSStefan Eßer */
119978bc019dSStefan Eßer static void
bc_parse_while(BcParse * p)120078bc019dSStefan Eßer bc_parse_while(BcParse* p)
120178bc019dSStefan Eßer {
120244d4804dSStefan Eßer // We are allowed relational operators, and we must have a value.
120350696a6eSStefan Eßer size_t idx;
120450696a6eSStefan Eßer uint8_t flags = (BC_PARSE_REL | BC_PARSE_NEEDVAL);
120550696a6eSStefan Eßer
120644d4804dSStefan Eßer // Get the left paren and barf if necessary.
120750696a6eSStefan Eßer bc_lex_next(&p->l);
120878bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
120950696a6eSStefan Eßer bc_lex_next(&p->l);
121050696a6eSStefan Eßer
121144d4804dSStefan Eßer // Create the labels. Loops need both.
121250696a6eSStefan Eßer bc_parse_createCondLabel(p, p->func->labels.len);
121350696a6eSStefan Eßer idx = p->func->labels.len;
121450696a6eSStefan Eßer bc_parse_createExitLabel(p, idx, true);
121550696a6eSStefan Eßer
121644d4804dSStefan Eßer // Parse the actual condition and barf on non-right paren.
121750696a6eSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_rel);
121878bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
121950696a6eSStefan Eßer bc_lex_next(&p->l);
122050696a6eSStefan Eßer
122144d4804dSStefan Eßer // Now we can push the conditional jump and start the body.
122250696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP_ZERO);
122350696a6eSStefan Eßer bc_parse_pushIndex(p, idx);
122450696a6eSStefan Eßer bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER);
122550696a6eSStefan Eßer }
122650696a6eSStefan Eßer
122744d4804dSStefan Eßer /**
122844d4804dSStefan Eßer * Parse a for loop.
122944d4804dSStefan Eßer * @param p The parser.
123044d4804dSStefan Eßer */
123178bc019dSStefan Eßer static void
bc_parse_for(BcParse * p)123278bc019dSStefan Eßer bc_parse_for(BcParse* p)
123378bc019dSStefan Eßer {
123450696a6eSStefan Eßer size_t cond_idx, exit_idx, body_idx, update_idx;
123550696a6eSStefan Eßer
123644d4804dSStefan Eßer // Barf on the missing left paren.
123750696a6eSStefan Eßer bc_lex_next(&p->l);
123878bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
123950696a6eSStefan Eßer bc_lex_next(&p->l);
124050696a6eSStefan Eßer
124144d4804dSStefan Eßer // The first statement can be empty, but if it is, check for error in POSIX
124244d4804dSStefan Eßer // mode. Otherwise, parse it.
124378bc019dSStefan Eßer if (p->l.t != BC_LEX_SCOLON) bc_parse_expr_status(p, 0, bc_parse_next_for);
124450696a6eSStefan Eßer else bc_parse_err(p, BC_ERR_POSIX_FOR);
124550696a6eSStefan Eßer
124644d4804dSStefan Eßer // Must have a semicolon.
124744d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_SCOLON)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
124850696a6eSStefan Eßer bc_lex_next(&p->l);
124950696a6eSStefan Eßer
125044d4804dSStefan Eßer // These are indices for labels. There are so many of them because the end
125144d4804dSStefan Eßer // of the loop must unconditionally jump to the update code. Then the update
125244d4804dSStefan Eßer // code must unconditionally jump to the condition code. Then the condition
125344d4804dSStefan Eßer // code must *conditionally* jump to the exit.
125450696a6eSStefan Eßer cond_idx = p->func->labels.len;
125550696a6eSStefan Eßer update_idx = cond_idx + 1;
125650696a6eSStefan Eßer body_idx = update_idx + 1;
125750696a6eSStefan Eßer exit_idx = body_idx + 1;
125850696a6eSStefan Eßer
125944d4804dSStefan Eßer // This creates the condition label.
126050696a6eSStefan Eßer bc_parse_createLabel(p, p->func->code.len);
126150696a6eSStefan Eßer
126244d4804dSStefan Eßer // Parse an expression if it exists.
126378bc019dSStefan Eßer if (p->l.t != BC_LEX_SCOLON)
126478bc019dSStefan Eßer {
126550696a6eSStefan Eßer uint8_t flags = (BC_PARSE_REL | BC_PARSE_NEEDVAL);
126650696a6eSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_for);
126750696a6eSStefan Eßer }
126878bc019dSStefan Eßer else
126978bc019dSStefan Eßer {
127044d4804dSStefan Eßer // Set this for the next call to bc_parse_number because an empty
127144d4804dSStefan Eßer // condition means that it is an infinite loop, so the condition must be
127244d4804dSStefan Eßer // non-zero. This is safe to set because the current token is a
127344d4804dSStefan Eßer // semicolon, which has no string requirement.
127450696a6eSStefan Eßer bc_vec_string(&p->l.str, sizeof(bc_parse_one) - 1, bc_parse_one);
127550696a6eSStefan Eßer bc_parse_number(p);
127650696a6eSStefan Eßer
127744d4804dSStefan Eßer // An empty condition makes POSIX mad.
127850696a6eSStefan Eßer bc_parse_err(p, BC_ERR_POSIX_FOR);
127950696a6eSStefan Eßer }
128050696a6eSStefan Eßer
128144d4804dSStefan Eßer // Must have a semicolon.
128278bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_SCOLON)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
128350696a6eSStefan Eßer bc_lex_next(&p->l);
128450696a6eSStefan Eßer
128544d4804dSStefan Eßer // Now we can set up the conditional jump to the exit and an unconditional
128644d4804dSStefan Eßer // jump to the body right after. The unconditional jump to the body is
128744d4804dSStefan Eßer // because there is update code coming right after the condition, so we need
128844d4804dSStefan Eßer // to skip it to get to the body.
128950696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP_ZERO);
129050696a6eSStefan Eßer bc_parse_pushIndex(p, exit_idx);
129150696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP);
129250696a6eSStefan Eßer bc_parse_pushIndex(p, body_idx);
129350696a6eSStefan Eßer
129444d4804dSStefan Eßer // Now create the label for the update code.
129550696a6eSStefan Eßer bc_parse_createCondLabel(p, update_idx);
129650696a6eSStefan Eßer
129744d4804dSStefan Eßer // Parse if not empty, and if it is, let POSIX yell if necessary.
129878bc019dSStefan Eßer if (p->l.t != BC_LEX_RPAREN) bc_parse_expr_status(p, 0, bc_parse_next_rel);
129950696a6eSStefan Eßer else bc_parse_err(p, BC_ERR_POSIX_FOR);
130050696a6eSStefan Eßer
130144d4804dSStefan Eßer // Must have a right paren.
130278bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RPAREN)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
130344d4804dSStefan Eßer
130444d4804dSStefan Eßer // Set up a jump to the condition right after the update code.
130550696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP);
130650696a6eSStefan Eßer bc_parse_pushIndex(p, cond_idx);
130750696a6eSStefan Eßer bc_parse_createLabel(p, p->func->code.len);
130850696a6eSStefan Eßer
130944d4804dSStefan Eßer // Create an exit label for the body and start the body.
131050696a6eSStefan Eßer bc_parse_createExitLabel(p, exit_idx, true);
131150696a6eSStefan Eßer bc_lex_next(&p->l);
131250696a6eSStefan Eßer bc_parse_startBody(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER);
131350696a6eSStefan Eßer }
131450696a6eSStefan Eßer
131544d4804dSStefan Eßer /**
131644d4804dSStefan Eßer * Parse a statement or token that indicates a loop exit. This includes an
131744d4804dSStefan Eßer * actual loop exit, the break keyword, or the continue keyword.
131844d4804dSStefan Eßer * @param p The parser.
131944d4804dSStefan Eßer * @param type The type of exit.
132044d4804dSStefan Eßer */
132178bc019dSStefan Eßer static void
bc_parse_loopExit(BcParse * p,BcLexType type)132278bc019dSStefan Eßer bc_parse_loopExit(BcParse* p, BcLexType type)
132378bc019dSStefan Eßer {
132450696a6eSStefan Eßer size_t i;
132550696a6eSStefan Eßer BcInstPtr* ip;
132650696a6eSStefan Eßer
132744d4804dSStefan Eßer // Must have a loop. If we don't, that's an error.
132850696a6eSStefan Eßer if (BC_ERR(!BC_PARSE_LOOP(p))) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
132950696a6eSStefan Eßer
133044d4804dSStefan Eßer // If we have a break statement...
133178bc019dSStefan Eßer if (type == BC_LEX_KW_BREAK)
133278bc019dSStefan Eßer {
133344d4804dSStefan Eßer // If there are no exits, something went wrong somewhere.
133450696a6eSStefan Eßer if (BC_ERR(!p->exits.len)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
133550696a6eSStefan Eßer
133644d4804dSStefan Eßer // Get the exit.
133750696a6eSStefan Eßer i = p->exits.len - 1;
133850696a6eSStefan Eßer ip = bc_vec_item(&p->exits, i);
133950696a6eSStefan Eßer
134044d4804dSStefan Eßer // The condition !ip->func is true if the exit is not for a loop, so we
134144d4804dSStefan Eßer // need to find the first actual loop exit.
134278bc019dSStefan Eßer while (!ip->func && i < p->exits.len)
134378bc019dSStefan Eßer {
134478bc019dSStefan Eßer ip = bc_vec_item(&p->exits, i);
134578bc019dSStefan Eßer i -= 1;
134678bc019dSStefan Eßer }
134744d4804dSStefan Eßer
134844d4804dSStefan Eßer // Make sure everything is hunky dory.
134950696a6eSStefan Eßer assert(ip != NULL && (i < p->exits.len || ip->func));
135044d4804dSStefan Eßer
135144d4804dSStefan Eßer // Set the index for the exit.
135250696a6eSStefan Eßer i = ip->idx;
135350696a6eSStefan Eßer }
135444d4804dSStefan Eßer // If we have a continue statement or just the loop end, jump to the
135544d4804dSStefan Eßer // condition (or update for a foor loop).
135650696a6eSStefan Eßer else i = *((size_t*) bc_vec_top(&p->conds));
135750696a6eSStefan Eßer
135844d4804dSStefan Eßer // Add the unconditional jump.
135950696a6eSStefan Eßer bc_parse_push(p, BC_INST_JUMP);
136050696a6eSStefan Eßer bc_parse_pushIndex(p, i);
136150696a6eSStefan Eßer
136250696a6eSStefan Eßer bc_lex_next(&p->l);
136350696a6eSStefan Eßer }
136450696a6eSStefan Eßer
136544d4804dSStefan Eßer /**
136644d4804dSStefan Eßer * Parse a function (header).
136744d4804dSStefan Eßer * @param p The parser.
136844d4804dSStefan Eßer */
136978bc019dSStefan Eßer static void
bc_parse_func(BcParse * p)137078bc019dSStefan Eßer bc_parse_func(BcParse* p)
137178bc019dSStefan Eßer {
137250696a6eSStefan Eßer bool comma = false, voidfn;
137350696a6eSStefan Eßer uint16_t flags;
137450696a6eSStefan Eßer size_t idx;
137550696a6eSStefan Eßer
137650696a6eSStefan Eßer bc_lex_next(&p->l);
137750696a6eSStefan Eßer
137844d4804dSStefan Eßer // Must have a name.
137944d4804dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_NAME)) bc_parse_err(p, BC_ERR_PARSE_FUNC);
138050696a6eSStefan Eßer
138144d4804dSStefan Eßer // If the name is "void", and POSIX is not on, mark as void.
138250696a6eSStefan Eßer voidfn = (!BC_IS_POSIX && p->l.t == BC_LEX_NAME &&
138350696a6eSStefan Eßer !strcmp(p->l.str.v, "void"));
138450696a6eSStefan Eßer
138544d4804dSStefan Eßer // We can safely do this because the expected token should not overwrite the
138644d4804dSStefan Eßer // function name.
138750696a6eSStefan Eßer bc_lex_next(&p->l);
138850696a6eSStefan Eßer
138944d4804dSStefan Eßer // If we *don't* have another name, then void is the name of the function.
139050696a6eSStefan Eßer voidfn = (voidfn && p->l.t == BC_LEX_NAME);
139150696a6eSStefan Eßer
139244d4804dSStefan Eßer // With a void function, allow POSIX to complain and get a new token.
139378bc019dSStefan Eßer if (voidfn)
139478bc019dSStefan Eßer {
139550696a6eSStefan Eßer bc_parse_err(p, BC_ERR_POSIX_VOID);
139644d4804dSStefan Eßer
139744d4804dSStefan Eßer // We can safely do this because the expected token should not overwrite
139844d4804dSStefan Eßer // the function name.
139950696a6eSStefan Eßer bc_lex_next(&p->l);
140050696a6eSStefan Eßer }
140150696a6eSStefan Eßer
140244d4804dSStefan Eßer // Must have a left paren.
140378bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_LPAREN)) bc_parse_err(p, BC_ERR_PARSE_FUNC);
140450696a6eSStefan Eßer
140544d4804dSStefan Eßer // Make sure the functions map and vector are synchronized.
140650696a6eSStefan Eßer assert(p->prog->fns.len == p->prog->fn_map.len);
140750696a6eSStefan Eßer
140844d4804dSStefan Eßer // Insert the function by name into the map and vector.
140950696a6eSStefan Eßer idx = bc_program_insertFunc(p->prog, p->l.str.v);
141050696a6eSStefan Eßer
141144d4804dSStefan Eßer // Make sure the insert worked.
141250696a6eSStefan Eßer assert(idx);
141344d4804dSStefan Eßer
141444d4804dSStefan Eßer // Update the function pointer and stuff in the parser and set its void.
141550696a6eSStefan Eßer bc_parse_updateFunc(p, idx);
141650696a6eSStefan Eßer p->func->voidfn = voidfn;
141750696a6eSStefan Eßer
141850696a6eSStefan Eßer bc_lex_next(&p->l);
141950696a6eSStefan Eßer
142044d4804dSStefan Eßer // While we do not have a right paren, we are still parsing arguments.
142178bc019dSStefan Eßer while (p->l.t != BC_LEX_RPAREN)
142278bc019dSStefan Eßer {
142350696a6eSStefan Eßer BcType t = BC_TYPE_VAR;
142450696a6eSStefan Eßer
142544d4804dSStefan Eßer // If we have an asterisk, we are parsing a reference argument.
142678bc019dSStefan Eßer if (p->l.t == BC_LEX_OP_MULTIPLY)
142778bc019dSStefan Eßer {
142850696a6eSStefan Eßer t = BC_TYPE_REF;
142950696a6eSStefan Eßer bc_lex_next(&p->l);
143044d4804dSStefan Eßer
143144d4804dSStefan Eßer // Let POSIX complain if necessary.
143250696a6eSStefan Eßer bc_parse_err(p, BC_ERR_POSIX_REF);
143350696a6eSStefan Eßer }
143450696a6eSStefan Eßer
143544d4804dSStefan Eßer // If we don't have a name, the argument will not have a name. Barf.
143678bc019dSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_NAME)) bc_parse_err(p, BC_ERR_PARSE_FUNC);
143750696a6eSStefan Eßer
143844d4804dSStefan Eßer // Increment the number of parameters.
143950696a6eSStefan Eßer p->func->nparams += 1;
144050696a6eSStefan Eßer
144144d4804dSStefan Eßer // Copy the string in the lexer so that we can use the lexer again.
144250696a6eSStefan Eßer bc_vec_string(&p->buf, p->l.str.len, p->l.str.v);
144350696a6eSStefan Eßer
144450696a6eSStefan Eßer bc_lex_next(&p->l);
144550696a6eSStefan Eßer
144644d4804dSStefan Eßer // We are parsing an array parameter if this is true.
144778bc019dSStefan Eßer if (p->l.t == BC_LEX_LBRACKET)
144878bc019dSStefan Eßer {
144944d4804dSStefan Eßer // Set the array type, unless we are already parsing a reference.
145050696a6eSStefan Eßer if (t == BC_TYPE_VAR) t = BC_TYPE_ARRAY;
145150696a6eSStefan Eßer
145250696a6eSStefan Eßer bc_lex_next(&p->l);
145350696a6eSStefan Eßer
145444d4804dSStefan Eßer // The brackets *must* be empty.
145550696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RBRACKET))
145678bc019dSStefan Eßer {
145750696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_FUNC);
145878bc019dSStefan Eßer }
145950696a6eSStefan Eßer
146050696a6eSStefan Eßer bc_lex_next(&p->l);
146150696a6eSStefan Eßer }
146244d4804dSStefan Eßer // If we did *not* get a bracket, but we are expecting a reference, we
146344d4804dSStefan Eßer // have a problem.
146450696a6eSStefan Eßer else if (BC_ERR(t == BC_TYPE_REF))
146578bc019dSStefan Eßer {
146650696a6eSStefan Eßer bc_parse_verr(p, BC_ERR_PARSE_REF_VAR, p->buf.v);
146778bc019dSStefan Eßer }
146850696a6eSStefan Eßer
146944d4804dSStefan Eßer // Test for comma and get the next token if it exists.
147050696a6eSStefan Eßer comma = (p->l.t == BC_LEX_COMMA);
147144d4804dSStefan Eßer if (comma) bc_lex_next(&p->l);
147250696a6eSStefan Eßer
147344d4804dSStefan Eßer // Insert the parameter into the function.
147450696a6eSStefan Eßer bc_func_insert(p->func, p->prog, p->buf.v, t, p->l.line);
147550696a6eSStefan Eßer }
147650696a6eSStefan Eßer
147744d4804dSStefan Eßer // If we have a comma, but no parameter, barf.
147850696a6eSStefan Eßer if (BC_ERR(comma)) bc_parse_err(p, BC_ERR_PARSE_FUNC);
147950696a6eSStefan Eßer
148044d4804dSStefan Eßer // Start the body.
148150696a6eSStefan Eßer flags = BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_FUNC_INNER;
148250696a6eSStefan Eßer bc_parse_startBody(p, flags);
148350696a6eSStefan Eßer
148450696a6eSStefan Eßer bc_lex_next(&p->l);
148550696a6eSStefan Eßer
148644d4804dSStefan Eßer // POSIX requires that a brace be on the same line as the function header.
148744d4804dSStefan Eßer // If we don't have a brace, let POSIX throw an error.
148850696a6eSStefan Eßer if (p->l.t != BC_LEX_LBRACE) bc_parse_err(p, BC_ERR_POSIX_BRACE);
148950696a6eSStefan Eßer }
149050696a6eSStefan Eßer
149144d4804dSStefan Eßer /**
149244d4804dSStefan Eßer * Parse an auto list.
149344d4804dSStefan Eßer * @param p The parser.
149444d4804dSStefan Eßer */
149578bc019dSStefan Eßer static void
bc_parse_auto(BcParse * p)149678bc019dSStefan Eßer bc_parse_auto(BcParse* p)
149778bc019dSStefan Eßer {
149850696a6eSStefan Eßer bool comma, one;
149950696a6eSStefan Eßer
150044d4804dSStefan Eßer // Error if the auto keyword appeared in the wrong place.
150150696a6eSStefan Eßer if (BC_ERR(!p->auto_part)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
150250696a6eSStefan Eßer bc_lex_next(&p->l);
150350696a6eSStefan Eßer
150450696a6eSStefan Eßer p->auto_part = comma = false;
150550696a6eSStefan Eßer
150644d4804dSStefan Eßer // We need at least one variable or array.
150744d4804dSStefan Eßer one = (p->l.t == BC_LEX_NAME);
150844d4804dSStefan Eßer
150944d4804dSStefan Eßer // While we have a variable or array.
151078bc019dSStefan Eßer while (p->l.t == BC_LEX_NAME)
151178bc019dSStefan Eßer {
151250696a6eSStefan Eßer BcType t;
151350696a6eSStefan Eßer
151444d4804dSStefan Eßer // Copy the name from the lexer, so we can use it again.
151550696a6eSStefan Eßer bc_vec_string(&p->buf, p->l.str.len - 1, p->l.str.v);
151650696a6eSStefan Eßer
151750696a6eSStefan Eßer bc_lex_next(&p->l);
151850696a6eSStefan Eßer
151944d4804dSStefan Eßer // If we are parsing an array...
152078bc019dSStefan Eßer if (p->l.t == BC_LEX_LBRACKET)
152178bc019dSStefan Eßer {
152250696a6eSStefan Eßer t = BC_TYPE_ARRAY;
152350696a6eSStefan Eßer
152450696a6eSStefan Eßer bc_lex_next(&p->l);
152550696a6eSStefan Eßer
152644d4804dSStefan Eßer // The brackets *must* be empty.
152750696a6eSStefan Eßer if (BC_ERR(p->l.t != BC_LEX_RBRACKET))
152878bc019dSStefan Eßer {
152950696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_FUNC);
153078bc019dSStefan Eßer }
153150696a6eSStefan Eßer
153250696a6eSStefan Eßer bc_lex_next(&p->l);
153350696a6eSStefan Eßer }
153450696a6eSStefan Eßer else t = BC_TYPE_VAR;
153550696a6eSStefan Eßer
153644d4804dSStefan Eßer // Test for comma and get the next token if it exists.
153750696a6eSStefan Eßer comma = (p->l.t == BC_LEX_COMMA);
153850696a6eSStefan Eßer if (comma) bc_lex_next(&p->l);
153950696a6eSStefan Eßer
154044d4804dSStefan Eßer // Insert the auto into the function.
154150696a6eSStefan Eßer bc_func_insert(p->func, p->prog, p->buf.v, t, p->l.line);
154250696a6eSStefan Eßer }
154350696a6eSStefan Eßer
154444d4804dSStefan Eßer // If we have a comma, but no auto, barf.
154550696a6eSStefan Eßer if (BC_ERR(comma)) bc_parse_err(p, BC_ERR_PARSE_FUNC);
154644d4804dSStefan Eßer
154744d4804dSStefan Eßer // If we don't have any variables or arrays, barf.
154850696a6eSStefan Eßer if (BC_ERR(!one)) bc_parse_err(p, BC_ERR_PARSE_NO_AUTO);
154944d4804dSStefan Eßer
155044d4804dSStefan Eßer // The auto statement should be all that's in the statement.
155178bc019dSStefan Eßer if (BC_ERR(!bc_parse_isDelimiter(p))) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
155250696a6eSStefan Eßer }
155350696a6eSStefan Eßer
155444d4804dSStefan Eßer /**
155544d4804dSStefan Eßer * Parses a body.
155644d4804dSStefan Eßer * @param p The parser.
155744d4804dSStefan Eßer * @param brace True if a brace was encountered, false otherwise.
155844d4804dSStefan Eßer */
155978bc019dSStefan Eßer static void
bc_parse_body(BcParse * p,bool brace)156078bc019dSStefan Eßer bc_parse_body(BcParse* p, bool brace)
156178bc019dSStefan Eßer {
156250696a6eSStefan Eßer uint16_t* flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
156350696a6eSStefan Eßer
156450696a6eSStefan Eßer assert(flag_ptr != NULL);
156550696a6eSStefan Eßer assert(p->flags.len >= 2);
156650696a6eSStefan Eßer
156744d4804dSStefan Eßer // The body flag is for when we expect a body. We got a body, so clear the
156844d4804dSStefan Eßer // flag.
156950696a6eSStefan Eßer *flag_ptr &= ~(BC_PARSE_FLAG_BODY);
157050696a6eSStefan Eßer
157144d4804dSStefan Eßer // If we are inside a function, that means we just barely entered it, and
157244d4804dSStefan Eßer // we can expect an auto list.
157378bc019dSStefan Eßer if (*flag_ptr & BC_PARSE_FLAG_FUNC_INNER)
157478bc019dSStefan Eßer {
157544d4804dSStefan Eßer // We *must* have a brace in this case.
157650696a6eSStefan Eßer if (BC_ERR(!brace)) bc_parse_err(p, BC_ERR_PARSE_TOKEN);
157750696a6eSStefan Eßer
157850696a6eSStefan Eßer p->auto_part = (p->l.t != BC_LEX_KW_AUTO);
157950696a6eSStefan Eßer
158078bc019dSStefan Eßer if (!p->auto_part)
158178bc019dSStefan Eßer {
158250696a6eSStefan Eßer // Make sure this is true to not get a parse error.
158350696a6eSStefan Eßer p->auto_part = true;
158450696a6eSStefan Eßer
158544d4804dSStefan Eßer // Since we already have the auto keyword, parse.
158650696a6eSStefan Eßer bc_parse_auto(p);
158750696a6eSStefan Eßer }
158850696a6eSStefan Eßer
158944d4804dSStefan Eßer // Eat a newline.
159050696a6eSStefan Eßer if (p->l.t == BC_LEX_NLINE) bc_lex_next(&p->l);
159150696a6eSStefan Eßer }
159278bc019dSStefan Eßer else
159378bc019dSStefan Eßer {
159444d4804dSStefan Eßer // This is the easy part.
159550696a6eSStefan Eßer size_t len = p->flags.len;
159650696a6eSStefan Eßer
159750696a6eSStefan Eßer assert(*flag_ptr);
159850696a6eSStefan Eßer
159944d4804dSStefan Eßer // Parse a statement.
160050696a6eSStefan Eßer bc_parse_stmt(p);
160150696a6eSStefan Eßer
160244d4804dSStefan Eßer // This is a very important condition to get right. If there is no
160344d4804dSStefan Eßer // brace, and no body flag, and the flags len hasn't shrunk, then we
160444d4804dSStefan Eßer // have a body that was not delimited by braces, so we need to end it
160544d4804dSStefan Eßer // now, after just one statement.
160650696a6eSStefan Eßer if (!brace && !BC_PARSE_BODY(p) && len <= p->flags.len)
160778bc019dSStefan Eßer {
160850696a6eSStefan Eßer bc_parse_endBody(p, false);
160950696a6eSStefan Eßer }
161050696a6eSStefan Eßer }
161178bc019dSStefan Eßer }
161250696a6eSStefan Eßer
161344d4804dSStefan Eßer /**
161444d4804dSStefan Eßer * Parses a statement. This is the entry point for just about everything, except
161544d4804dSStefan Eßer * function definitions.
161644d4804dSStefan Eßer * @param p The parser.
161744d4804dSStefan Eßer */
161878bc019dSStefan Eßer static void
bc_parse_stmt(BcParse * p)161978bc019dSStefan Eßer bc_parse_stmt(BcParse* p)
162078bc019dSStefan Eßer {
162150696a6eSStefan Eßer size_t len;
162250696a6eSStefan Eßer uint16_t flags;
162350696a6eSStefan Eßer BcLexType type = p->l.t;
162450696a6eSStefan Eßer
162544d4804dSStefan Eßer // Eat newline.
162678bc019dSStefan Eßer if (type == BC_LEX_NLINE)
162778bc019dSStefan Eßer {
162850696a6eSStefan Eßer bc_lex_next(&p->l);
162950696a6eSStefan Eßer return;
163050696a6eSStefan Eßer }
163144d4804dSStefan Eßer
163244d4804dSStefan Eßer // Eat auto list.
163378bc019dSStefan Eßer if (type == BC_LEX_KW_AUTO)
163478bc019dSStefan Eßer {
163550696a6eSStefan Eßer bc_parse_auto(p);
163650696a6eSStefan Eßer return;
163750696a6eSStefan Eßer }
163850696a6eSStefan Eßer
163944d4804dSStefan Eßer // If we reach this point, no auto list is allowed.
164050696a6eSStefan Eßer p->auto_part = false;
164150696a6eSStefan Eßer
164244d4804dSStefan Eßer // Everything but an else needs to be taken care of here, but else is
164344d4804dSStefan Eßer // special.
164478bc019dSStefan Eßer if (type != BC_LEX_KW_ELSE)
164578bc019dSStefan Eßer {
164644d4804dSStefan Eßer // After an if, no else found.
164778bc019dSStefan Eßer if (BC_PARSE_IF_END(p))
164878bc019dSStefan Eßer {
164944d4804dSStefan Eßer // Clear the expectation for else, end body, and return. Returning
165044d4804dSStefan Eßer // gives us a clean slate for parsing again.
165150696a6eSStefan Eßer bc_parse_noElse(p);
165250696a6eSStefan Eßer if (p->flags.len > 1 && !BC_PARSE_BRACE(p))
165378bc019dSStefan Eßer {
165450696a6eSStefan Eßer bc_parse_endBody(p, false);
165578bc019dSStefan Eßer }
165678bc019dSStefan Eßer
165750696a6eSStefan Eßer return;
165850696a6eSStefan Eßer }
165944d4804dSStefan Eßer // With a left brace, we are parsing a body.
166078bc019dSStefan Eßer else if (type == BC_LEX_LBRACE)
166178bc019dSStefan Eßer {
166244d4804dSStefan Eßer // We need to start a body if we are not expecting one yet.
166378bc019dSStefan Eßer if (!BC_PARSE_BODY(p))
166478bc019dSStefan Eßer {
166550696a6eSStefan Eßer bc_parse_startBody(p, BC_PARSE_FLAG_BRACE);
166650696a6eSStefan Eßer bc_lex_next(&p->l);
166750696a6eSStefan Eßer }
166844d4804dSStefan Eßer // If we *are* expecting a body, that body should get a brace. This
166944d4804dSStefan Eßer // takes care of braces being on a different line than if and loop
167044d4804dSStefan Eßer // headers.
167178bc019dSStefan Eßer else
167278bc019dSStefan Eßer {
167350696a6eSStefan Eßer *(BC_PARSE_TOP_FLAG_PTR(p)) |= BC_PARSE_FLAG_BRACE;
167450696a6eSStefan Eßer bc_lex_next(&p->l);
167550696a6eSStefan Eßer bc_parse_body(p, true);
167650696a6eSStefan Eßer }
167750696a6eSStefan Eßer
167844d4804dSStefan Eßer // If we have reached this point, we need to return for a clean
167944d4804dSStefan Eßer // slate.
168050696a6eSStefan Eßer return;
168150696a6eSStefan Eßer }
168244d4804dSStefan Eßer // This happens when we are expecting a body and get a single statement,
168344d4804dSStefan Eßer // i.e., a body with no braces surrounding it. Returns after for a clean
168444d4804dSStefan Eßer // slate.
168578bc019dSStefan Eßer else if (BC_PARSE_BODY(p) && !BC_PARSE_BRACE(p))
168678bc019dSStefan Eßer {
168750696a6eSStefan Eßer bc_parse_body(p, false);
168850696a6eSStefan Eßer return;
168950696a6eSStefan Eßer }
169050696a6eSStefan Eßer }
169150696a6eSStefan Eßer
169250696a6eSStefan Eßer len = p->flags.len;
169350696a6eSStefan Eßer flags = BC_PARSE_TOP_FLAG(p);
169450696a6eSStefan Eßer
169578bc019dSStefan Eßer switch (type)
169678bc019dSStefan Eßer {
169744d4804dSStefan Eßer // All of these are valid for expressions.
169850696a6eSStefan Eßer case BC_LEX_OP_INC:
169950696a6eSStefan Eßer case BC_LEX_OP_DEC:
170050696a6eSStefan Eßer case BC_LEX_OP_MINUS:
170150696a6eSStefan Eßer case BC_LEX_OP_BOOL_NOT:
170250696a6eSStefan Eßer case BC_LEX_LPAREN:
170350696a6eSStefan Eßer case BC_LEX_NAME:
170450696a6eSStefan Eßer case BC_LEX_NUMBER:
170550696a6eSStefan Eßer case BC_LEX_KW_IBASE:
170650696a6eSStefan Eßer case BC_LEX_KW_LAST:
170750696a6eSStefan Eßer case BC_LEX_KW_LENGTH:
170850696a6eSStefan Eßer case BC_LEX_KW_OBASE:
170950696a6eSStefan Eßer case BC_LEX_KW_SCALE:
171044d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
171150696a6eSStefan Eßer case BC_LEX_KW_SEED:
171244d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
171350696a6eSStefan Eßer case BC_LEX_KW_SQRT:
171450696a6eSStefan Eßer case BC_LEX_KW_ABS:
1715d101cdd6SStefan Eßer case BC_LEX_KW_IS_NUMBER:
1716d101cdd6SStefan Eßer case BC_LEX_KW_IS_STRING:
171744d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
171850696a6eSStefan Eßer case BC_LEX_KW_IRAND:
171944d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
172044d4804dSStefan Eßer case BC_LEX_KW_ASCIIFY:
172144d4804dSStefan Eßer case BC_LEX_KW_MODEXP:
172244d4804dSStefan Eßer case BC_LEX_KW_DIVMOD:
172350696a6eSStefan Eßer case BC_LEX_KW_READ:
172444d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
172550696a6eSStefan Eßer case BC_LEX_KW_RAND:
172644d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
172750696a6eSStefan Eßer case BC_LEX_KW_MAXIBASE:
172850696a6eSStefan Eßer case BC_LEX_KW_MAXOBASE:
172950696a6eSStefan Eßer case BC_LEX_KW_MAXSCALE:
173044d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
173150696a6eSStefan Eßer case BC_LEX_KW_MAXRAND:
173244d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
1733d43fa8efSStefan Eßer case BC_LEX_KW_LINE_LENGTH:
1734d43fa8efSStefan Eßer case BC_LEX_KW_GLOBAL_STACKS:
1735d43fa8efSStefan Eßer case BC_LEX_KW_LEADING_ZERO:
173650696a6eSStefan Eßer {
173750696a6eSStefan Eßer bc_parse_expr_status(p, BC_PARSE_PRINT, bc_parse_next_expr);
173850696a6eSStefan Eßer break;
173950696a6eSStefan Eßer }
174050696a6eSStefan Eßer
174150696a6eSStefan Eßer case BC_LEX_KW_ELSE:
174250696a6eSStefan Eßer {
174350696a6eSStefan Eßer bc_parse_else(p);
174450696a6eSStefan Eßer break;
174550696a6eSStefan Eßer }
174650696a6eSStefan Eßer
174744d4804dSStefan Eßer // Just eat.
174850696a6eSStefan Eßer case BC_LEX_SCOLON:
174950696a6eSStefan Eßer {
175050696a6eSStefan Eßer // Do nothing.
175150696a6eSStefan Eßer break;
175250696a6eSStefan Eßer }
175350696a6eSStefan Eßer
175450696a6eSStefan Eßer case BC_LEX_RBRACE:
175550696a6eSStefan Eßer {
175650696a6eSStefan Eßer bc_parse_endBody(p, true);
175750696a6eSStefan Eßer break;
175850696a6eSStefan Eßer }
175950696a6eSStefan Eßer
176050696a6eSStefan Eßer case BC_LEX_STR:
176150696a6eSStefan Eßer {
176250696a6eSStefan Eßer bc_parse_str(p, BC_INST_PRINT_STR);
176350696a6eSStefan Eßer break;
176450696a6eSStefan Eßer }
176550696a6eSStefan Eßer
176650696a6eSStefan Eßer case BC_LEX_KW_BREAK:
176750696a6eSStefan Eßer case BC_LEX_KW_CONTINUE:
176850696a6eSStefan Eßer {
176950696a6eSStefan Eßer bc_parse_loopExit(p, p->l.t);
177050696a6eSStefan Eßer break;
177150696a6eSStefan Eßer }
177250696a6eSStefan Eßer
177350696a6eSStefan Eßer case BC_LEX_KW_FOR:
177450696a6eSStefan Eßer {
177550696a6eSStefan Eßer bc_parse_for(p);
177650696a6eSStefan Eßer break;
177750696a6eSStefan Eßer }
177850696a6eSStefan Eßer
177950696a6eSStefan Eßer case BC_LEX_KW_HALT:
178050696a6eSStefan Eßer {
178150696a6eSStefan Eßer bc_parse_push(p, BC_INST_HALT);
178250696a6eSStefan Eßer bc_lex_next(&p->l);
178350696a6eSStefan Eßer break;
178450696a6eSStefan Eßer }
178550696a6eSStefan Eßer
178650696a6eSStefan Eßer case BC_LEX_KW_IF:
178750696a6eSStefan Eßer {
178850696a6eSStefan Eßer bc_parse_if(p);
178950696a6eSStefan Eßer break;
179050696a6eSStefan Eßer }
179150696a6eSStefan Eßer
179250696a6eSStefan Eßer case BC_LEX_KW_LIMITS:
179350696a6eSStefan Eßer {
179444d4804dSStefan Eßer // `limits` is a compile-time command, so execute it right away.
179550696a6eSStefan Eßer bc_vm_printf("BC_LONG_BIT = %lu\n", (ulong) BC_LONG_BIT);
179650696a6eSStefan Eßer bc_vm_printf("BC_BASE_DIGS = %lu\n", (ulong) BC_BASE_DIGS);
179750696a6eSStefan Eßer bc_vm_printf("BC_BASE_POW = %lu\n", (ulong) BC_BASE_POW);
179850696a6eSStefan Eßer bc_vm_printf("BC_OVERFLOW_MAX = %lu\n", (ulong) BC_NUM_BIGDIG_MAX);
179950696a6eSStefan Eßer bc_vm_printf("\n");
180050696a6eSStefan Eßer bc_vm_printf("BC_BASE_MAX = %lu\n", BC_MAX_OBASE);
180150696a6eSStefan Eßer bc_vm_printf("BC_DIM_MAX = %lu\n", BC_MAX_DIM);
180250696a6eSStefan Eßer bc_vm_printf("BC_SCALE_MAX = %lu\n", BC_MAX_SCALE);
180350696a6eSStefan Eßer bc_vm_printf("BC_STRING_MAX = %lu\n", BC_MAX_STRING);
180450696a6eSStefan Eßer bc_vm_printf("BC_NAME_MAX = %lu\n", BC_MAX_NAME);
180550696a6eSStefan Eßer bc_vm_printf("BC_NUM_MAX = %lu\n", BC_MAX_NUM);
180644d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
180750696a6eSStefan Eßer bc_vm_printf("BC_RAND_MAX = %lu\n", BC_MAX_RAND);
180844d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
180950696a6eSStefan Eßer bc_vm_printf("MAX Exponent = %lu\n", BC_MAX_EXP);
181050696a6eSStefan Eßer bc_vm_printf("Number of vars = %lu\n", BC_MAX_VARS);
181150696a6eSStefan Eßer
181250696a6eSStefan Eßer bc_lex_next(&p->l);
181350696a6eSStefan Eßer
181450696a6eSStefan Eßer break;
181550696a6eSStefan Eßer }
181650696a6eSStefan Eßer
181744d4804dSStefan Eßer case BC_LEX_KW_STREAM:
181850696a6eSStefan Eßer case BC_LEX_KW_PRINT:
181950696a6eSStefan Eßer {
182044d4804dSStefan Eßer bc_parse_print(p, type);
182150696a6eSStefan Eßer break;
182250696a6eSStefan Eßer }
182350696a6eSStefan Eßer
182450696a6eSStefan Eßer case BC_LEX_KW_QUIT:
182550696a6eSStefan Eßer {
182644d4804dSStefan Eßer // Quit is a compile-time command. We don't exit directly, so the vm
182744d4804dSStefan Eßer // can clean up.
1828d101cdd6SStefan Eßer vm->status = BC_STATUS_QUIT;
182944d4804dSStefan Eßer BC_JMP;
183050696a6eSStefan Eßer break;
183150696a6eSStefan Eßer }
183250696a6eSStefan Eßer
183350696a6eSStefan Eßer case BC_LEX_KW_RETURN:
183450696a6eSStefan Eßer {
183550696a6eSStefan Eßer bc_parse_return(p);
183650696a6eSStefan Eßer break;
183750696a6eSStefan Eßer }
183850696a6eSStefan Eßer
183950696a6eSStefan Eßer case BC_LEX_KW_WHILE:
184050696a6eSStefan Eßer {
184150696a6eSStefan Eßer bc_parse_while(p);
184250696a6eSStefan Eßer break;
184350696a6eSStefan Eßer }
184450696a6eSStefan Eßer
1845d101cdd6SStefan Eßer case BC_LEX_EOF:
1846d101cdd6SStefan Eßer case BC_LEX_INVALID:
1847d101cdd6SStefan Eßer case BC_LEX_NEG:
1848d101cdd6SStefan Eßer #if BC_ENABLE_EXTRA_MATH
1849d101cdd6SStefan Eßer case BC_LEX_OP_TRUNC:
1850d101cdd6SStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
1851d101cdd6SStefan Eßer case BC_LEX_OP_POWER:
1852d101cdd6SStefan Eßer case BC_LEX_OP_MULTIPLY:
1853d101cdd6SStefan Eßer case BC_LEX_OP_DIVIDE:
1854d101cdd6SStefan Eßer case BC_LEX_OP_MODULUS:
1855d101cdd6SStefan Eßer case BC_LEX_OP_PLUS:
1856d101cdd6SStefan Eßer #if BC_ENABLE_EXTRA_MATH
1857d101cdd6SStefan Eßer case BC_LEX_OP_PLACES:
1858d101cdd6SStefan Eßer case BC_LEX_OP_LSHIFT:
1859d101cdd6SStefan Eßer case BC_LEX_OP_RSHIFT:
1860d101cdd6SStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
1861d101cdd6SStefan Eßer case BC_LEX_OP_REL_EQ:
1862d101cdd6SStefan Eßer case BC_LEX_OP_REL_LE:
1863d101cdd6SStefan Eßer case BC_LEX_OP_REL_GE:
1864d101cdd6SStefan Eßer case BC_LEX_OP_REL_NE:
1865d101cdd6SStefan Eßer case BC_LEX_OP_REL_LT:
1866d101cdd6SStefan Eßer case BC_LEX_OP_REL_GT:
1867d101cdd6SStefan Eßer case BC_LEX_OP_BOOL_OR:
1868d101cdd6SStefan Eßer case BC_LEX_OP_BOOL_AND:
1869d101cdd6SStefan Eßer case BC_LEX_OP_ASSIGN_POWER:
1870d101cdd6SStefan Eßer case BC_LEX_OP_ASSIGN_MULTIPLY:
1871d101cdd6SStefan Eßer case BC_LEX_OP_ASSIGN_DIVIDE:
1872d101cdd6SStefan Eßer case BC_LEX_OP_ASSIGN_MODULUS:
1873d101cdd6SStefan Eßer case BC_LEX_OP_ASSIGN_PLUS:
1874d101cdd6SStefan Eßer case BC_LEX_OP_ASSIGN_MINUS:
1875d101cdd6SStefan Eßer #if BC_ENABLE_EXTRA_MATH
1876d101cdd6SStefan Eßer case BC_LEX_OP_ASSIGN_PLACES:
1877d101cdd6SStefan Eßer case BC_LEX_OP_ASSIGN_LSHIFT:
1878d101cdd6SStefan Eßer case BC_LEX_OP_ASSIGN_RSHIFT:
1879d101cdd6SStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
1880d101cdd6SStefan Eßer case BC_LEX_OP_ASSIGN:
1881d101cdd6SStefan Eßer case BC_LEX_NLINE:
1882d101cdd6SStefan Eßer case BC_LEX_WHITESPACE:
1883d101cdd6SStefan Eßer case BC_LEX_RPAREN:
1884d101cdd6SStefan Eßer case BC_LEX_LBRACKET:
1885d101cdd6SStefan Eßer case BC_LEX_COMMA:
1886d101cdd6SStefan Eßer case BC_LEX_RBRACKET:
1887d101cdd6SStefan Eßer case BC_LEX_LBRACE:
1888d101cdd6SStefan Eßer case BC_LEX_KW_AUTO:
1889d101cdd6SStefan Eßer case BC_LEX_KW_DEFINE:
1890d101cdd6SStefan Eßer #if DC_ENABLED
1891103d7cdfSStefan Eßer case BC_LEX_EXTENDED_REGISTERS:
1892d101cdd6SStefan Eßer case BC_LEX_EQ_NO_REG:
1893d101cdd6SStefan Eßer case BC_LEX_COLON:
1894d101cdd6SStefan Eßer case BC_LEX_EXECUTE:
1895d101cdd6SStefan Eßer case BC_LEX_PRINT_STACK:
1896d101cdd6SStefan Eßer case BC_LEX_CLEAR_STACK:
1897d101cdd6SStefan Eßer case BC_LEX_REG_STACK_LEVEL:
1898d101cdd6SStefan Eßer case BC_LEX_STACK_LEVEL:
1899d101cdd6SStefan Eßer case BC_LEX_DUPLICATE:
1900d101cdd6SStefan Eßer case BC_LEX_SWAP:
1901d101cdd6SStefan Eßer case BC_LEX_POP:
1902d101cdd6SStefan Eßer case BC_LEX_STORE_IBASE:
1903d101cdd6SStefan Eßer case BC_LEX_STORE_OBASE:
1904d101cdd6SStefan Eßer case BC_LEX_STORE_SCALE:
1905d101cdd6SStefan Eßer #if BC_ENABLE_EXTRA_MATH
1906d101cdd6SStefan Eßer case BC_LEX_STORE_SEED:
1907d101cdd6SStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
1908d101cdd6SStefan Eßer case BC_LEX_LOAD:
1909d101cdd6SStefan Eßer case BC_LEX_LOAD_POP:
1910d101cdd6SStefan Eßer case BC_LEX_STORE_PUSH:
1911d101cdd6SStefan Eßer case BC_LEX_PRINT_POP:
1912d101cdd6SStefan Eßer case BC_LEX_NQUIT:
1913d101cdd6SStefan Eßer case BC_LEX_EXEC_STACK_LENGTH:
1914d101cdd6SStefan Eßer case BC_LEX_SCALE_FACTOR:
1915d101cdd6SStefan Eßer case BC_LEX_ARRAY_LENGTH:
1916d101cdd6SStefan Eßer #endif // DC_ENABLED
191750696a6eSStefan Eßer {
191850696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN);
191950696a6eSStefan Eßer }
192050696a6eSStefan Eßer }
192150696a6eSStefan Eßer
192244d4804dSStefan Eßer // If the flags did not change, we expect a delimiter.
192378bc019dSStefan Eßer if (len == p->flags.len && flags == BC_PARSE_TOP_FLAG(p))
192478bc019dSStefan Eßer {
192550696a6eSStefan Eßer if (BC_ERR(!bc_parse_isDelimiter(p)))
192678bc019dSStefan Eßer {
192750696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN);
192850696a6eSStefan Eßer }
192978bc019dSStefan Eßer }
193050696a6eSStefan Eßer
193150696a6eSStefan Eßer // Make sure semicolons are eaten.
1932d101cdd6SStefan Eßer while (p->l.t == BC_LEX_SCOLON || p->l.t == BC_LEX_NLINE)
193378bc019dSStefan Eßer {
193478bc019dSStefan Eßer bc_lex_next(&p->l);
193578bc019dSStefan Eßer }
193610041e99SStefan Eßer
193710041e99SStefan Eßer // POSIX's grammar does not allow a function definition after a semicolon
193810041e99SStefan Eßer // without a newline, so check specifically for that case and error if
193910041e99SStefan Eßer // the POSIX standard flag is set.
194010041e99SStefan Eßer if (p->l.last == BC_LEX_SCOLON && p->l.t == BC_LEX_KW_DEFINE && BC_IS_POSIX)
194110041e99SStefan Eßer {
194210041e99SStefan Eßer bc_parse_err(p, BC_ERR_POSIX_FUNC_AFTER_SEMICOLON);
194310041e99SStefan Eßer }
194450696a6eSStefan Eßer }
194550696a6eSStefan Eßer
194678bc019dSStefan Eßer void
bc_parse_parse(BcParse * p)194778bc019dSStefan Eßer bc_parse_parse(BcParse* p)
194878bc019dSStefan Eßer {
194950696a6eSStefan Eßer assert(p);
195050696a6eSStefan Eßer
1951d101cdd6SStefan Eßer BC_SETJMP_LOCKED(vm, exit);
195250696a6eSStefan Eßer
195344d4804dSStefan Eßer // We should not let an EOF get here unless some partial parse was not
195444d4804dSStefan Eßer // completed, in which case, it's the user's fault.
195550696a6eSStefan Eßer if (BC_ERR(p->l.t == BC_LEX_EOF)) bc_parse_err(p, BC_ERR_PARSE_EOF);
195644d4804dSStefan Eßer
195744d4804dSStefan Eßer // Functions need special parsing.
195878bc019dSStefan Eßer else if (p->l.t == BC_LEX_KW_DEFINE)
195978bc019dSStefan Eßer {
196078bc019dSStefan Eßer if (BC_ERR(BC_PARSE_NO_EXEC(p)))
196178bc019dSStefan Eßer {
1962a30efc5cSStefan Eßer bc_parse_endif(p);
1963a30efc5cSStefan Eßer if (BC_ERR(BC_PARSE_NO_EXEC(p)))
196478bc019dSStefan Eßer {
1965a30efc5cSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN);
1966d43fa8efSStefan Eßer }
196778bc019dSStefan Eßer }
196850696a6eSStefan Eßer bc_parse_func(p);
196950696a6eSStefan Eßer }
197044d4804dSStefan Eßer
197144d4804dSStefan Eßer // Otherwise, parse a normal statement.
197250696a6eSStefan Eßer else bc_parse_stmt(p);
197350696a6eSStefan Eßer
197450696a6eSStefan Eßer exit:
197544d4804dSStefan Eßer
197644d4804dSStefan Eßer // We need to reset on error.
1977d101cdd6SStefan Eßer if (BC_ERR(((vm->status && vm->status != BC_STATUS_QUIT) || vm->sig != 0)))
197878bc019dSStefan Eßer {
197950696a6eSStefan Eßer bc_parse_reset(p);
198078bc019dSStefan Eßer }
198144d4804dSStefan Eßer
1982d101cdd6SStefan Eßer BC_LONGJMP_CONT(vm);
198310041e99SStefan Eßer BC_SIG_MAYLOCK;
198450696a6eSStefan Eßer }
198550696a6eSStefan Eßer
198644d4804dSStefan Eßer /**
198744d4804dSStefan Eßer * Parse an expression. This is the actual implementation of the Shunting-Yard
198844d4804dSStefan Eßer * Algorithm.
198944d4804dSStefan Eßer * @param p The parser.
199044d4804dSStefan Eßer * @param flags The flags for what is valid in the expression.
199144d4804dSStefan Eßer * @param next A set of tokens for what is valid *after* the expression.
199244d4804dSStefan Eßer * @return A parse status. In some places, an empty expression is an
199344d4804dSStefan Eßer * error, and sometimes, it is required. This allows this function
199444d4804dSStefan Eßer * to tell the caller if the expression was empty and let the
199544d4804dSStefan Eßer * caller handle it.
199644d4804dSStefan Eßer */
199778bc019dSStefan Eßer static BcParseStatus
bc_parse_expr_err(BcParse * p,uint8_t flags,BcParseNext next)199878bc019dSStefan Eßer bc_parse_expr_err(BcParse* p, uint8_t flags, BcParseNext next)
199950696a6eSStefan Eßer {
200050696a6eSStefan Eßer BcInst prev = BC_INST_PRINT;
200150696a6eSStefan Eßer uchar inst = BC_INST_INVALID;
200244d4804dSStefan Eßer BcLexType top, t;
200344d4804dSStefan Eßer size_t nexprs, ops_bgn;
200450696a6eSStefan Eßer uint32_t i, nparens, nrelops;
2005*12e0d316SStefan Eßer bool pfirst, rprn, array_last, done, get_token, assign;
2006*12e0d316SStefan Eßer bool bin_last, incdec, can_assign;
200750696a6eSStefan Eßer
200844d4804dSStefan Eßer // One of these *must* be true.
200950696a6eSStefan Eßer assert(!(flags & BC_PARSE_PRINT) || !(flags & BC_PARSE_NEEDVAL));
201050696a6eSStefan Eßer
201144d4804dSStefan Eßer // These are set very carefully. In fact, controlling the values of these
201244d4804dSStefan Eßer // locals is the biggest part of making this work. ops_bgn especially is
201344d4804dSStefan Eßer // important because it marks where the operator stack begins for *this*
201444d4804dSStefan Eßer // invocation of this function. That's because bc_parse_expr_err() is
201544d4804dSStefan Eßer // recursive (the Shunting-Yard Algorithm is most easily expressed
201644d4804dSStefan Eßer // recursively when parsing subexpressions), and each invocation needs to
201744d4804dSStefan Eßer // know where to stop.
201844d4804dSStefan Eßer //
201944d4804dSStefan Eßer // - nparens is the number of left parens without matches.
202044d4804dSStefan Eßer // - nrelops is the number of relational operators that appear in the expr.
202144d4804dSStefan Eßer // - nexprs is the number of unused expressions.
202244d4804dSStefan Eßer // - rprn is a right paren encountered last.
2023*12e0d316SStefan Eßer // - array_last is an array item encountered last.
202444d4804dSStefan Eßer // - done means the expression has been fully parsed.
202544d4804dSStefan Eßer // - get_token is true when a token is needed at the end of an iteration.
202644d4804dSStefan Eßer // - assign is true when an assignment statement was parsed last.
202744d4804dSStefan Eßer // - incdec is true when the previous operator was an inc or dec operator.
202844d4804dSStefan Eßer // - can_assign is true when an assignemnt is valid.
202944d4804dSStefan Eßer // - bin_last is true when the previous instruction was a binary operator.
203044d4804dSStefan Eßer t = p->l.t;
203150696a6eSStefan Eßer pfirst = (p->l.t == BC_LEX_LPAREN);
203250696a6eSStefan Eßer nparens = nrelops = 0;
203344d4804dSStefan Eßer nexprs = 0;
203444d4804dSStefan Eßer ops_bgn = p->ops.len;
2035*12e0d316SStefan Eßer rprn = array_last = done = get_token = assign = incdec = can_assign = false;
203650696a6eSStefan Eßer bin_last = true;
203750696a6eSStefan Eßer
203850696a6eSStefan Eßer // We want to eat newlines if newlines are not a valid ending token.
203950696a6eSStefan Eßer // This is for spacing in things like for loop headers.
204078bc019dSStefan Eßer if (!(flags & BC_PARSE_NOREAD))
204178bc019dSStefan Eßer {
204278bc019dSStefan Eßer while ((t = p->l.t) == BC_LEX_NLINE)
204378bc019dSStefan Eßer {
204478bc019dSStefan Eßer bc_lex_next(&p->l);
204578bc019dSStefan Eßer }
204650696a6eSStefan Eßer }
204750696a6eSStefan Eßer
204844d4804dSStefan Eßer // This is the Shunting-Yard algorithm loop.
204950696a6eSStefan Eßer for (; !done && BC_PARSE_EXPR(t); t = p->l.t)
205050696a6eSStefan Eßer {
2051*12e0d316SStefan Eßer // Make sure an array expression is not mixed with any others. However,
2052*12e0d316SStefan Eßer // a right parenthesis may end the expression, so we will need to take
2053*12e0d316SStefan Eßer // care of that right there.
2054*12e0d316SStefan Eßer if (BC_ERR(array_last && t != BC_LEX_RPAREN))
2055*12e0d316SStefan Eßer {
2056*12e0d316SStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
2057*12e0d316SStefan Eßer }
2058*12e0d316SStefan Eßer
205978bc019dSStefan Eßer switch (t)
206078bc019dSStefan Eßer {
206150696a6eSStefan Eßer case BC_LEX_OP_INC:
206250696a6eSStefan Eßer case BC_LEX_OP_DEC:
206350696a6eSStefan Eßer {
206444d4804dSStefan Eßer // These operators can only be used with items that can be
206544d4804dSStefan Eßer // assigned to.
206650696a6eSStefan Eßer if (BC_ERR(incdec)) bc_parse_err(p, BC_ERR_PARSE_ASSIGN);
206744d4804dSStefan Eßer
206850696a6eSStefan Eßer bc_parse_incdec(p, &prev, &can_assign, &nexprs, flags);
206944d4804dSStefan Eßer
207050696a6eSStefan Eßer rprn = get_token = bin_last = false;
207150696a6eSStefan Eßer incdec = true;
207250696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY);
207344d4804dSStefan Eßer
207450696a6eSStefan Eßer break;
207550696a6eSStefan Eßer }
207650696a6eSStefan Eßer
207750696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH
207850696a6eSStefan Eßer case BC_LEX_OP_TRUNC:
207950696a6eSStefan Eßer {
208044d4804dSStefan Eßer // The previous token must have been a leaf expression, or the
208144d4804dSStefan Eßer // operator is in the wrong place.
208250696a6eSStefan Eßer if (BC_ERR(!BC_PARSE_LEAF(prev, bin_last, rprn)))
208378bc019dSStefan Eßer {
208450696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN);
208578bc019dSStefan Eßer }
208650696a6eSStefan Eßer
208750696a6eSStefan Eßer // I can just add the instruction because
208850696a6eSStefan Eßer // negative will already be taken care of.
208950696a6eSStefan Eßer bc_parse_push(p, BC_INST_TRUNC);
209044d4804dSStefan Eßer
209150696a6eSStefan Eßer rprn = can_assign = incdec = false;
209250696a6eSStefan Eßer get_token = true;
209350696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY);
209444d4804dSStefan Eßer
209550696a6eSStefan Eßer break;
209650696a6eSStefan Eßer }
209750696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
209850696a6eSStefan Eßer
209950696a6eSStefan Eßer case BC_LEX_OP_MINUS:
210050696a6eSStefan Eßer {
210150696a6eSStefan Eßer bc_parse_minus(p, &prev, ops_bgn, rprn, bin_last, &nexprs);
210244d4804dSStefan Eßer
210350696a6eSStefan Eßer rprn = get_token = can_assign = false;
210444d4804dSStefan Eßer
210544d4804dSStefan Eßer // This is true if it was a binary operator last.
210650696a6eSStefan Eßer bin_last = (prev == BC_INST_MINUS);
210750696a6eSStefan Eßer if (bin_last) incdec = false;
210844d4804dSStefan Eßer
210950696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY);
211044d4804dSStefan Eßer
211150696a6eSStefan Eßer break;
211250696a6eSStefan Eßer }
211350696a6eSStefan Eßer
211444d4804dSStefan Eßer // All of this group, including the fallthrough, is to parse binary
211544d4804dSStefan Eßer // operators.
211650696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_POWER:
211750696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_MULTIPLY:
211850696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_DIVIDE:
211950696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_MODULUS:
212050696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_PLUS:
212150696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_MINUS:
212250696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH
212350696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_PLACES:
212450696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_LSHIFT:
212550696a6eSStefan Eßer case BC_LEX_OP_ASSIGN_RSHIFT:
212650696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
212750696a6eSStefan Eßer case BC_LEX_OP_ASSIGN:
212850696a6eSStefan Eßer {
212944d4804dSStefan Eßer // We need to make sure the assignment is valid.
213050696a6eSStefan Eßer if (!BC_PARSE_INST_VAR(prev))
213178bc019dSStefan Eßer {
213250696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_ASSIGN);
213350696a6eSStefan Eßer }
213478bc019dSStefan Eßer
213550696a6eSStefan Eßer // Fallthrough.
213650696a6eSStefan Eßer BC_FALLTHROUGH
213778bc019dSStefan Eßer }
213850696a6eSStefan Eßer
213950696a6eSStefan Eßer case BC_LEX_OP_POWER:
214050696a6eSStefan Eßer case BC_LEX_OP_MULTIPLY:
214150696a6eSStefan Eßer case BC_LEX_OP_DIVIDE:
214250696a6eSStefan Eßer case BC_LEX_OP_MODULUS:
214350696a6eSStefan Eßer case BC_LEX_OP_PLUS:
214450696a6eSStefan Eßer #if BC_ENABLE_EXTRA_MATH
214550696a6eSStefan Eßer case BC_LEX_OP_PLACES:
214650696a6eSStefan Eßer case BC_LEX_OP_LSHIFT:
214750696a6eSStefan Eßer case BC_LEX_OP_RSHIFT:
214850696a6eSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
214950696a6eSStefan Eßer case BC_LEX_OP_REL_EQ:
215050696a6eSStefan Eßer case BC_LEX_OP_REL_LE:
215150696a6eSStefan Eßer case BC_LEX_OP_REL_GE:
215250696a6eSStefan Eßer case BC_LEX_OP_REL_NE:
215350696a6eSStefan Eßer case BC_LEX_OP_REL_LT:
215450696a6eSStefan Eßer case BC_LEX_OP_REL_GT:
215550696a6eSStefan Eßer case BC_LEX_OP_BOOL_NOT:
215650696a6eSStefan Eßer case BC_LEX_OP_BOOL_OR:
215750696a6eSStefan Eßer case BC_LEX_OP_BOOL_AND:
215850696a6eSStefan Eßer {
215944d4804dSStefan Eßer // This is true if the operator if the token is a prefix
216044d4804dSStefan Eßer // operator. This is only for boolean not.
216178bc019dSStefan Eßer if (BC_PARSE_OP_PREFIX(t))
216278bc019dSStefan Eßer {
216344d4804dSStefan Eßer // Prefix operators are only allowed after binary operators
216444d4804dSStefan Eßer // or prefix operators.
216550696a6eSStefan Eßer if (BC_ERR(!bin_last && !BC_PARSE_OP_PREFIX(p->l.last)))
216678bc019dSStefan Eßer {
216750696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
216850696a6eSStefan Eßer }
216978bc019dSStefan Eßer }
217044d4804dSStefan Eßer // If we execute the else, that means we have a binary operator.
217144d4804dSStefan Eßer // If the previous operator was a prefix or a binary operator,
217244d4804dSStefan Eßer // then a binary operator is not allowed.
217350696a6eSStefan Eßer else if (BC_ERR(BC_PARSE_PREV_PREFIX(prev) || bin_last))
217478bc019dSStefan Eßer {
217550696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
217678bc019dSStefan Eßer }
217750696a6eSStefan Eßer
217850696a6eSStefan Eßer nrelops += (t >= BC_LEX_OP_REL_EQ && t <= BC_LEX_OP_REL_GT);
217950696a6eSStefan Eßer prev = BC_PARSE_TOKEN_INST(t);
218044d4804dSStefan Eßer
218150696a6eSStefan Eßer bc_parse_operator(p, t, ops_bgn, &nexprs);
218244d4804dSStefan Eßer
218350696a6eSStefan Eßer rprn = incdec = can_assign = false;
218450696a6eSStefan Eßer get_token = true;
218550696a6eSStefan Eßer bin_last = !BC_PARSE_OP_PREFIX(t);
218650696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY);
218750696a6eSStefan Eßer
218850696a6eSStefan Eßer break;
218950696a6eSStefan Eßer }
219050696a6eSStefan Eßer
219150696a6eSStefan Eßer case BC_LEX_LPAREN:
219250696a6eSStefan Eßer {
219344d4804dSStefan Eßer // A left paren is *not* allowed right after a leaf expr.
219450696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn)))
219578bc019dSStefan Eßer {
219650696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
219778bc019dSStefan Eßer }
219850696a6eSStefan Eßer
219950696a6eSStefan Eßer nparens += 1;
220050696a6eSStefan Eßer rprn = incdec = can_assign = false;
220150696a6eSStefan Eßer get_token = true;
220244d4804dSStefan Eßer
220344d4804dSStefan Eßer // Push the paren onto the operator stack.
220450696a6eSStefan Eßer bc_vec_push(&p->ops, &t);
220550696a6eSStefan Eßer
220650696a6eSStefan Eßer break;
220750696a6eSStefan Eßer }
220850696a6eSStefan Eßer
220950696a6eSStefan Eßer case BC_LEX_RPAREN:
221050696a6eSStefan Eßer {
221144d4804dSStefan Eßer // This needs to be a status. The error is handled in
221244d4804dSStefan Eßer // bc_parse_expr_status().
221350696a6eSStefan Eßer if (BC_ERR(p->l.last == BC_LEX_LPAREN))
221478bc019dSStefan Eßer {
221550696a6eSStefan Eßer return BC_PARSE_STATUS_EMPTY_EXPR;
221678bc019dSStefan Eßer }
221750696a6eSStefan Eßer
221844d4804dSStefan Eßer // The right paren must not come after a prefix or binary
221944d4804dSStefan Eßer // operator.
222050696a6eSStefan Eßer if (BC_ERR(bin_last || BC_PARSE_PREV_PREFIX(prev)))
222178bc019dSStefan Eßer {
222250696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
222378bc019dSStefan Eßer }
222450696a6eSStefan Eßer
222544d4804dSStefan Eßer // If there are no parens left, we are done, but we need another
222644d4804dSStefan Eßer // token.
222778bc019dSStefan Eßer if (!nparens)
222878bc019dSStefan Eßer {
222950696a6eSStefan Eßer done = true;
223050696a6eSStefan Eßer get_token = false;
223150696a6eSStefan Eßer break;
223250696a6eSStefan Eßer }
223350696a6eSStefan Eßer
2234*12e0d316SStefan Eßer // Now that we know the right paren has not ended the
2235*12e0d316SStefan Eßer // expression, make sure an array expression is not mixed with
2236*12e0d316SStefan Eßer // any others.
2237*12e0d316SStefan Eßer if (BC_ERR(array_last))
2238*12e0d316SStefan Eßer {
2239*12e0d316SStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
2240*12e0d316SStefan Eßer }
2241*12e0d316SStefan Eßer
224250696a6eSStefan Eßer nparens -= 1;
224350696a6eSStefan Eßer rprn = true;
224450696a6eSStefan Eßer get_token = bin_last = incdec = false;
224550696a6eSStefan Eßer
224650696a6eSStefan Eßer bc_parse_rightParen(p, &nexprs);
224750696a6eSStefan Eßer
224850696a6eSStefan Eßer break;
224950696a6eSStefan Eßer }
225050696a6eSStefan Eßer
225144d4804dSStefan Eßer case BC_LEX_STR:
225244d4804dSStefan Eßer {
225344d4804dSStefan Eßer // POSIX only allows strings alone.
225444d4804dSStefan Eßer if (BC_IS_POSIX) bc_parse_err(p, BC_ERR_POSIX_EXPR_STRING);
225544d4804dSStefan Eßer
225644d4804dSStefan Eßer // A string is a leaf and cannot come right after a leaf.
225744d4804dSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn)))
225878bc019dSStefan Eßer {
225944d4804dSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
226078bc019dSStefan Eßer }
226144d4804dSStefan Eßer
226244d4804dSStefan Eßer bc_parse_addString(p);
226344d4804dSStefan Eßer
226444d4804dSStefan Eßer get_token = true;
226544d4804dSStefan Eßer bin_last = rprn = false;
226644d4804dSStefan Eßer nexprs += 1;
226744d4804dSStefan Eßer
226844d4804dSStefan Eßer break;
226944d4804dSStefan Eßer }
227044d4804dSStefan Eßer
227150696a6eSStefan Eßer case BC_LEX_NAME:
227250696a6eSStefan Eßer {
227344d4804dSStefan Eßer // A name is a leaf and cannot come right after a leaf.
227450696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn)))
227578bc019dSStefan Eßer {
227650696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
227778bc019dSStefan Eßer }
227850696a6eSStefan Eßer
227950696a6eSStefan Eßer get_token = bin_last = false;
228044d4804dSStefan Eßer
228144d4804dSStefan Eßer bc_parse_name(p, &prev, &can_assign, flags & ~BC_PARSE_NOCALL);
228244d4804dSStefan Eßer
228350696a6eSStefan Eßer rprn = (prev == BC_INST_CALL);
2284*12e0d316SStefan Eßer array_last = (prev == BC_INST_ARRAY);
228550696a6eSStefan Eßer nexprs += 1;
228650696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY);
228750696a6eSStefan Eßer
228850696a6eSStefan Eßer break;
228950696a6eSStefan Eßer }
229050696a6eSStefan Eßer
229150696a6eSStefan Eßer case BC_LEX_NUMBER:
229250696a6eSStefan Eßer {
229344d4804dSStefan Eßer // A number is a leaf and cannot come right after a leaf.
229450696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn)))
229578bc019dSStefan Eßer {
229650696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
229778bc019dSStefan Eßer }
229850696a6eSStefan Eßer
229944d4804dSStefan Eßer // The number instruction is pushed in here.
230050696a6eSStefan Eßer bc_parse_number(p);
230144d4804dSStefan Eßer
230250696a6eSStefan Eßer nexprs += 1;
230350696a6eSStefan Eßer prev = BC_INST_NUM;
230450696a6eSStefan Eßer get_token = true;
230550696a6eSStefan Eßer rprn = bin_last = can_assign = false;
230650696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY);
230750696a6eSStefan Eßer
230850696a6eSStefan Eßer break;
230950696a6eSStefan Eßer }
231050696a6eSStefan Eßer
231150696a6eSStefan Eßer case BC_LEX_KW_IBASE:
231250696a6eSStefan Eßer case BC_LEX_KW_LAST:
231350696a6eSStefan Eßer case BC_LEX_KW_OBASE:
231444d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
231550696a6eSStefan Eßer case BC_LEX_KW_SEED:
231644d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
231750696a6eSStefan Eßer {
231844d4804dSStefan Eßer // All of these are leaves and cannot come right after a leaf.
231950696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn)))
232078bc019dSStefan Eßer {
232150696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
232278bc019dSStefan Eßer }
232350696a6eSStefan Eßer
232450696a6eSStefan Eßer prev = t - BC_LEX_KW_LAST + BC_INST_LAST;
232550696a6eSStefan Eßer bc_parse_push(p, prev);
232650696a6eSStefan Eßer
232750696a6eSStefan Eßer get_token = can_assign = true;
232850696a6eSStefan Eßer rprn = bin_last = false;
232950696a6eSStefan Eßer nexprs += 1;
233050696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY);
233150696a6eSStefan Eßer
233250696a6eSStefan Eßer break;
233350696a6eSStefan Eßer }
233450696a6eSStefan Eßer
233550696a6eSStefan Eßer case BC_LEX_KW_LENGTH:
233650696a6eSStefan Eßer case BC_LEX_KW_SQRT:
233750696a6eSStefan Eßer case BC_LEX_KW_ABS:
2338d101cdd6SStefan Eßer case BC_LEX_KW_IS_NUMBER:
2339d101cdd6SStefan Eßer case BC_LEX_KW_IS_STRING:
234044d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
234150696a6eSStefan Eßer case BC_LEX_KW_IRAND:
234244d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
234344d4804dSStefan Eßer case BC_LEX_KW_ASCIIFY:
234450696a6eSStefan Eßer {
234544d4804dSStefan Eßer // All of these are leaves and cannot come right after a leaf.
234650696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn)))
234778bc019dSStefan Eßer {
234850696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
234978bc019dSStefan Eßer }
235050696a6eSStefan Eßer
235150696a6eSStefan Eßer bc_parse_builtin(p, t, flags, &prev);
235244d4804dSStefan Eßer
235350696a6eSStefan Eßer rprn = get_token = bin_last = incdec = can_assign = false;
235450696a6eSStefan Eßer nexprs += 1;
235550696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY);
235650696a6eSStefan Eßer
235750696a6eSStefan Eßer break;
235850696a6eSStefan Eßer }
235950696a6eSStefan Eßer
236050696a6eSStefan Eßer case BC_LEX_KW_READ:
236144d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
236250696a6eSStefan Eßer case BC_LEX_KW_RAND:
236344d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
236450696a6eSStefan Eßer case BC_LEX_KW_MAXIBASE:
236550696a6eSStefan Eßer case BC_LEX_KW_MAXOBASE:
236650696a6eSStefan Eßer case BC_LEX_KW_MAXSCALE:
236744d4804dSStefan Eßer #if BC_ENABLE_EXTRA_MATH
236850696a6eSStefan Eßer case BC_LEX_KW_MAXRAND:
236944d4804dSStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
2370d43fa8efSStefan Eßer case BC_LEX_KW_LINE_LENGTH:
2371d43fa8efSStefan Eßer case BC_LEX_KW_GLOBAL_STACKS:
2372d43fa8efSStefan Eßer case BC_LEX_KW_LEADING_ZERO:
237350696a6eSStefan Eßer {
237444d4804dSStefan Eßer // All of these are leaves and cannot come right after a leaf.
237550696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn)))
237678bc019dSStefan Eßer {
237750696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
237878bc019dSStefan Eßer }
237944d4804dSStefan Eßer
238044d4804dSStefan Eßer // Error if we have read and it's not allowed.
238150696a6eSStefan Eßer else if (t == BC_LEX_KW_READ && BC_ERR(flags & BC_PARSE_NOREAD))
238278bc019dSStefan Eßer {
238350696a6eSStefan Eßer bc_parse_err(p, BC_ERR_EXEC_REC_READ);
238478bc019dSStefan Eßer }
238544d4804dSStefan Eßer
238650696a6eSStefan Eßer prev = t - BC_LEX_KW_READ + BC_INST_READ;
238750696a6eSStefan Eßer bc_parse_noArgBuiltin(p, prev);
238850696a6eSStefan Eßer
238950696a6eSStefan Eßer rprn = get_token = bin_last = incdec = can_assign = false;
239050696a6eSStefan Eßer nexprs += 1;
239150696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY);
239250696a6eSStefan Eßer
239350696a6eSStefan Eßer break;
239450696a6eSStefan Eßer }
239550696a6eSStefan Eßer
239650696a6eSStefan Eßer case BC_LEX_KW_SCALE:
239750696a6eSStefan Eßer {
239844d4804dSStefan Eßer // This is a leaf and cannot come right after a leaf.
239950696a6eSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn)))
240078bc019dSStefan Eßer {
240150696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
240278bc019dSStefan Eßer }
240350696a6eSStefan Eßer
240444d4804dSStefan Eßer // Scale needs special work because it can be a variable *or* a
240544d4804dSStefan Eßer // function.
240650696a6eSStefan Eßer bc_parse_scale(p, &prev, &can_assign, flags);
240744d4804dSStefan Eßer
240850696a6eSStefan Eßer rprn = get_token = bin_last = false;
240950696a6eSStefan Eßer nexprs += 1;
241050696a6eSStefan Eßer flags &= ~(BC_PARSE_ARRAY);
241150696a6eSStefan Eßer
241250696a6eSStefan Eßer break;
241350696a6eSStefan Eßer }
241450696a6eSStefan Eßer
241544d4804dSStefan Eßer case BC_LEX_KW_MODEXP:
241644d4804dSStefan Eßer case BC_LEX_KW_DIVMOD:
241744d4804dSStefan Eßer {
241844d4804dSStefan Eßer // This is a leaf and cannot come right after a leaf.
241944d4804dSStefan Eßer if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn)))
242078bc019dSStefan Eßer {
242144d4804dSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
242278bc019dSStefan Eßer }
242344d4804dSStefan Eßer
242444d4804dSStefan Eßer bc_parse_builtin3(p, t, flags, &prev);
242544d4804dSStefan Eßer
242644d4804dSStefan Eßer rprn = get_token = bin_last = incdec = can_assign = false;
242744d4804dSStefan Eßer nexprs += 1;
242844d4804dSStefan Eßer flags &= ~(BC_PARSE_ARRAY);
242944d4804dSStefan Eßer
243044d4804dSStefan Eßer break;
243144d4804dSStefan Eßer }
243244d4804dSStefan Eßer
2433d101cdd6SStefan Eßer case BC_LEX_EOF:
2434d101cdd6SStefan Eßer case BC_LEX_INVALID:
2435d101cdd6SStefan Eßer case BC_LEX_NEG:
2436d101cdd6SStefan Eßer case BC_LEX_NLINE:
2437d101cdd6SStefan Eßer case BC_LEX_WHITESPACE:
2438d101cdd6SStefan Eßer case BC_LEX_LBRACKET:
2439d101cdd6SStefan Eßer case BC_LEX_COMMA:
2440d101cdd6SStefan Eßer case BC_LEX_RBRACKET:
2441d101cdd6SStefan Eßer case BC_LEX_LBRACE:
2442d101cdd6SStefan Eßer case BC_LEX_SCOLON:
2443d101cdd6SStefan Eßer case BC_LEX_RBRACE:
2444d101cdd6SStefan Eßer case BC_LEX_KW_AUTO:
2445d101cdd6SStefan Eßer case BC_LEX_KW_BREAK:
2446d101cdd6SStefan Eßer case BC_LEX_KW_CONTINUE:
2447d101cdd6SStefan Eßer case BC_LEX_KW_DEFINE:
2448d101cdd6SStefan Eßer case BC_LEX_KW_FOR:
2449d101cdd6SStefan Eßer case BC_LEX_KW_IF:
2450d101cdd6SStefan Eßer case BC_LEX_KW_LIMITS:
2451d101cdd6SStefan Eßer case BC_LEX_KW_RETURN:
2452d101cdd6SStefan Eßer case BC_LEX_KW_WHILE:
2453d101cdd6SStefan Eßer case BC_LEX_KW_HALT:
2454d101cdd6SStefan Eßer case BC_LEX_KW_PRINT:
2455d101cdd6SStefan Eßer case BC_LEX_KW_QUIT:
2456d101cdd6SStefan Eßer case BC_LEX_KW_STREAM:
2457d101cdd6SStefan Eßer case BC_LEX_KW_ELSE:
2458d101cdd6SStefan Eßer #if DC_ENABLED
2459103d7cdfSStefan Eßer case BC_LEX_EXTENDED_REGISTERS:
2460d101cdd6SStefan Eßer case BC_LEX_EQ_NO_REG:
2461d101cdd6SStefan Eßer case BC_LEX_COLON:
2462d101cdd6SStefan Eßer case BC_LEX_EXECUTE:
2463d101cdd6SStefan Eßer case BC_LEX_PRINT_STACK:
2464d101cdd6SStefan Eßer case BC_LEX_CLEAR_STACK:
2465d101cdd6SStefan Eßer case BC_LEX_REG_STACK_LEVEL:
2466d101cdd6SStefan Eßer case BC_LEX_STACK_LEVEL:
2467d101cdd6SStefan Eßer case BC_LEX_DUPLICATE:
2468d101cdd6SStefan Eßer case BC_LEX_SWAP:
2469d101cdd6SStefan Eßer case BC_LEX_POP:
2470d101cdd6SStefan Eßer case BC_LEX_STORE_IBASE:
2471d101cdd6SStefan Eßer case BC_LEX_STORE_OBASE:
2472d101cdd6SStefan Eßer case BC_LEX_STORE_SCALE:
2473d101cdd6SStefan Eßer #if BC_ENABLE_EXTRA_MATH
2474d101cdd6SStefan Eßer case BC_LEX_STORE_SEED:
2475d101cdd6SStefan Eßer #endif // BC_ENABLE_EXTRA_MATH
2476d101cdd6SStefan Eßer case BC_LEX_LOAD:
2477d101cdd6SStefan Eßer case BC_LEX_LOAD_POP:
2478d101cdd6SStefan Eßer case BC_LEX_STORE_PUSH:
2479d101cdd6SStefan Eßer case BC_LEX_PRINT_POP:
2480d101cdd6SStefan Eßer case BC_LEX_NQUIT:
2481d101cdd6SStefan Eßer case BC_LEX_EXEC_STACK_LENGTH:
2482d101cdd6SStefan Eßer case BC_LEX_SCALE_FACTOR:
2483d101cdd6SStefan Eßer case BC_LEX_ARRAY_LENGTH:
2484d101cdd6SStefan Eßer #endif // DC_ENABLED
248550696a6eSStefan Eßer {
2486103d7cdfSStefan Eßer #if BC_DEBUG
248744d4804dSStefan Eßer // We should never get here, even in debug builds.
248850696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_TOKEN);
248950696a6eSStefan Eßer break;
2490103d7cdfSStefan Eßer #endif // BC_DEBUG
249150696a6eSStefan Eßer }
249250696a6eSStefan Eßer }
249350696a6eSStefan Eßer
249450696a6eSStefan Eßer if (get_token) bc_lex_next(&p->l);
249550696a6eSStefan Eßer }
249650696a6eSStefan Eßer
249744d4804dSStefan Eßer // Now that we have parsed the expression, we need to empty the operator
249844d4804dSStefan Eßer // stack.
249978bc019dSStefan Eßer while (p->ops.len > ops_bgn)
250078bc019dSStefan Eßer {
250150696a6eSStefan Eßer top = BC_PARSE_TOP_OP(p);
250250696a6eSStefan Eßer assign = top >= BC_LEX_OP_ASSIGN_POWER && top <= BC_LEX_OP_ASSIGN;
250350696a6eSStefan Eßer
250444d4804dSStefan Eßer // There should not be *any* parens on the stack anymore.
250550696a6eSStefan Eßer if (BC_ERR(top == BC_LEX_LPAREN || top == BC_LEX_RPAREN))
250678bc019dSStefan Eßer {
250750696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
250878bc019dSStefan Eßer }
250950696a6eSStefan Eßer
251050696a6eSStefan Eßer bc_parse_push(p, BC_PARSE_TOKEN_INST(top));
251150696a6eSStefan Eßer
251244d4804dSStefan Eßer // Adjust the number of unused expressions.
251350696a6eSStefan Eßer nexprs -= !BC_PARSE_OP_PREFIX(top);
251450696a6eSStefan Eßer bc_vec_pop(&p->ops);
251550696a6eSStefan Eßer
251650696a6eSStefan Eßer incdec = false;
251750696a6eSStefan Eßer }
251850696a6eSStefan Eßer
251944d4804dSStefan Eßer // There must be only one expression at the top.
252050696a6eSStefan Eßer if (BC_ERR(nexprs != 1)) bc_parse_err(p, BC_ERR_PARSE_EXPR);
252150696a6eSStefan Eßer
252244d4804dSStefan Eßer // Check that the next token is correct.
252378bc019dSStefan Eßer for (i = 0; i < next.len && t != next.tokens[i]; ++i)
252478bc019dSStefan Eßer {
252578bc019dSStefan Eßer continue;
252678bc019dSStefan Eßer }
252750696a6eSStefan Eßer if (BC_ERR(i == next.len && !bc_parse_isDelimiter(p)))
252878bc019dSStefan Eßer {
252950696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EXPR);
253078bc019dSStefan Eßer }
253150696a6eSStefan Eßer
253244d4804dSStefan Eßer // Check that POSIX would be happy with the number of relational operators.
253350696a6eSStefan Eßer if (!(flags & BC_PARSE_REL) && nrelops)
253478bc019dSStefan Eßer {
253550696a6eSStefan Eßer bc_parse_err(p, BC_ERR_POSIX_REL_POS);
253678bc019dSStefan Eßer }
253750696a6eSStefan Eßer else if ((flags & BC_PARSE_REL) && nrelops > 1)
253878bc019dSStefan Eßer {
253950696a6eSStefan Eßer bc_parse_err(p, BC_ERR_POSIX_MULTIREL);
254078bc019dSStefan Eßer }
254150696a6eSStefan Eßer
254244d4804dSStefan Eßer // If this is true, then we might be in a situation where we don't print.
254344d4804dSStefan Eßer // We would want to have the increment/decrement operator not make an extra
254444d4804dSStefan Eßer // copy if it's not necessary.
254578bc019dSStefan Eßer if (!(flags & BC_PARSE_NEEDVAL) && !pfirst)
254678bc019dSStefan Eßer {
254744d4804dSStefan Eßer // We have the easy case if the last operator was an assignment
254844d4804dSStefan Eßer // operator.
254978bc019dSStefan Eßer if (assign)
255078bc019dSStefan Eßer {
255150696a6eSStefan Eßer inst = *((uchar*) bc_vec_top(&p->func->code));
255250696a6eSStefan Eßer inst += (BC_INST_ASSIGN_POWER_NO_VAL - BC_INST_ASSIGN_POWER);
255350696a6eSStefan Eßer incdec = false;
255450696a6eSStefan Eßer }
255544d4804dSStefan Eßer // If we have an inc/dec operator and we are *not* printing, implement
255644d4804dSStefan Eßer // the optimization to get rid of the extra copy.
255778bc019dSStefan Eßer else if (incdec && !(flags & BC_PARSE_PRINT))
255878bc019dSStefan Eßer {
255950696a6eSStefan Eßer inst = *((uchar*) bc_vec_top(&p->func->code));
256050696a6eSStefan Eßer incdec = (inst <= BC_INST_DEC);
256178bc019dSStefan Eßer inst = BC_INST_ASSIGN_PLUS_NO_VAL +
256278bc019dSStefan Eßer (inst != BC_INST_INC && inst != BC_INST_ASSIGN_PLUS);
256350696a6eSStefan Eßer }
256450696a6eSStefan Eßer
256544d4804dSStefan Eßer // This condition allows us to change the previous assignment
256644d4804dSStefan Eßer // instruction (which does a copy) for a NO_VAL version, which does not.
256744d4804dSStefan Eßer // This condition is set if either of the above if statements ends up
256844d4804dSStefan Eßer // being true.
256950696a6eSStefan Eßer if (inst >= BC_INST_ASSIGN_POWER_NO_VAL &&
257050696a6eSStefan Eßer inst <= BC_INST_ASSIGN_NO_VAL)
257150696a6eSStefan Eßer {
257244d4804dSStefan Eßer // Pop the previous assignment instruction and push a new one.
257344d4804dSStefan Eßer // Inc/dec needs the extra instruction because it is now a binary
257444d4804dSStefan Eßer // operator and needs a second operand.
257550696a6eSStefan Eßer bc_vec_pop(&p->func->code);
257650696a6eSStefan Eßer if (incdec) bc_parse_push(p, BC_INST_ONE);
257750696a6eSStefan Eßer bc_parse_push(p, inst);
257850696a6eSStefan Eßer }
257950696a6eSStefan Eßer }
258050696a6eSStefan Eßer
258144d4804dSStefan Eßer // If we might have to print...
258278bc019dSStefan Eßer if ((flags & BC_PARSE_PRINT))
258378bc019dSStefan Eßer {
258444d4804dSStefan Eßer // With a paren first or the last operator not being an assignment, we
258544d4804dSStefan Eßer // *do* want to print.
258650696a6eSStefan Eßer if (pfirst || !assign) bc_parse_push(p, BC_INST_PRINT);
258750696a6eSStefan Eßer }
258844d4804dSStefan Eßer // We need to make sure to push a pop instruction for assignment statements
258944d4804dSStefan Eßer // that will not print. The print will pop, but without it, we need to pop.
259050696a6eSStefan Eßer else if (!(flags & BC_PARSE_NEEDVAL) &&
259150696a6eSStefan Eßer (inst < BC_INST_ASSIGN_POWER_NO_VAL ||
259250696a6eSStefan Eßer inst > BC_INST_ASSIGN_NO_VAL))
259350696a6eSStefan Eßer {
259450696a6eSStefan Eßer bc_parse_push(p, BC_INST_POP);
259550696a6eSStefan Eßer }
259650696a6eSStefan Eßer
259750696a6eSStefan Eßer // We want to eat newlines if newlines are not a valid ending token.
259850696a6eSStefan Eßer // This is for spacing in things like for loop headers.
259944d4804dSStefan Eßer //
260044d4804dSStefan Eßer // Yes, this is one case where I reuse a variable for a different purpose;
260144d4804dSStefan Eßer // in this case, incdec being true now means that newlines are not valid.
260250696a6eSStefan Eßer for (incdec = true, i = 0; i < next.len && incdec; ++i)
260378bc019dSStefan Eßer {
260450696a6eSStefan Eßer incdec = (next.tokens[i] != BC_LEX_NLINE);
260578bc019dSStefan Eßer }
260678bc019dSStefan Eßer if (incdec)
260778bc019dSStefan Eßer {
260878bc019dSStefan Eßer while (p->l.t == BC_LEX_NLINE)
260978bc019dSStefan Eßer {
261078bc019dSStefan Eßer bc_lex_next(&p->l);
261178bc019dSStefan Eßer }
261250696a6eSStefan Eßer }
261350696a6eSStefan Eßer
261450696a6eSStefan Eßer return BC_PARSE_STATUS_SUCCESS;
261550696a6eSStefan Eßer }
261650696a6eSStefan Eßer
261744d4804dSStefan Eßer /**
261844d4804dSStefan Eßer * Parses an expression with bc_parse_expr_err(), but throws an error if it gets
261944d4804dSStefan Eßer * an empty expression.
262044d4804dSStefan Eßer * @param p The parser.
262144d4804dSStefan Eßer * @param flags The flags for what is valid in the expression.
262244d4804dSStefan Eßer * @param next A set of tokens for what is valid *after* the expression.
262344d4804dSStefan Eßer */
262478bc019dSStefan Eßer static void
bc_parse_expr_status(BcParse * p,uint8_t flags,BcParseNext next)262578bc019dSStefan Eßer bc_parse_expr_status(BcParse* p, uint8_t flags, BcParseNext next)
262678bc019dSStefan Eßer {
262750696a6eSStefan Eßer BcParseStatus s = bc_parse_expr_err(p, flags, next);
262850696a6eSStefan Eßer
262950696a6eSStefan Eßer if (BC_ERR(s == BC_PARSE_STATUS_EMPTY_EXPR))
263078bc019dSStefan Eßer {
263150696a6eSStefan Eßer bc_parse_err(p, BC_ERR_PARSE_EMPTY_EXPR);
263250696a6eSStefan Eßer }
263378bc019dSStefan Eßer }
263450696a6eSStefan Eßer
263578bc019dSStefan Eßer void
bc_parse_expr(BcParse * p,uint8_t flags)263678bc019dSStefan Eßer bc_parse_expr(BcParse* p, uint8_t flags)
263778bc019dSStefan Eßer {
263850696a6eSStefan Eßer assert(p);
263950696a6eSStefan Eßer bc_parse_expr_status(p, flags, bc_parse_next_read);
264050696a6eSStefan Eßer }
264150696a6eSStefan Eßer #endif // BC_ENABLED
2642