summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2023-11-06 15:59:17 +0100
committerJo-Philipp Wich <jo@mein.io>2023-11-06 16:33:51 +0100
commit1468cc406bf9b46ae7942791eaa78f74a185062e (patch)
treedfdd03718e33e6a5b1a358b15191d0009f1d7ac3
parentcfb24ea4f12131dcefe4f1ede2f51d3d16b88dec (diff)
syntax: don't treat `as` and `from` as reserved keywords
ECMAScript allows using `as` and `from` as identifiers so follow suit and don't treat them specially while parsing. Extend the compiler logic instead to check for TK_LABEL tokens with the expected value to properly parse import and export statements. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r--compiler.c57
-rw-r--r--include/ucode/lexer.h2
-rw-r--r--lexer.c2
-rw-r--r--tests/custom/99_bugs/44_compiler_as_from_identifier44
4 files changed, 91 insertions, 14 deletions
diff --git a/compiler.c b/compiler.c
index 43ad9da..17c0249 100644
--- a/compiler.c
+++ b/compiler.c
@@ -285,6 +285,40 @@ uc_compiler_parse_match(uc_compiler_t *compiler, uc_tokentype_t type)
return true;
}
+static bool
+uc_compiler_keyword_check(uc_compiler_t *compiler, const char *keyword)
+{
+ size_t keywordlen = strlen(keyword);
+
+ return (compiler->parser->curr.type == TK_LABEL &&
+ ucv_string_length(compiler->parser->curr.uv) == keywordlen &&
+ strcmp(ucv_string_get(compiler->parser->curr.uv), keyword) == 0);
+}
+
+static bool
+uc_compiler_keyword_match(uc_compiler_t *compiler, const char *keyword)
+{
+ if (!uc_compiler_keyword_check(compiler, keyword))
+ return false;
+
+ uc_compiler_parse_advance(compiler);
+
+ return true;
+}
+
+static void
+uc_compiler_keyword_consume(uc_compiler_t *compiler, const char *keyword)
+{
+ if (uc_compiler_keyword_check(compiler, keyword)) {
+ uc_compiler_parse_advance(compiler);
+
+ return;
+ }
+
+ uc_compiler_syntax_error(compiler, compiler->parser->curr.pos,
+ "Unexpected token\nExpecting '%s'", keyword);
+}
+
static void
uc_compiler_parse_synchronize(uc_compiler_t *compiler)
{
@@ -3130,7 +3164,7 @@ uc_compiler_compile_exportlist(uc_compiler_t *compiler)
ucv_string_get(label));
}
- if (uc_compiler_parse_match(compiler, TK_AS)) {
+ if (uc_compiler_keyword_match(compiler, "as")) {
if (uc_compiler_parse_match(compiler, TK_LABEL) || uc_compiler_parse_match(compiler, TK_STRING)) {
name = ucv_get(compiler->parser->prev.uv);
}
@@ -3553,7 +3587,7 @@ uc_compiler_compile_importlist(uc_compiler_t *compiler, uc_value_t *namelist)
label = NULL;
if (uc_compiler_parse_match(compiler, TK_DEFAULT)) {
- uc_compiler_parse_consume(compiler, TK_AS);
+ uc_compiler_keyword_consume(compiler, "as");
uc_compiler_parse_consume(compiler, TK_LABEL);
label = ucv_get(compiler->parser->prev.uv);
@@ -3561,7 +3595,7 @@ uc_compiler_compile_importlist(uc_compiler_t *compiler, uc_value_t *namelist)
else if (uc_compiler_parse_match(compiler, TK_STRING)) {
name = ucv_get(compiler->parser->prev.uv);
- uc_compiler_parse_consume(compiler, TK_AS);
+ uc_compiler_keyword_consume(compiler, "as");
uc_compiler_parse_consume(compiler, TK_LABEL);
label = ucv_get(compiler->parser->prev.uv);
@@ -3569,7 +3603,7 @@ uc_compiler_compile_importlist(uc_compiler_t *compiler, uc_value_t *namelist)
else if (uc_compiler_parse_match(compiler, TK_LABEL)) {
name = ucv_get(compiler->parser->prev.uv);
- if (uc_compiler_parse_match(compiler, TK_AS)) {
+ if (uc_compiler_keyword_match(compiler, "as")) {
uc_compiler_parse_consume(compiler, TK_LABEL);
label = ucv_get(compiler->parser->prev.uv);
@@ -3588,9 +3622,12 @@ uc_compiler_compile_importlist(uc_compiler_t *compiler, uc_value_t *namelist)
ucv_put(label);
if (uc_compiler_parse_match(compiler, TK_RBRACE))
- break;
+ return;
}
while (uc_compiler_parse_match(compiler, TK_COMMA));
+
+ uc_compiler_syntax_error(compiler, compiler->parser->curr.pos,
+ "Unexpected token\nExpecting 'as', ',' or '}'");
}
static void
@@ -3610,19 +3647,19 @@ uc_compiler_compile_import(uc_compiler_t *compiler)
/* import { ... } from */
if (uc_compiler_parse_match(compiler, TK_LBRACE)) {
uc_compiler_compile_importlist(compiler, namelist);
- uc_compiler_parse_consume(compiler, TK_FROM);
+ uc_compiler_keyword_consume(compiler, "from");
}
/* import * as name from */
else if (uc_compiler_parse_match(compiler, TK_MUL)) {
- uc_compiler_parse_consume(compiler, TK_AS);
+ uc_compiler_keyword_consume(compiler, "as");
uc_compiler_parse_consume(compiler, TK_LABEL);
uc_compiler_declare_local(compiler, compiler->parser->prev.uv, true);
uc_compiler_initialize_local(compiler);
ucv_array_push(namelist, ucv_boolean_new(true));
- uc_compiler_parse_consume(compiler, TK_FROM);
+ uc_compiler_keyword_consume(compiler, "from");
}
/* import defaultExport [, ... ] from */
@@ -3639,7 +3676,7 @@ uc_compiler_compile_import(uc_compiler_t *compiler)
/* import defaultExport, * as name from */
else if (uc_compiler_parse_match(compiler, TK_MUL)) {
- uc_compiler_parse_consume(compiler, TK_AS);
+ uc_compiler_keyword_consume(compiler, "as");
uc_compiler_parse_consume(compiler, TK_LABEL);
uc_compiler_declare_local(compiler, compiler->parser->prev.uv, true);
@@ -3654,7 +3691,7 @@ uc_compiler_compile_import(uc_compiler_t *compiler)
}
}
- uc_compiler_parse_consume(compiler, TK_FROM);
+ uc_compiler_keyword_consume(compiler, "from");
}
uc_compiler_parse_consume(compiler, TK_STRING);
diff --git a/include/ucode/lexer.h b/include/ucode/lexer.h
index c013aac..1728aa3 100644
--- a/include/ucode/lexer.h
+++ b/include/ucode/lexer.h
@@ -119,8 +119,6 @@ typedef enum {
TK_TEMPLATE,
TK_IMPORT,
TK_EXPORT,
- TK_FROM,
- TK_AS,
TK_EOF,
TK_ERROR
diff --git a/lexer.c b/lexer.c
index 28188c3..53f00f5 100644
--- a/lexer.c
+++ b/lexer.c
@@ -68,13 +68,11 @@ static const struct keyword reserved_words[] = {
{ TK_THIS, "this", 4 },
{ TK_NULL, "null", 4 },
{ TK_CASE, "case", 4 },
- { TK_FROM, "from", 4 },
{ TK_TRY, "try", 3 },
{ TK_FOR, "for", 3 },
{ TK_LOCAL, "let", 3 },
{ TK_IF, "if", 2 },
{ TK_IN, "in", 2 },
- { TK_AS, "as", 2 },
};
diff --git a/tests/custom/99_bugs/44_compiler_as_from_identifier b/tests/custom/99_bugs/44_compiler_as_from_identifier
new file mode 100644
index 0000000..6897cb0
--- /dev/null
+++ b/tests/custom/99_bugs/44_compiler_as_from_identifier
@@ -0,0 +1,44 @@
+Ensure that `as` and `from` are valid identifiers while their special
+meaning in import statements is retained.
+
+-- Testcase --
+import { foo as bar } from 'mod';
+import * as mod from 'mod';
+
+function fn(as, from) {
+ return as + from;
+}
+
+as = 1;
+from = true;
+
+printf("%.J\n", [
+ bar,
+ mod,
+ fn(1, 2),
+ as,
+ from
+]);
+-- End --
+
+-- File mod.uc --
+export let foo = false;
+export default 'test';
+-- End --
+
+-- Args --
+-R -L files/
+-- End --
+
+-- Expect stdout --
+[
+ false,
+ {
+ "foo": false,
+ "default": "test"
+ },
+ 3,
+ 1,
+ true
+]
+-- End --