diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bc_parse.c | 108 | ||||
-rw-r--r-- | src/data.c | 68 | ||||
-rw-r--r-- | src/num.c | 2 | ||||
-rw-r--r-- | src/program.c | 39 |
4 files changed, 188 insertions, 29 deletions
diff --git a/src/bc_parse.c b/src/bc_parse.c index 30946ad5..8d8d2577 100644 --- a/src/bc_parse.c +++ b/src/bc_parse.c @@ -498,6 +498,96 @@ static void bc_parse_builtin(BcParse *p, BcLexType type, } /** + * Parses a builtin function that takes 3 arguments. This includes modexp() and + * divmod(). + */ +static void bc_parse_builtin3(BcParse *p, BcLexType type, + uint8_t flags, BcInst *prev) +{ + assert(type == BC_LEX_KW_MODEXP || type == BC_LEX_KW_DIVMOD); + + // Must have a left paren. + bc_lex_next(&p->l); + if (BC_ERR(p->l.t != BC_LEX_LPAREN)) + bc_parse_err(p, BC_ERR_PARSE_TOKEN); + + bc_lex_next(&p->l); + + // Change the flags as needed for parsing the argument. + flags &= ~(BC_PARSE_PRINT | BC_PARSE_REL); + flags |= BC_PARSE_NEEDVAL; + + bc_parse_expr_status(p, flags, bc_parse_next_builtin); + + // Must have a comma. + if (BC_ERR(p->l.t != BC_LEX_COMMA)) + bc_parse_err(p, BC_ERR_PARSE_TOKEN); + + bc_lex_next(&p->l); + + bc_parse_expr_status(p, flags, bc_parse_next_builtin); + + // Must have a comma. + if (BC_ERR(p->l.t != BC_LEX_COMMA)) + bc_parse_err(p, BC_ERR_PARSE_TOKEN); + + bc_lex_next(&p->l); + + // If it is a divmod, parse an array name. Otherwise, just parse another + // expression. + if (type == BC_LEX_KW_DIVMOD) { + + // Must have a name. + if (BC_ERR(p->l.t != BC_LEX_NAME)) bc_parse_err(p, BC_ERR_PARSE_TOKEN); + + // This is safe because the next token should not overwrite the name. + bc_lex_next(&p->l); + + // Must have a left bracket. + if (BC_ERR(p->l.t != BC_LEX_LBRACKET)) + bc_parse_err(p, BC_ERR_PARSE_TOKEN); + + // This is safe because the next token should not overwrite the name. + bc_lex_next(&p->l); + + // Must have a right bracket. + if (BC_ERR(p->l.t != BC_LEX_RBRACKET)) + bc_parse_err(p, BC_ERR_PARSE_TOKEN); + + // This is safe because the next token should not overwrite the name. + bc_lex_next(&p->l); + } + else bc_parse_expr_status(p, flags, bc_parse_next_rel); + + // Must have a right paren. + if (BC_ERR(p->l.t != BC_LEX_RPAREN)) + bc_parse_err(p, BC_ERR_PARSE_TOKEN); + + // Adjust previous based on the token and push it. + *prev = type - BC_LEX_KW_MODEXP + BC_INST_MODEXP; + bc_parse_push(p, *prev); + + // If we have divmod, we need to assign the modulus to the array element, so + // we need to push the instructions for doing so. + if (type == BC_LEX_KW_DIVMOD) { + + // The zeroth element. + bc_parse_push(p, BC_INST_ZERO); + bc_parse_push(p, BC_INST_ARRAY_ELEM); + + // Push the array. + bc_parse_pushName(p, p->l.str.v, false); + + // Swap them and assign. After this, the top item on the stack should + // be the quotient. + bc_parse_push(p, BC_INST_SWAP); + bc_parse_push(p, BC_INST_ASSIGN_NO_VAL); + } + + bc_lex_next(&p->l); +} + +/** * Parses the scale keyword. This is special because scale can be a value or a * builtin function. * @param p The parser. @@ -1485,6 +1575,8 @@ static void bc_parse_stmt(BcParse *p) { #if BC_ENABLE_EXTRA_MATH case BC_LEX_KW_IRAND: #endif // BC_ENABLE_EXTRA_MATH + case BC_LEX_KW_MODEXP: + case BC_LEX_KW_DIVMOD: case BC_LEX_KW_READ: #if BC_ENABLE_EXTRA_MATH case BC_LEX_KW_RAND: @@ -2000,6 +2092,22 @@ static BcParseStatus bc_parse_expr_err(BcParse *p, uint8_t flags, break; } + case BC_LEX_KW_MODEXP: + case BC_LEX_KW_DIVMOD: + { + // This is a leaf and cannot come right after a leaf. + if (BC_ERR(BC_PARSE_LEAF(prev, bin_last, rprn))) + bc_parse_err(p, BC_ERR_PARSE_EXPR); + + bc_parse_builtin3(p, t, flags, &prev); + + rprn = get_token = bin_last = incdec = can_assign = false; + nexprs += 1; + flags &= ~(BC_PARSE_ARRAY); + + break; + } + default: { #ifndef NDEBUG @@ -825,7 +825,7 @@ const char bc_parse_one[2] = "1"; #if BC_ENABLED -/// A list of keywords for bc. +/// A list of keywords for bc. This needs to be updated if keywords change. const BcLexKeyword bc_lex_kws[] = { BC_LEX_KW_ENTRY("auto", 4, true), BC_LEX_KW_ENTRY("break", 5, true), @@ -851,6 +851,8 @@ const BcLexKeyword bc_lex_kws[] = { #if BC_ENABLE_EXTRA_MATH BC_LEX_KW_ENTRY("irand", 5, false), #endif // BC_ENABLE_EXTRA_MATH + BC_LEX_KW_ENTRY("modexp", 6, false), + BC_LEX_KW_ENTRY("divmod", 6, false), BC_LEX_KW_ENTRY("quit", 4, true), BC_LEX_KW_ENTRY("read", 4, false), #if BC_ENABLE_EXTRA_MATH @@ -880,25 +882,64 @@ static_assert(sizeof(bc_lex_kws) / sizeof(BcLexKeyword) == BC_LEX_NKWS, #endif // BC_C11 /// An array of booleans that correspond to token types. An entry is true if the -/// token is valid in an expression, false otherwise. +/// token is valid in an expression, false otherwise. This will need to change +/// if tokens change. const uint8_t bc_parse_exprs[] = { + + // Starts with BC_LEX_EOF. BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, true), + + // Starts with BC_LEX_OP_MULTIPLY if extra math is enabled, BC_LEX_OP_DIVIDE + // otherwise. BC_PARSE_EXPR_ENTRY(true, true, true, true, true, true, true, true), + + // Starts with BC_LEX_OP_REL_EQ if extra math is enabled, BC_LEX_OP_REL_LT + // otherwise. BC_PARSE_EXPR_ENTRY(true, true, true, true, true, true, true, true), + #if BC_ENABLE_EXTRA_MATH + + // Starts with BC_LEX_OP_ASSIGN_POWER. BC_PARSE_EXPR_ENTRY(true, true, true, true, true, true, true, true), + + // Starts with BC_LEX_OP_ASSIGN_RSHIFT. BC_PARSE_EXPR_ENTRY(true, true, false, false, true, true, false, false), + + // Starts with BC_LEX_RBRACKET. BC_PARSE_EXPR_ENTRY(false, false, false, false, false, true, true, false), + + // Starts with BC_LEX_KW_BREAK. BC_PARSE_EXPR_ENTRY(false, false, false, false, false, false, false, false), + + // Starts with BC_LEX_KW_HALT. BC_PARSE_EXPR_ENTRY(false, true, true, true, true, true, true, false), - BC_PARSE_EXPR_ENTRY(true, true, true, false, true, true, true, true), - BC_PARSE_EXPR_ENTRY(true, true, false, 0, 0, 0, 0, 0) + + // Starts with BC_LEX_KW_SQRT. + BC_PARSE_EXPR_ENTRY(true, true, true, true, true, false, true, true), + + // Starts with BC_LEX_KW_MAXIBASE. + BC_PARSE_EXPR_ENTRY(true, true, true, true, false, 0, 0, 0) + #else // BC_ENABLE_EXTRA_MATH + + // Starts with BC_LEX_OP_ASSIGN_PLUS. BC_PARSE_EXPR_ENTRY(true, true, true, false, false, true, true, false), + + // Starts with BC_LEX_COMMA. BC_PARSE_EXPR_ENTRY(false, false, false, false, false, false, true, true), + + // Starts with BC_LEX_KW_AUTO. BC_PARSE_EXPR_ENTRY(false, false, false, false, false, false, false, false), + + // Starts with BC_LEX_KW_WHILE. BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, false), - BC_PARSE_EXPR_ENTRY(true, true, false, true, true, true, true, false) + + // Starts with BC_LEX_KW_SQRT. + BC_PARSE_EXPR_ENTRY(true, true, true, true, false, true, true, true), + + // Starts with BC_LEX_KW_MAXSCALE, + BC_PARSE_EXPR_ENTRY(true, false, 0, 0, 0, 0, 0, 0) + #endif // BC_ENABLE_EXTRA_MATH }; @@ -959,6 +1000,11 @@ const BcParseNext bc_parse_next_for = BC_PARSE_NEXT(1, BC_LEX_SCOLON); /// The valid next tokens for read expressions. const BcParseNext bc_parse_next_read = BC_PARSE_NEXT(2, BC_LEX_NLINE, BC_LEX_EOF); + +/// The valid next tokens for the arguments of a builtin function with multiple +/// arguments. +const BcParseNext bc_parse_next_builtin = BC_PARSE_NEXT(1, BC_LEX_COMMA); + #endif // BC_ENABLED #if DC_ENABLED @@ -977,7 +1023,8 @@ const size_t dc_lex_regs_len = sizeof(dc_lex_regs) / sizeof(uint8_t); /// A list corresponding to characters starting at double quote ("). If an entry /// is BC_LEX_INVALID, then that character needs extra lexing in dc. If it does /// not, the character can trivially be replaced by the entry. Positions are -/// kept because it corresponds to the ASCII table. +/// kept because it corresponds to the ASCII table. This may need to be changed +/// if tokens change. const uchar dc_lex_tokens[] = { #if BC_ENABLE_EXTRA_MATH BC_LEX_KW_IRAND, @@ -1051,13 +1098,14 @@ const uchar dc_lex_tokens[] = { BC_LEX_KW_QUIT, BC_LEX_SWAP, BC_LEX_OP_ASSIGN, BC_LEX_INVALID, BC_LEX_INVALID, BC_LEX_KW_SQRT, BC_LEX_INVALID, BC_LEX_EXECUTE, BC_LEX_REG_STACK_LEVEL, BC_LEX_STACK_LEVEL, - BC_LEX_LBRACE, BC_LEX_OP_MODEXP, BC_LEX_RBRACE, BC_LEX_OP_DIVMOD, + BC_LEX_LBRACE, BC_LEX_KW_MODEXP, BC_LEX_RBRACE, BC_LEX_KW_DIVMOD, BC_LEX_INVALID }; /// A list of instructions that correspond to lex tokens. If an entry is /// BC_INST_INVALID, that lex token needs extra parsing in the dc parser. -/// Otherwise, the token can trivially be replaced by the entry. +/// Otherwise, the token can trivially be replaced by the entry. This needs to +/// be updated if the tokens change. const uchar dc_parse_insts[] = { BC_INST_INVALID, BC_INST_INVALID, #if BC_ENABLED @@ -1102,7 +1150,7 @@ const uchar dc_parse_insts[] = { #if BC_ENABLE_EXTRA_MATH BC_INST_IRAND, #endif // BC_ENABLE_EXTRA_MATH - BC_INST_QUIT, BC_INST_INVALID, + BC_INST_MODEXP, BC_INST_DIVMOD, BC_INST_QUIT, BC_INST_INVALID, #if BC_ENABLE_EXTRA_MATH BC_INST_RAND, #endif // BC_ENABLE_EXTRA_MATH @@ -1112,7 +1160,7 @@ const uchar dc_parse_insts[] = { BC_INST_MAXRAND, #endif // BC_ENABLE_EXTRA_MATH BC_INST_INVALID, - BC_INST_REL_EQ, BC_INST_MODEXP, BC_INST_DIVMOD, BC_INST_INVALID, + BC_INST_REL_EQ, BC_INST_INVALID, BC_INST_EXECUTE, BC_INST_PRINT_STACK, BC_INST_CLEAR_STACK, BC_INST_INVALID, BC_INST_STACK_LEN, BC_INST_DUPLICATE, BC_INST_SWAP, BC_INST_POP, BC_INST_ASCIIFY, BC_INST_PRINT_STREAM, @@ -3777,7 +3777,6 @@ err: } } -#if DC_ENABLED void bc_num_modexp(BcNum *a, BcNum *b, BcNum *c, BcNum *restrict d) { BcNum base, exp, two, temp, atemp, btemp, ctemp; @@ -3859,7 +3858,6 @@ err: assert(BC_NUM_RDX_VALID(d)); assert(!d->len || d->num[d->len - 1] || BC_NUM_RDX_VAL(d) == d->len); } -#endif // DC_ENABLED #if BC_DEBUG_CODE void bc_num_printDebug(const BcNum *n, const char *name, bool emptyline) { diff --git a/src/program.c b/src/program.c index fc61f50b..3338c593 100644 --- a/src/program.c +++ b/src/program.c @@ -1858,10 +1858,8 @@ static void bc_program_builtin(BcProgram *p, uchar inst) { bc_program_retire(p, 1, 1); } -#if DC_ENABLED - /** - * Executes a divmod for dc. + * Executes a divmod. * @param p The program. */ static void bc_program_divmod(BcProgram *p) { @@ -1898,7 +1896,7 @@ static void bc_program_divmod(BcProgram *p) { } /** - * Executes modular exponentiation for dc. + * Executes modular exponentiation. * @param p The program. */ static void bc_program_modexp(BcProgram *p) { @@ -1906,8 +1904,13 @@ static void bc_program_modexp(BcProgram *p) { BcResult *r1, *r2, *r3, *res; BcNum *n1, *n2, *n3; +#if DC_ENABLED + // Check the stack. - if (BC_ERR(!BC_PROG_STACK(&p->results, 3))) bc_err(BC_ERR_EXEC_STACK); + if (BC_IS_DC && BC_ERR(!BC_PROG_STACK(&p->results, 3))) + bc_err(BC_ERR_EXEC_STACK); + +#endif // DC_ENABLED assert(BC_PROG_STACK(&p->results, 3)); @@ -1936,6 +1939,8 @@ static void bc_program_modexp(BcProgram *p) { bc_program_retire(p, 1, 3); } +#if DC_ENABLED + /** * Gets the length of a register in dc and pushes it onto the results stack. * @param p The program. @@ -2899,6 +2904,18 @@ void bc_program_exec(BcProgram *p) { break; } + case BC_INST_MODEXP: + { + bc_program_modexp(p); + break; + } + + case BC_INST_DIVMOD: + { + bc_program_divmod(p); + break; + } + #if DC_ENABLED case BC_INST_POP_EXEC: { @@ -2919,18 +2936,6 @@ void bc_program_exec(BcProgram *p) { break; } - case BC_INST_MODEXP: - { - bc_program_modexp(p); - break; - } - - case BC_INST_DIVMOD: - { - bc_program_divmod(p); - break; - } - case BC_INST_EXECUTE: case BC_INST_EXEC_COND: { |