[git commit] dc: unbreak interactive mode - was trying to get next tokens instead of executing

Denys Vlasenko vda.linux at googlemail.com
Sat Dec 22 17:10:50 UTC 2018


commit: https://git.busybox.net/busybox/commit/?id=badf683b0aa693de9b1a7c1634ad20665463c503
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

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 at googlemail.com>
---
 miscutils/bc.c     | 96 +++++++++++++++++++++++++++++++++++++-----------------
 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


More information about the busybox-cvs mailing list