summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2020-11-12 18:56:45 +0100
committerJo-Philipp Wich <jo@mein.io>2020-11-12 18:56:45 +0100
commit225a2807f10ac02844eb2a7a65fdf7999a62ba66 (patch)
tree1d7a80adbd25096e9f55a0ee652d3286cb76de6a
parentc447e58ebf3b04f129009f642d5d1be4ca8cee92 (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.c24
-rw-r--r--parser.y106
-rw-r--r--tests/00_syntax/16_for_loop3
3 files changed, 62 insertions, 71 deletions
diff --git a/eval.c b/eval.c
index 7a76dde..50986e0 100644
--- a/eval.c
+++ b/eval.c
@@ -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);
}
}
diff --git a/parser.y b/parser.y
index e5f9853..d36ab0c 100644
--- a/parser.y
+++ b/parser.y
@@ -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 {})`