aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2019-01-04 13:58:46 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2019-02-14 14:40:57 +0100
commit5b35d599e8c47e2d3ecc4a44ef742973c1f8aa83 (patch)
treeee04752886c6818fd8af1b6c3273032948921bd7
parentc7679c759a1f90b96fa31bdbdd94cf62e0ae344e (diff)
downloadbusybox-w32-5b35d599e8c47e2d3ecc4a44ef742973c1f8aa83.tar.gz
busybox-w32-5b35d599e8c47e2d3ecc4a44ef742973c1f8aa83.tar.bz2
busybox-w32-5b35d599e8c47e2d3ecc4a44ef742973c1f8aa83.zip
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 <vda.linux@googlemail.com>
-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 e497b0d02..69d3920c0 100644
--- a/miscutils/bc.c
+++ b/miscutils/bc.c
@@ -5,7 +5,6 @@
5 * Original code copyright (c) 2018 Gavin D. Howard and contributors. 5 * Original code copyright (c) 2018 Gavin D. Howard and contributors.
6 */ 6 */
7//TODO: GNU extensions: 7//TODO: GNU extensions:
8// support "define void f()..."
9// support "define f(*param[])" - "pass array by reference" syntax 8// support "define f(*param[])" - "pass array by reference" syntax
10 9
11#define DEBUG_LEXER 0 10#define DEBUG_LEXER 0
@@ -344,10 +343,12 @@ typedef struct BcFunc {
344 IF_BC(BcVec strs;) 343 IF_BC(BcVec strs;)
345 IF_BC(BcVec consts;) 344 IF_BC(BcVec consts;)
346 IF_BC(size_t nparams;) 345 IF_BC(size_t nparams;)
346 IF_BC(bool voidfunc;)
347} BcFunc; 347} BcFunc;
348 348
349typedef enum BcResultType { 349typedef enum BcResultType {
350 XC_RESULT_TEMP, 350 XC_RESULT_TEMP,
351 IF_BC(BC_RESULT_VOID,) // same as TEMP, but INST_PRINT will ignore it
351 352
352 XC_RESULT_VAR, 353 XC_RESULT_VAR,
353 XC_RESULT_ARRAY_ELEM, 354 XC_RESULT_ARRAY_ELEM,
@@ -2451,11 +2452,12 @@ static void dc_result_copy(BcResult *d, BcResult *src)
2451 d->d.id.name = xstrdup(src->d.id.name); 2452 d->d.id.name = xstrdup(src->d.id.name);
2452 break; 2453 break;
2453 case XC_RESULT_CONSTANT: 2454 case XC_RESULT_CONSTANT:
2454 IF_BC(case BC_RESULT_LAST:)
2455 IF_BC(case BC_RESULT_ONE:)
2456 case XC_RESULT_STR: 2455 case XC_RESULT_STR:
2457 memcpy(&d->d.n, &src->d.n, sizeof(BcNum)); 2456 memcpy(&d->d.n, &src->d.n, sizeof(BcNum));
2458 break; 2457 break;
2458 default: // placate compiler
2459 // BC_RESULT_VOID, BC_RESULT_LAST, BC_RESULT_ONE - do not happen
2460 break;
2459 } 2461 }
2460} 2462}
2461#endif // ENABLE_DC 2463#endif // ENABLE_DC
@@ -2466,6 +2468,7 @@ static FAST_FUNC void bc_result_free(void *result)
2466 2468
2467 switch (r->t) { 2469 switch (r->t) {
2468 case XC_RESULT_TEMP: 2470 case XC_RESULT_TEMP:
2471 IF_BC(case BC_RESULT_VOID:)
2469 case XC_RESULT_IBASE: 2472 case XC_RESULT_IBASE:
2470 case XC_RESULT_SCALE: 2473 case XC_RESULT_SCALE:
2471 case XC_RESULT_OBASE: 2474 case XC_RESULT_OBASE:
@@ -4123,6 +4126,7 @@ static BC_STATUS zbc_parse_return(void)
4123 if (t == XC_LEX_NLINE || t == BC_LEX_SCOLON || t == BC_LEX_RBRACE) 4126 if (t == XC_LEX_NLINE || t == BC_LEX_SCOLON || t == BC_LEX_RBRACE)
4124 xc_parse_push(BC_INST_RET0); 4127 xc_parse_push(BC_INST_RET0);
4125 else { 4128 else {
4129//TODO: if (p->func->voidfunc) ERROR
4126 s = zbc_parse_expr(0); 4130 s = zbc_parse_expr(0);
4127 if (s) RETURN_STATUS(s); 4131 if (s) RETURN_STATUS(s);
4128 4132
@@ -4374,7 +4378,7 @@ static BC_STATUS zbc_parse_funcdef(void)
4374{ 4378{
4375 BcParse *p = &G.prs; 4379 BcParse *p = &G.prs;
4376 BcStatus s; 4380 BcStatus s;
4377 bool var, comma = false; 4381 bool var, comma, voidfunc;
4378 char *name; 4382 char *name;
4379 4383
4380 dbg_lex_enter("%s:%d entered", __func__, __LINE__); 4384 dbg_lex_enter("%s:%d entered", __func__, __LINE__);
@@ -4383,17 +4387,34 @@ static BC_STATUS zbc_parse_funcdef(void)
4383 if (p->lex != XC_LEX_NAME) 4387 if (p->lex != XC_LEX_NAME)
4384 RETURN_STATUS(bc_error_bad_function_definition()); 4388 RETURN_STATUS(bc_error_bad_function_definition());
4385 4389
4386 name = xstrdup(p->lex_strnumbuf.v); 4390 // To be maximally both POSIX and GNU-compatible,
4387 p->fidx = bc_program_addFunc(name); 4391 // "void" is not treated as a normal keyword:
4388 p->func = xc_program_func(p->fidx); 4392 // you can have variable named "void", and even a function
4393 // named "void": "define void() { return 6; }" is ok.
4394 // _Only_ "define void f() ..." syntax treats "void"
4395 // specially.
4396 voidfunc = (strcmp(p->lex_strnumbuf.v, "void") == 0);
4389 4397
4390 s = zxc_lex_next(); 4398 s = zxc_lex_next();
4391 if (s) RETURN_STATUS(s); 4399 if (s) RETURN_STATUS(s);
4400
4401 voidfunc = (voidfunc && p->lex == XC_LEX_NAME);
4402 if (voidfunc) {
4403 s = zxc_lex_next();
4404 if (s) RETURN_STATUS(s);
4405 }
4406
4392 if (p->lex != BC_LEX_LPAREN) 4407 if (p->lex != BC_LEX_LPAREN)
4393 RETURN_STATUS(bc_error_bad_function_definition()); 4408 RETURN_STATUS(bc_error_bad_function_definition());
4409
4410 p->fidx = bc_program_addFunc(xstrdup(p->lex_strnumbuf.v));
4411 p->func = xc_program_func(p->fidx);
4412 p->func->voidfunc = voidfunc;
4413
4394 s = zxc_lex_next(); 4414 s = zxc_lex_next();
4395 if (s) RETURN_STATUS(s); 4415 if (s) RETURN_STATUS(s);
4396 4416
4417 comma = false;
4397 while (p->lex != BC_LEX_RPAREN) { 4418 while (p->lex != BC_LEX_RPAREN) {
4398 if (p->lex != XC_LEX_NAME) 4419 if (p->lex != XC_LEX_NAME)
4399 RETURN_STATUS(bc_error_bad_function_definition()); 4420 RETURN_STATUS(bc_error_bad_function_definition());
@@ -4442,7 +4463,7 @@ static BC_STATUS zbc_parse_funcdef(void)
4442 // Prevent "define z()<newline>" from being interpreted as function with empty stmt as body 4463 // Prevent "define z()<newline>" from being interpreted as function with empty stmt as body
4443 s = zbc_lex_skip_if_at_NLINE(); 4464 s = zbc_lex_skip_if_at_NLINE();
4444 if (s) RETURN_STATUS(s); 4465 if (s) RETURN_STATUS(s);
4445 //GNU bc requires a {} block even if function body has single stmt, enforce this? 4466 // GNU bc requires a {} block even if function body has single stmt, enforce this
4446 if (p->lex != BC_LEX_LBRACE) 4467 if (p->lex != BC_LEX_LBRACE)
4447 RETURN_STATUS(bc_error("function { body } expected")); 4468 RETURN_STATUS(bc_error("function { body } expected"));
4448 4469
@@ -5127,6 +5148,7 @@ static BC_STATUS zxc_program_num(BcResult *r, BcNum **num)
5127 switch (r->t) { 5148 switch (r->t) {
5128 case XC_RESULT_STR: 5149 case XC_RESULT_STR:
5129 case XC_RESULT_TEMP: 5150 case XC_RESULT_TEMP:
5151 IF_BC(case BC_RESULT_VOID:)
5130 case XC_RESULT_IBASE: 5152 case XC_RESULT_IBASE:
5131 case XC_RESULT_SCALE: 5153 case XC_RESULT_SCALE:
5132 case XC_RESULT_OBASE: 5154 case XC_RESULT_OBASE:
@@ -5584,22 +5606,32 @@ static BC_STATUS zxc_num_print(BcNum *n, bool newline)
5584} 5606}
5585#define zxc_num_print(...) (zxc_num_print(__VA_ARGS__) COMMA_SUCCESS) 5607#define zxc_num_print(...) (zxc_num_print(__VA_ARGS__) COMMA_SUCCESS)
5586 5608
5587static BC_STATUS zxc_program_print(char inst, size_t idx) 5609#if !ENABLE_DC
5610// for bc, idx is always 0
5611#define xc_program_print(inst, idx) \
5612 xc_program_print(inst)
5613#endif
5614static BC_STATUS xc_program_print(char inst, size_t idx)
5588{ 5615{
5589 BcStatus s; 5616 BcStatus s;
5590 BcResult *r; 5617 BcResult *r;
5591 BcNum *num; 5618 BcNum *num;
5592 bool pop = (inst != XC_INST_PRINT); 5619 IF_NOT_DC(size_t idx = 0);
5593 5620
5594 if (!STACK_HAS_MORE_THAN(&G.prog.results, idx)) 5621 if (!STACK_HAS_MORE_THAN(&G.prog.results, idx))
5595 RETURN_STATUS(bc_error_stack_has_too_few_elements()); 5622 RETURN_STATUS(bc_error_stack_has_too_few_elements());
5596 5623
5597 r = bc_vec_item_rev(&G.prog.results, idx); 5624 r = bc_vec_item_rev(&G.prog.results, idx);
5625#if ENABLE_BC
5626 if (inst == XC_INST_PRINT && r->t == BC_RESULT_VOID)
5627 // void function's result on stack, ignore
5628 RETURN_STATUS(BC_STATUS_SUCCESS);
5629#endif
5598 s = zxc_program_num(r, &num); 5630 s = zxc_program_num(r, &num);
5599 if (s) RETURN_STATUS(s); 5631 if (s) RETURN_STATUS(s);
5600 5632
5601 if (BC_PROG_NUM(r, num)) { 5633 if (BC_PROG_NUM(r, num)) {
5602 s = zxc_num_print(num, !pop); 5634 s = zxc_num_print(num, /*newline:*/ inst == XC_INST_PRINT);
5603#if ENABLE_BC 5635#if ENABLE_BC
5604 if (!s && IS_BC) bc_num_copy(&G.prog.last, num); 5636 if (!s && IS_BC) bc_num_copy(&G.prog.last, num);
5605#endif 5637#endif
@@ -5622,11 +5654,11 @@ static BC_STATUS zxc_program_print(char inst, size_t idx)
5622 } 5654 }
5623 } 5655 }
5624 5656
5625 if (!s && pop) bc_vec_pop(&G.prog.results); 5657 if (!s && inst != XC_INST_PRINT) bc_vec_pop(&G.prog.results);
5626 5658
5627 RETURN_STATUS(s); 5659 RETURN_STATUS(s);
5628} 5660}
5629#define zxc_program_print(...) (zxc_program_print(__VA_ARGS__) COMMA_SUCCESS) 5661#define zxc_program_print(...) (xc_program_print(__VA_ARGS__) COMMA_SUCCESS)
5630 5662
5631static BC_STATUS zxc_program_negate(void) 5663static BC_STATUS zxc_program_negate(void)
5632{ 5664{
@@ -5790,8 +5822,12 @@ static BC_STATUS zxc_program_assign(char inst)
5790 } 5822 }
5791#endif 5823#endif
5792 5824
5793 if (left->t == XC_RESULT_CONSTANT || left->t == XC_RESULT_TEMP) 5825 if (left->t == XC_RESULT_CONSTANT
5826 || left->t == XC_RESULT_TEMP
5827 IF_BC(|| left->t == BC_RESULT_VOID)
5828 ) {
5794 RETURN_STATUS(bc_error_bad_assignment()); 5829 RETURN_STATUS(bc_error_bad_assignment());
5830 }
5795 5831
5796#if ENABLE_BC 5832#if ENABLE_BC
5797 if (assign) 5833 if (assign)
@@ -6019,6 +6055,9 @@ static BC_STATUS zbc_program_return(char inst)
6019 size_t i; 6055 size_t i;
6020 BcInstPtr *ip = bc_vec_top(&G.prog.exestack); 6056 BcInstPtr *ip = bc_vec_top(&G.prog.exestack);
6021 6057
6058 f = xc_program_func(ip->func);
6059
6060 res.t = XC_RESULT_TEMP;
6022 if (inst == XC_INST_RET) { 6061 if (inst == XC_INST_RET) {
6023 // bc needs this for e.g. RESULT_CONSTANT ("return 5") 6062 // bc needs this for e.g. RESULT_CONSTANT ("return 5")
6024 // because bc constants are per-function. 6063 // because bc constants are per-function.
@@ -6032,19 +6071,17 @@ static BC_STATUS zbc_program_return(char inst)
6032 bc_num_init(&res.d.n, num->len); 6071 bc_num_init(&res.d.n, num->len);
6033 bc_num_copy(&res.d.n, num); 6072 bc_num_copy(&res.d.n, num);
6034 bc_vec_pop(&G.prog.results); 6073 bc_vec_pop(&G.prog.results);
6035 //} else if (f->void_func) {
6036 //prepare "void" result in res
6037 } else { 6074 } else {
6075 if (f->voidfunc)
6076 res.t = BC_RESULT_VOID;
6038 bc_num_init_DEF_SIZE(&res.d.n); 6077 bc_num_init_DEF_SIZE(&res.d.n);
6039 //bc_num_zero(&res.d.n); - already is 6078 //bc_num_zero(&res.d.n); - already is
6040 } 6079 }
6041 res.t = XC_RESULT_TEMP;
6042 bc_vec_push(&G.prog.results, &res); 6080 bc_vec_push(&G.prog.results, &res);
6043 6081
6044 bc_vec_pop(&G.prog.exestack); 6082 bc_vec_pop(&G.prog.exestack);
6045 6083
6046 // We need to pop arguments as well, so this takes that into account. 6084 // We need to pop arguments as well, so this takes that into account.
6047 f = xc_program_func(ip->func);
6048 a = (void*)f->autos.v; 6085 a = (void*)f->autos.v;
6049 for (i = 0; i < f->autos.len; i++, a++) { 6086 for (i = 0; i < f->autos.len; i++, a++) {
6050 BcVec *v; 6087 BcVec *v;
@@ -6568,7 +6605,7 @@ static BC_STATUS zxc_program_exec(void)
6568 case XC_INST_PRINT: 6605 case XC_INST_PRINT:
6569 case XC_INST_PRINT_POP: 6606 case XC_INST_PRINT_POP:
6570 case XC_INST_PRINT_STR: 6607 case XC_INST_PRINT_STR:
6571 dbg_exec("XC_INST_PRINTxyz:"); 6608 dbg_exec("XC_INST_PRINTxyz(%d):", inst - XC_INST_PRINT);
6572 s = zxc_program_print(inst, 0); 6609 s = zxc_program_print(inst, 0);
6573 break; 6610 break;
6574 case XC_INST_STR: 6611 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" \
123 "8\n9\n" \ 123 "8\n9\n" \
124 "" "define w()\n{ auto z; return 8; }\nw()\n9" 124 "" "define w()\n{ auto z; return 8; }\nw()\n9"
125 125
126testing "bc void function" \
127 "bc" \
128 "void9\n" \
129 "" "define void w() {print \"void\"}\nw()\n9"
130
131# Extra POSIX compat - GNU bc does not allow this
132testing "bc function named 'void'" \
133 "bc" \
134 "void0\n9\n" \
135 "" "define void() {print \"void\"}\nvoid()\n9"
136
137# Extra POSIX compat - GNU bc does not allow this
138testing "bc variable named 'void'" \
139 "bc" \
140 "6\n9\n" \
141 "" "void=6\nvoid\n9"
142
126testing "bc if(cond)<NL>" \ 143testing "bc if(cond)<NL>" \
127 "bc" \ 144 "bc" \
128 "9\n" \ 145 "9\n" \