[git commit] bc: upstream fixes

Denys Vlasenko vda.linux at googlemail.com
Tue Jan 1 20:50:14 UTC 2019


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

function                                             old     new   delta
bc_parse_expr_empty_ok                              1764    1843     +79
bc_error_at                                            -      62     +62
bc_parse_inst_isLeaf                                   -      30     +30
zbc_func_insert                                      100     120     +20
bc_error_bad_function_definition                       -      10     +10
bc_error_bad_assignment                                -      10     +10
zxc_lex_next                                        1608    1614      +6
ok_in_expr                                            30       -     -30
zxc_vm_process                                       874     839     -35
------------------------------------------------------------------------------
(add/remove: 4/1 grow/shrink: 3/1 up/down: 217/-65)           Total: 152 bytes

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 miscutils/bc.c     | 177 ++++++++++++++++++++++++++++-------------------------
 testsuite/bc.tests |  16 +++++
 2 files changed, 110 insertions(+), 83 deletions(-)

diff --git a/miscutils/bc.c b/miscutils/bc.c
index 23b3521d4..1b9cdce5e 100644
--- a/miscutils/bc.c
+++ b/miscutils/bc.c
@@ -971,19 +971,42 @@ static ERRORFUNC int bc_error(const char *msg)
 {
 	IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt("%s", msg);
 }
+static ERRORFUNC int bc_error_at(const char *msg)
+{
+	const char *err_at = G.prs.lex_next_at;
+	if (err_at) {
+		IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt(
+			"%s at '%.*s'",
+			msg,
+			(int)(strchrnul(err_at, '\n') - err_at),
+			err_at
+		);
+	}
+	IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt("%s", msg);
+}
 static ERRORFUNC int bc_error_bad_character(char c)
 {
 	if (!c)
 		IF_ERROR_RETURN_POSSIBLE(return) bc_error("NUL character");
 	IF_ERROR_RETURN_POSSIBLE(return) bc_error_fmt("bad character '%c'", c);
 }
+static ERRORFUNC int bc_error_bad_function_definition(void)
+{
+	IF_ERROR_RETURN_POSSIBLE(return) bc_error_at("bad function definition");
+}
 static ERRORFUNC int bc_error_bad_expression(void)
 {
-	IF_ERROR_RETURN_POSSIBLE(return) bc_error("bad expression");
+	IF_ERROR_RETURN_POSSIBLE(return) bc_error_at("bad expression");
+}
+static ERRORFUNC int bc_error_bad_assignment(void)
+{
+	IF_ERROR_RETURN_POSSIBLE(return) bc_error_at(
+		"bad assignment: left side must be variable or array element"
+	);
 }
 static ERRORFUNC int bc_error_bad_token(void)
 {
-	IF_ERROR_RETURN_POSSIBLE(return) bc_error("bad token");
+	IF_ERROR_RETURN_POSSIBLE(return) bc_error_at("bad token");
 }
 static ERRORFUNC int bc_error_stack_has_too_few_elements(void)
 {
@@ -2853,6 +2876,7 @@ static BC_STATUS zxc_lex_number(char last)
 		if (c == '\\' && p->lex_inbuf[1] == '\n') {
 			p->lex_inbuf += 2;
 			p->lex_line++;
+			dbg_lex("++p->lex_line=%zd", p->lex_line);
 			c = peek_inbuf(); // force next line to be read
 			goto check_c;
 		}
@@ -2919,6 +2943,7 @@ static BC_STATUS zxc_lex_next(void)
 	BcParse *p = &G.prs;
 	BcStatus s;
 
+	G.err_line = p->lex_line;
 	p->lex_last = p->lex;
 //why?
 //	if (p->lex_last == XC_LEX_EOF)
@@ -3031,8 +3056,10 @@ static BC_STATUS zbc_lex_string(void)
 		}
 		if (c == '"')
 			break;
-		if (c == '\n')
+		if (c == '\n') {
 			p->lex_line++;
+			dbg_lex("++p->lex_line=%zd", p->lex_line);
+		}
 		bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf);
 		p->lex_inbuf++;
 	}
