From 54f5c1d6006ffbf03455a180ba6b7cbd5352b7f5 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 4 Jan 2019 13:58:46 +0100 Subject: [PATCH] bc: support void functions (GNU compat) function old new delta xc_program_print - 689 +689 zxc_vm_process 814 869 +55 zxc_program_exec 4098 4116 +18 zxc_program_assign 385 392 +7 bc_result_free 43 46 +3 zxc_program_binOpPrep 243 245 +2 zdc_program_execStr 518 520 +2 zxc_program_print 683 - -683 ------------------------------------------------------------------------------ (add/remove: 1/1 grow/shrink: 6/0 up/down: 776/-683) Total: 93 bytes Signed-off-by: Denys Vlasenko --- miscutils/bc.c | 75 ++++++++++++++++++++++++++++++++++------------ testsuite/bc.tests | 17 +++++++++++ 2 files changed, 73 insertions(+), 19 deletions(-) diff --git a/miscutils/bc.c b/miscutils/bc.c index e497b0d02..69d3920c0 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()" 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 0a8222be6..3fde60a2c 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)" \ "bc" \ "9\n" \