aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 @@
#define DEBUG_EXEC 0
#if DEBUG_LEXER
-static unsigned lex_indent;
+static uint8_t lex_indent;
#define dbg_lex(...) \
do { \
fprintf(stderr, "%*s", lex_indent, ""); \
@@ -558,19 +558,16 @@ enum {
#endif
typedef struct BcLex {
-
const char *buf;
size_t i;
size_t line;
size_t len;
bool newline;
-
struct {
BcLexType t;
BcLexType last;
BcVec v;
} t;
-
} BcLex;
#define BC_PARSE_STREND ((char) UCHAR_MAX)
@@ -581,37 +578,9 @@ typedef struct BcLex {
#define BC_PARSE_NOREAD (1 << 3)
#define BC_PARSE_ARRAY (1 << 4)
-#define BC_PARSE_TOP_FLAG_PTR(parse) ((parse)->bf_top)
-#define BC_PARSE_TOP_FLAG(parse) (*(BC_PARSE_TOP_FLAG_PTR(parse)))
-#define BC_PARSE_FLAG_STACK_EMPTY(p) ((p)->bf_top == (p)->bf_base)
-
-#define BC_PARSE_FLAG_FUNC_INNER (1 << 0)
-#define BC_PARSE_FLAG_FUNC (1 << 1)
-#define BC_PARSE_FLAG_BODY (1 << 2)
-#define BC_PARSE_FLAG_LOOP (1 << 3)
-#define BC_PARSE_FLAG_LOOP_INNER (1 << 4)
-#define BC_PARSE_FLAG_IF (1 << 5)
-#define BC_PARSE_FLAG_ELSE (1 << 6)
-#define BC_PARSE_FLAG_IF_END (1 << 7)
-
-// If we have none of the above bits, we can stop parsing and execute already parsed chunk
-#define BC_PARSE_CAN_EXEC(parse) (BC_PARSE_TOP_FLAG(parse) == 0)
-
-#define BC_PARSE_FUNC_INNER(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_FUNC_INNER)
-#define BC_PARSE_FUNC(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_FUNC)
-#define BC_PARSE_BODY(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_BODY)
-#define BC_PARSE_LOOP(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_LOOP)
-#define BC_PARSE_LOOP_INNER(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_LOOP_INNER)
-#define BC_PARSE_IF(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_IF)
-#define BC_PARSE_ELSE(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_ELSE)
-#define BC_PARSE_IF_END(parse) (BC_PARSE_TOP_FLAG(parse) & BC_PARSE_FLAG_IF_END)
-
typedef struct BcParse {
BcLex l;
- uint8_t *bf_base;
- uint8_t *bf_top;
-
BcVec exits;
BcVec conds;
@@ -620,9 +589,8 @@ typedef struct BcParse {
BcFunc *func;
size_t fidx;
+//TODO: needed? Example?
size_t nbraces;
-//FIXME: "define w(x) { auto z; return 1; }" fails to parse
- bool auto_part;
} BcParse;
typedef struct BcProgram {
@@ -666,26 +634,6 @@ typedef struct BcProgram {
} BcProgram;
-static void bc_parse_push_block_flag(BcParse *p, uint8_t flags)
-{
- size_t size;
- uint8_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
- flags |= (*flag_ptr & (BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_LOOP));
- flags |= BC_PARSE_FLAG_BODY;
-
- size = p->bf_top - p->bf_base;
- p->bf_base = xrealloc(p->bf_base, size + 2);
- p->bf_top = p->bf_base + size + 1;
- dbg_lex("%s:%d pushed block flag lvl:%d bits:0x%02x", __func__, __LINE__, size + 1, flags);
- *p->bf_top = flags;
-}
-
-static ALWAYS_INLINE void bc_parse_pop_block_flag(BcParse *p)
-{
- p->bf_top--;
- dbg_lex("%s:%d popped block flag lvl:%d bits:0x%02x", __func__, __LINE__, p->bf_top - p->bf_base, *p->bf_top);
-}
-
#define BC_PROG_STACK(s, n) ((s)->len >= ((size_t) n))
#define BC_PROG_MAIN (0)
@@ -747,6 +695,7 @@ struct globals {
IF_FEATURE_BC_SIGNALS(smallint ttyin;)
IF_FEATURE_CLEAN_UP(smallint exiting;)
smallint in_read;
+ smallint use_stdin;
BcParse prs;
BcProgram prog;
@@ -756,6 +705,7 @@ struct globals {
unsigned err_line;
BcVec files;
+ BcVec stdin_buffer;
char *env_args;
@@ -1052,6 +1002,7 @@ static void quit(void)
if (ferror(stdin))
bb_perror_msg_and_die("input error");
fflush_and_check();
+ dbg_exec("quit(): exiting with exitcode SUCCESS");
exit(0);
}
@@ -2955,6 +2906,79 @@ static BC_STATUS zcommon_lex_token(BcLex *l)
IF_DC(RETURN_STATUS(zdc_lex_token(l));)
}
+static bool bc_lex_more_input(BcLex *l)
+{
+ size_t str;
+ bool comment;
+
+ bc_vec_pop_all(&G.stdin_buffer);
+
+ // This loop is complex because the vm tries not to send any lines that end
+ // with a backslash to the parser. The reason for that is because the parser
+ // treats a backslash+newline combo as whitespace, per the bc spec. In that
+ // case, and for strings and comments, the parser will expect more stuff.
+ comment = false;
+ str = 0;
+ for (;;) {
+ size_t prevlen = G.stdin_buffer.len;
+ char *string;
+
+ bc_read_line(&G.stdin_buffer);
+ // No more input means EOF
+ if (G.stdin_buffer.len <= prevlen + 1) // (we expect +1 for NUL byte)
+ break;
+
+ string = G.stdin_buffer.v + prevlen;
+ while (*string) {
+ char c = *string;
+ if (string == G.stdin_buffer.v || string[-1] != '\\') {
+ if (IS_BC)
+ str ^= (c == '"');
+ else {
+ if (c == ']')
+ str -= 1;
+ else if (c == '[')
+ str += 1;
+ }
+ }
+ string++;
+ if (c == '/' && *string == '*') {
+ comment = true;
+ string++;
+ continue;
+ }
+ if (c == '*' && *string == '/') {
+ comment = false;
+ string++;
+ }
+ }
+ if (str != 0 || comment) {
+ G.stdin_buffer.len--; // backstep over the trailing NUL byte
+ continue;
+ }
+
+ // Check for backslash+newline.
+ // we do not check that last char is '\n' -
+ // if it is not, then it's EOF, and looping back
+ // to bc_read_line() will detect it:
+ string -= 2;
+ if (string >= G.stdin_buffer.v && *string == '\\') {
+ G.stdin_buffer.len--;
+ continue;
+ }
+
+ break;
+ }
+
+ l->buf = G.stdin_buffer.v;
+ l->i = 0;
+//bb_error_msg("G.stdin_buffer.len:%d '%s'", G.stdin_buffer.len, G.stdin_buffer.v);
+ l->len = G.stdin_buffer.len - 1; // do not include NUL
+
+ G.use_stdin = (l->len != 0);
+ return G.use_stdin;
+}
+
static BC_STATUS zbc_lex_next(BcLex *l)
{
BcStatus s;
@@ -2964,10 +2988,16 @@ static BC_STATUS zbc_lex_next(BcLex *l)
l->line += l->newline;
G.err_line = l->line;
- l->t.t = BC_LEX_EOF;
+ l->t.t = BC_LEX_EOF;
+//this NL handling is bogus
l->newline = (l->i == l->len);
- if (l->newline) RETURN_STATUS(BC_STATUS_SUCCESS);
+ if (l->newline) {
+ if (!G.use_stdin || !bc_lex_more_input(l))
+ RETURN_STATUS(BC_STATUS_SUCCESS);
+ // here it's guaranteed that l->i is below l->len
+ l->newline = false;
+ }
// Loop until failure or we don't have whitespace. This
// is so the parser doesn't get inundated with whitespace.
@@ -3528,19 +3558,8 @@ static BC_STATUS zcommon_parse(BcParse *p)
static BC_STATUS zbc_parse_text_init(BcParse *p, const char *text)
{
- BcStatus s;
-
p->func = bc_program_func(p->fidx);
- if (!text[0] && !BC_PARSE_CAN_EXEC(p)) {
- p->l.t.t = BC_LEX_INVALID;
- s = BC_STATUS_SUCCESS;
- ERROR_RETURN(s =) zcommon_parse(p);
- if (s) RETURN_STATUS(s);
- if (!BC_PARSE_CAN_EXEC(p))
- RETURN_STATUS(bc_error("file is not executable"));
- }
-
RETURN_STATUS(zbc_lex_text_init(&p->l, text));
}
#if ERRORS_ARE_FATAL
@@ -3580,9 +3599,8 @@ static void bc_parse_reset(BcParse *p)
p->l.i = p->l.len;
p->l.t.t = BC_LEX_EOF;
- p->auto_part = (p->nbraces = 0);
+ p->nbraces = 0;
- p->bf_top = p->bf_base; // pop all flags
bc_vec_pop_all(&p->exits);
bc_vec_pop_all(&p->conds);
bc_vec_pop_all(&p->ops);
@@ -3592,7 +3610,6 @@ static void bc_parse_reset(BcParse *p)
static void bc_parse_free(BcParse *p)
{
- free(p->bf_base);
bc_vec_free(&p->exits);
bc_vec_free(&p->conds);
bc_vec_free(&p->ops);
@@ -3604,12 +3621,11 @@ static void bc_parse_create(BcParse *p, size_t func)
memset(p, 0, sizeof(BcParse));
bc_lex_init(&p->l);
- p->bf_top = p->bf_base = xzalloc(1);
bc_vec_init(&p->exits, sizeof(BcInstPtr), NULL);
bc_vec_init(&p->conds, sizeof(size_t), NULL);
bc_vec_init(&p->ops, sizeof(BcLexType), NULL);
- // p->auto_part = p->nbraces = 0; - already is
+ // p->nbraces = 0; - already is
bc_parse_updateFunc(p, func);
}
@@ -3625,14 +3641,20 @@ static void bc_parse_create(BcParse *p, size_t func)
// first in the expr enum. Note: This only works for binary operators.
#define BC_TOKEN_2_INST(t) ((char) ((t) - BC_LEX_NEG + BC_INST_NEG))
-static BC_STATUS zbc_parse_else(BcParse *p);
-static BC_STATUS zbc_parse_stmt(BcParse *p);
+static BC_STATUS zbc_parse_stmt_possibly_auto(BcParse *p, bool auto_allowed);
static BC_STATUS zbc_parse_expr(BcParse *p, uint8_t flags, BcParseNext next);
static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext next);
#if ERRORS_ARE_FATAL
-# define zbc_parse_else(...) (zbc_parse_else(__VA_ARGS__), BC_STATUS_SUCCESS)
-# define zbc_parse_stmt(...) (zbc_parse_stmt(__VA_ARGS__), BC_STATUS_SUCCESS)
# define zbc_parse_expr(...) (zbc_parse_expr(__VA_ARGS__), BC_STATUS_SUCCESS)
+# defone zbc_parse_stmt_possibly_auto(...) (zbc_parse_stmt_possibly_auto(__VA_ARGS__), BC_STATUS_SUCCESS)
+#endif
+
+static BC_STATUS zbc_parse_stmt(BcParse *p)
+{
+ RETURN_STATUS(zbc_parse_stmt_possibly_auto(p, false));
+}
+#if ERRORS_ARE_FATAL
+# define zbc_parse_stmt(...) (zbc_parse_stmt(__VA_ARGS__), BC_STATUS_SUCCESS)
#endif
static void bc_parse_operator(BcParse *p, BcLexType type, size_t start,
@@ -4037,7 +4059,7 @@ static BC_STATUS zbc_parse_return(BcParse *p)
BcLexType t;
bool paren;
- if (!BC_PARSE_FUNC(p)) RETURN_STATUS(bc_error_bad_token());
+ dbg_lex_enter("%s:%d entered", __func__, __LINE__);
s = zbc_lex_next(&p->l);
if (s) RETURN_STATUS(s);
@@ -4063,97 +4085,62 @@ static BC_STATUS zbc_parse_return(BcParse *p)
bc_parse_push(p, BC_INST_RET);
}
+ dbg_lex_done("%s:%d done", __func__, __LINE__);
RETURN_STATUS(s);
}
#if ERRORS_ARE_FATAL
# define zbc_parse_return(...) (zbc_parse_return(__VA_ARGS__), BC_STATUS_SUCCESS)
#endif
-static BC_STATUS zbc_parse_endBody(BcParse *p)
+static void bc_parse_noElse(BcParse *p)
{
- BcStatus s = BC_STATUS_SUCCESS;
-
- if (BC_PARSE_FLAG_STACK_EMPTY(p))
- RETURN_STATUS(bc_error_bad_token());
-
- if (BC_PARSE_IF(p)) {
- uint8_t *flag_ptr;
+ BcInstPtr *ip;
+ size_t *label;
- while (p->l.t.t == BC_LEX_NLINE) {
- s = zbc_lex_next(&p->l);
- if (s) RETURN_STATUS(s);
- }
+ ip = bc_vec_top(&p->exits);
+ label = bc_vec_item(&p->func->labels, ip->idx);
+ dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len);
+ *label = p->func->code.len;
- bc_parse_pop_block_flag(p);
+ bc_vec_pop(&p->exits);
+}
- flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
- dbg_lex("%s:%d setting BC_PARSE_FLAG_IF_END bit", __func__, __LINE__);
- *flag_ptr = (*flag_ptr | BC_PARSE_FLAG_IF_END);
+static BC_STATUS zbc_parse_else(BcParse *p)
+{
+ BcStatus s;
+ BcInstPtr ip;
- if (p->l.t.t == BC_LEX_KEY_ELSE)
- s = zbc_parse_else(p);
- }
- else if (BC_PARSE_ELSE(p)) {
- BcInstPtr *ip;
- size_t *label;
+ dbg_lex_enter("%s:%d entered", __func__, __LINE__);
- ip = bc_vec_top(&p->exits);
- label = bc_vec_item(&p->func->labels, ip->idx);
- dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len);
- *label = p->func->code.len;
+ ip.idx = p->func->labels.len;
+ ip.func = ip.len = 0;
- bc_vec_pop(&p->exits);
- bc_parse_pop_block_flag(p);
- }
- else if (BC_PARSE_FUNC_INNER(p)) {
- bc_parse_push(p, BC_INST_RET0);
- bc_parse_updateFunc(p, BC_PROG_MAIN);
- bc_parse_pop_block_flag(p);
- }
- else {
- BcInstPtr *ip = bc_vec_top(&p->exits);
- size_t *label = bc_vec_top(&p->conds);
+ dbg_lex("%s:%d after if() body: BC_INST_JUMP to %d", __func__, __LINE__, ip.idx);
+ bc_parse_push(p, BC_INST_JUMP);
+ bc_parse_pushIndex(p, ip.idx);
- dbg_lex("%s:%d BC_INST_JUMP to %d", __func__, __LINE__, *label);
- bc_parse_push(p, BC_INST_JUMP);
- bc_parse_pushIndex(p, *label);
+ dbg_lex("%s:%d calling bc_parse_noElse()", __func__, __LINE__);
+ bc_parse_noElse(p);
- label = bc_vec_item(&p->func->labels, ip->idx);
- dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len);
- *label = p->func->code.len;
+ bc_vec_push(&p->exits, &ip);
+ bc_vec_push(&p->func->labels, &ip.idx);
- bc_vec_pop(&p->exits);
- bc_vec_pop(&p->conds);
- bc_parse_pop_block_flag(p);
- }
+ s = zbc_parse_stmt(p);
+ if (s) RETURN_STATUS(s);
+ dbg_lex_done("%s:%d done", __func__, __LINE__);
RETURN_STATUS(s);
}
#if ERRORS_ARE_FATAL
-# define zbc_parse_endBody(...) (zbc_parse_endBody(__VA_ARGS__), BC_STATUS_SUCCESS)
+# define zbc_parse_else(...) (zbc_parse_else(__VA_ARGS__), BC_STATUS_SUCCESS)
#endif
-static void bc_parse_noElse(BcParse *p)
-{
- BcInstPtr *ip;
- size_t *label;
- uint8_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
-
- dbg_lex("%s:%d clearing BC_PARSE_FLAG_IF_END bit", __func__, __LINE__);
- *flag_ptr = (*flag_ptr & ~(BC_PARSE_FLAG_IF_END));
-
- ip = bc_vec_top(&p->exits);
- label = bc_vec_item(&p->func->labels, ip->idx);
- dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len);
- *label = p->func->code.len;
-
- bc_vec_pop(&p->exits);
-}
-
static BC_STATUS zbc_parse_if(BcParse *p)
{
BcStatus s;
BcInstPtr ip;
+ BcInstPtr *ipp;
+ size_t *label;
dbg_lex_enter("%s:%d entered", __func__, __LINE__);
s = zbc_lex_next(&p->l);
@@ -4164,60 +4151,50 @@ static BC_STATUS zbc_parse_if(BcParse *p)
if (s) RETURN_STATUS(s);
s = zbc_parse_expr(p, BC_PARSE_REL, bc_parse_next_rel);
if (s) RETURN_STATUS(s);
- if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
+ if (p->l.t.t != BC_LEX_RPAREN) RETURN_STATUS(bc_error_bad_token());
s = zbc_lex_next(&p->l);
if (s) RETURN_STATUS(s);
- bc_parse_push(p, BC_INST_JUMP_ZERO);
+ bc_parse_push(p, BC_INST_JUMP_ZERO);
ip.idx = p->func->labels.len;
ip.func = ip.len = 0;
-
bc_parse_pushIndex(p, ip.idx);
+//TODO: can get rid of p->exits stack?
bc_vec_push(&p->exits, &ip);
bc_vec_push(&p->func->labels, &ip.idx);
- bc_parse_push_block_flag(p, BC_PARSE_FLAG_IF);
- dbg_lex_done("%s:%d done", __func__, __LINE__);
- RETURN_STATUS(BC_STATUS_SUCCESS);
-}
-#if ERRORS_ARE_FATAL
-# define zbc_parse_if(...) (zbc_parse_if(__VA_ARGS__), BC_STATUS_SUCCESS)
-#endif
-
-#undef zbc_parse_else
-static BC_STATUS zbc_parse_else(BcParse *p)
-{
- BcInstPtr ip;
-
- dbg_lex_enter("%s:%d entered", __func__, __LINE__);
- if (!BC_PARSE_IF_END(p)) RETURN_STATUS(bc_error_bad_token());
-
- ip.idx = p->func->labels.len;
- ip.func = ip.len = 0;
-
- dbg_lex("%s:%d after if() body: BC_INST_JUMP to %d", __func__, __LINE__, ip.idx);
- bc_parse_push(p, BC_INST_JUMP);
- bc_parse_pushIndex(p, ip.idx);
+ s = zbc_parse_stmt(p);
+ if (s) RETURN_STATUS(s);
+ dbg_lex("%s:%d in if after stmt: p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
+ if (p->l.t.t == BC_LEX_KEY_ELSE) {
+ s = zbc_lex_next(&p->l);
+ if (s) RETURN_STATUS(s);
+ dbg_lex("%s:%d calling zbc_parse_else(), p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
+ s = zbc_parse_else(p);
+ }
- dbg_lex("%s:%d calling bc_parse_noElse()", __func__, __LINE__);
- bc_parse_noElse(p);
+ ipp = bc_vec_top(&p->exits);
+ label = bc_vec_item(&p->func->labels, ipp->idx);
+ dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len);
+ *label = p->func->code.len;
- bc_vec_push(&p->exits, &ip);
- bc_vec_push(&p->func->labels, &ip.idx);
- bc_parse_push_block_flag(p, BC_PARSE_FLAG_ELSE);
+ bc_vec_pop(&p->exits);
dbg_lex_done("%s:%d done", __func__, __LINE__);
- RETURN_STATUS(zbc_lex_next(&p->l));
+ RETURN_STATUS(s);
}
#if ERRORS_ARE_FATAL
-# define zbc_parse_else(...) (zbc_parse_else(__VA_ARGS__), BC_STATUS_SUCCESS)
+# define zbc_parse_if(...) (zbc_parse_if(__VA_ARGS__), BC_STATUS_SUCCESS)
#endif
static BC_STATUS zbc_parse_while(BcParse *p)
{
BcStatus s;
BcInstPtr ip;
+ BcInstPtr *ipp;
+ size_t *label;
+ size_t n;
s = zbc_lex_next(&p->l);
if (s) RETURN_STATUS(s);
@@ -4245,9 +4222,29 @@ static BC_STATUS zbc_parse_while(BcParse *p)
bc_parse_push(p, BC_INST_JUMP_ZERO);
bc_parse_pushIndex(p, ip.idx);
- bc_parse_push_block_flag(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER);
- RETURN_STATUS(BC_STATUS_SUCCESS);
+ s = zbc_parse_stmt(p);
+ if (s) RETURN_STATUS(s);
+
+ n = *((size_t *) bc_vec_top(&p->conds));
+ bc_parse_push(p, BC_INST_JUMP);
+ bc_parse_pushIndex(p, n);
+
+ ipp = bc_vec_top(&p->exits);
+ label = bc_vec_top(&p->conds);
+
+ dbg_lex("%s:%d BC_INST_JUMP to %d", __func__, __LINE__, *label);
+ bc_parse_push(p, BC_INST_JUMP);
+ bc_parse_pushIndex(p, *label);
+
+ label = bc_vec_item(&p->func->labels, ipp->idx);
+ dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len);
+ *label = p->func->code.len;
+
+ bc_vec_pop(&p->exits);
+ bc_vec_pop(&p->conds);
+
+ RETURN_STATUS(s);
}
#if ERRORS_ARE_FATAL
# define zbc_parse_while(...) (zbc_parse_while(__VA_ARGS__), BC_STATUS_SUCCESS)
@@ -4257,7 +4254,10 @@ static BC_STATUS zbc_parse_for(BcParse *p)
{
BcStatus s;
BcInstPtr ip;
+ BcInstPtr *ipp;
+ size_t *label;
size_t cond_idx, exit_idx, body_idx, update_idx;
+ size_t n;
dbg_lex("%s:%d p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
s = zbc_lex_next(&p->l);
@@ -4324,7 +4324,28 @@ static BC_STATUS zbc_parse_for(BcParse *p)
bc_vec_push(&p->func->labels, &ip.idx);
s = zbc_lex_next(&p->l);
if (s) RETURN_STATUS(s);
- bc_parse_push_block_flag(p, BC_PARSE_FLAG_LOOP | BC_PARSE_FLAG_LOOP_INNER);
+
+ s = zbc_parse_stmt(p);
+ if (s) RETURN_STATUS(s);
+
+ n = *((size_t *) bc_vec_top(&p->conds));
+ bc_parse_push(p, BC_INST_JUMP);
+ bc_parse_pushIndex(p, n);
+
+ ipp = bc_vec_top(&p->exits);
+ label = bc_vec_top(&p->conds);
+
+//TODO: commonalize?
+ dbg_lex("%s:%d BC_INST_JUMP to %d", __func__, __LINE__, *label);
+ bc_parse_push(p, BC_INST_JUMP);
+ bc_parse_pushIndex(p, *label);
+
+ label = bc_vec_item(&p->func->labels, ipp->idx);
+ dbg_lex("%s:%d rewriting label: %d -> %d", __func__, __LINE__, *label, p->func->code.len);
+ *label = p->func->code.len;
+
+ bc_vec_pop(&p->exits);
+ bc_vec_pop(&p->conds);
RETURN_STATUS(BC_STATUS_SUCCESS);
}
@@ -4332,14 +4353,12 @@ static BC_STATUS zbc_parse_for(BcParse *p)
# define zbc_parse_for(...) (zbc_parse_for(__VA_ARGS__), BC_STATUS_SUCCESS)
#endif
-static BC_STATUS zbc_parse_loopExit(BcParse *p, BcLexType type)
+static BC_STATUS zbc_parse_break_or_continue(BcParse *p, BcLexType type)
{
BcStatus s;
size_t i;
BcInstPtr *ip;
- if (!BC_PARSE_LOOP(p)) RETURN_STATUS(bc_error_bad_token());
-
if (type == BC_LEX_KEY_BREAK) {
if (p->exits.len == 0) RETURN_STATUS(bc_error_bad_token());
@@ -4368,14 +4387,13 @@ static BC_STATUS zbc_parse_loopExit(BcParse *p, BcLexType type)
RETURN_STATUS(zbc_lex_next(&p->l));
}
#if ERRORS_ARE_FATAL
-# define zbc_parse_loopExit(...) (zbc_parse_loopExit(__VA_ARGS__), BC_STATUS_SUCCESS)
+# define zbc_parse_break_or_continue(...) (zbc_parse_break_or_continue(__VA_ARGS__), BC_STATUS_SUCCESS)
#endif
static BC_STATUS zbc_parse_func(BcParse *p)
{
BcStatus s;
bool var, comma = false;
- uint8_t flags;
char *name;
s = zbc_lex_next(&p->l);
@@ -4430,15 +4448,18 @@ static BC_STATUS zbc_parse_func(BcParse *p)
if (comma) RETURN_STATUS(bc_error("bad function definition"));
- flags = BC_PARSE_FLAG_FUNC | BC_PARSE_FLAG_FUNC_INNER | BC_PARSE_FLAG_BODY;
- bc_parse_push_block_flag(p, flags);
-
s = zbc_lex_next(&p->l);
if (s) RETURN_STATUS(s);
if (p->l.t.t != BC_LEX_LBRACE)
s = bc_POSIX_requires("the left brace be on the same line as the function header");
+ s = zbc_parse_stmt_possibly_auto(p, true);
+ if (s) RETURN_STATUS(s);
+
+ bc_parse_push(p, BC_INST_RET0);
+ bc_parse_updateFunc(p, BC_PROG_MAIN);
+
RETURN_STATUS(s);
err:
@@ -4455,12 +4476,11 @@ static BC_STATUS zbc_parse_auto(BcParse *p)
bool comma, var, one;
char *name;
- if (!p->auto_part) RETURN_STATUS(bc_error_bad_token());
-
+ dbg_lex_enter("%s:%d entered", __func__, __LINE__);
s = zbc_lex_next(&p->l);
if (s) RETURN_STATUS(s);
- p->auto_part = comma = false;
+ comma = false;
one = p->l.t.t == BC_LEX_NAME;
while (p->l.t.t == BC_LEX_NAME) {
@@ -4498,91 +4518,53 @@ static BC_STATUS zbc_parse_auto(BcParse *p)
if (p->l.t.t != BC_LEX_NLINE && p->l.t.t != BC_LEX_SCOLON)
RETURN_STATUS(bc_error_bad_token());
+ dbg_lex_done("%s:%d done", __func__, __LINE__);
RETURN_STATUS(zbc_lex_next(&p->l));
err:
free(name);
+ dbg_lex_done("%s:%d done (ERROR)", __func__, __LINE__);
RETURN_STATUS(s);
}
#if ERRORS_ARE_FATAL
# define zbc_parse_auto(...) (zbc_parse_auto(__VA_ARGS__), BC_STATUS_SUCCESS)
#endif
-static BC_STATUS zbc_parse_body(BcParse *p, bool brace)
+#undef zbc_parse_stmt_possibly_auto
+static BC_STATUS zbc_parse_stmt_possibly_auto(BcParse *p, bool auto_allowed)
{
BcStatus s = BC_STATUS_SUCCESS;
- uint8_t *flag_ptr = BC_PARSE_TOP_FLAG_PTR(p);
-
- dbg_lex_enter("%s:%d entered", __func__, __LINE__);
- *flag_ptr &= ~(BC_PARSE_FLAG_BODY);
- if (*flag_ptr & BC_PARSE_FLAG_FUNC_INNER) {
- dbg_lex("%s:%d BC_PARSE_FLAG_FUNC_INNER", __func__, __LINE__);
- if (!brace) RETURN_STATUS(bc_error_bad_token());
-
- p->auto_part = p->l.t.t != BC_LEX_KEY_AUTO;
-
- if (!p->auto_part) {
- s = zbc_parse_auto(p);
- if (s) RETURN_STATUS(s);
- }
+ dbg_lex_enter("%s:%d entered, p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
- if (p->l.t.t == BC_LEX_NLINE) s = zbc_lex_next(&p->l);
+ if (p->l.t.t == BC_LEX_NLINE) {
+ dbg_lex_done("%s:%d done (seen BC_LEX_NLINE)", __func__, __LINE__);
+ RETURN_STATUS(zbc_lex_next(&p->l));
}
- else {
- dbg_lex("%s:%d !BC_PARSE_FLAG_FUNC_INNER", __func__, __LINE__);
- s = zbc_parse_stmt(p);
- if (!s && !brace && !BC_PARSE_BODY(p)) s = zbc_parse_endBody(p);
+ if (p->l.t.t == BC_LEX_SCOLON) {
+ dbg_lex_done("%s:%d done (seen BC_LEX_SCOLON)", __func__, __LINE__);
+ RETURN_STATUS(zbc_lex_next(&p->l));
}
- dbg_lex_done("%s:%d done", __func__, __LINE__);
- RETURN_STATUS(s);
-}
-#if ERRORS_ARE_FATAL
-# define zbc_parse_body(...) (zbc_parse_body(__VA_ARGS__), BC_STATUS_SUCCESS)
-#endif
-
-#undef zbc_parse_stmt
-static BC_STATUS zbc_parse_stmt(BcParse *p)
-{
- BcStatus s = BC_STATUS_SUCCESS;
-
- dbg_lex_enter("%s:%d entered, p->l.t.t:%d", __func__, __LINE__, p->l.t.t);
- switch (p->l.t.t) {
- case BC_LEX_NLINE:
- dbg_lex_done("%s:%d done (seen BC_LEX_NLINE)", __func__, __LINE__);
- RETURN_STATUS(zbc_lex_next(&p->l));
-
- case BC_LEX_KEY_ELSE:
- dbg_lex("%s:%d BC_LEX_KEY_ELSE:", __func__, __LINE__);
- p->auto_part = false;
- break;
-
- case BC_LEX_LBRACE:
- dbg_lex("%s:%d BC_LEX_LBRACE:", __func__, __LINE__);
- if (!BC_PARSE_BODY(p)) RETURN_STATUS(bc_error_bad_token());
- ++p->nbraces;
+ if (p->l.t.t == BC_LEX_LBRACE) {
+ dbg_lex("%s:%d BC_LEX_LBRACE: (auto_allowed:%d)", __func__, __LINE__, auto_allowed);
+ do {
s = zbc_lex_next(&p->l);
if (s) RETURN_STATUS(s);
- dbg_lex_done("%s:%d done (returning zbc_parse_body())", __func__, __LINE__);
- RETURN_STATUS(zbc_parse_body(p, true));
-
- case BC_LEX_KEY_AUTO:
- dbg_lex("%s:%d BC_LEX_KEY_AUTO:", __func__, __LINE__);
- RETURN_STATUS(zbc_parse_auto(p));
-
- default:
- p->auto_part = false;
- if (BC_PARSE_IF_END(p)) {
- bc_parse_noElse(p);
- dbg_lex_done("%s:%d done (BC_PARSE_IF_END is true)", __func__, __LINE__);
- RETURN_STATUS(BC_STATUS_SUCCESS);
- }
- if (BC_PARSE_BODY(p)) {
- dbg_lex_done("%s:%d done (returning zbc_parse_body())", __func__, __LINE__);
- RETURN_STATUS(zbc_parse_body(p, false));
- }
- break;
+ } while (p->l.t.t == BC_LEX_NLINE);
+ if (auto_allowed && p->l.t.t == BC_LEX_KEY_AUTO) {
+ dbg_lex("%s:%d calling zbc_parse_auto()", __func__, __LINE__);
+ s = zbc_parse_auto(p);
+ if (s) RETURN_STATUS(s);
+ }
+ while (p->l.t.t != BC_LEX_RBRACE) {
+ dbg_lex("%s:%d block parsing loop", __func__, __LINE__);
+ s = zbc_parse_stmt(p);
+ if (s) RETURN_STATUS(s);
+ }
+ s = zbc_lex_next(&p->l);
+ dbg_lex_done("%s:%d done (seen BC_LEX_RBRACE)", __func__, __LINE__);
+ RETURN_STATUS(s);
}
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)
case BC_LEX_KEY_SQRT:
s = zbc_parse_expr(p, BC_PARSE_PRINT, bc_parse_next_expr);
break;
- case BC_LEX_KEY_ELSE:
- s = zbc_parse_else(p);
- break;
- case BC_LEX_SCOLON:
- while (!s && p->l.t.t == BC_LEX_SCOLON) s = zbc_lex_next(&p->l);
- break;
- case BC_LEX_RBRACE:
- if (p->nbraces == 0)
- RETURN_STATUS(bc_error_bad_token());
- --p->nbraces;
- s = zbc_lex_next(&p->l);
- if (!s)
- s = zbc_parse_endBody(p);
- break;
case BC_LEX_STR:
s = zbc_parse_string(p, BC_INST_PRINT_STR);
break;
case BC_LEX_KEY_BREAK:
case BC_LEX_KEY_CONTINUE:
- s = zbc_parse_loopExit(p, p->l.t.t);
+ s = zbc_parse_break_or_continue(p, p->l.t.t);
break;
case BC_LEX_KEY_FOR:
s = zbc_parse_for(p);
@@ -4637,8 +4605,6 @@ static BC_STATUS zbc_parse_stmt(BcParse *p)
case BC_LEX_KEY_LIMITS:
// "limits" is a compile-time command,
// the output is produced at _parse time_.
- s = zbc_lex_next(&p->l);
- if (s) RETURN_STATUS(s);
printf(
"BC_BASE_MAX = "BC_MAX_OBASE_STR "\n"
"BC_DIM_MAX = "BC_MAX_DIM_STR "\n"
@@ -4649,6 +4615,7 @@ static BC_STATUS zbc_parse_stmt(BcParse *p)
"MAX Exponent = "BC_MAX_EXP_STR "\n"
"Number of vars = "BC_MAX_VARS_STR "\n"
);
+ s = zbc_lex_next(&p->l);
break;
case BC_LEX_KEY_PRINT:
s = zbc_parse_print(p);
@@ -4669,11 +4636,16 @@ static BC_STATUS zbc_parse_stmt(BcParse *p)
break;
}
+ if (s || G_interrupt) {
+ bc_parse_reset(p);
+ s = BC_STATUS_FAILURE;
+ }
+
dbg_lex_done("%s:%d done", __func__, __LINE__);
RETURN_STATUS(s);
}
#if ERRORS_ARE_FATAL
-# define zbc_parse_stmt(...) (zbc_parse_stmt(__VA_ARGS__), BC_STATUS_SUCCESS)
+# define zbc_parse_stmt_possibly_auto(...) (zbc_parse_stmt_possibly_auto(__VA_ARGS__), BC_STATUS_SUCCESS)
#endif
static BC_STATUS zbc_parse_stmt_or_funcdef(BcParse *p)
@@ -4682,22 +4654,15 @@ static BC_STATUS zbc_parse_stmt_or_funcdef(BcParse *p)
dbg_lex_enter("%s:%d entered", __func__, __LINE__);
if (p->l.t.t == BC_LEX_EOF)
- s = BC_PARSE_FLAG_STACK_EMPTY(p) ? bc_error("end of file") : bc_error("block end could not be found");
+ s = bc_error("end of file");
else if (p->l.t.t == BC_LEX_KEY_DEFINE) {
dbg_lex("%s:%d p->l.t.t:BC_LEX_KEY_DEFINE", __func__, __LINE__);
- if (!BC_PARSE_CAN_EXEC(p))
- RETURN_STATUS(bc_error_bad_token());
s = zbc_parse_func(p);
} else {
dbg_lex("%s:%d p->l.t.t:%d (not BC_LEX_KEY_DEFINE)", __func__, __LINE__, p->l.t.t);
s = zbc_parse_stmt(p);
}
- if (s || G_interrupt) {
- bc_parse_reset(p);
- s = BC_STATUS_FAILURE;
- }
-
dbg_lex_done("%s:%d done", __func__, __LINE__);
RETURN_STATUS(s);
}
@@ -4940,6 +4905,7 @@ static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext ne
if (prev == BC_INST_BOOL_NOT || nexprs != 1)
return bc_error_bad_expression();
+//TODO: why is this needed at all?
// next is BcParseNext, byte array of up to 4 BC_LEX's, packed into 32-bit word
for (;;) {
if (t == (next & 0x7f))
@@ -4948,7 +4914,8 @@ static BcStatus bc_parse_expr_empty_ok(BcParse *p, uint8_t flags, BcParseNext ne
break;
next >>= 8;
}
- return bc_error_bad_expression();
+ if (t != BC_LEX_KEY_ELSE)
+ return bc_error_bad_expression();
ok:
if (!(flags & BC_PARSE_REL) && nrelops) {
@@ -6766,6 +6733,7 @@ static BC_STATUS zbc_program_exec(void)
s = zbc_program_incdec(inst);
break;
case BC_INST_HALT:
+ dbg_exec("BC_INST_HALT:");
QUIT_OR_RETURN_TO_MAIN;
break;
case BC_INST_RET:
@@ -6939,6 +6907,7 @@ static BC_STATUS zbc_program_exec(void)
break;
}
case BC_INST_QUIT:
+ dbg_exec("BC_INST_NEG:");
if (G.prog.stack.len <= 2)
QUIT_OR_RETURN_TO_MAIN;
bc_vec_npop(&G.prog.stack, 2);
@@ -6994,14 +6963,12 @@ static BC_STATUS zbc_vm_process(const char *text)
dbg_lex("%s:%d G.prs.l.t.t:%d", __func__, __LINE__, G.prs.l.t.t);
ERROR_RETURN(s =) zcommon_parse(&G.prs);
if (s) RETURN_STATUS(s);
- }
- dbg_lex("%s:%d G.prs.l.t.t:BC_LEX_EOF", __func__, __LINE__);
-
- if (BC_PARSE_CAN_EXEC(&G.prs)) {
s = zbc_program_exec();
- fflush_and_check();
- if (s)
+ if (s) {
bc_program_reset();
+ break;
+ }
+ fflush_and_check();
}
dbg_lex_done("%s:%d done", __func__, __LINE__);
@@ -7049,91 +7016,12 @@ err:
static BC_STATUS zbc_vm_stdin(void)
{
BcStatus s;
- BcVec buffer;
- size_t str;
- bool comment;
//G.prog.file = NULL; - already is
bc_lex_file(&G.prs.l);
- bc_char_vec_init(&buffer);
-
- // This loop is complex because the vm tries not to send any lines that end
- // with a backslash to the parser. The reason for that is because the parser
- // treats a backslash+newline combo as whitespace, per the bc spec. In that
- // case, and for strings and comments, the parser will expect more stuff.
- s = BC_STATUS_SUCCESS;
- comment = false;
- str = 0;
- for (;;) {
- size_t prevlen = buffer.len;
- char *string;
-
- bc_read_line(&buffer);
- // No more input means EOF
- if (buffer.len <= prevlen + 1) // (we expect +1 for NUL byte)
- break;
-
- string = buffer.v + prevlen;
- while (*string) {
- char c = *string;
- if (string == buffer.v || string[-1] != '\\') {
- if (IS_BC)
- str ^= (c == '"');
- else {
- if (c == ']')
- str -= 1;
- else if (c == '[')
- str += 1;
- }
- }
- string++;
- if (c == '/' && *string == '*') {
- comment = true;
- string++;
- continue;
- }
- if (c == '*' && *string == '/') {
- comment = false;
- string++;
- }
- }
- if (str || comment) {
- buffer.len--; // backstep over the trailing NUL byte
- continue;
- }
-
- // Check for backslash+newline.
- // we do not check that last char is '\n' -
- // if it is not, then it's EOF, and looping back
- // to bc_read_line() will detect it:
- string -= 2;
- if (string >= buffer.v && *string == '\\') {
- buffer.len--;
- continue;
- }
-
- s = zbc_vm_process(buffer.v);
- if (s) {
- if (ENABLE_FEATURE_CLEAN_UP && !G_ttyin) {
- // Debug config, non-interactive mode:
- // return all the way back to main.
- // Non-debug builds do not come here, they exit.
- break;
- }
- }
-
- bc_vec_pop_all(&buffer);
- }
-
- if (str) {
- s = bc_error("string end could not be found");
- }
- else if (comment) {
- s = bc_error("comment end could not be found");
- }
-
- bc_vec_free(&buffer);
+ G.use_stdin = 1;
+ s = zbc_vm_process("");
RETURN_STATUS(s);
}
#if ERRORS_ARE_FATAL
@@ -7412,9 +7300,6 @@ static BC_STATUS zbc_vm_exec(void)
if (IS_BC || (option_mask32 & BC_FLAG_I))
s = zbc_vm_stdin();
- if (!s && !BC_PARSE_CAN_EXEC(&G.prs))
- s = zbc_vm_process("");
-
RETURN_STATUS(s);
}
#if ERRORS_ARE_FATAL
@@ -7443,6 +7328,7 @@ static void bc_program_free(void)
bc_num_free(&G.prog.last);
bc_num_free(&G.prog.zero);
bc_num_free(&G.prog.one);
+ bc_vec_free(&G.stdin_buffer);
}
static void bc_vm_free(void)
@@ -7506,6 +7392,8 @@ static void bc_program_init(void)
bc_vec_init(&G.prog.results, sizeof(BcResult), bc_result_free);
bc_vec_init(&G.prog.stack, sizeof(BcInstPtr), NULL);
bc_vec_push(&G.prog.stack, &ip);
+
+ bc_char_vec_init(&G.stdin_buffer);
}
static int bc_vm_init(const char *env_len)
@@ -7562,6 +7450,7 @@ static BcStatus bc_vm_run(void)
# endif
FREE_G();
#endif
+ dbg_exec("exiting with exitcode %d", st);
return st;
}
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" \
"2\n9\n" \
"" "if (0) 1 else if (1) 2; 9"
+testing "bc define auto" \
+ "bc" \
+ "8\n9\n" \
+ "" "define w() { auto z; return 8; }; w(); 9"
+
tar xJf bc_large.tar.xz
for 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() {
u()
-if (x == -4) x
-else x - 4
+if (x == -4) x else x - 4