@@ -3079,8 +3106,10 @@ static BC_STATUS zbc_lex_comment(void)
 		if (c == '\0') {
 			RETURN_STATUS(bc_error("unterminated comment"));
 		}
-		if (c == '\n')
+		if (c == '\n') {
 			p->lex_line++;
+			dbg_lex("++p->lex_line=%zd", p->lex_line);
+		}
 	}
 	p->lex_inbuf++; // skip trailing '/'
 
@@ -3105,6 +3134,7 @@ static BC_STATUS zbc_lex_token(void)
 //		break;
 	case '\n':
 		p->lex_line++;
+		dbg_lex("++p->lex_line=%zd", p->lex_line);
 		p->lex = XC_LEX_NLINE;
 		break;
 	case '\t':
@@ -3341,8 +3371,10 @@ static BC_STATUS zdc_lex_string(void)
 		if (c == ']')
 			if (--depth == 0)
 				break;
-		if (c == '\n')
+		if (c == '\n') {
 			p->lex_line++;
+			dbg_lex("++p->lex_line=%zd", p->lex_line);
+		}
 		bc_vec_push(&p->lex_strnumbuf, p->lex_inbuf);
 		p->lex_inbuf++;
 	}
@@ -3399,6 +3431,7 @@ static BC_STATUS zdc_lex_token(void)
 		// IOW: typing "1p<enter>" should print "1" _at once_,
 		// not after some more input.
 		p->lex_line++;
+		dbg_lex("++p->lex_line=%zd", p->lex_line);
 		p->lex = XC_LEX_NLINE;
 		break;
 	case '\t':
@@ -3960,8 +3993,7 @@ static BC_STATUS zbc_parse_scale(BcInst *type, uint8_t flags)
 }
 #define zbc_parse_scale(...) (zbc_parse_scale(__VA_ARGS__) COMMA_SUCCESS)
 
