dc: unbreak interactive mode - was trying to get next tokens instead of executing
authorDenys Vlasenko <vda.linux@googlemail.com>
Sat, 22 Dec 2018 17:04:08 +0000 (18:04 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sat, 22 Dec 2018 17:10:50 +0000 (18:10 +0100)
function                                             old     new   delta
zbc_program_read                                       -     268    +268
zdc_program_printStream                                -     146    +146
zbc_program_exec                                    4046    4182    +136
zdc_program_execStr                                  472     512     +40
zdc_parse_exprs_until_eof                              -      26     +26
zbc_vm_process                                       740     765     +25
zbc_lex_next                                        2225    2240     +15
zdc_parse_expr                                       569     535     -34
zbc_program_pushArray                                147       -    -147
zdc_program_asciify                                  370       -    -370
------------------------------------------------------------------------------
(add/remove: 3/2 grow/shrink: 4/1 up/down: 656/-551)          Total: 105 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
miscutils/bc.c
testsuite/dc.tests

index 5d5449efa4a1628cf33d9874d57f911cebaa7a71..d8fbb6ed7b5ace7247bb75b77db467e2ca9e277b 100644 (file)
@@ -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);
+                       }
                }
        }
 
index 8edfa07ca4a86fb3799ed9bd06c34e5138677d4b..cb2dcbc232e6ef197fd76e29b2ada5224816593e 100755 (executable)
@@ -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