diff options
author | Jo-Philipp Wich <jo@mein.io> | 2020-11-12 18:56:45 +0100 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2020-11-12 18:56:45 +0100 |
commit | 225a2807f10ac02844eb2a7a65fdf7999a62ba66 (patch) | |
tree | 1d7a80adbd25096e9f55a0ee652d3286cb76de6a | |
parent | c447e58ebf3b04f129009f642d5d1be4ca8cee92 (diff) |
ast: simplify declaration AST structure
Do not emit dummy T_ASSIGN nodes for plain variable declarations without
initialization (`let foo` or `local foo`).
This also allows simplifying `ut_check_for_in()` since we only need to
deal with one common structure for both `for (... in ...)` and
`for (local ... in ...)` cases.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r-- | eval.c | 24 | ||||
-rw-r--r-- | parser.y | 106 | ||||
-rw-r--r-- | tests/00_syntax/16_for_loop | 3 |
3 files changed, 62 insertions, 71 deletions
@@ -416,22 +416,34 @@ ut_execute_local(struct ut_state *state, uint32_t off) { struct ut_op *op = ut_get_op(state, off), *asop, *alop; uint32_t as = op ? op->tree.operand[0] : 0; - struct json_object *label, *val, *rv = NULL; + struct json_object *val, *rv = NULL; while (as) { asop = ut_get_op(state, as); - alop = asop ? ut_get_child(state, as, 0) : NULL; as = asop ? asop->tree.next : 0; - if (alop) { - label = alop->val; - val = asop->tree.operand[1] ? ut_execute_op_sequence(state, asop->tree.operand[1]) : NULL; + switch (asop ? asop->type : 0) { + case T_ASSIGN: + alop = ut_get_op(state, asop->tree.operand[0]); + val = ut_execute_op_sequence(state, asop->tree.operand[1]); if (ut_is_type(val, T_EXCEPTION)) return val; + break; + + case T_LABEL: + alop = asop; + val = NULL; + break; + + default: + continue; + } + + if (alop) { json_object_put(rv); - rv = ut_setval(state->scope->scope, label, val); + rv = ut_setval(state->scope->scope, alop->val, val); } } @@ -103,13 +103,28 @@ ut_expect_token(struct ut_state *s, uint32_t off, int token) } static inline uint32_t -ut_check_op_seq_type(struct ut_state *s, uint32_t off, int type) +_ut_check_op_seq_types(struct ut_state *s, uint32_t off, ...) { + uint64_t tokens[(__T_MAX + 63) & -64] = {}; struct ut_op *arg = ut_get_op(s, off); + int token, max_token = 0; + va_list ap; + + va_start(ap, off); + + while ((token = va_arg(ap, int)) != 0) { + tokens[token / 64] |= ((uint64_t)1 << (token % 64)); + max_token = (token > max_token) ? token : max_token; + } + + va_end(ap); while (arg) { - if (arg->type != type) - return ut_expect_token(s, ut_get_off(s, arg), type); + if (!(tokens[arg->type / 64] & ((uint64_t)1 << (arg->type % 64)))) { + ut_parse_error(s, off, tokens, max_token); + + return 0; + } arg = ut_get_op(s, arg->tree.next); } @@ -117,6 +132,8 @@ ut_check_op_seq_type(struct ut_state *s, uint32_t off, int type) return off; } +#define ut_check_op_seq_types(s, off, ...) _ut_check_op_seq_types(s, off, __VA_ARGS__, 0) + static inline uint32_t ut_reject_local(struct ut_state *s, uint32_t off) { @@ -138,76 +155,39 @@ ut_check_for_in(struct ut_state *s, uint32_t off) struct ut_op *arg; uint32_t idx = 0; - /* for (let ... in ...) */ - if (op->type == T_LOCAL) { - arg = ut_get_op(s, op->tree.operand[0]); - - if (arg->type == T_ASSIGN) { - if (arg->tree.operand[1]) - return ut_expect_token(s, op->tree.operand[0], T_COMMA); - - if (!arg->tree.next) { - arg = ut_get_op(s, arg->tree.operand[0]); - ut_new_exception(s, arg->off + json_object_get_string_len(arg->val), - "Syntax error: Unexpected token\nExpecting ',' or 'in'"); - - return 0; - } - - idx = arg->tree.operand[0]; - arg = ut_get_op(s, arg->tree.next); - } + arg = (op->type == T_LOCAL) ? ut_get_op(s, op->tree.operand[0]) : op; - if (arg->type != T_IN || arg->tree.next) { - if (arg->type == T_IN && arg->tree.next) - arg = ut_get_op(s, arg->tree.next); + if (arg->type == T_LABEL) { + idx = ut_get_off(s, arg); - ut_new_exception(s, arg->off, "Syntax error: Invalid for-in expression"); + if (!arg->tree.next) { + ut_new_exception(s, arg->off + json_object_get_string_len(arg->val), + "Syntax error: Unexpected token\nExpecting ',' or 'in'"); return 0; } - /* transform T_LOCAL(T_ASSIGN(T_LABEL)->T_IN(T_LABEL,...)) into - * T_LOCAL(T_IN(T_LABEL->T_LABEL,...)) */ - if (idx) - arg->tree.operand[0] = append_op(idx, arg->tree.operand[0]); - - op->tree.operand[0] = ut_get_off(s, arg); - op->tree.operand[1] = 0; + arg = ut_get_op(s, arg->tree.next); } - /* for (... in ...) */ - else { - arg = op; - - if (arg->type == T_LABEL) { - idx = off; - - if (!arg->tree.next) { - ut_new_exception(s, arg->off + json_object_get_string_len(arg->val), - "Syntax error: Unexpected token\nExpecting ',' or 'in'"); - - return 0; - } - + if (arg->type != T_IN || arg->tree.next || ut_get_op(s, arg->tree.operand[0])->type != T_LABEL) { + if (arg->type == T_IN && arg->tree.next) arg = ut_get_op(s, arg->tree.next); - } - if (arg->type != T_IN || arg->tree.next || ut_get_op(s, arg->tree.operand[0])->type != T_LABEL) { - if (arg->type == T_IN && arg->tree.next) - arg = ut_get_op(s, arg->tree.next); + ut_new_exception(s, arg->off, "Syntax error: Invalid for-in expression"); - ut_new_exception(s, arg->off, "Syntax error: Invalid for-in expression"); + return 0; + } - return 0; - } + /* transform T_LABEL->T_IN(T_LABEL, ...) into T_IN(T_LABEL->T_LABEL, ...) */ + if (idx) { + ut_get_op(s, idx)->tree.next = 0; + arg->tree.operand[0] = append_op(idx, arg->tree.operand[0]); - /* transform T_LABEL->T_IN(T_LABEL,...) into T_IN(T_LABEL->T_LABEL,...) */ - if (idx) { - op->tree.next = 0; - arg->tree.operand[0] = append_op(idx, arg->tree.operand[0]); + if (op->type == T_LOCAL) + op->tree.operand[0] = ut_get_off(s, arg); + else off = ut_get_off(s, arg); - } } return off; @@ -347,14 +327,14 @@ ret_stmt(A) ::= T_RETURN(B) T_SCOL. { A = B; } break_stmt(A) ::= T_BREAK(B) T_SCOL. { A = B; } break_stmt(A) ::= T_CONTINUE(B) T_SCOL. { A = B; } -decl_stmt(A) ::= T_LOCAL(B) decls(C) T_SCOL. { A = wrap_op(B, ut_check_op_seq_type(s, C, T_ASSIGN)); } +decl_stmt(A) ::= T_LOCAL(B) decls(C) T_SCOL. { A = wrap_op(B, ut_check_op_seq_types(s, C, T_ASSIGN, T_LABEL)); } decls(A) ::= decls(B) T_COMMA decl(C). { A = append_op(B, C); } decls(A) ::= decl(B). { A = B; } decl(A) ::= T_LABEL(B) T_ASSIGN(C) arrow_exp(D). { A = wrap_op(C, B, D); } decl(A) ::= T_LABEL(B) T_IN(C) arrow_exp(D). { A = wrap_op(C, B, D); } -decl(A) ::= T_LABEL(B). { A = new_op(T_ASSIGN, NULL, B); ut_get_op(s, A)->off = ut_get_op(s, B)->off; } +decl(A) ::= T_LABEL(B). { A = B; } arrowfn_body(A) ::= cpd_stmt(B). { A = B; } arrowfn_body(A) ::= assign_exp(B). { A = no_empty_obj(B); } @@ -378,13 +358,13 @@ assign_exp(A) ::= unary_exp(B) T_ASBOR arrow_exp(C). { A = new_op(T_BOR, NULL, B assign_exp(A) ::= arrow_exp(B). { A = B; } arrow_exp(A) ::= unary_exp(B) T_ARROW(C) arrowfn_body(D). - { A = wrap_op(C, 0, ut_check_op_seq_type(s, B, T_LABEL), D); } + { A = wrap_op(C, 0, ut_check_op_seq_types(s, B, T_LABEL), D); } arrow_exp(A) ::= T_LPAREN T_RPAREN T_ARROW(C) arrowfn_body(D). { A = wrap_op(C, 0, 0, D); } arrow_exp(A) ::= T_LPAREN T_ELLIP T_LABEL(B) T_RPAREN T_ARROW(C) arrowfn_body(D). { A = wrap_op(C, 0, B, D); ut_get_op(s, B)->is_ellip = 1; } arrow_exp(A) ::= T_LPAREN exp(B) T_COMMA T_ELLIP T_LABEL(C) T_RPAREN T_ARROW(D) arrowfn_body(E). - { A = append_op(B, C); A = wrap_op(D, 0, ut_check_op_seq_type(s, A, T_LABEL), E); ut_get_op(s, C)->is_ellip = 1; } + { A = append_op(B, C); A = wrap_op(D, 0, ut_check_op_seq_types(s, A, T_LABEL), E); ut_get_op(s, C)->is_ellip = 1; } arrow_exp(A) ::= ternary_exp(B). { A = B; } ternary_exp(A) ::= or_exp(B) T_QMARK(C) assign_exp(D) T_COLON ternary_exp(E). diff --git a/tests/00_syntax/16_for_loop b/tests/00_syntax/16_for_loop index 2f8ca9f..a16e0cc 100644 --- a/tests/00_syntax/16_for_loop +++ b/tests/00_syntax/16_for_loop @@ -238,8 +238,7 @@ In line 2, byte 16: Ensure that assignments in for-in loop expressions are rejected. -- Expect stderr -- -Syntax error: Unexpected token -Expecting ',' +Syntax error: Invalid for-in expression In line 2, byte 15: ` for (local x = 1, y in {})` |