-static BC_STATUS zbc_parse_incdec(BcInst *prev, bool *paren_expr,
-				size_t *nexprs, uint8_t flags)
+static BC_STATUS zbc_parse_incdec(BcInst *prev, size_t *nexs, uint8_t flags)
 {
 	BcParse *p = &G.prs;
 	BcStatus s;
@@ -3978,7 +4010,6 @@ static BC_STATUS zbc_parse_incdec(BcInst *prev, bool *paren_expr,
 		s = zxc_lex_next();
 	} else {
 		*prev = inst = BC_INST_INC_PRE + (p->lex != BC_LEX_OP_INC);
-		*paren_expr = true;
 
 		s = zxc_lex_next();
 		if (s) RETURN_STATUS(s);
@@ -3986,7 +4017,7 @@ static BC_STATUS zbc_parse_incdec(BcInst *prev, bool *paren_expr,
 
 		// Because we parse the next part of the expression
 		// right here, we need to increment this.
-		*nexprs = *nexprs + 1;
+		*nexs = *nexs + 1;
 
 		switch (type) {
 		case XC_LEX_NAME:
@@ -4018,36 +4049,27 @@ static BC_STATUS zbc_parse_incdec(BcInst *prev, bool *paren_expr,
 }
 #define zbc_parse_incdec(...) (zbc_parse_incdec(__VA_ARGS__) COMMA_SUCCESS)
 
-#if 0
-#define BC_PARSE_LEAF(p, rparen) \
-	((rparen) \
-	 || ((p) >= XC_INST_NUM && (p) <= XC_INST_SQRT) \
-	 || (p) == BC_INST_INC_POST \
-	 || (p) == BC_INST_DEC_POST \
-	)
-#else
-static int ok_in_expr(BcInst p)
+static int bc_parse_inst_isLeaf(BcInst p)
 {
 	return (p >= XC_INST_NUM && p <= XC_INST_SQRT)
 		|| p == BC_INST_INC_POST
 		|| p == BC_INST_DEC_POST
 		;
 }
-#define BC_PARSE_LEAF(p, rparen) ((rparen) || ok_in_expr(p))
-#endif
+#define BC_PARSE_LEAF(prev, bin_last, rparen) \
+	(!(bin_last) && ((rparen) || bc_parse_inst_isLeaf(prev)))
 
 static BC_STATUS zbc_parse_minus(BcInst *prev, size_t ops_bgn,
-				bool rparen, size_t *nexprs)
+				bool rparen, bool bin_last, size_t *nexprs)
 {
 	BcParse *p = &G.prs;
 	BcStatus s;
 	BcLexType type;
-	BcInst etype = *prev;
 
 	s = zxc_lex_next();
 	if (s) RETURN_STATUS(s);
 
-	type = BC_PARSE_LEAF(etype, rparen) ? XC_LEX_OP_MINUS : XC_LEX_NEG;
+	type = BC_PARSE_LEAF(*prev, bin_last, rparen) ? XC_LEX_OP_MINUS : XC_LEX_NEG;
 	*prev = BC_TOKEN_2_INST(type);
 
 	// We can just push onto the op stack because this is the largest
@@ -4334,8 +4356,11 @@ static BC_STATUS zbc_func_insert(BcFunc *f, char *name, bool var)
 
 	autoid = (void*)f->autos.v;
 	for (i = 0; i < f->autos.len; i++, autoid++) {
-		if (strcmp(name, autoid->name) == 0)
+		if (strcmp(name, autoid->name) == 0
+		 && var == autoid->idx
+		) {
 			RETURN_STATUS(bc_error("duplicate function parameter or auto name"));
+		}
 	}
 
 	a.idx = var;
@@ -4358,7 +4383,7 @@ static BC_STATUS zbc_parse_funcdef(void)
 	s = zxc_lex_next();
 	if (s) RETURN_STATUS(s);
 	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);
 	p->fidx = bc_program_addFunc(name);
@@ -4367,13 +4392,13 @@ static BC_STATUS zbc_parse_funcdef(void)
 	s = zxc_lex_next();
 	if (s) RETURN_STATUS(s);
 	if (p->lex != BC_LEX_LPAREN)
-		RETURN_STATUS(bc_error("bad function definition"));
+		RETURN_STATUS(bc_error_bad_function_definition());
 	s = zxc_lex_next();
 	if (s) RETURN_STATUS(s);
 
 	while (p->lex != BC_LEX_RPAREN) {
 		if (p->lex != XC_LEX_NAME)
-			RETURN_STATUS(bc_error("bad function definition"));
+			RETURN_STATUS(bc_error_bad_function_definition());
 
 		++p->func->nparams;
 
@@ -4388,7 +4413,7 @@ static BC_STATUS zbc_parse_funcdef(void)
 			if (s) goto err;
 
 			if (p->lex != BC_LEX_RBRACKET) {
-				s = bc_error("bad function definition");
+				s = bc_error_bad_function_definition();
 				goto err;
 			}
 
@@ -4406,7 +4431,7 @@ static BC_STATUS zbc_parse_funcdef(void)
 		if (s) goto err;
 	}
 
-	if (comma) RETURN_STATUS(bc_error("bad function definition"));
+	if (comma) RETURN_STATUS(bc_error_bad_function_definition());
 
 	s = zxc_lex_next();
 	if (s) RETURN_STATUS(s);
@@ -4457,7 +4482,7 @@ static BC_STATUS zbc_parse_auto(void)
 		bool var;
 
 		if (p->lex != XC_LEX_NAME)
-			RETURN_STATUS(bc_error("bad 'auto' syntax"));
+			RETURN_STATUS(bc_error_at("bad 'auto' syntax"));
 
 		name = xstrdup(p->lex_strnumbuf.v);
 		s = zxc_lex_next();
@@ -4469,7 +4494,7 @@ static BC_STATUS zbc_parse_auto(void)
 			if (s) goto err;
 
 			if (p->lex != BC_LEX_RBRACKET) {
-				s = bc_error("bad 'auto' syntax");
+				s = bc_error_at("bad 'auto' syntax");
 				goto err;
 			}
 			s = zxc_lex_next();
@@ -4486,7 +4511,7 @@ static BC_STATUS zbc_parse_auto(void)
 			break;
 		}
 		if (p->lex != BC_LEX_COMMA)
-			RETURN_STATUS(bc_error("bad 'auto' syntax"));
+			RETURN_STATUS(bc_error_at("bad 'auto' syntax"));
 		s = zxc_lex_next(); // skip comma
 		if (s) RETURN_STATUS(s);
 	}
@@ -4643,12 +4668,12 @@ static BcStatus bc_parse_expr_empty_ok(uint8_t flags)
 	BcInst prev = XC_INST_PRINT;
 	size_t nexprs = 0, ops_bgn = p->ops.len;
 	unsigned nparens, nrelops;
-	bool paren_first, paren_expr, rprn, assign, bin_last;
+	bool paren_first, rprn, assign, bin_last, incdec;
 
 	dbg_lex_enter("%s:%d entered", __func__, __LINE__);
 	paren_first = (p->lex == BC_LEX_LPAREN);
 	nparens = nrelops = 0;
-	paren_expr = rprn = assign = false;
+	rprn = assign = incdec = false;
 	bin_last = true;
 
 	for (;;) {
@@ -4666,16 +4691,19 @@ static BcStatus bc_parse_expr_empty_ok(uint8_t flags)
 		case BC_LEX_OP_INC:
 		case BC_LEX_OP_DEC:
 			dbg_lex("%s:%d LEX_OP_INC/DEC", __func__, __LINE__);
-			s = zbc_parse_incdec(&prev, &paren_expr, &nexprs, flags);
+			if (incdec) return bc_error_bad_assignment();
+			s = zbc_parse_incdec(&prev, &nexprs, flags);
+			incdec = true;
 			rprn = bin_last = false;
 			//get_token = false; - already is
 			break;
 		case XC_LEX_OP_MINUS:
 			dbg_lex("%s:%d LEX_OP_MINUS", __func__, __LINE__);
-			s = zbc_parse_minus(&prev, ops_bgn, rprn, &nexprs);
+			s = zbc_parse_minus(&prev, ops_bgn, rprn, bin_last, &nexprs);
 			rprn = false;
 			//get_token = false; - already is
 			bin_last = (prev == XC_INST_MINUS);
+			if (bin_last) incdec = false;
 			break;
 		case BC_LEX_OP_ASSIGN_POWER:
 		case BC_LEX_OP_ASSIGN_MULTIPLY:
@@ -4689,10 +4717,7 @@ static BcStatus bc_parse_expr_empty_ok(uint8_t flags)
 			 && prev != XC_INST_SCALE && prev != XC_INST_IBASE
 			 && prev != XC_INST_OBASE && prev != BC_INST_LAST
 			) {
-				return bc_error("bad assignment:"
-					" left side must be variable"
-					" or array element"
-				); // note: shared string
+				return bc_error_bad_assignment();
 			}
 		// Fallthrough.
 		case XC_LEX_OP_POWER:
@@ -4710,64 +4735,63 @@ static BcStatus bc_parse_expr_empty_ok(uint8_t flags)
 		case BC_LEX_OP_BOOL_OR:
 		case BC_LEX_OP_BOOL_AND:
 			dbg_lex("%s:%d LEX_OP_xyz", __func__, __LINE__);
-			if (((t == BC_LEX_OP_BOOL_NOT) != bin_last)
-			 || (t != BC_LEX_OP_BOOL_NOT && prev == XC_INST_BOOL_NOT)
-			) {
+			if (t == BC_LEX_OP_BOOL_NOT) {
+				if (!bin_last && p->lex_last != BC_LEX_OP_BOOL_NOT)
+					return bc_error_bad_expression();
+			} else if (prev == XC_INST_BOOL_NOT) {
 				return bc_error_bad_expression();
 			}
+
 			nrelops += (t >= XC_LEX_OP_REL_EQ && t <= XC_LEX_OP_REL_GT);
 			prev = BC_TOKEN_2_INST(t);
 			bc_parse_operator(t, ops_bgn, &nexprs);
-			s = zxc_lex_next();
-			rprn = false;
-			//get_token = false; - already is
+			rprn = incdec = false;
+			get_token = true;
 			bin_last = (t != BC_LEX_OP_BOOL_NOT);
 			break;
 		case BC_LEX_LPAREN:
 			dbg_lex("%s:%d LEX_LPAREN", __func__, __LINE__);
-			if (BC_PARSE_LEAF(prev, rprn))
+			if (BC_PARSE_LEAF(prev, bin_last, rprn))
 				return bc_error_bad_expression();
 			bc_vec_push(&p->ops, &t);
 			nparens++;
 			get_token = true;
-			paren_expr = false;
-			rprn = bin_last = false;
+			rprn = incdec = false;
 			break;
 		case BC_LEX_RPAREN:
 			dbg_lex("%s:%d LEX_RPAREN", __func__, __LINE__);
+			if (p->lex_last == BC_LEX_LPAREN) {
+				dbg_lex_done("%s:%d done (returning EMPTY_EXP)", __func__, __LINE__);
+				return BC_STATUS_PARSE_EMPTY_EXP;
+			}
 			if (bin_last || prev == XC_INST_BOOL_NOT)
 				return bc_error_bad_expression();
 			if (nparens == 0) {
 				goto exit_loop;
 			}
-			if (!paren_expr) {
-				dbg_lex_done("%s:%d done (returning EMPTY_EXP)", __func__, __LINE__);
-				return BC_STATUS_PARSE_EMPTY_EXP;
-			}
 			s = zbc_parse_rightParen(ops_bgn, &nexprs);
 			nparens--;
 			get_token = true;
-			paren_expr = rprn = true;
-			bin_last = false;
+			rprn = true;
+			bin_last = incdec = false;
 			break;
 		case XC_LEX_NAME:
 			dbg_lex("%s:%d LEX_NAME", __func__, __LINE__);
-			if (BC_PARSE_LEAF(prev, rprn))
+			if (BC_PARSE_LEAF(prev, bin_last, rprn))
 				return bc_error_bad_expression();
 			s = zbc_parse_name(&prev, flags & ~BC_PARSE_NOCALL);
-			paren_expr = true;
-			rprn = bin_last = false;
+			rprn = (prev == BC_INST_CALL);
+			bin_last = false;
 			//get_token = false; - already is
 			nexprs++;
 			break;
 		case XC_LEX_NUMBER:
 			dbg_lex("%s:%d LEX_NUMBER", __func__, __LINE__);
-			if (BC_PARSE_LEAF(prev, rprn))
+			if (BC_PARSE_LEAF(prev, bin_last, rprn))
 				return bc_error_bad_expression();
 			xc_parse_pushNUM();
 			prev = XC_INST_NUM;
 			get_token = true;
-			paren_expr = true;
 			rprn = bin_last = false;
 			nexprs++;
 			break;
@@ -4775,45 +4799,40 @@ static BcStatus bc_parse_expr_empty_ok(uint8_t flags)
 		case BC_LEX_KEY_LAST:
 		case BC_LEX_KEY_OBASE:
 			dbg_lex("%s:%d LEX_IBASE/LAST/OBASE", __func__, __LINE__);
-			if (BC_PARSE_LEAF(prev, rprn))
+			if (BC_PARSE_LEAF(prev, bin_last, rprn))
 				return bc_error_bad_expression();
 			prev = (char) (t - BC_LEX_KEY_IBASE + XC_INST_IBASE);
 			xc_parse_push((char) prev);
 			get_token = true;
-			paren_expr = true;
 			rprn = bin_last = false;
 			nexprs++;
 			break;
 		case BC_LEX_KEY_LENGTH:
 		case BC_LEX_KEY_SQRT:
 			dbg_lex("%s:%d LEX_LEN/SQRT", __func__, __LINE__);
-			if (BC_PARSE_LEAF(prev, rprn))
+			if (BC_PARSE_LEAF(prev, bin_last, rprn))
 				return bc_error_bad_expression();
 			s = zbc_parse_builtin(t, flags, &prev);
 			get_token = true;
-			paren_expr = true;
-			rprn = bin_last = false;
+			rprn = bin_last = incdec = false;
 			nexprs++;
 			break;
 		case BC_LEX_KEY_READ:
 			dbg_lex("%s:%d LEX_READ", __func__, __LINE__);
-			if (BC_PARSE_LEAF(prev, rprn))
+			if (BC_PARSE_LEAF(prev, bin_last, rprn))
 				return bc_error_bad_expression();
 			s = zbc_parse_read();
 			prev = XC_INST_READ;
 			get_token = true;
-			paren_expr = true;
-			rprn = bin_last = false;
+			rprn = bin_last = incdec = false;
 			nexprs++;
 			break;
 		case BC_LEX_KEY_SCALE:
 			dbg_lex("%s:%d LEX_SCALE", __func__, __LINE__);
-			if (BC_PARSE_LEAF(prev, rprn))
+			if (BC_PARSE_LEAF(prev, bin_last, rprn))
 				return bc_error_bad_expression();
 			s = zbc_parse_scale(&prev, flags);
-			prev = XC_INST_SCALE;
 			//get_token = false; - already is
-			paren_expr = true;
 			rprn = bin_last = false;
 			nexprs++;
 			break;
@@ -5286,7 +5305,7 @@ static BC_STATUS zxc_program_read(void)
 	}
 	if (s) goto exec_err;
 	if (G.prs.lex != XC_LEX_NLINE && G.prs.lex != XC_LEX_EOF) {
-		s = bc_error("bad read() expression");
+		s = bc_error_at("bad read() expression");
 		goto exec_err;
 	}
 	xc_parse_push(XC_INST_RET);
@@ -5794,10 +5813,7 @@ static BC_STATUS zxc_program_assign(char inst)
 #endif
 
 	if (left->t == XC_RESULT_CONSTANT || left->t == XC_RESULT_TEMP)
-		RETURN_STATUS(bc_error("bad assignment:"
-				" left side must be variable"
-				" or array element"
-		)); // note: shared string
+		RETURN_STATUS(bc_error_bad_assignment());
 
 #if ENABLE_BC
 	if (inst == BC_INST_ASSIGN_DIVIDE && !bc_num_cmp(r, &G.prog.zero))
@@ -6771,13 +6787,7 @@ static BC_STATUS zxc_vm_process(const char *text)
 			 && G.prs.lex != XC_LEX_NLINE
 			 && G.prs.lex != XC_LEX_EOF
 			) {
-				const char *err_at;
-//TODO: commonalize for other parse errors:
-				err_at = G.prs.lex_next_at ? G.prs.lex_next_at : "UNKNOWN";
-				bc_error_fmt("bad statement terminator at '%.*s'",
-					(int)(strchrnul(err_at, '\n') - err_at),
-					err_at
-				);
+				bc_error_at("bad statement terminator");
 				goto err;
 			}
 			// The above logic is fragile. Check these examples:
