summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--miscutils/bc.c75
-rwxr-xr-xtestsuite/bc.tests17
2 files changed, 73 insertions, 19 deletions
diff --git a/miscutils/bc.c b/miscutils/bc.c
index e497b0d..69d3920 100644
--- a/miscutils/bc.c
+++ b/miscutils/bc.c
@@ -5,7 +5,6 @@
* Original code copyright (c) 2018 Gavin D. Howard and contributors.
*/
//TODO: GNU extensions:
-// support "define void f()..."
// support "define f(*param[])" - "pass array by reference" syntax
#define DEBUG_LEXER 0
@@ -344,10 +343,12 @@ typedef struct BcFunc {
IF_BC(BcVec strs;)
IF_BC(BcVec consts;)
IF_BC(size_t nparams;)
+ IF_BC(bool voidfunc;)
} BcFunc;
typedef enum BcResultType {
XC_RESULT_TEMP,
+ IF_BC(BC_RESULT_VOID,) // same as TEMP, but INST_PRINT will ignore it
XC_RESULT_VAR,
XC_RESULT_ARRAY_ELEM,
@@ -2451,11 +2452,12 @@ static void dc_result_copy(BcResult *d, BcResult *src)
d->d.id.name = xstrdup(src->d.id.name);
break;
case XC_RESULT_CONSTANT:
- IF_BC(case BC_RESULT_LAST:)
- IF_BC(case BC_RESULT_ONE:)
case XC_RESULT_STR:
memcpy(&d->d.n, &src->d.n, sizeof(BcNum));
break;
+ default: // placate compiler
+ // BC_RESULT_VOID, BC_RESULT_LAST, BC_RESULT_ONE - do not happen
+ break;
}
}
#endif // ENABLE_DC
@@ -2466,6 +2468,7 @@ static FAST_FUNC void bc_result_free(void *result)
switch (r->t) {
case XC_RESULT_TEMP:
+ IF_BC(case BC_RESULT_VOID:)
case XC_RESULT_IBASE:
case XC_RESULT_SCALE:
case XC_RESULT_OBASE:
@@ -4123,6 +4126,7 @@ static BC_STATUS zbc_parse_return(void)
if (t == XC_LEX_NLINE || t == BC_LEX_SCOLON || t == BC_LEX_RBRACE)
xc_parse_push(BC_INST_RET0);
else {
+//TODO: if (p->func->voidfunc) ERROR
s = zbc_parse_expr(0);
if (s) RETURN_STATUS(s);
@@ -4374,7 +4378,7 @@ static BC_STATUS zbc_parse_funcdef(void)
{
BcParse *p = &G.prs;
BcStatus s;
- bool var, comma = false;
+ bool var, comma, voidfunc;
char *name;
dbg_lex_enter("%s:%d entered", __func__, __LINE__);
@@ -4383,17 +4387,34 @@ static BC_STATUS zbc_parse_funcdef(void)
if (p->lex != XC_LEX_NAME)
RETURN_STATUS(bc_error_bad_function_definition());
- name = xstrdup(p->lex_strnumbuf.v);
- p->fidx = bc_program_addFunc(name);
- p->func = xc_program_func(p->fidx);
+ // To be maximally both POSIX and GNU-compatible,
+ // "void" is not treated as a normal keyword:
+ // you can have variable named "void", and even a function
+ // named "void": "define void() { return 6; }" is ok.
+ // _Only_ "define void f() ..." syntax treats "void"
+ // specially.
+ voidfunc = (strcmp(p->lex_strnumbuf.v, "void") == 0);
s = zxc_lex_next();
if (s) RETURN_STATUS(s);
+
+ voidfunc = (voidfunc && p->lex == XC_LEX_NAME);
+ if (voidfunc) {
+ s = zxc_lex_next();
+ if (s) RETURN_STATUS(s);
+ }
+
if (p->lex != BC_LEX_LPAREN)
RETURN_STATUS(bc_error_bad_function_definition());
+
+ p->fidx = bc_program_addFunc(xstrdup(p->lex_strnumbuf.v));
+ p->func = xc_program_func(p->fidx);
+ p->func->voidfunc = voidfunc;
+
s = zxc_lex_next();
if (s) RETURN_STATUS(s);
+ comma = false;
while (p->lex != BC_LEX_RPAREN) {
if (p->lex != XC_LEX_NAME)
RETURN_STATUS(bc_error_bad_function_definition());
@@ -4442,7 +4463,7 @@ static BC_STATUS zbc_parse_funcdef(void)
// Prevent "define z()<newline>" from being interpreted as function with empty stmt as body
s = zbc_lex_skip_if_at_NLINE();
if (s) RETURN_STATUS(s);
- //GNU bc requires a {} block even if function body has single stmt, enforce this?
+ // GNU bc requires a {} block even if function body has single stmt, enforce this
if (p->lex != BC_LEX_LBRACE)
RETURN_STATUS(bc_error("function { body } expected"));
@@ -5127,6 +5148,7 @@ static BC_STATUS zxc_program_num(BcResult *r, BcNum **num)
switch (r->t) {
case XC_RESULT_STR:
case XC_RESULT_TEMP:
+ IF_BC(case BC_RESULT_VOID:)
case XC_RESULT_IBASE:
case XC_RESULT_SCALE:
case XC_RESULT_OBASE:
@@ -5584,22 +5606,32 @@ static BC_STATUS zxc_num_print(BcNum *n, bool newline)
}
#define zxc_num_print(...) (zxc_num_print(__VA_ARGS__) COMMA_SUCCESS)
-static BC_STATUS zxc_program_print(char inst, size_t idx)
+#if !ENABLE_DC
+// for bc, idx is always 0
+#define xc_program_print(inst, idx) \
+ xc_program_print(inst)
+#endif
+static BC_STATUS xc_program_print(char inst, size_t idx)
{
BcStatus s;
BcResult *r;
BcNum *num;
- bool pop = (inst != XC_INST_PRINT);
+ IF_NOT_DC(size_t idx = 0);
if (!STACK_HAS_MORE_THAN(&G.prog.results, idx))
RETURN_STATUS(bc_error_stack_has_too_few_elements());
r = bc_vec_item_rev(&G.prog.results, idx);
+#if ENABLE_BC
+ if (inst == XC_INST_PRINT && r->t == BC_RESULT_VOID)
+ // void function's result on stack, ignore
+ RETURN_STATUS(BC_STATUS_SUCCESS);
+#endif
s = zxc_program_num(r, &num);
if (s) RETURN_STATUS(s);
if (BC_PROG_NUM(r, num)) {
- s = zxc_num_print(num, !pop);
+ s = zxc_num_print(num, /*newline:*/ inst == XC_INST_PRINT);
#if ENABLE_BC
if (!s && IS_BC) bc_num_copy(&G.prog.last, num);
#endif
@@ -5622,11 +5654,11 @@ static BC_STATUS zxc_program_print(char inst, size_t idx)
}
}
- if (!s && pop) bc_vec_pop(&G.prog.results);
+ if (!s && inst != XC_INST_PRINT) bc_vec_pop(&G.prog.results);
RETURN_STATUS(s);
}
-#define zxc_program_print(...) (zxc_program_print(__VA_ARGS__) COMMA_SUCCESS)
+#define zxc_program_print(...) (xc_program_print(__VA_ARGS__) COMMA_SUCCESS)
static BC_STATUS zxc_program_negate(void)
{
@@ -5790,8 +5822,12 @@ static BC_STATUS zxc_program_assign(char inst)
}
#endif
- if (left->t == XC_RESULT_CONSTANT || left->t == XC_RESULT_TEMP)
+ if (left->t == XC_RESULT_CONSTANT
+ || left->t == XC_RESULT_TEMP
+ IF_BC(|| left->t == BC_RESULT_VOID)
+ ) {
RETURN_STATUS(bc_error_bad_assignment());
+ }
#if ENABLE_BC
if (assign)
@@ -6019,6 +6055,9 @@ static BC_STATUS zbc_program_return(char inst)
size_t i;
BcInstPtr *ip = bc_vec_top(&G.prog.exestack);
+ f = xc_program_func(ip->func);
+
+ res.t = XC_RESULT_TEMP;
if (inst == XC_INST_RET) {
// bc needs this for e.g. RESULT_CONSTANT ("return 5")
// because bc constants are per-function.
@@ -6032,19 +6071,17 @@ static BC_STATUS zbc_program_return(char inst)
bc_num_init(&res.d.n, num->len);
bc_num_copy(&res.d.n, num);
bc_vec_pop(&G.prog.results);
- //} else if (f->void_func) {
- //prepare "void" result in res
} else {
+ if (f->voidfunc)
+ res.t = BC_RESULT_VOID;
bc_num_init_DEF_SIZE(&res.d.n);
//bc_num_zero(&res.d.n); - already is
}
- res.t = XC_RESULT_TEMP;
bc_vec_push(&G.prog.results, &res);
bc_vec_pop(&G.prog.exestack);
// We need to pop arguments as well, so this takes that into account.
- f = xc_program_func(ip->func);
a = (void*)f->autos.v;
for (i = 0; i < f->autos.len; i++, a++) {
BcVec *v;
@@ -6568,7 +6605,7 @@ static BC_STATUS zxc_program_exec(void)
case XC_INST_PRINT:
case XC_INST_PRINT_POP:
case XC_INST_PRINT_STR:
- dbg_exec("XC_INST_PRINTxyz:");
+ dbg_exec("XC_INST_PRINTxyz(%d):", inst - XC_INST_PRINT);
s = zxc_program_print(inst, 0);
break;
case XC_INST_STR:
diff --git a/testsuite/bc.tests b/testsuite/bc.tests
index 0a8222b..3fde60a 100755
--- a/testsuite/bc.tests
+++ b/testsuite/bc.tests
@@ -123,6 +123,23 @@ testing "bc define with body on next line" \
"8\n9\n" \
"" "define w()\n{ auto z; return 8; }\nw()\n9"
+testing "bc void function" \
+ "bc" \
+ "void9\n" \
+ "" "define void w() {print \"void\"}\nw()\n9"
+
+# Extra POSIX compat - GNU bc does not allow this
+testing "bc function named 'void'" \
+ "bc" \
+ "void0\n9\n" \
+ "" "define void() {print \"void\"}\nvoid()\n9"
+
+# Extra POSIX compat - GNU bc does not allow this
+testing "bc variable named 'void'" \
+ "bc" \
+ "6\n9\n" \
+ "" "void=6\nvoid\n9"
+
testing "bc if(cond)<NL>" \
"bc" \
"9\n" \