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>
This commit is contained in:
parent
1db367a8e6
commit
54f5c1d600
@ -5,7 +5,6 @@
|
|||||||
* Original code copyright (c) 2018 Gavin D. Howard and contributors.
|
* Original code copyright (c) 2018 Gavin D. Howard and contributors.
|
||||||
*/
|
*/
|
||||||
//TODO: GNU extensions:
|
//TODO: GNU extensions:
|
||||||
// support "define void f()..."
|
|
||||||
// support "define f(*param[])" - "pass array by reference" syntax
|
// support "define f(*param[])" - "pass array by reference" syntax
|
||||||
|
|
||||||
#define DEBUG_LEXER 0
|
#define DEBUG_LEXER 0
|
||||||
@ -344,10 +343,12 @@ typedef struct BcFunc {
|
|||||||
IF_BC(BcVec strs;)
|
IF_BC(BcVec strs;)
|
||||||
IF_BC(BcVec consts;)
|
IF_BC(BcVec consts;)
|
||||||
IF_BC(size_t nparams;)
|
IF_BC(size_t nparams;)
|
||||||
|
IF_BC(bool voidfunc;)
|
||||||
} BcFunc;
|
} BcFunc;
|
||||||
|
|
||||||
typedef enum BcResultType {
|
typedef enum BcResultType {
|
||||||
XC_RESULT_TEMP,
|
XC_RESULT_TEMP,
|
||||||
|
IF_BC(BC_RESULT_VOID,) // same as TEMP, but INST_PRINT will ignore it
|
||||||
|
|
||||||
XC_RESULT_VAR,
|
XC_RESULT_VAR,
|
||||||
XC_RESULT_ARRAY_ELEM,
|
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);
|
d->d.id.name = xstrdup(src->d.id.name);
|
||||||
break;
|
break;
|
||||||
case XC_RESULT_CONSTANT:
|
case XC_RESULT_CONSTANT:
|
||||||
IF_BC(case BC_RESULT_LAST:)
|
|
||||||
IF_BC(case BC_RESULT_ONE:)
|
|
||||||
case XC_RESULT_STR:
|
case XC_RESULT_STR:
|
||||||
memcpy(&d->d.n, &src->d.n, sizeof(BcNum));
|
memcpy(&d->d.n, &src->d.n, sizeof(BcNum));
|
||||||
break;
|
break;
|
||||||
|
default: // placate compiler
|
||||||
|
// BC_RESULT_VOID, BC_RESULT_LAST, BC_RESULT_ONE - do not happen
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // ENABLE_DC
|
#endif // ENABLE_DC
|
||||||
@ -2466,6 +2468,7 @@ static FAST_FUNC void bc_result_free(void *result)
|
|||||||
|
|
||||||
switch (r->t) {
|
switch (r->t) {
|
||||||
case XC_RESULT_TEMP:
|
case XC_RESULT_TEMP:
|
||||||
|
IF_BC(case BC_RESULT_VOID:)
|
||||||
case XC_RESULT_IBASE:
|
case XC_RESULT_IBASE:
|
||||||
case XC_RESULT_SCALE:
|
case XC_RESULT_SCALE:
|
||||||
case XC_RESULT_OBASE:
|
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)
|
if (t == XC_LEX_NLINE || t == BC_LEX_SCOLON || t == BC_LEX_RBRACE)
|
||||||
xc_parse_push(BC_INST_RET0);
|
xc_parse_push(BC_INST_RET0);
|
||||||
else {
|
else {
|
||||||
|
//TODO: if (p->func->voidfunc) ERROR
|
||||||
s = zbc_parse_expr(0);
|
s = zbc_parse_expr(0);
|
||||||
if (s) RETURN_STATUS(s);
|
if (s) RETURN_STATUS(s);
|
||||||
|
|
||||||
@ -4374,7 +4378,7 @@ static BC_STATUS zbc_parse_funcdef(void)
|
|||||||
{
|
{
|
||||||
BcParse *p = &G.prs;
|
BcParse *p = &G.prs;
|
||||||
BcStatus s;
|
BcStatus s;
|
||||||
bool var, comma = false;
|
bool var, comma, voidfunc;
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
dbg_lex_enter("%s:%d entered", __func__, __LINE__);
|
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)
|
if (p->lex != XC_LEX_NAME)
|
||||||
RETURN_STATUS(bc_error_bad_function_definition());
|
RETURN_STATUS(bc_error_bad_function_definition());
|
||||||
|
|
||||||
name = xstrdup(p->lex_strnumbuf.v);
|
// To be maximally both POSIX and GNU-compatible,
|
||||||
p->fidx = bc_program_addFunc(name);
|
// "void" is not treated as a normal keyword:
|
||||||
p->func = xc_program_func(p->fidx);
|
// 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();
|
s = zxc_lex_next();
|
||||||
if (s) RETURN_STATUS(s);
|
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)
|
if (p->lex != BC_LEX_LPAREN)
|
||||||
RETURN_STATUS(bc_error_bad_function_definition());
|
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();
|
s = zxc_lex_next();
|
||||||
if (s) RETURN_STATUS(s);
|
if (s) RETURN_STATUS(s);
|
||||||
|
|
||||||
|
comma = false;
|
||||||
while (p->lex != BC_LEX_RPAREN) {
|
while (p->lex != BC_LEX_RPAREN) {
|
||||||
if (p->lex != XC_LEX_NAME)
|
if (p->lex != XC_LEX_NAME)
|
||||||
RETURN_STATUS(bc_error_bad_function_definition());
|
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
|
// Prevent "define z()<newline>" from being interpreted as function with empty stmt as body
|
||||||
s = zbc_lex_skip_if_at_NLINE();
|
s = zbc_lex_skip_if_at_NLINE();
|
||||||
if (s) RETURN_STATUS(s);
|
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)
|
if (p->lex != BC_LEX_LBRACE)
|
||||||
RETURN_STATUS(bc_error("function { body } expected"));
|
RETURN_STATUS(bc_error("function { body } expected"));
|
||||||
|
|
||||||
@ -5127,6 +5148,7 @@ static BC_STATUS zxc_program_num(BcResult *r, BcNum **num)
|
|||||||
switch (r->t) {
|
switch (r->t) {
|
||||||
case XC_RESULT_STR:
|
case XC_RESULT_STR:
|
||||||
case XC_RESULT_TEMP:
|
case XC_RESULT_TEMP:
|
||||||
|
IF_BC(case BC_RESULT_VOID:)
|
||||||
case XC_RESULT_IBASE:
|
case XC_RESULT_IBASE:
|
||||||
case XC_RESULT_SCALE:
|
case XC_RESULT_SCALE:
|
||||||
case XC_RESULT_OBASE:
|
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)
|
#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;
|
BcStatus s;
|
||||||
BcResult *r;
|
BcResult *r;
|
||||||
BcNum *num;
|
BcNum *num;
|
||||||
bool pop = (inst != XC_INST_PRINT);
|
IF_NOT_DC(size_t idx = 0);
|
||||||
|
|
||||||
if (!STACK_HAS_MORE_THAN(&G.prog.results, idx))
|
if (!STACK_HAS_MORE_THAN(&G.prog.results, idx))
|
||||||
RETURN_STATUS(bc_error_stack_has_too_few_elements());
|
RETURN_STATUS(bc_error_stack_has_too_few_elements());
|
||||||
|
|
||||||
r = bc_vec_item_rev(&G.prog.results, idx);
|
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);
|
s = zxc_program_num(r, &num);
|
||||||
if (s) RETURN_STATUS(s);
|
if (s) RETURN_STATUS(s);
|
||||||
|
|
||||||
if (BC_PROG_NUM(r, num)) {
|
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 ENABLE_BC
|
||||||
if (!s && IS_BC) bc_num_copy(&G.prog.last, num);
|
if (!s && IS_BC) bc_num_copy(&G.prog.last, num);
|
||||||
#endif
|
#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);
|
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)
|
static BC_STATUS zxc_program_negate(void)
|
||||||
{
|
{
|
||||||
@ -5790,8 +5822,12 @@ static BC_STATUS zxc_program_assign(char inst)
|
|||||||
}
|
}
|
||||||
#endif
|
#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());
|
RETURN_STATUS(bc_error_bad_assignment());
|
||||||
|
}
|
||||||
|
|
||||||
#if ENABLE_BC
|
#if ENABLE_BC
|
||||||
if (assign)
|
if (assign)
|
||||||
@ -6019,6 +6055,9 @@ static BC_STATUS zbc_program_return(char inst)
|
|||||||
size_t i;
|
size_t i;
|
||||||
BcInstPtr *ip = bc_vec_top(&G.prog.exestack);
|
BcInstPtr *ip = bc_vec_top(&G.prog.exestack);
|
||||||
|
|
||||||
|
f = xc_program_func(ip->func);
|
||||||
|
|
||||||
|
res.t = XC_RESULT_TEMP;
|
||||||
if (inst == XC_INST_RET) {
|
if (inst == XC_INST_RET) {
|
||||||
// bc needs this for e.g. RESULT_CONSTANT ("return 5")
|
// bc needs this for e.g. RESULT_CONSTANT ("return 5")
|
||||||
// because bc constants are per-function.
|
// 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_init(&res.d.n, num->len);
|
||||||
bc_num_copy(&res.d.n, num);
|
bc_num_copy(&res.d.n, num);
|
||||||
bc_vec_pop(&G.prog.results);
|
bc_vec_pop(&G.prog.results);
|
||||||
//} else if (f->void_func) {
|
|
||||||
//prepare "void" result in res
|
|
||||||
} else {
|
} else {
|
||||||
|
if (f->voidfunc)
|
||||||
|
res.t = BC_RESULT_VOID;
|
||||||
bc_num_init_DEF_SIZE(&res.d.n);
|
bc_num_init_DEF_SIZE(&res.d.n);
|
||||||
//bc_num_zero(&res.d.n); - already is
|
//bc_num_zero(&res.d.n); - already is
|
||||||
}
|
}
|
||||||
res.t = XC_RESULT_TEMP;
|
|
||||||
bc_vec_push(&G.prog.results, &res);
|
bc_vec_push(&G.prog.results, &res);
|
||||||
|
|
||||||
bc_vec_pop(&G.prog.exestack);
|
bc_vec_pop(&G.prog.exestack);
|
||||||
|
|
||||||
// We need to pop arguments as well, so this takes that into account.
|
// We need to pop arguments as well, so this takes that into account.
|
||||||
f = xc_program_func(ip->func);
|
|
||||||
a = (void*)f->autos.v;
|
a = (void*)f->autos.v;
|
||||||
for (i = 0; i < f->autos.len; i++, a++) {
|
for (i = 0; i < f->autos.len; i++, a++) {
|
||||||
BcVec *v;
|
BcVec *v;
|
||||||
@ -6568,7 +6605,7 @@ static BC_STATUS zxc_program_exec(void)
|
|||||||
case XC_INST_PRINT:
|
case XC_INST_PRINT:
|
||||||
case XC_INST_PRINT_POP:
|
case XC_INST_PRINT_POP:
|
||||||
case XC_INST_PRINT_STR:
|
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);
|
s = zxc_program_print(inst, 0);
|
||||||
break;
|
break;
|
||||||
case XC_INST_STR:
|
case XC_INST_STR:
|
||||||
|
@ -123,6 +123,23 @@ testing "bc define with body on next line" \
|
|||||||
"8\n9\n" \
|
"8\n9\n" \
|
||||||
"" "define w()\n{ auto z; return 8; }\nw()\n9"
|
"" "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>" \
|
testing "bc if(cond)<NL>" \
|
||||||
"bc" \
|
"bc" \
|
||||||
"9\n" \
|
"9\n" \
|
||||||
|
Loading…
Reference in New Issue
Block a user