diff options
author | Jo-Philipp Wich <jo@mein.io> | 2020-09-22 14:14:21 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2020-09-22 14:14:21 +0200 |
commit | d36709f63e4bae54d5c9c3defd35eb3e23e64260 (patch) | |
tree | f567a64081e7650b9f85f290cda41592e6a9a929 | |
parent | fa947fd0d8ca3888c18f6106570f9831f72ae128 (diff) |
syntax: introduce case statement support
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r-- | eval.c | 152 | ||||
-rw-r--r-- | lexer.c | 26 | ||||
-rw-r--r-- | lexer.h | 2 | ||||
-rw-r--r-- | parser.y | 10 |
4 files changed, 137 insertions, 53 deletions
@@ -859,60 +859,57 @@ ut_execute_rel(struct ut_state *state, uint32_t off) return rv; } -static struct json_object * -ut_execute_equality(struct ut_state *state, uint32_t off) +static bool +ut_eq(struct json_object *v1, struct json_object *v2) { - struct ut_op *tag1, *tag2, *op = ut_get_op(state, off); - struct json_object *v[2], *rv; - enum json_type t1, t2; - bool equal = false; + struct ut_op *tag1 = json_object_get_userdata(v1); + struct ut_op *tag2 = json_object_get_userdata(v2); + enum json_type t1 = json_object_get_type(v1); + enum json_type t2 = json_object_get_type(v2); - ut_get_operands(state, op, v); + if ((tag1 ? tag1->type : 0) != (tag2 ? tag2->type : 0)) + return false; - tag1 = json_object_get_userdata(v[0]); - tag2 = json_object_get_userdata(v[1]); - t1 = json_object_get_type(v[0]); - t2 = json_object_get_type(v[1]); + if (t1 != t2) + return false; - if ((tag1 ? tag1->type : 0) != (tag2 ? tag2->type : 0)) { - equal = false; - } - else if (t1 != t2) { - equal = false; - } - else { - switch (t1) { - case json_type_array: - case json_type_object: - equal = (v[0] == v[1]); - break; + switch (t1) { + case json_type_array: + case json_type_object: + return (v1 == v2); - case json_type_boolean: - equal = (json_object_get_boolean(v[0]) == json_object_get_boolean(v[1])); - break; + case json_type_boolean: + return (json_object_get_boolean(v1) == json_object_get_boolean(v2)); - case json_type_double: - if (isnan(json_object_get_double(v[0])) || isnan(json_object_get_double(v[1]))) - equal = false; - else - equal = (json_object_get_double(v[0]) == json_object_get_double(v[1])); + case json_type_double: + if (isnan(json_object_get_double(v1)) || isnan(json_object_get_double(v2))) + return false; - break; + return (json_object_get_double(v1) == json_object_get_double(v2)); - case json_type_int: - equal = (json_object_get_int64(v[0]) == json_object_get_int64(v[1])); - break; + case json_type_int: + return (json_object_get_int64(v1) == json_object_get_int64(v2)); - case json_type_string: - equal = !strcmp(json_object_get_string(v[0]), json_object_get_string(v[1])); - break; + case json_type_string: + return !strcmp(json_object_get_string(v1), json_object_get_string(v2)); - case json_type_null: - equal = true; - break; - } + case json_type_null: + return true; } + return false; +} + +static struct json_object * +ut_execute_equality(struct ut_state *state, uint32_t off) +{ + struct ut_op *op = ut_get_op(state, off); + struct json_object *v[2], *rv; + bool equal = false; + + ut_get_operands(state, op, v); + + equal = ut_eq(v[0], v[1]); rv = json_object_new_boolean((op->type == T_EQS) ? equal : !equal); ut_putval(v[0]); @@ -1459,6 +1456,74 @@ ut_execute_try_catch(struct ut_state *state, uint32_t off) return rv; } +static bool +ut_match_case(struct ut_state *state, struct json_object *v, struct ut_op *Case) +{ + struct json_object *caseval = ut_execute_op_sequence(state, Case->tree.operand[0]); + bool rv = ut_eq(v, caseval); + + ut_putval(caseval); + return rv; +} + +static struct json_object * +ut_execute_switch_case(struct ut_state *state, uint32_t off) +{ + struct ut_op *Default = NULL, *Case = NULL, *jmp = NULL; + struct ut_op *op = ut_get_op(state, off); + struct json_object *v[1], *rv = NULL; + + ut_get_operands(state, op, v); + + /* First try to find matching case... */ + for (Case = ut_get_child(state, off, 1); + Case != NULL; + Case = ut_get_op(state, Case->tree.next)) + { + /* remember default case and throw on dupes */ + if (Case->type == T_DEFAULT) { + if (Default) { + ut_putval(v[0]); + + return ut_exception(state, ut_get_off(state, Case), + "Syntax error: more than one switch default case"); + } + + Default = Case; + continue; + } + + /* Found a matching case, remember jump offset */ + if (ut_match_case(state, v[0], Case)) { + jmp = Case; + break; + } + } + + /* jump to matching case (or default) and continue until break */ + for (Case = jmp ? jmp : Default; + Case != NULL; + Case = ut_get_op(state, Case->tree.next)) + { + ut_putval(rv); + + if (Case == Default) + rv = ut_execute_op_sequence(state, Default->tree.operand[0]); + else + rv = ut_execute_op_sequence(state, Case->tree.operand[1]); + + if (ut_is_type(rv, T_BREAK)) { + ut_putval(rv); + rv = NULL; + break; + } + } + + ut_putval(v[0]); + + return rv; +} + static struct json_object * ut_execute_op(struct ut_state *state, uint32_t off) { @@ -1621,6 +1686,9 @@ ut_execute_op(struct ut_state *state, uint32_t off) case T_TRY: return ut_execute_try_catch(state, off); + case T_SWITCH: + return ut_execute_switch_case(state, off); + default: return ut_exception(state, off, "Runtime error: Unrecognized opcode %d", op->type); } @@ -126,8 +126,10 @@ static const struct token reserved_words[] = { { T_CONTINUE, "continue", 8 }, { T_ENDWHILE, "endwhile", 8 }, { T_FUNC, "function", 8 }, + { T_DEFAULT, "default", 7 }, { T_RETURN, "return", 6 }, { T_ENDFOR, "endfor", 6 }, + { T_SWITCH, "switch", 6 }, { T_LOCAL, "local", 5 }, { T_ENDIF, "endif", 5 }, { T_WHILE, "while", 5 }, @@ -138,6 +140,7 @@ static const struct token reserved_words[] = { { T_ELSE, "else", 4 }, { T_THIS, "this", 4 }, { T_NULL, "null", 4 }, + { T_CASE, "case", 4 }, { T_NUMBER, "NaN", 3, parse_number }, { T_TRY, "try", 3 }, { T_FOR, "for", 3 }, @@ -146,12 +149,12 @@ static const struct token reserved_words[] = { const char *tokennames[__T_MAX] = { [0] = "End of file", - [T_FUNC] = "'function'", + [T_FUNC] = "'function'", [T_LOCAL] = "'local'", - [T_WHILE] = "'while", + [T_WHILE] = "'while", [T_ELSE] = "'else'", [T_FOR] = "'for'", - [T_IF] = "'if'", + [T_IF] = "'if'", [T_IN] = "'in'", [T_ASLEFT] = "'x<<=y'", [T_ASRIGHT] = "'x>>=y'", @@ -210,14 +213,17 @@ const char *tokennames[__T_MAX] = { [T_ENDIF] = "'endif'", [T_ENDFOR] = "'endfor'", [T_ENDWHILE] = "'endwhile'", - [T_ENDFUNC] = "'endfuncton'", - [T_RETURN] = "'return'", - [T_BREAK] = "'break'", - [T_CONTINUE] = "'continue'", + [T_ENDFUNC] = "'endfuncton'", + [T_RETURN] = "'return'", + [T_BREAK] = "'break'", + [T_CONTINUE] = "'continue'", [T_NULL] = "'null'", - [T_THIS] = "'this'", - [T_TRY] = "'try'", - [T_CATCH] = "'catch'", + [T_THIS] = "'this'", + [T_TRY] = "'try'", + [T_CATCH] = "'catch'", + [T_SWITCH] = "'switch'", + [T_CASE] = "'case'", + [T_DEFAULT] = "'default'", //[T_LSTM] = "'{%'", //[T_RSTM] = "'%}'" }; @@ -19,7 +19,7 @@ #include "ast.h" -#define __T_MAX 75 +#define __T_MAX 78 #define T_EXCEPTION (__T_MAX + 0) #define T_CFUNC (__T_MAX + 1) #define T_RESSOURCE (__T_MAX + 2) @@ -115,6 +115,7 @@ stmt(A) ::= sel_stmt(B). { A = B; } stmt(A) ::= iter_stmt(B). { A = B; } stmt(A) ::= func_stmt(B). { A = B; } stmt(A) ::= try_stmt(B). { A = B; } +stmt(A) ::= switch_stmt(B). { A = B; } stmt(A) ::= ret_stmt(B). { A = B; } stmt(A) ::= break_stmt(B). { A = B; } stmt(A) ::= decl_stmt(B). { A = B; } @@ -174,6 +175,15 @@ try_stmt(A) ::= T_TRY(B) try_catch_block(C) T_CATCH try_catch_block(D). try_catch_block(A) ::= cpd_stmt(B). { A = B; } try_catch_block(A) ::= empty_object. { A = 0; } +switch_stmt(A) ::= T_SWITCH(B) T_LPAREN exp(C) T_RPAREN T_LBRACE switch_cases(D) T_RBRACE. + { A = wrap_op(B, C, D); } + +switch_cases(A) ::= switch_cases(B) switch_case(C). { A = append_op(B, C); } +switch_cases(A) ::= switch_case(B). { A = B; } + +switch_case(A) ::= T_CASE(B) exp(C) T_COLON stmts(D). { A = wrap_op(B, C, D); } +switch_case(A) ::= T_DEFAULT(B) T_COLON stmts(C). { A = wrap_op(B, C); } + args(A) ::= args(B) T_COMMA T_LABEL(C). { A = append_op(B, C); } args(A) ::= T_LABEL(B). { A = B; } |