From ff6811f29065951ab3917460f3d76ffe6ddb0c81 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Tue, 18 May 2021 10:45:21 +0200 Subject: syntax: implement `delete` as proper operator Turn `delete` into a proper operator mimicking ECMAScript semantics. Also ensure to transparently turn deprecated `delete(obj, propname)` function calls into `delete obj.propname` expressions during compilation. When strict mode is active, legacy delete() calls throw a syntax error instead. Finally drop the `delete()` function from the stdlib as it is shadowed by the delete operator syntax now. Signed-off-by: Jo-Philipp Wich --- compiler.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'compiler.c') diff --git a/compiler.c b/compiler.c index 889a8dd..cf9ca25 100644 --- a/compiler.c +++ b/compiler.c @@ -24,6 +24,7 @@ static void uc_compiler_compile_unary(uc_compiler *compiler, bool assignable); static void uc_compiler_compile_binary(uc_compiler *compiler, bool assignable); +static void uc_compiler_compile_delete(uc_compiler *compiler, bool assignable); static void uc_compiler_compile_paren(uc_compiler *compiler, bool assignable); static void uc_compiler_compile_call(uc_compiler *compiler, bool assignable); static void uc_compiler_compile_post_inc(uc_compiler *compiler, bool assignable); @@ -50,6 +51,7 @@ uc_compiler_parse_rules[TK_ERROR + 1] = { [TK_ADD] = { uc_compiler_compile_unary, uc_compiler_compile_binary, P_ADD }, [TK_COMPL] = { uc_compiler_compile_unary, NULL, P_UNARY }, [TK_NOT] = { uc_compiler_compile_unary, NULL, P_UNARY }, + [TK_DELETE] = { uc_compiler_compile_delete, NULL, P_UNARY }, [TK_INC] = { uc_compiler_compile_unary, uc_compiler_compile_post_inc, P_INC }, [TK_DEC] = { uc_compiler_compile_unary, uc_compiler_compile_post_inc, P_INC }, [TK_DIV] = { NULL, uc_compiler_compile_binary, P_MUL }, @@ -966,6 +968,57 @@ uc_compiler_compile_binary(uc_compiler *compiler, bool assignable) } } +static void +uc_compiler_compile_delete(uc_compiler *compiler, bool assignable) +{ + uc_chunk *chunk = uc_compiler_current_chunk(compiler); + enum insn_type type; + + /* If the delete keyword is followed by an opening paren, it might be a + * legacy delete(object, propname) call */ + if (uc_compiler_parse_match(compiler, TK_LPAREN)) { + uc_compiler_parse_precedence(compiler, P_ASSIGN); + + if (uc_compiler_parse_match(compiler, TK_RPAREN)) { + type = chunk->entries[compiler->last_insn]; + + if (type != I_LVAL) + uc_compiler_syntax_error(compiler, 0, + "expecting a property access expression"); + + chunk->entries[compiler->last_insn] = I_DELETE; + } + else if (uc_compiler_parse_match(compiler, TK_COMMA)) { + if (uc_compiler_is_strict(compiler)) { + uc_compiler_syntax_error(compiler, 0, + "attempt to apply 'delete' operator on non-property access expression"); + } + else { + uc_compiler_parse_precedence(compiler, P_ASSIGN); + uc_compiler_emit_insn(compiler, 0, I_DELETE); + uc_compiler_parse_consume(compiler, TK_RPAREN); + } + } + else { + uc_compiler_syntax_error(compiler, 0, "expecting ')' or ','"); + } + } + + /* Otherwise compile expression, ensure that it results in a property + * access (I_LVAL) and overwrite it with delete operation. */ + else { + uc_compiler_parse_precedence(compiler, P_UNARY); + + type = chunk->entries[compiler->last_insn]; + + if (type != I_LVAL) + uc_compiler_syntax_error(compiler, 0, + "expecting a property access expression"); + + chunk->entries[compiler->last_insn] = I_DELETE; + } +} + static enum insn_type uc_compiler_emit_variable_rw(uc_compiler *compiler, uc_value_t *varname, uc_tokentype_t type) { -- cgit v1.2.3 From ed32c42eefbc4560408d53445c603767469c85bd Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Tue, 18 May 2021 11:12:01 +0200 Subject: compiler, lexer: add NO_LEGACY define to disable legacy syntax features Signed-off-by: Jo-Philipp Wich --- CMakeLists.txt | 6 ++++++ compiler.c | 5 ++++- lexer.c | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'compiler.c') diff --git a/CMakeLists.txt b/CMakeLists.txt index 0dfa62a..1452631 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,8 @@ OPTION(MATH_SUPPORT "Math plugin support" ON) OPTION(UBUS_SUPPORT "Ubus plugin support" ON) OPTION(UCI_SUPPORT "UCI plugin support" ON) +OPTION(LEGACY_SUPPORT "Support deprecated syntax features" ON) + SET(LIB_SEARCH_PATH "/usr/lib/ucode/*.so:/usr/share/ucode/*.uc:./*.so:./*.uc" CACHE STRING "Default library search path") ADD_DEFINITIONS(-DLIB_SEARCH_PATH="${LIB_SEARCH_PATH}") @@ -31,6 +33,10 @@ ELSE() ADD_DEFINITIONS(-DNDEBUG) ENDIF() +IF(NOT LEGACY_SUPPORT) + ADD_DEFINITIONS(-DNO_LEGACY) +ENDIF() + INCLUDE(FindPkgConfig) PKG_CHECK_MODULES(JSONC json-c json) IF(JSONC_FOUND) diff --git a/compiler.c b/compiler.c index cf9ca25..5a9b579 100644 --- a/compiler.c +++ b/compiler.c @@ -974,6 +974,7 @@ uc_compiler_compile_delete(uc_compiler *compiler, bool assignable) uc_chunk *chunk = uc_compiler_current_chunk(compiler); enum insn_type type; +#ifndef NO_LEGACY /* If the delete keyword is followed by an opening paren, it might be a * legacy delete(object, propname) call */ if (uc_compiler_parse_match(compiler, TK_LPAREN)) { @@ -1006,7 +1007,9 @@ uc_compiler_compile_delete(uc_compiler *compiler, bool assignable) /* Otherwise compile expression, ensure that it results in a property * access (I_LVAL) and overwrite it with delete operation. */ - else { + else +#endif /* NO_LEGACY */ + { uc_compiler_parse_precedence(compiler, P_UNARY); type = chunk->entries[compiler->last_insn]; diff --git a/lexer.c b/lexer.c index 84af45d..cc35d66 100644 --- a/lexer.c +++ b/lexer.c @@ -145,7 +145,9 @@ static const struct keyword reserved_words[] = { { TK_RETURN, "return", 6, { 0 } }, { TK_ENDFOR, "endfor", 6, { 0 } }, { TK_SWITCH, "switch", 6, { 0 } }, +#ifndef NO_LEGACY { TK_LOCAL, "local", 5, { 0 } }, +#endif { TK_ENDIF, "endif", 5, { 0 } }, { TK_WHILE, "while", 5, { 0 } }, { TK_BREAK, "break", 5, { 0 } }, -- cgit v1.2.3