@@ -6871,6 +6881,7 @@ static BC_STATUS zxc_vm_execute_FILE(FILE *fp, const char *filename)
 	G.prs.lex_filename = filename;
 	G.prs.lex_input_fp = fp;
 	G.err_line = G.prs.lex_line = 1;
+	dbg_lex("p->lex_line reset to 1");
 
 	do {
 		s = zxc_vm_process("");
diff --git a/testsuite/bc.tests b/testsuite/bc.tests
index 1d4545559..13525ea52 100755
--- a/testsuite/bc.tests
+++ b/testsuite/bc.tests
@@ -108,6 +108,11 @@ testing "bc define auto" \
 	"8\n9\n" \
 	"" "define w() { auto z; return 8; }; w(); 9"
 
+testing "bc define auto array same name" \
+	"bc" \
+	"8\n9\n" \
+	"" "define w(x) { auto x[]; return x; }; w(8); 9"
+
 testing "bc define with body on next line" \
 	"bc" \
 	"8\n9\n" \
@@ -133,6 +138,17 @@ testing "bc ifz does not match if keyword" \
 	"1\n2\n2\n3\n" \
 	"" "ifz=1;ifz\n++ifz;ifz++\nifz"
 
+# had parse error on "f()-N"
+testing "bc -l 'e(0)-2'" \
+	"bc -l" \
+	"-1.00000000000000000000\n" \
+	"" "e(0)-2"
+
+testing "bc (!a&&b)" \
+	"bc" \
+	"0\n" \
+	"" "(!a&&b)"
+
 testing "bc print 1,2,3" \
 	"bc" \
 	"123" \


More information about the busybox-cvs mailing list