aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2018-12-16 16:03:03 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2018-12-16 16:10:38 +0100
commitd1d29b4245a29e56ca598d7a03d93bdf11ebc5d0 (patch)
tree28425d3549a37a53503334b57ace61fef58417ce
parent99b37623356a1555359df1c011d4a6732918a5c4 (diff)
downloadbusybox-w32-d1d29b4245a29e56ca598d7a03d93bdf11ebc5d0.tar.gz
busybox-w32-d1d29b4245a29e56ca598d7a03d93bdf11ebc5d0.tar.bz2
busybox-w32-d1d29b4245a29e56ca598d7a03d93bdf11ebc5d0.zip
bc: partially rewrite parser, tests pass, ^C might be broken now
The entire control construct (if/while/for/funcdef) or {} block is "eaten" by the corresponding parsing function, instead of maintaining special "block flag stack" with magic bits in it, and returning to main input loop after every inner statement (every input line, essentially). This required moving line input deep into lexer - now zbc_lex_next() triggers more reading when needed. "block flag stack" is gone. Correctness of ^C handling wasn't checked, might need fixing now. if/else syntax is changed to match GNU bc: "else" can not be on the next line (the rationale is that "if (1) 2" statement in interactive mode should execute and print 2 instead of waiting for possible "else ..." line). This change fixes the following examples: if (1) if (1) 1 else 2 else 3 if (0) 1 else if (1) 2 define w() { auto z; return 1; } function old new delta zbc_parse_stmt_possibly_auto - 2232 +2232 zbc_vm_process 89 561 +472 zbc_lex_next 1982 2296 +314 bc_vm_init 749 757 +8 bc_parse_expr_empty_ok 2016 2021 +5 bc_num_printNewline 54 51 -3 zbc_program_read 289 280 -9 bc_parse_free 47 38 -9 bc_parse_reset 126 113 -13 bc_parse_create 108 92 -16 bc_parse_push_block_flag 47 - -47 bc_parse_noElse 48 - -48 zbc_parse_text_init 113 59 -54 zbc_parse_body 121 - -121 zbc_parse_else 125 - -125 zbc_parse_endBody 254 - -254 bc_vm_run 421 134 -287 zbc_parse_auto 290 - -290 zcommon_parse 476 - -476 zbc_parse_stmt 1682 7 -1675 ------------------------------------------------------------------------------ (add/remove: 1/7 grow/shrink: 4/8 up/down: 3031/-3427) Total: -396 bytes text data bss dec hex filename 982586 485 7296 990367 f1c9f busybox_old 982138 485 7296 989919 f1adf busybox_unstripped Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--miscutils/bc.c631
-rwxr-xr-xtestsuite/bc.tests5
-rw-r--r--testsuite/bc_misc2.bc3
3 files changed, 266 insertions, 373 deletions
diff --git a/miscutils/bc.c b/miscutils/bc.c
index 374279889..95ba8b094 100644
--- a/miscutils/bc.c
+++ b/miscutils/bc.c
@@ -171,7 +171,7 @@
171#define DEBUG_EXEC 0 171#define DEBUG_EXEC 0
172 172
173#if DEBUG_LEXER 173#if DEBUG_LEXER
174static unsigned lex_indent; 174static uint8_t lex_indent;
175#define dbg_lex(...) \ 175#define dbg_lex(...) \
176 do { \ 176 do { \
177 fprintf(stderr, "%*s", lex_indent, ""); \ 177 fprintf(stderr, "%*s", lex_indent, ""); \
@@ -558,19 +558,16 @@ enum {
558#endif 558#endif
559 559
560typedef struct BcLex { 560typedef struct BcLex {
561
562 const char *buf; 561 const char *buf;
563 size_t i; 562 size_t i;
564 size_t line; 563 size_t line;
565 size_t len; 564 size_t len;
566 bool newline; 565 bool newline;
567
568 struct { 566 struct {
569 BcLexType t; 567 BcLexType t;
570 BcLexType last; 568 BcLexType last;
571 BcVec v; 569 BcVec v;
572 } t; 570 } t;
573
574} BcLex; 571} BcLex;
575 572
576#define BC_PARSE_STREND ((char) UCHAR_MAX) 573#define BC_PARSE_STREND ((char) UCHAR_MAX)
@@ -581,37 +578,9 @@ typedef struct BcLex {
581#define BC_PARSE_NOREAD (1 << 3) 578#define BC_PARSE_NOREAD (1 << 3)
582#define BC_PARSE_ARRAY (1 << 4) 579#define BC_PARSE_ARRAY (1 << 4)
583 580
584#define BC_PARSE_TOP_FLAG_PTR(parse) ((parse)->bf_top)
585#define BC_PARSE_TOP_FLAG(parse) (*(BC_PARSE_TOP_FLAG_PTR(parse)))
586#define BC_PARSE_FLAG_STACK_EMPTY(p) ((p)->bf_top == (p)->bf_base)
587
588#define BC_PARSE_FLAG_FUNC_INNER (1 << 0)
589#define BC_PARSE_FLAG_FUNC (1 << 1)
590#define BC_PARSE_FLAG_BODY (1 << 2)
591#define BC_PARSE_FLAG_LOOP (1 << 3)
592#define BC_PARSE_FLAG_LOOP_INNER (1 << 4)
593#define BC_PARSE_FLAG_IF (1 << 5)
594#define BC_PARSE_FLAG_ELSE (1 << 6)
595#define BC_PARSE_FLAG_IF_END (1 << 7)
596
597// If we have none of the above bits, we can stop parsing and execute already parsed chunk
598#define BC_PARSE_CAN_EXEC(parse) (BC_PARSE_TOP_FLAG(parse) == 0)
599
600#define BC_PARSE_FUNC_INNER(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_FUNC_INNER)
601#define BC_PARSE_FUNC(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_FUNC)
602#define BC_PARSE_BODY(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_BODY)
603#define BC_PARSE_LOOP(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_LOOP)
604#define BC_PARSE_LOOP_INNER(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_LOOP_INNER)
605#define BC_PARSE_IF(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_IF)
606#define BC_PARSE_ELSE(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_ELSE)
607#define BC_PARSE_IF_END(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_IF_END)
608
609typedef struct BcParse { 581typedef struct BcParse {
610 BcLex l; 582 BcLex l;
611 583
612 uint8_t *bf_base;
613 uint8_t *bf_top;
614
615 BcVec exits; 584 BcVec exits;
616 BcVec conds; 585 BcVec conds;
617 586
@@ -620,9 +589,8 @@ typedef struct BcParse {
620 BcFunc *func; 589 BcFunc *func;
621 size_t fidx; 590 size_t fidx;
622 591
592//TODO: needed? Example?
623 size_t nbraces; 593 size_t nbraces;
624//FIXME: "define w(x) { auto z; return 1; }" fails to parse
625 bool auto_part;
626} BcParse; 594} BcParse;
627 595
628typedef struct BcProgram { 596typedef struct BcProgram {
@@ -666,26 +634,6 @@ typedef struct BcProgram {
666 634
667} BcProgram; 635} BcProgram;
668 636
669static void bc_parse_push_block_flag(BcParse *p, uint8_t flags)
670{
671 size_t size;
672 uint8_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
673 flags |= (*flag_ptr & (BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_LOOP));
674 flags |= BC_PARSE_FLAG_BODY;
675
676 size = p->bf_top - p->bf_base;
677 p->bf_base = xrealloc(p->bf_base, size + 2);
678 p->bf_top = p->bf_base + size + 1;
679 dbg_lex("%s:%d pushed block flag lvl:%d bits:0x%02x", __func__, __LINE__, size + 1, flags);
680 *p->bf_top = flags;
681}
682
683static ALWAYS_INLINE void bc_parse_pop_block_flag(BcParse *p)
684{
685 p->bf_top--;
686 dbg_lex("%s:%d popped block flag lvl:%d bits:0x%02x", __func__, __LINE__, p->bf_top - p->bf_base, *p->bf_top);
687}
688
689#define BC_PROG_STACK(s, n) ((s)->len >= ((size_t) n)) 637#define BC_PROG_STACK(s, n) ((s)->len >= ((size_t) n))
690 638
691#define BC_PROG_MAIN (0) 639#define BC_PROG_MAIN (0)
@@ -747,6 +695,7 @@ struct globals {
747 IF_FEATURE_BC_SIGNALS(smallint ttyin;) 695 IF_FEATURE_BC_SIGNALS(smallint ttyin;)
748 IF_FEATURE_CLEAN_UP(smallint exiting;) 696 IF_FEATURE_CLEAN_UP(smallint exiting;)
749 smallint in_read; 697 smallint in_read;
698 smallint use_stdin;
750 699
751 BcParse prs; 700 BcParse prs;
752 BcProgram prog; 701 BcProgram prog;
@@ -756,6 +705,7 @@ struct globals {
756 unsigned err_line; 705 unsigned err_line;
757 706
758 BcVec files; 707 BcVec files;
708 BcVec stdin_buffer;
759 709
760 char *env_args; 710 char *env_args;
761 711
@@ -1052,6 +1002,7 @@ static void quit(void)
1052 if (ferror(stdin)) 1002 if (ferror(stdin))
1053 bb_perror_msg_and_die("input error"); 1003 bb_perror_msg_and_die("input error");
1054 fflush_and_check(); 1004 fflush_and_check();
1005 dbg_exec("quit(): exiting with exitcode SUCCESS");
1055 exit(0); 1006 exit(0);
1056} 1007}
1057 1008
@@ -2955,6 +2906,79 @@ static BC_STATUS zcommon_lex_token(BcLex *l)
2955 IF_DC(RETURN_STATUS(zdc_lex_token(l));) 2906 IF_DC(RETURN_STATUS(zdc_lex_token(l));)
2956} 2907}
2957 2908
2909static bool bc_lex_more_input(BcLex *l)
2910{
2911 size_t str;
2912 bool comment;
2913
2914 bc_vec_pop_all(&G.stdin_buffer);
2915
2916 // This loop is complex because the vm tries not to send any lines that end
2917 // with a backslash to the parser. The reason for that is because the parser
2918 // treats a backslash+newline combo as whitespace, per the bc spec. In that
2919 // case, and for strings and comments, the parser will expect more stuff.
2920 comment = false;
2921 str = 0;
2922 for (;;) {
2923 size_t prevlen = G.stdin_buffer.len;
2924 char *string;
2925
2926 bc_read_line(&G.stdin_buffer);
2927 // No more input means EOF
2928 if (G.stdin_buffer.len <= prevlen + 1) // (we expect +1 for NUL byte)
2929 break;
2930
2931 string = G.stdin_buffer.v + prevlen;
2932 while (*string) {
2933 char c = *string;
2934 if (string == G.stdin_buffer.v || string[-1] != '\\') {
2935 if (IS_BC)
2936 str ^= (c == '"');
2937 else {
2938 if (c == ']')
2939 str -= 1;
2940 else if (c == '[')
2941 str += 1;
2942 }
2943 }
2944 string++;
2945 if (c == '/' && *string == '*') {
2946 comment = true;
2947 string++;
2948 continue;
2949 }
2950 if (c == '*' && *string == '/') {
2951 comment = false;
2952 string++;
2953 }
2954 }
2955 if (str != 0 || comment) {
2956 G.stdin_buffer.len--; // backstep over the trailing NUL byte
2957 continue;
2958 }
2959
2960 // Check for backslash+newline.
2961 // we do not check that last char is '\n' -
2962 // if it is not, then it's EOF, and looping back
2963 // to bc_read_line() will detect it:
2964 string -= 2;
2965 if (string >= G.stdin_buffer.v && *string == '\\') {
2966 G.stdin_buffer.len--;
2967 continue;
2968 }
2969
2970 break;
2971 }
2972
2973 l->buf = G.stdin_buffer.v;
2974 l->i = 0;
2975//bb_error_msg("G.stdin_buffer.len:%d '%s'", G.stdin_buffer.len, G.stdin_buffer.v);
2976 l->len = G.stdin_buffer.len - 1; // do not include NUL
2977
2978 G.use_stdin = (l->len != 0);
2979 return G.use_stdin;
2980}
2981
2958static BC_STATUS zbc_lex_next(BcLex *l) 2982static BC_STATUS zbc_lex_next(BcLex *l)
2959{ 2983{
2960 BcStatus s; 2984 BcStatus s;
@@ -2964,10 +2988,16 @@ static BC_STATUS zbc_lex_next(BcLex *l)
2964 2988
2965 l->line += l->newline; 2989 l->line += l->newline;
2966 G.err_line = l->line; 2990 G.err_line = l->line;
2967 l->t.t = BC_LEX_EOF;
2968 2991
2992 l->t.t = BC_LEX_EOF;
2993//this NL handling is bogus
2969 l->newline = (l->i == l->len); 2994 l->newline = (l->i == l->len);
2970 if (l->newline) RETURN_STATUS(BC_STATUS_SUCCESS); 2995 if (l->newline) {
2996 if (!G.use_stdin || !bc_lex_more_input(l))
2997 RETURN_STATUS(BC_STATUS_SUCCESS);
2998 // here it's guaranteed that l->i is below l->len
2999 l->newline = false;
3000 }
2971 3001
2972 // Loop until failure or we don't have whitespace. This 3002 // Loop until failure or we don't have whitespace. This
2973 // is so the parser doesn't get inundated with whitespace. 3003 // is so the parser doesn't get inundated with whitespace.
@@ -3528,19 +3558,8 @@ static BC_STATUS zcommon_parse(BcParse *p)
3528 3558
3529static BC_STATUS zbc_parse_text_init(BcParse *p, const char *text) 3559static BC_STATUS zbc_parse_text_init(BcParse *p, const char *text)
3530{ 3560{
3531 BcStatus s;
3532
3533 p->func = bc_program_func(p->fidx); 3561 p->func = bc_program_func(p->fidx);
3534 3562
3535 if (!text[0] && !BC_PARSE_CAN_EXEC(p)) {
3536 p->l.t.t = BC_LEX_INVALID;
3537 s = BC_STATUS_SUCCESS;
3538 ERROR_RETURN(s =) zcommon_parse(p);
3539 if (s) RETURN_STATUS(s);
3540 if (!BC_PARSE_CAN_EXEC(p))
3541 RETURN_STATUS(bc_error("file is not executable"));
3542 }
3543
3544 RETURN_STATUS(zbc_lex_text_init(&p->l, text)); 3563 RETURN_STATUS(zbc_lex_text_init(&p->l, text));
3545} 3564}
3546#if ERRORS_ARE_FATAL 3565#if ERRORS_ARE_FATAL
@@ -3580,9 +3599,8 @@ static void bc_parse_reset(BcParse *p)
3580 3599
3581 p->l.i = p->l.len; 3600 p->l.i = p->l.len;
3582 p->l.t.t = BC_LEX_EOF; 3601 p->l.t.t = BC_LEX_EOF;
3583 p->auto_part = (p->nbraces = 0); 3602 p->nbraces = 0;
3584 3603
3585 p->bf_top = p->bf_base; // pop all flags
3586 bc_vec_pop_all(&p->exits); 3604 bc_vec_pop_all(&p->exits);
3587 bc_vec_pop_all(&p->conds); 3605 bc_vec_pop_all(&p->conds);
3588 bc_vec_pop_all(&p->ops); 3606 bc_vec_pop_all(&p->ops);
@@ -3592,7 +3610,6 @@ static void bc_parse_reset(BcParse *p)
3592 3610
3593static void bc_parse_free(BcParse *p) 3611static void bc_parse_free(BcParse *p)
3594{ 3612{
3595 free(p->bf_base);
3596 bc_vec_free(&p->exits); 3613 bc_vec_free(&p->exits);
3597 bc_vec_free(&p->conds); 3614 bc_vec_free(&p->conds);
3598 bc_vec_free(&p->ops); 3615 bc_vec_free(&p->ops);
@@ -3604,12 +3621,11 @@ static void bc_parse_create(BcParse *p, size_t func)
3604 memset(p, 0, sizeof(BcParse)); 3621 memset(p, 0, sizeof(BcParse));
3605 3622
3606 bc_lex_init(&p->l); 3623 bc_lex_init(&p->l);
3607 p->bf_top = p->bf_base = xzalloc(1);
3608 bc_vec_init(&p->exits, sizeof(BcInstPtr), NULL); 3624 bc_vec_init(&p->exits, sizeof(BcInstPtr), NULL);
3609 bc_vec_init(&p->conds, sizeof(size_t), NULL); 3625 bc_vec_init(&p->conds, sizeof(size_t), NULL);
3610 bc_vec_init(&p->ops, sizeof(BcLexType), NULL); 3626 bc_vec_init(&p->ops, sizeof(BcLexType), NULL);
3611 3627
3612 // p->auto_part = p->nbraces = 0; - already is 3628 // p->nbraces = 0; - already is
3613 bc_parse_updateFunc(p, func); 3629 bc_parse_updateFunc(p, func);
3614} 3630}
3615 3631
@@ -3625,14 +3641,20 @@ static void bc_parse_create(BcParse *p, size_t func)
3625// first in the expr enum. Note: This only works for binary operators. 3641// first in the expr enum. Note: This only works for binary operators.
3626#define BC_TOKEN_2_INST(t) ((char) ((t) - BC_LEX_NEG + BC_INST_NEG)) 3642#define BC_TOKEN_2_INST(t) ((char) ((t) - BC_LEX_NEG + BC_INST_NEG))
3627 3643
3628static BC_STATUS zbc_parse_else(BcParse *p); 3644static BC_STATUS zbc_parse_stmt_possibly_auto(BcParse *p, bool auto_allowed);
3629static BC_STATUS zbc_parse_stmt(BcParse *p);
3630static BC_STATUS zbc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next); 3645static BC_STATUS zbc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next);
3631static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext next); 3646static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext next);
3632#if ERRORS_ARE_FATAL 3647#if ERRORS_ARE_FATAL
3633# define zbc_parse_else(...) (zbc_parse_else(__VA_ARGS__), BC_STATUS_SUCCESS)
3634# define zbc_parse_stmt(...) (zbc_parse_stmt(__VA_ARGS__), BC_STATUS_SUCCESS)
3635# define zbc_parse_expr(...) (zbc_parse_expr(__VA_ARGS__), BC_STATUS_SUCCESS) 3648# define zbc_parse_expr(...) (zbc_parse_expr(__VA_ARGS__), BC_STATUS_SUCCESS)
3649# defone zbc_parse_stmt_possibly_auto(...) (zbc_parse_stmt_possibly_auto(__VA_ARGS__), BC_STATUS_SUCCESS)
3650#endif
3651
3652static BC_STATUS zbc_parse_stmt(BcParse *p)
3653{
3654 RETURN_STATUS(zbc_parse_stmt_possibly_auto(p, false));
3655}
3656#if ERRORS_ARE_FATAL
3657# define zbc_parse_stmt(...) (zbc_parse_stmt(__VA_ARGS__), BC_STATUS_SUCCESS)
3636#endif 3658#endif
3637 3659
3638static void bc_parse_operator(BcParse *p, BcLexType type, size_t start, 3660static void bc_parse_operator(BcParse *p, BcLexType type, size_t start,
@@ -4037,7 +4059,7 @@ static BC_STATUS zbc_parse_return(BcParse *p)
4037 BcLexType t; 4059 BcLexType t;
4038 bool paren; 4060 bool paren;
4039 4061
4040 if (!BC_PARSE_FUNC(p)) RETURN_STATUS(bc_error_bad_token()); 4062 dbg_lex_enter("%s:%d entered", __func__, __LINE__);
4041 4063
4042 s = zbc_lex_next(&p->l); 4064 s = zbc_lex_next(&p->l);
4043 if (s) RETURN_STATUS(s); 4065 if (s) RETURN_STATUS(s);
@@ -4063,97 +4085,62 @@ static BC_STATUS zbc_parse_return(BcParse *p)
4063 bc_parse_push(p, BC_INST_RET); 4085 bc_parse_push(p, BC_INST_RET);
4064 } 4086 }
4065 4087
4088 dbg_lex_done("%s:%d done", __func__, __LINE__);
4066 RETURN_STATUS(s); 4089 RETURN_STATUS(s);
4067} 4090}
4068#if ERRORS_ARE_FATAL 4091#if ERRORS_ARE_FATAL
4069# define zbc_parse_return(...) (zbc_parse_return(__VA_ARGS__), BC_STATUS_SUCCESS) 4092# define zbc_parse_return(...) (zbc_parse_return(__VA_ARGS__), BC_STATUS_SUCCESS)
4070#endif 4093#endif
4071 4094
4072static BC_STATUS zbc_parse_endBody(BcParse *p) 4095static void bc_parse_noElse(BcParse *p)
4073{ 4096{
4074 BcStatus s = BC_STATUS_SUCCESS; 4097 BcInstPtr *ip;
4075 4098 size_t *label;
4076 if (BC_PARSE_FLAG_STACK_EMPTY(p))
4077 RETURN_STATUS(bc_error_bad_token());
4078
4079 if (BC_PARSE_IF(p)) {
4080 uint8_t *flag_ptr;
4081 4099
4082 while (p->l.t.t == BC_LEX_NLINE) { 4100 ip = bc_vec_top(&p->exits);
4083 s = zbc_lex_next(&p->l); 4101 label = bc_vec_item(&p->func->labels, ip->idx);
4084 if (s) RETURN_STATUS(s); 4102 dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len);
4085 } 4103 *label = p->func->code.len;
4086 4104
4087 bc_parse_pop_block_flag(p); 4105 bc_vec_pop(&p->exits);
4106}
4088 4107
4089 flag_ptr = BC_PARSE_TOP_FLAG_PTR(p); 4108static BC_STATUS zbc_parse_else(BcParse *p)
4090 dbg_lex("%s:%d setting BC_PARSE_FLAG_IF_END bit", __func__, __LINE__); 4109{
4091 *flag_ptr = (*flag_ptr | BC_PARSE_FLAG_IF_END); 4110 BcStatus s;
4111 BcInstPtr ip;
4092 4112
4093 if (p->l.t.t == BC_LEX_KEY_ELSE) 4113 dbg_lex_enter("%s:%d entered", __func__, __LINE__);
4094 s = zbc_parse_else(p);
4095 }
4096 else if (BC_PARSE_ELSE(p)) {
4097 BcInstPtr *ip;
4098 size_t *label;
4099 4114
4100 ip = bc_vec_top(&p->exits); 4115 ip.idx = p->func->labels.len;
4101 label = bc_vec_item(&p->func->labels, ip->idx); 4116 ip.func = ip.len = 0;
4102 dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len);
4103 *label = p->func->code.len;
4104 4117
4105 bc_vec_pop(&p->exits); 4118 dbg_lex("%s:%d after if() body: BC_INST_JUMP to %d", __func__, __LINE__, ip.idx);
4106 bc_parse_pop_block_flag(p); 4119 bc_parse_push(p, BC_INST_JUMP);
4107 } 4120 bc_parse_pushIndex(p, ip.idx);
4108 else if (BC_PARSE_FUNC_INNER(p)) {
4109 bc_parse_push(p, BC_INST_RET0);
4110 bc_parse_updateFunc(p, BC_PROG_MAIN);
4111 bc_parse_pop_block_flag(p);
4112 }
4113 else {
4114 BcInstPtr *ip = bc_vec_top(&p->exits);
4115 size_t *label = bc_vec_top(&p->conds);
4116 4121
4117 dbg_lex("%s:%d BC_INST_JUMP to %d", __func__, __LINE__, *label); 4122 dbg_lex("%s:%d calling bc_parse_noElse()", __func__, __LINE__);
4118 bc_parse_push(p, BC_INST_JUMP); 4123 bc_parse_noElse(p);
4119 bc_parse_pushIndex(p, *label);
4120 4124
4121 label = bc_vec_item(&p->func->labels, ip->idx); 4125 bc_vec_push(&p->exits, &ip);
4122 dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len); 4126 bc_vec_push(&p->func->labels, &ip.idx);
4123 *label = p->func->code.len;
4124 4127
4125 bc_vec_pop(&p->exits); 4128 s = zbc_parse_stmt(p);
4126 bc_vec_pop(&p->conds); 4129 if (s) RETURN_STATUS(s);
4127 bc_parse_pop_block_flag(p);
4128 }
4129 4130
4131 dbg_lex_done("%s:%d done", __func__, __LINE__);
4130 RETURN_STATUS(s); 4132 RETURN_STATUS(s);
4131} 4133}
4132#if ERRORS_ARE_FATAL 4134#if ERRORS_ARE_FATAL
4133# define zbc_parse_endBody(...) (zbc_parse_endBody(__VA_ARGS__), BC_STATUS_SUCCESS) 4135# define zbc_parse_else(...) (zbc_parse_else(__VA_ARGS__), BC_STATUS_SUCCESS)
4134#endif 4136#endif
4135 4137
4136static void bc_parse_noElse(BcParse *p)
4137{
4138 BcInstPtr *ip;
4139 size_t *label;
4140 uint8_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
4141
4142 dbg_lex("%s:%d clearing BC_PARSE_FLAG_IF_END bit", __func__, __LINE__);
4143 *flag_ptr = (*flag_ptr & ~(BC_PARSE_FLAG_IF_END));
4144
4145 ip = bc_vec_top(&p->exits);
4146 label = bc_vec_item(&p->func->labels, ip->idx);
4147 dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len);
4148 *label = p->func->code.len;
4149
4150 bc_vec_pop(&p->exits);
4151}
4152
4153static BC_STATUS zbc_parse_if(BcParse *p) 4138static BC_STATUS zbc_parse_if(BcParse *p)
4154{ 4139{
4155 BcStatus s; 4140 BcStatus s;
4156 BcInstPtr ip; 4141 BcInstPtr ip;
4142 BcInstPtr *ipp;
4143 size_t *label;
4157 4144
4158 dbg_lex_enter("%s:%d entered", __func__, __LINE__); 4145 dbg_lex_enter("%s:%d entered", __func__, __LINE__);
4159 s = zbc_lex_next(&p->l); 4146 s = zbc_lex_next(&p->l);
@@ -4164,60 +4151,50 @@ static BC_STATUS zbc_parse_if(BcParse *p)
4164 if (s) RETURN_STATUS(s); 4151 if (s) RETURN_STATUS(s);
4165 s = zbc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel); 4152 s = zbc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel);
4166 if (s) RETURN_STATUS(s); 4153 if (s) RETURN_STATUS(s);
4167 if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
4168 4154
4155 if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
4169 s = zbc_lex_next(&p->l); 4156 s = zbc_lex_next(&p->l);
4170 if (s) RETURN_STATUS(s); 4157 if (s) RETURN_STATUS(s);
4171 bc_parse_push(p, BC_INST_JUMP_ZERO);
4172 4158
4159 bc_parse_push(p, BC_INST_JUMP_ZERO);
4173 ip.idx = p->func->labels.len; 4160 ip.idx = p->func->labels.len;
4174 ip.func = ip.len = 0; 4161 ip.func = ip.len = 0;
4175
4176 bc_parse_pushIndex(p, ip.idx); 4162 bc_parse_pushIndex(p, ip.idx);
4163//TODO: can get rid of p->exits stack?
4177 bc_vec_push(&p->exits, &ip); 4164 bc_vec_push(&p->exits, &ip);
4178 bc_vec_push(&p->func->labels, &ip.idx); 4165 bc_vec_push(&p->func->labels, &ip.idx);
4179 bc_parse_push_block_flag(p, BC_PARSE_FLAG_IF);
4180 4166
4181 dbg_lex_done("%s:%d done", __func__, __LINE__); 4167 s = zbc_parse_stmt(p);
4182 RETURN_STATUS(BC_STATUS_SUCCESS); 4168 if (s) RETURN_STATUS(s);
4183} 4169 dbg_lex("%s:%d in if after stmt: p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
4184#if ERRORS_ARE_FATAL 4170 if (p->l.t.t == BC_LEX_KEY_ELSE) {
4185# define zbc_parse_if(...) (zbc_parse_if(__VA_ARGS__), BC_STATUS_SUCCESS) 4171 s = zbc_lex_next(&p->l);
4186#endif 4172 if (s) RETURN_STATUS(s);
4187 4173 dbg_lex("%s:%d calling zbc_parse_else(), p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
4188#undef zbc_parse_else 4174 s = zbc_parse_else(p);
4189static BC_STATUS zbc_parse_else(BcParse *p) 4175 }
4190{
4191 BcInstPtr ip;
4192
4193 dbg_lex_enter("%s:%d entered", __func__, __LINE__);
4194 if (!BC_PARSE_IF_END(p)) RETURN_STATUS(bc_error_bad_token());
4195
4196 ip.idx = p->func->labels.len;
4197 ip.func = ip.len = 0;
4198
4199 dbg_lex("%s:%d after if() body: BC_INST_JUMP to %d", __func__, __LINE__, ip.idx);
4200 bc_parse_push(p, BC_INST_JUMP);
4201 bc_parse_pushIndex(p, ip.idx);
4202 4176
4203 dbg_lex("%s:%d calling bc_parse_noElse()", __func__, __LINE__); 4177 ipp = bc_vec_top(&p->exits);
4204 bc_parse_noElse(p); 4178 label = bc_vec_item(&p->func->labels, ipp->idx);
4179 dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len);
4180 *label = p->func->code.len;
4205 4181
4206 bc_vec_push(&p->exits, &ip); 4182 bc_vec_pop(&p->exits);
4207 bc_vec_push(&p->func->labels, &ip.idx);
4208 bc_parse_push_block_flag(p, BC_PARSE_FLAG_ELSE);
4209 4183
4210 dbg_lex_done("%s:%d done", __func__, __LINE__); 4184 dbg_lex_done("%s:%d done", __func__, __LINE__);
4211 RETURN_STATUS(zbc_lex_next(&p->l)); 4185 RETURN_STATUS(s);
4212} 4186}
4213#if ERRORS_ARE_FATAL 4187#if ERRORS_ARE_FATAL
4214# define zbc_parse_else(...) (zbc_parse_else(__VA_ARGS__), BC_STATUS_SUCCESS) 4188# define zbc_parse_if(...) (zbc_parse_if(__VA_ARGS__), BC_STATUS_SUCCESS)
4215#endif 4189#endif
4216 4190
4217static BC_STATUS zbc_parse_while(BcParse *p) 4191static BC_STATUS zbc_parse_while(BcParse *p)
4218{ 4192{
4219 BcStatus s; 4193 BcStatus s;
4220 BcInstPtr ip; 4194 BcInstPtr ip;
4195 BcInstPtr *ipp;
4196 size_t *label;
4197 size_t n;
4221 4198
4222 s = zbc_lex_next(&p->l); 4199 s = zbc_lex_next(&p->l);
4223 if (s) RETURN_STATUS(s); 4200 if (s) RETURN_STATUS(s);
@@ -4245,9 +4222,29 @@ static BC_STATUS zbc_parse_while(BcParse *p)
4245 4222
4246 bc_parse_push(p, BC_INST_JUMP_ZERO); 4223 bc_parse_push(p, BC_INST_JUMP_ZERO);
4247 bc_parse_pushIndex(p, ip.idx); 4224 bc_parse_pushIndex(p, ip.idx);
4248 bc_parse_push_block_flag(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER);
4249 4225
4250 RETURN_STATUS(BC_STATUS_SUCCESS); 4226 s = zbc_parse_stmt(p);
4227 if (s) RETURN_STATUS(s);
4228
4229 n = *((size_t *) bc_vec_top(&p->conds));
4230 bc_parse_push(p, BC_INST_JUMP);
4231 bc_parse_pushIndex(p, n);
4232
4233 ipp = bc_vec_top(&p->exits);
4234 label = bc_vec_top(&p->conds);
4235
4236 dbg_lex("%s:%d BC_INST_JUMP to %d", __func__, __LINE__, *label);
4237 bc_parse_push(p, BC_INST_JUMP);
4238 bc_parse_pushIndex(p, *label);
4239
4240 label = bc_vec_item(&p->func->labels, ipp->idx);
4241 dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len);
4242 *label = p->func->code.len;
4243
4244 bc_vec_pop(&p->exits);
4245 bc_vec_pop(&p->conds);
4246
4247 RETURN_STATUS(s);
4251} 4248}
4252#if ERRORS_ARE_FATAL 4249#if ERRORS_ARE_FATAL
4253# define zbc_parse_while(...) (zbc_parse_while(__VA_ARGS__), BC_STATUS_SUCCESS) 4250# define zbc_parse_while(...) (zbc_parse_while(__VA_ARGS__), BC_STATUS_SUCCESS)
@@ -4257,7 +4254,10 @@ static BC_STATUS zbc_parse_for(BcParse *p)
4257{ 4254{
4258 BcStatus s; 4255 BcStatus s;
4259 BcInstPtr ip; 4256 BcInstPtr ip;
4257 BcInstPtr *ipp;
4258 size_t *label;
4260 size_t cond_idx, exit_idx, body_idx, update_idx; 4259 size_t cond_idx, exit_idx, body_idx, update_idx;
4260 size_t n;
4261 4261
4262 dbg_lex("%s:%d p->l.t.t:%d", __func__, __LINE__, p->l.t.t); 4262 dbg_lex("%s:%d p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
4263 s = zbc_lex_next(&p->l); 4263 s = zbc_lex_next(&p->l);
@@ -4324,7 +4324,28 @@ static BC_STATUS zbc_parse_for(BcParse *p)
4324 bc_vec_push(&p->func->labels, &ip.idx); 4324 bc_vec_push(&p->func->labels, &ip.idx);
4325 s = zbc_lex_next(&p->l); 4325 s = zbc_lex_next(&p->l);
4326 if (s) RETURN_STATUS(s); 4326 if (s) RETURN_STATUS(s);
4327 bc_parse_push_block_flag(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER); 4327
4328 s = zbc_parse_stmt(p);
4329 if (s) RETURN_STATUS(s);
4330
4331 n = *((size_t *) bc_vec_top(&p->conds));
4332 bc_parse_push(p, BC_INST_JUMP);
4333 bc_parse_pushIndex(p, n);
4334
4335 ipp = bc_vec_top(&p->exits);
4336 label = bc_vec_top(&p->conds);
4337
4338//TODO: commonalize?
4339 dbg_lex("%s:%d BC_INST_JUMP to %d", __func__, __LINE__, *label);
4340 bc_parse_push(p, BC_INST_JUMP);
4341 bc_parse_pushIndex(p, *label);
4342
4343 label = bc_vec_item(&p->func->labels, ipp->idx);
4344 dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len);
4345 *label = p->func->code.len;
4346
4347 bc_vec_pop(&p->exits);
4348 bc_vec_pop(&p->conds);
4328 4349
4329 RETURN_STATUS(BC_STATUS_SUCCESS); 4350 RETURN_STATUS(BC_STATUS_SUCCESS);
4330} 4351}
@@ -4332,14 +4353,12 @@ static BC_STATUS zbc_parse_for(BcParse *p)
4332# define zbc_parse_for(...) (zbc_parse_for(__VA_ARGS__), BC_STATUS_SUCCESS) 4353# define zbc_parse_for(...) (zbc_parse_for(__VA_ARGS__), BC_STATUS_SUCCESS)
4333#endif 4354#endif
4334 4355
4335static BC_STATUS zbc_parse_loopExit(BcParse *p, BcLexType type) 4356static BC_STATUS zbc_parse_break_or_continue(BcParse *p, BcLexType type)
4336{ 4357{
4337 BcStatus s; 4358 BcStatus s;
4338 size_t i; 4359 size_t i;
4339 BcInstPtr *ip; 4360 BcInstPtr *ip;
4340 4361
4341 if (!BC_PARSE_LOOP(p)) RETURN_STATUS(bc_error_bad_token());
4342
4343 if (type == BC_LEX_KEY_BREAK) { 4362 if (type == BC_LEX_KEY_BREAK) {
4344 if (p->exits.len == 0) RETURN_STATUS(bc_error_bad_token()); 4363 if (p->exits.len == 0) RETURN_STATUS(bc_error_bad_token());
4345 4364
@@ -4368,14 +4387,13 @@ static BC_STATUS zbc_parse_loopExit(BcParse *p, BcLexType type)
4368 RETURN_STATUS(zbc_lex_next(&p->l)); 4387 RETURN_STATUS(zbc_lex_next(&p->l));
4369} 4388}
4370#if ERRORS_ARE_FATAL 4389#if ERRORS_ARE_FATAL
4371# define zbc_parse_loopExit(...) (zbc_parse_loopExit(__VA_ARGS__), BC_STATUS_SUCCESS) 4390# define zbc_parse_break_or_continue(...) (zbc_parse_break_or_continue(__VA_ARGS__), BC_STATUS_SUCCESS)
4372#endif 4391#endif
4373 4392
4374static BC_STATUS zbc_parse_func(BcParse *p) 4393static BC_STATUS zbc_parse_func(BcParse *p)
4375{ 4394{
4376 BcStatus s; 4395 BcStatus s;
4377 bool var, comma = false; 4396 bool var, comma = false;
4378 uint8_t flags;
4379 char *name; 4397 char *name;
4380 4398
4381 s = zbc_lex_next(&p->l); 4399 s = zbc_lex_next(&p->l);
@@ -4430,15 +4448,18 @@ static BC_STATUS zbc_parse_func(BcParse *p)
4430 4448
4431 if (comma) RETURN_STATUS(bc_error("bad function definition")); 4449 if (comma) RETURN_STATUS(bc_error("bad function definition"));
4432 4450
4433 flags = BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_FUNC_INNER | BC_PARSE_FLAG_BODY;
4434 bc_parse_push_block_flag(p, flags);
4435
4436 s = zbc_lex_next(&p->l); 4451 s = zbc_lex_next(&p->l);
4437 if (s) RETURN_STATUS(s); 4452 if (s) RETURN_STATUS(s);
4438 4453
4439 if (p->l.t.t != BC_LEX_LBRACE) 4454 if (p->l.t.t != BC_LEX_LBRACE)
4440 s = bc_POSIX_requires("the left brace be on the same line as the function header"); 4455 s = bc_POSIX_requires("the left brace be on the same line as the function header");
4441 4456
4457 s = zbc_parse_stmt_possibly_auto(p, true);
4458 if (s) RETURN_STATUS(s);
4459
4460 bc_parse_push(p, BC_INST_RET0);
4461 bc_parse_updateFunc(p, BC_PROG_MAIN);
4462
4442 RETURN_STATUS(s); 4463 RETURN_STATUS(s);
4443 4464
4444err: 4465err:
@@ -4455,12 +4476,11 @@ static BC_STATUS zbc_parse_auto(BcParse *p)
4455 bool comma, var, one; 4476 bool comma, var, one;
4456 char *name; 4477 char *name;
4457 4478
4458 if (!p->auto_part) RETURN_STATUS(bc_error_bad_token()); 4479 dbg_lex_enter("%s:%d entered", __func__, __LINE__);
4459
4460 s = zbc_lex_next(&p->l); 4480 s = zbc_lex_next(&p->l);
4461 if (s) RETURN_STATUS(s); 4481 if (s) RETURN_STATUS(s);
4462 4482
4463 p->auto_part = comma = false; 4483 comma = false;
4464 one = p->l.t.t == BC_LEX_NAME; 4484 one = p->l.t.t == BC_LEX_NAME;
4465 4485
4466 while (p->l.t.t == BC_LEX_NAME) { 4486 while (p->l.t.t == BC_LEX_NAME) {
@@ -4498,91 +4518,53 @@ static BC_STATUS zbc_parse_auto(BcParse *p)
4498 if (p->l.t.t != BC_LEX_NLINE && p->l.t.t != BC_LEX_SCOLON) 4518 if (p->l.t.t != BC_LEX_NLINE && p->l.t.t != BC_LEX_SCOLON)
4499 RETURN_STATUS(bc_error_bad_token()); 4519 RETURN_STATUS(bc_error_bad_token());
4500 4520
4521 dbg_lex_done("%s:%d done", __func__, __LINE__);
4501 RETURN_STATUS(zbc_lex_next(&p->l)); 4522 RETURN_STATUS(zbc_lex_next(&p->l));
4502 4523
4503err: 4524err:
4504 free(name); 4525 free(name);
4526 dbg_lex_done("%s:%d done (ERROR)", __func__, __LINE__);
4505 RETURN_STATUS(s); 4527 RETURN_STATUS(s);
4506} 4528}
4507#if ERRORS_ARE_FATAL 4529#if ERRORS_ARE_FATAL
4508# define zbc_parse_auto(...) (zbc_parse_auto(__VA_ARGS__), BC_STATUS_SUCCESS) 4530# define zbc_parse_auto(...) (zbc_parse_auto(__VA_ARGS__), BC_STATUS_SUCCESS)
4509#endif 4531#endif
4510 4532
4511static BC_STATUS zbc_parse_body(BcParse *p, bool brace) 4533#undef zbc_parse_stmt_possibly_auto
4534static BC_STATUS zbc_parse_stmt_possibly_auto(BcParse *p, bool auto_allowed)
4512{ 4535{
4513 BcStatus s = BC_STATUS_SUCCESS; 4536 BcStatus s = BC_STATUS_SUCCESS;
4514 uint8_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
4515
4516 dbg_lex_enter("%s:%d entered", __func__, __LINE__);
4517 *flag_ptr &= ~(BC_PARSE_FLAG_BODY);
4518 4537
4519 if (*flag_ptr & BC_PARSE_FLAG_FUNC_INNER) { 4538 dbg_lex_enter("%s:%d entered, p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
4520 dbg_lex("%s:%d BC_PARSE_FLAG_FUNC_INNER", __func__, __LINE__);
4521 if (!brace) RETURN_STATUS(bc_error_bad_token());
4522
4523 p->auto_part = p->l.t.t != BC_LEX_KEY_AUTO;
4524
4525 if (!p->auto_part) {
4526 s = zbc_parse_auto(p);
4527 if (s) RETURN_STATUS(s);
4528 }
4529 4539
4530 if (p->l.t.t == BC_LEX_NLINE) s = zbc_lex_next(&p->l); 4540 if (p->l.t.t == BC_LEX_NLINE) {
4541 dbg_lex_done("%s:%d done (seen BC_LEX_NLINE)", __func__, __LINE__);
4542 RETURN_STATUS(zbc_lex_next(&p->l));
4531 } 4543 }
4532 else { 4544 if (p->l.t.t == BC_LEX_SCOLON) {
4533 dbg_lex("%s:%d !BC_PARSE_FLAG_FUNC_INNER", __func__, __LINE__); 4545 dbg_lex_done("%s:%d done (seen BC_LEX_SCOLON)", __func__, __LINE__);
4534 s = zbc_parse_stmt(p); 4546 RETURN_STATUS(zbc_lex_next(&p->l));
4535 if (!s && !brace && !BC_PARSE_BODY(p)) s = zbc_parse_endBody(p);
4536 } 4547 }
4537 4548
4538 dbg_lex_done("%s:%d done", __func__, __LINE__); 4549 if (p->l.t.t == BC_LEX_LBRACE) {
4539 RETURN_STATUS(s); 4550 dbg_lex("%s:%d BC_LEX_LBRACE: (auto_allowed:%d)", __func__, __LINE__, auto_allowed);
4540} 4551 do {
4541#if ERRORS_ARE_FATAL
4542# define zbc_parse_body(...) (zbc_parse_body(__VA_ARGS__), BC_STATUS_SUCCESS)
4543#endif
4544
4545#undef zbc_parse_stmt
4546static BC_STATUS zbc_parse_stmt(BcParse *p)
4547{
4548 BcStatus s = BC_STATUS_SUCCESS;
4549
4550 dbg_lex_enter("%s:%d entered, p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
4551 switch (p->l.t.t) {
4552 case BC_LEX_NLINE:
4553 dbg_lex_done("%s:%d done (seen BC_LEX_NLINE)", __func__, __LINE__);
4554 RETURN_STATUS(zbc_lex_next(&p->l));
4555
4556 case BC_LEX_KEY_ELSE:
4557 dbg_lex("%s:%d BC_LEX_KEY_ELSE:", __func__, __LINE__);
4558 p->auto_part = false;
4559 break;
4560
4561 case BC_LEX_LBRACE:
4562 dbg_lex("%s:%d BC_LEX_LBRACE:", __func__, __LINE__);
4563 if (!BC_PARSE_BODY(p)) RETURN_STATUS(bc_error_bad_token());
4564 ++p->nbraces;
4565 s = zbc_lex_next(&p->l); 4552 s = zbc_lex_next(&p->l);
4566 if (s) RETURN_STATUS(s); 4553 if (s) RETURN_STATUS(s);
4567 dbg_lex_done("%s:%d done (returning zbc_parse_body())", __func__, __LINE__); 4554 } while (p->l.t.t == BC_LEX_NLINE);
4568 RETURN_STATUS(zbc_parse_body(p, true)); 4555 if (auto_allowed && p->l.t.t == BC_LEX_KEY_AUTO) {
4569 4556 dbg_lex("%s:%d calling zbc_parse_auto()", __func__, __LINE__);
4570 case BC_LEX_KEY_AUTO: 4557 s = zbc_parse_auto(p);
4571 dbg_lex("%s:%d BC_LEX_KEY_AUTO:", __func__, __LINE__); 4558 if (s) RETURN_STATUS(s);
4572 RETURN_STATUS(zbc_parse_auto(p)); 4559 }
4573 4560 while (p->l.t.t != BC_LEX_RBRACE) {
4574 default: 4561 dbg_lex("%s:%d block parsing loop", __func__, __LINE__);
4575 p->auto_part = false; 4562 s = zbc_parse_stmt(p);
4576 if (BC_PARSE_IF_END(p)) { 4563 if (s) RETURN_STATUS(s);
4577 bc_parse_noElse(p); 4564 }
4578 dbg_lex_done("%s:%d done (BC_PARSE_IF_END is true)", __func__, __LINE__); 4565 s = zbc_lex_next(&p->l);
4579 RETURN_STATUS(BC_STATUS_SUCCESS); 4566 dbg_lex_done("%s:%d done (seen BC_LEX_RBRACE)", __func__, __LINE__);
4580 } 4567 RETURN_STATUS(s);
4581 if (BC_PARSE_BODY(p)) {
4582 dbg_lex_done("%s:%d done (returning zbc_parse_body())", __func__, __LINE__);
4583 RETURN_STATUS(zbc_parse_body(p, false));
4584 }
4585 break;
4586 } 4568 }
4587 4569
4588 dbg_lex("%s:%d p->l.t.t:%d", __func__, __LINE__, p->l.t.t); 4570 dbg_lex("%s:%d p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
@@ -4603,26 +4585,12 @@ static BC_STATUS zbc_parse_stmt(BcParse *p)
4603 case BC_LEX_KEY_SQRT: 4585 case BC_LEX_KEY_SQRT:
4604 s = zbc_parse_expr(p, BC_PARSE_PRINT, bc_parse_next_expr); 4586 s = zbc_parse_expr(p, BC_PARSE_PRINT, bc_parse_next_expr);
4605 break; 4587 break;
4606 case BC_LEX_KEY_ELSE:
4607 s = zbc_parse_else(p);
4608 break;
4609 case BC_LEX_SCOLON:
4610 while (!s && p->l.t.t == BC_LEX_SCOLON) s = zbc_lex_next(&p->l);
4611 break;
4612 case BC_LEX_RBRACE:
4613 if (p->nbraces == 0)
4614 RETURN_STATUS(bc_error_bad_token());
4615 --p->nbraces;
4616 s = zbc_lex_next(&p->l);
4617 if (!s)
4618 s = zbc_parse_endBody(p);
4619 break;
4620 case BC_LEX_STR: 4588 case BC_LEX_STR:
4621 s = zbc_parse_string(p, BC_INST_PRINT_STR); 4589 s = zbc_parse_string(p, BC_INST_PRINT_STR);
4622 break; 4590 break;
4623 case BC_LEX_KEY_BREAK: 4591 case BC_LEX_KEY_BREAK:
4624 case BC_LEX_KEY_CONTINUE: 4592 case BC_LEX_KEY_CONTINUE:
4625 s = zbc_parse_loopExit(p, p->l.t.t); 4593 s = zbc_parse_break_or_continue(p, p->l.t.t);
4626 break; 4594 break;
4627 case BC_LEX_KEY_FOR: 4595 case BC_LEX_KEY_FOR:
4628 s = zbc_parse_for(p); 4596 s = zbc_parse_for(p);
@@ -4637,8 +4605,6 @@ static BC_STATUS zbc_parse_stmt(BcParse *p)
4637 case BC_LEX_KEY_LIMITS: 4605 case BC_LEX_KEY_LIMITS:
4638 // "limits" is a compile-time command, 4606 // "limits" is a compile-time command,
4639 // the output is produced at _parse time_. 4607 // the output is produced at _parse time_.
4640 s = zbc_lex_next(&p->l);
4641 if (s) RETURN_STATUS(s);
4642 printf( 4608 printf(
4643 "BC_BASE_MAX = "BC_MAX_OBASE_STR "\n" 4609 "BC_BASE_MAX = "BC_MAX_OBASE_STR "\n"
4644 "BC_DIM_MAX = "BC_MAX_DIM_STR "\n" 4610 "BC_DIM_MAX = "BC_MAX_DIM_STR "\n"
@@ -4649,6 +4615,7 @@ static BC_STATUS zbc_parse_stmt(BcParse *p)
4649 "MAX Exponent = "BC_MAX_EXP_STR "\n" 4615 "MAX Exponent = "BC_MAX_EXP_STR "\n"
4650 "Number of vars = "BC_MAX_VARS_STR "\n" 4616 "Number of vars = "BC_MAX_VARS_STR "\n"
4651 ); 4617 );
4618 s = zbc_lex_next(&p->l);
4652 break; 4619 break;
4653 case BC_LEX_KEY_PRINT: 4620 case BC_LEX_KEY_PRINT:
4654 s = zbc_parse_print(p); 4621 s = zbc_parse_print(p);
@@ -4669,11 +4636,16 @@ static BC_STATUS zbc_parse_stmt(BcParse *p)
4669 break; 4636 break;
4670 } 4637 }
4671 4638
4639 if (s || G_interrupt) {
4640 bc_parse_reset(p);
4641 s = BC_STATUS_FAILURE;
4642 }
4643
4672 dbg_lex_done("%s:%d done", __func__, __LINE__); 4644 dbg_lex_done("%s:%d done", __func__, __LINE__);
4673 RETURN_STATUS(s); 4645 RETURN_STATUS(s);
4674} 4646}
4675#if ERRORS_ARE_FATAL 4647#if ERRORS_ARE_FATAL
4676# define zbc_parse_stmt(...) (zbc_parse_stmt(__VA_ARGS__), BC_STATUS_SUCCESS) 4648# define zbc_parse_stmt_possibly_auto(...) (zbc_parse_stmt_possibly_auto(__VA_ARGS__), BC_STATUS_SUCCESS)
4677#endif 4649#endif
4678 4650
4679static BC_STATUS zbc_parse_stmt_or_funcdef(BcParse *p) 4651static BC_STATUS zbc_parse_stmt_or_funcdef(BcParse *p)
@@ -4682,22 +4654,15 @@ static BC_STATUS zbc_parse_stmt_or_funcdef(BcParse *p)
4682 4654
4683 dbg_lex_enter("%s:%d entered", __func__, __LINE__); 4655 dbg_lex_enter("%s:%d entered", __func__, __LINE__);
4684 if (p->l.t.t == BC_LEX_EOF) 4656 if (p->l.t.t == BC_LEX_EOF)
4685 s = BC_PARSE_FLAG_STACK_EMPTY(p) ? bc_error("end of file") : bc_error("block end could not be found"); 4657 s = bc_error("end of file");
4686 else if (p->l.t.t == BC_LEX_KEY_DEFINE) { 4658 else if (p->l.t.t == BC_LEX_KEY_DEFINE) {
4687 dbg_lex("%s:%d p->l.t.t:BC_LEX_KEY_DEFINE", __func__, __LINE__); 4659 dbg_lex("%s:%d p->l.t.t:BC_LEX_KEY_DEFINE", __func__, __LINE__);
4688 if (!BC_PARSE_CAN_EXEC(p))
4689 RETURN_STATUS(bc_error_bad_token());
4690 s = zbc_parse_func(p); 4660 s = zbc_parse_func(p);
4691 } else { 4661 } else {
4692 dbg_lex("%s:%d p->l.t.t:%d (not BC_LEX_KEY_DEFINE)", __func__, __LINE__, p->l.t.t); 4662 dbg_lex("%s:%d p->l.t.t:%d (not BC_LEX_KEY_DEFINE)", __func__, __LINE__, p->l.t.t);
4693 s = zbc_parse_stmt(p); 4663 s = zbc_parse_stmt(p);
4694 } 4664 }
4695 4665
4696 if (s || G_interrupt) {
4697 bc_parse_reset(p);
4698 s = BC_STATUS_FAILURE;
4699 }
4700
4701 dbg_lex_done("%s:%d done", __func__, __LINE__); 4666 dbg_lex_done("%s:%d done", __func__, __LINE__);
4702 RETURN_STATUS(s); 4667 RETURN_STATUS(s);
4703} 4668}
@@ -4940,6 +4905,7 @@ static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext ne
4940 if (prev == BC_INST_BOOL_NOT || nexprs != 1) 4905 if (prev == BC_INST_BOOL_NOT || nexprs != 1)
4941 return bc_error_bad_expression(); 4906 return bc_error_bad_expression();
4942 4907
4908//TODO: why is this needed at all?
4943 // next is BcParseNext, byte array of up to 4 BC_LEX's, packed into 32-bit word 4909 // next is BcParseNext, byte array of up to 4 BC_LEX's, packed into 32-bit word
4944 for (;;) { 4910 for (;;) {
4945 if (t == (next & 0x7f)) 4911 if (t == (next & 0x7f))
@@ -4948,7 +4914,8 @@ static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext ne
4948 break; 4914 break;
4949 next >>= 8; 4915 next >>= 8;
4950 } 4916 }
4951 return bc_error_bad_expression(); 4917 if (t != BC_LEX_KEY_ELSE)
4918 return bc_error_bad_expression();
4952 ok: 4919 ok:
4953 4920
4954 if (!(flags & BC_PARSE_REL) && nrelops) { 4921 if (!(flags & BC_PARSE_REL) && nrelops) {
@@ -6766,6 +6733,7 @@ static BC_STATUS zbc_program_exec(void)
6766 s = zbc_program_incdec(inst); 6733 s = zbc_program_incdec(inst);
6767 break; 6734 break;
6768 case BC_INST_HALT: 6735 case BC_INST_HALT:
6736 dbg_exec("BC_INST_HALT:");
6769 QUIT_OR_RETURN_TO_MAIN; 6737 QUIT_OR_RETURN_TO_MAIN;
6770 break; 6738 break;
6771 case BC_INST_RET: 6739 case BC_INST_RET:
@@ -6939,6 +6907,7 @@ static BC_STATUS zbc_program_exec(void)
6939 break; 6907 break;
6940 } 6908 }
6941 case BC_INST_QUIT: 6909 case BC_INST_QUIT:
6910 dbg_exec("BC_INST_NEG:");
6942 if (G.prog.stack.len <= 2) 6911 if (G.prog.stack.len <= 2)
6943 QUIT_OR_RETURN_TO_MAIN; 6912 QUIT_OR_RETURN_TO_MAIN;
6944 bc_vec_npop(&G.prog.stack, 2); 6913 bc_vec_npop(&G.prog.stack, 2);
@@ -6994,14 +6963,12 @@ static BC_STATUS zbc_vm_process(const char *text)
6994 dbg_lex("%s:%d G.prs.l.t.t:%d", __func__, __LINE__, G.prs.l.t.t); 6963 dbg_lex("%s:%d G.prs.l.t.t:%d", __func__, __LINE__, G.prs.l.t.t);
6995 ERROR_RETURN(s =) zcommon_parse(&G.prs); 6964 ERROR_RETURN(s =) zcommon_parse(&G.prs);
6996 if (s) RETURN_STATUS(s); 6965 if (s) RETURN_STATUS(s);
6997 }
6998 dbg_lex("%s:%d G.prs.l.t.t:BC_LEX_EOF", __func__, __LINE__);
6999
7000 if (BC_PARSE_CAN_EXEC(&G.prs)) {
7001 s = zbc_program_exec(); 6966 s = zbc_program_exec();
7002 fflush_and_check(); 6967 if (s) {
7003 if (s)
7004 bc_program_reset(); 6968 bc_program_reset();
6969 break;
6970 }
6971 fflush_and_check();
7005 } 6972 }
7006 6973
7007 dbg_lex_done("%s:%d done", __func__, __LINE__); 6974 dbg_lex_done("%s:%d done", __func__, __LINE__);
@@ -7049,91 +7016,12 @@ err:
7049static BC_STATUS zbc_vm_stdin(void) 7016static BC_STATUS zbc_vm_stdin(void)
7050{ 7017{
7051 BcStatus s; 7018 BcStatus s;
7052 BcVec buffer;
7053 size_t str;
7054 bool comment;
7055 7019
7056 //G.prog.file = NULL; - already is 7020 //G.prog.file = NULL; - already is
7057 bc_lex_file(&G.prs.l); 7021 bc_lex_file(&G.prs.l);
7058 7022
7059 bc_char_vec_init(&buffer); 7023 G.use_stdin = 1;
7060 7024 s = zbc_vm_process("");
7061 // This loop is complex because the vm tries not to send any lines that end
7062 // with a backslash to the parser. The reason for that is because the parser
7063 // treats a backslash+newline combo as whitespace, per the bc spec. In that
7064 // case, and for strings and comments, the parser will expect more stuff.
7065 s = BC_STATUS_SUCCESS;
7066 comment = false;
7067 str = 0;
7068 for (;;) {
7069 size_t prevlen = buffer.len;
7070 char *string;
7071
7072 bc_read_line(&buffer);
7073 // No more input means EOF
7074 if (buffer.len <= prevlen + 1) // (we expect +1 for NUL byte)
7075 break;
7076
7077 string = buffer.v + prevlen;
7078 while (*string) {
7079 char c = *string;
7080 if (string == buffer.v || string[-1] != '\\') {
7081 if (IS_BC)
7082 str ^= (c == '"');
7083 else {
7084 if (c == ']')
7085 str -= 1;
7086 else if (c == '[')
7087 str += 1;
7088 }
7089 }
7090 string++;
7091 if (c == '/' && *string == '*') {
7092 comment = true;
7093 string++;
7094 continue;
7095 }
7096 if (c == '*' && *string == '/') {
7097 comment = false;
7098 string++;
7099 }
7100 }
7101 if (str || comment) {
7102 buffer.len--; // backstep over the trailing NUL byte
7103 continue;
7104 }
7105
7106 // Check for backslash+newline.
7107 // we do not check that last char is '\n' -
7108 // if it is not, then it's EOF, and looping back
7109 // to bc_read_line() will detect it:
7110 string -= 2;
7111 if (string >= buffer.v && *string == '\\') {
7112 buffer.len--;
7113 continue;
7114 }
7115
7116 s = zbc_vm_process(buffer.v);
7117 if (s) {
7118 if (ENABLE_FEATURE_CLEAN_UP && !G_ttyin) {
7119 // Debug config, non-interactive mode:
7120 // return all the way back to main.
7121 // Non-debug builds do not come here, they exit.
7122 break;
7123 }
7124 }
7125
7126 bc_vec_pop_all(&buffer);
7127 }
7128
7129 if (str) {
7130 s = bc_error("string end could not be found");
7131 }
7132 else if (comment) {
7133 s = bc_error("comment end could not be found");
7134 }
7135
7136 bc_vec_free(&buffer);
7137 RETURN_STATUS(s); 7025 RETURN_STATUS(s);
7138} 7026}
7139#if ERRORS_ARE_FATAL 7027#if ERRORS_ARE_FATAL
@@ -7412,9 +7300,6 @@ static BC_STATUS zbc_vm_exec(void)
7412 if (IS_BC || (option_mask32 & BC_FLAG_I)) 7300 if (IS_BC || (option_mask32 & BC_FLAG_I))
7413 s = zbc_vm_stdin(); 7301 s = zbc_vm_stdin();
7414 7302
7415 if (!s && !BC_PARSE_CAN_EXEC(&G.prs))
7416 s = zbc_vm_process("");
7417
7418 RETURN_STATUS(s); 7303 RETURN_STATUS(s);
7419} 7304}
7420#if ERRORS_ARE_FATAL 7305#if ERRORS_ARE_FATAL
@@ -7443,6 +7328,7 @@ static void bc_program_free(void)
7443 bc_num_free(&G.prog.last); 7328 bc_num_free(&G.prog.last);
7444 bc_num_free(&G.prog.zero); 7329 bc_num_free(&G.prog.zero);
7445 bc_num_free(&G.prog.one); 7330 bc_num_free(&G.prog.one);
7331 bc_vec_free(&G.stdin_buffer);
7446} 7332}
7447 7333
7448static void bc_vm_free(void) 7334static void bc_vm_free(void)
@@ -7506,6 +7392,8 @@ static void bc_program_init(void)
7506 bc_vec_init(&G.prog.results, sizeof(BcResult), bc_result_free); 7392 bc_vec_init(&G.prog.results, sizeof(BcResult), bc_result_free);
7507 bc_vec_init(&G.prog.stack, sizeof(BcInstPtr), NULL); 7393 bc_vec_init(&G.prog.stack, sizeof(BcInstPtr), NULL);
7508 bc_vec_push(&G.prog.stack, &ip); 7394 bc_vec_push(&G.prog.stack, &ip);
7395
7396 bc_char_vec_init(&G.stdin_buffer);
7509} 7397}
7510 7398
7511static int bc_vm_init(const char *env_len) 7399static int bc_vm_init(const char *env_len)
@@ -7562,6 +7450,7 @@ static BcStatus bc_vm_run(void)
7562# endif 7450# endif
7563 FREE_G(); 7451 FREE_G();
7564#endif 7452#endif
7453 dbg_exec("exiting with exitcode %d", st);
7565 return st; 7454 return st;
7566} 7455}
7567 7456
diff --git a/testsuite/bc.tests b/testsuite/bc.tests
index 79ece2669..86220ad19 100755
--- a/testsuite/bc.tests
+++ b/testsuite/bc.tests
@@ -46,6 +46,11 @@ testing "bc if 0 else if 1" \
46 "2\n9\n" \ 46 "2\n9\n" \
47 "" "if (0) 1 else if (1) 2; 9" 47 "" "if (0) 1 else if (1) 2; 9"
48 48
49testing "bc define auto" \
50 "bc" \
51 "8\n9\n" \
52 "" "define w() { auto z; return 8; }; w(); 9"
53
49tar xJf bc_large.tar.xz 54tar xJf bc_large.tar.xz
50 55
51for f in bc*.bc; do 56for f in bc*.bc; do
diff --git a/testsuite/bc_misc2.bc b/testsuite/bc_misc2.bc
index f5a6a6b13..44fc40fa1 100644
--- a/testsuite/bc_misc2.bc
+++ b/testsuite/bc_misc2.bc
@@ -41,5 +41,4 @@ define u() {
41 41
42u() 42u()
43 43
44if (x == -4) x 44if (x == -4) x else x - 4
45else x - 4