aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bc_parse.c108
-rw-r--r--src/data.c68
-rw-r--r--src/num.c2
-rw-r--r--src/program.c39
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
diff --git a/src/data.c b/src/data.c
index e2461b13..fe61a711 100644
--- a/src/data.c
+++ b/src/data.c
@@ -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,
diff --git a/src/num.c b/src/num.c
index 0aa6305c..f4209198 100644
--- a/src/num.c
+++ b/src/num.c
@@ -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:
{