diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2019-01-04 13:58:46 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2019-02-14 14:40:57 +0100 |
commit | 5b35d599e8c47e2d3ecc4a44ef742973c1f8aa83 (patch) | |
tree | ee04752886c6818fd8af1b6c3273032948921bd7 | |
parent | c7679c759a1f90b96fa31bdbdd94cf62e0ae344e (diff) | |
download | busybox-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.c | 75 | ||||
-rwxr-xr-x | 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 @@ | |||
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 | ||
349 | typedef enum BcResultType { | 349 | typedef 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 | ||
5587 | static 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 | ||
5614 | static 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 | ||
5631 | static BC_STATUS zxc_program_negate(void) | 5663 | static 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 | ||
126 | testing "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 | ||
132 | testing "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 | ||
138 | testing "bc variable named 'void'" \ | ||
139 | "bc" \ | ||
140 | "6\n9\n" \ | ||
141 | "" "void=6\nvoid\n9" | ||
142 | |||
126 | testing "bc if(cond)<NL>" \ | 143 | testing "bc if(cond)<NL>" \ |
127 | "bc" \ | 144 | "bc" \ |
128 | "9\n" \ | 145 | "9\n" \ |