diff options
-rw-r--r-- | miscutils/bc.c | 96 | ||||
-rwxr-xr-x | testsuite/dc.tests | 15 |
2 files changed, 81 insertions, 30 deletions
diff --git a/miscutils/bc.c b/miscutils/bc.c index 5d5449efa..d8fbb6ed7 100644 --- a/miscutils/bc.c +++ b/miscutils/bc.c @@ -3431,6 +3431,17 @@ static BC_STATUS zdc_lex_token(BcLex *l) // l->t.t = BC_LEX_EOF; // break; case '\n': + // '\n' is BC_LEX_NLINE, not BC_LEX_WHITESPACE + // (and "case '\n'" is not just empty here) + // only to allow interactive dc have a way to exit + // "parse" stage of "parse,execute" loop + // on '\n', not on _next_ token (which would mean + // command are not executed on pressing <enter>). + // IOW: typing "1p<enter>" should print "1" _at once_, + // not after some more input. + l->t.t = BC_LEX_NLINE; + l->newline = true; + break; case '\t': case '\v': case '\f': @@ -4861,12 +4872,16 @@ static BC_STATUS zdc_parse_cond(BcParse *p, uint8_t inst) s = zbc_lex_next(&p->l); if (s) RETURN_STATUS(s); + // Note that 'else' part can not be on the next line: + // echo -e '[1p]sa [2p]sb 2 1>a eb' | dc - OK, prints "2" + // echo -e '[1p]sa [2p]sb 2 1>a\neb' | dc - parse error if (p->l.t.t == BC_LEX_ELSE) { s = zdc_parse_register(p); if (s) RETURN_STATUS(s); s = zbc_lex_next(&p->l); - } else + } else { bc_parse_push(p, BC_PARSE_STREND); + } RETURN_STATUS(s); } @@ -4945,33 +4960,32 @@ static BC_STATUS zdc_parse_token(BcParse *p, BcLexType t) static BC_STATUS zdc_parse_expr(BcParse *p) { - BcLexType t; - - dbg_lex_enter("%s:%d entered, p->l.t.t:%d", __func__, __LINE__, p->l.t.t); - for (;;) { - BcInst inst; - BcStatus s; + BcInst inst; + BcStatus s; - t = p->l.t.t; - dbg_lex("%s:%d p->l.t.t:%d", __func__, __LINE__, p->l.t.t); - if (t == BC_LEX_EOF) break; + inst = dc_parse_insts[p->l.t.t]; + if (inst != BC_INST_INVALID) { + bc_parse_push(p, inst); + s = zbc_lex_next(&p->l); + } else { + s = zdc_parse_token(p, p->l.t.t); + } + RETURN_STATUS(s); +} +#define zdc_parse_expr(...) (zdc_parse_expr(__VA_ARGS__) COMMA_SUCCESS) - inst = dc_parse_insts[t]; - if (inst != BC_INST_INVALID) { - dbg_lex("%s:%d", __func__, __LINE__); - bc_parse_push(p, inst); - s = zbc_lex_next(&p->l); - } else { - dbg_lex("%s:%d", __func__, __LINE__); - s = zdc_parse_token(p, t); - } +static BC_STATUS zdc_parse_exprs_until_eof(BcParse *p) +{ + dbg_lex_enter("%s:%d entered, p->l.t.t:%d", __func__, __LINE__, p->l.t.t); + while (p->l.t.t != BC_LEX_EOF) { + BcStatus s = zdc_parse_expr(p); if (s) RETURN_STATUS(s); } dbg_lex_done("%s:%d done", __func__, __LINE__); RETURN_STATUS(BC_STATUS_SUCCESS); } -#define zdc_parse_expr(...) (zdc_parse_expr(__VA_ARGS__) COMMA_SUCCESS) +#define zdc_parse_exprs_until_eof(...) (zdc_parse_exprs_until_eof(__VA_ARGS__) COMMA_SUCCESS) #endif // ENABLE_DC @@ -5182,7 +5196,7 @@ static BC_STATUS zbc_program_read(void) if (IS_BC) { IF_BC(s = zbc_parse_expr(&parse, 0)); } else { - IF_DC(s = zdc_parse_expr(&parse)); + IF_DC(s = zdc_parse_exprs_until_eof(&parse)); } if (s) goto exec_err; @@ -6304,6 +6318,7 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond) f = bc_program_func(fidx); if (f->code.len == 0) { + FILE *sv_input_fp; BcParse prs; char *str; @@ -6311,7 +6326,12 @@ static BC_STATUS zdc_program_execStr(char *code, size_t *bgn, bool cond) str = *bc_program_str(sidx); s = zbc_parse_text_init(&prs, str); if (s) goto err; - s = zdc_parse_expr(&prs); + + sv_input_fp = G.input_fp; + G.input_fp = NULL; // "do not read from input file when <EOL> reached" + s = zdc_parse_exprs_until_eof(&prs); + G.input_fp = sv_input_fp; + if (s) goto err; if (prs.l.t.t != BC_LEX_EOF) { s = bc_error_bad_expression(); @@ -6439,12 +6459,15 @@ static BC_STATUS zbc_program_exec(void) s = zbc_program_pushArray(code, &ip->inst_idx, inst); break; case BC_INST_LAST: +//TODO: this can't happen on dc, right? + dbg_exec("BC_INST_LAST:"); r.t = BC_RESULT_LAST; bc_vec_push(&G.prog.results, &r); break; case BC_INST_IBASE: case BC_INST_SCALE: case BC_INST_OBASE: + dbg_exec("BC_INST_internalvar:"); bc_program_pushGlobal(inst); break; case BC_INST_SCALE_FUNC: @@ -6519,17 +6542,21 @@ static BC_STATUS zbc_program_exec(void) bc_vec_pop(&G.prog.exestack); goto read_updated_ip; case BC_INST_MODEXP: + dbg_exec("BC_INST_MODEXP:"); s = zdc_program_modexp(); break; case BC_INST_DIVMOD: + dbg_exec("BC_INST_DIVMOD:"); s = zdc_program_divmod(); break; case BC_INST_EXECUTE: case BC_INST_EXEC_COND: + dbg_exec("BC_INST_EXEC[_COND]:"); s = zdc_program_execStr(code, &ip->inst_idx, inst == BC_INST_EXEC_COND); goto read_updated_ip; case BC_INST_PRINT_STACK: { size_t idx; + dbg_exec("BC_INST_PRINT_STACK:"); for (idx = 0; idx < G.prog.results.len; ++idx) { s = zbc_program_print(BC_INST_PRINT, idx); if (s) break; @@ -6537,12 +6564,15 @@ static BC_STATUS zbc_program_exec(void) break; } case BC_INST_CLEAR_STACK: + dbg_exec("BC_INST_CLEAR_STACK:"); bc_vec_pop_all(&G.prog.results); break; case BC_INST_STACK_LEN: + dbg_exec("BC_INST_STACK_LEN:"); dc_program_stackLen(); break; case BC_INST_DUPLICATE: + dbg_exec("BC_INST_DUPLICATE:"); if (!STACK_HAS_MORE_THAN(&G.prog.results, 0)) RETURN_STATUS(bc_error_stack_has_too_few_elements()); ptr = bc_vec_top(&G.prog.results); @@ -6551,6 +6581,7 @@ static BC_STATUS zbc_program_exec(void) break; case BC_INST_SWAP: { BcResult *ptr2; + dbg_exec("BC_INST_SWAP:"); if (!STACK_HAS_MORE_THAN(&G.prog.results, 1)) RETURN_STATUS(bc_error_stack_has_too_few_elements()); ptr = bc_vec_item_rev(&G.prog.results, 0); @@ -6561,9 +6592,11 @@ static BC_STATUS zbc_program_exec(void) break; } case BC_INST_ASCIIFY: + dbg_exec("BC_INST_ASCIIFY:"); s = zdc_program_asciify(); break; case BC_INST_PRINT_STREAM: + dbg_exec("BC_INST_STREAM:"); s = zdc_program_printStream(); break; case BC_INST_LOAD: @@ -6585,6 +6618,7 @@ static BC_STATUS zbc_program_exec(void) bc_vec_npop(&G.prog.exestack, 2); goto read_updated_ip; case BC_INST_NQUIT: + dbg_exec("BC_INST_NQUIT:"); s = zdc_program_nquit(); //goto read_updated_ip; - just fall through to it #endif // ENABLE_DC @@ -6629,25 +6663,20 @@ static BC_STATUS zbc_vm_process(const char *text) BcStatus s; dbg_lex_enter("%s:%d entered", __func__, __LINE__); - s = zbc_parse_text_init(&G.prs, text); + s = zbc_parse_text_init(&G.prs, text); // does the first zbc_lex_next() if (s) RETURN_STATUS(s); while (G.prs.l.t.t != BC_LEX_EOF) { dbg_lex("%s:%d G.prs.l.t.t:%d, parsing...", __func__, __LINE__, G.prs.l.t.t); if (IS_BC) { -// FIXME: "eating" of stmt delemiters is coded inconsistently +// FIXME: "eating" of stmt delimiters is coded inconsistently // (sometimes zbc_parse_stmt() eats the delimiter, sometimes don't), // which causes bugs such as "print 1 print 2" erroneously accepted, // or "print 1 else 2" detecting parse error only after executing // "print 1" part. IF_BC(s = zbc_parse_stmt_or_funcdef(&G.prs)); } else { -#if ENABLE_DC - if (G.prs.l.t.t == BC_LEX_EOF) - s = bc_error("end of file"); - else - s = zdc_parse_expr(&G.prs); -#endif + IF_DC(s = zdc_parse_expr(&G.prs)); } if (s || G_interrupt) { bc_parse_reset(&G.prs); // includes bc_program_reset() @@ -6689,6 +6718,13 @@ static BC_STATUS zbc_vm_process(const char *text) ip->inst_idx = 0; IF_BC(bc_vec_pop_all(&f->strs);) IF_BC(bc_vec_pop_all(&f->consts);) + } else { + // Most of dc parsing assumes all whitespace, + // including '\n', is eaten. + if (G.prs.l.t.t == BC_LEX_NLINE) { + s = zbc_lex_next(&G.prs.l); + if (s) RETURN_STATUS(s); + } } } diff --git a/testsuite/dc.tests b/testsuite/dc.tests index 8edfa07ca..cb2dcbc23 100755 --- a/testsuite/dc.tests +++ b/testsuite/dc.tests @@ -41,6 +41,21 @@ testing "dc complex without spaces (multiple args)" \ "16\n" \ "" "" +testing "dc '>a' (conditional execute string) 1" \ + "dc" \ + "1\n9\n" \ + "" "[1p]sa [2p]sb 1 2>a\n9p" + +testing "dc '>a' (conditional execute string) 2" \ + "dc" \ + "9\n" \ + "" "[1p]sa [2p]sb 2 1>a\n9p" + +testing "dc '>aeb' (conditional execute string with else)" \ + "dc" \ + "2\n9\n" \ + "" "[1p]sa [2p]sb 2 1>aeb\n9p" + for f in dc_*.dc; do r="`basename "$f" .dc`_results.txt" test -f "$r" || continue |