From ba6bf27a7ddcab8ff40fc3f8d714760f0f45b383 Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Fri, 29 May 2020 20:00:32 +0300 Subject: [PATCH] Parser refactoring. --- auto/sources | 2 - src/njs_function.c | 7 +- src/njs_lexer.c | 164 +- src/njs_lexer.h | 91 +- src/njs_lexer_tables.h | 491 +- src/njs_module.c | 175 +- src/njs_module.h | 3 +- src/njs_parser.c | 8860 +++++++++++++++++++++++++++++------ src/njs_parser.h | 308 +- src/njs_parser_expression.c | 1121 ----- src/njs_parser_terminal.c | 1296 ----- src/njs_regexp.c | 95 +- src/njs_regexp.h | 4 +- src/njs_variable.c | 24 +- src/njs_vm.c | 3 +- src/test/njs_unit_test.c | 57 +- test/njs_expect_test.exp | 4 +- utils/lexer_keyword.py | 131 +- 18 files changed, 8342 insertions(+), 4494 deletions(-) delete mode 100644 src/njs_parser_expression.c delete mode 100644 src/njs_parser_terminal.c diff --git a/auto/sources b/auto/sources index a14cd0ca..66381b57 100644 --- a/auto/sources +++ b/auto/sources @@ -49,8 +49,6 @@ NJS_LIB_SRCS=" \ src/njs_lexer.c \ src/njs_lexer_keyword.c \ src/njs_parser.c \ - src/njs_parser_terminal.c \ - src/njs_parser_expression.c \ src/njs_generator.c \ src/njs_disassembler.c \ src/njs_array_buffer.c \ diff --git a/src/njs_function.c b/src/njs_function.c index 1fc44b37..a50cde43 100644 --- a/src/njs_function.c +++ b/src/njs_function.c @@ -889,7 +889,9 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return ret; } - njs_chb_append_literal(&chain, ","); + if (i != (nargs - 2)) { + njs_chb_append_literal(&chain, ","); + } } njs_chb_append_literal(&chain, "){"); @@ -923,9 +925,10 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, return ret; } + parser->vm = vm; parser->lexer = &lexer; - ret = njs_parser(vm, parser, NULL); + ret = njs_parser(parser, NULL); if (njs_slow_path(ret != NJS_OK)) { return ret; } diff --git a/src/njs_lexer.c b/src/njs_lexer.c index 6f8308f5..215f005e 100644 --- a/src/njs_lexer.c +++ b/src/njs_lexer.c @@ -27,10 +27,6 @@ static void njs_lexer_multi(njs_lexer_t *lexer, njs_lexer_token_t *token, const njs_lexer_multi_t *multi, size_t length); static void njs_lexer_division(njs_lexer_t *lexer, njs_lexer_token_t *token); -static njs_lexer_token_t *njs_lexer_token_push(njs_vm_t *vm, - njs_lexer_t *lexer); -static njs_lexer_token_t *njs_lexer_token_pop(njs_lexer_t *lexer); - const njs_lvlhsh_proto_t njs_lexer_hash_proto njs_aligned(64) = @@ -311,128 +307,134 @@ njs_lexer_init(njs_vm_t *vm, njs_lexer_t *lexer, njs_str_t *file, } -njs_token_type_t -njs_lexer_token(njs_vm_t *vm, njs_lexer_t *lexer) +njs_inline njs_lexer_token_t * +njs_lexer_next_token(njs_lexer_t *lexer) { - njs_lexer_token_t *lt; - - lexer->prev_start = lexer->start; + njs_int_t ret; + njs_lexer_token_t *token; - if (lexer->token != NULL) { - lexer->prev_type = lexer->token->type; - njs_mp_free(vm->mem_pool, lexer->token); + token = njs_mp_zalloc(lexer->mem_pool, sizeof(njs_lexer_token_t)); + if (njs_slow_path(token == NULL)) { + return NULL; } - if (njs_queue_is_empty(&lexer->preread)) { - lt = njs_lexer_token_push(vm, lexer); - if (njs_slow_path(lt == NULL)) { - return NJS_TOKEN_ERROR; + do { + ret = njs_lexer_make_token(lexer, token); + if (njs_slow_path(ret != NJS_OK)) { + return NULL; } - } - lexer->token = njs_lexer_token_pop(lexer); + } while (token->type == NJS_TOKEN_COMMENT); + + njs_queue_insert_tail(&lexer->preread, &token->link); - return lexer->token->type; + return token; } -njs_token_type_t -njs_lexer_peek_token(njs_vm_t *vm, njs_lexer_t *lexer, size_t offset) +njs_lexer_token_t * +njs_lexer_token(njs_lexer_t *lexer, njs_bool_t with_end_line) { - size_t i; - njs_queue_link_t *link; - njs_lexer_token_t *lt; - - /* GCC and Clang complain about uninitialized lt. */ - lt = NULL; - - link = njs_queue_first(&lexer->preread); + njs_queue_link_t *lnk; + njs_lexer_token_t *token; - for (i = 0; i <= offset; i++) { + lnk = njs_queue_first(&lexer->preread); - if (link != njs_queue_tail(&lexer->preread)) { + while (lnk != njs_queue_head(&lexer->preread)) { + token = njs_queue_link_data(lnk, njs_lexer_token_t, link); - lt = njs_queue_link_data(link, njs_lexer_token_t, link); + if (!with_end_line && token->type == NJS_TOKEN_LINE_END) { + lexer->prev_type = token->type; - /* NJS_TOKEN_DIVISION stands for regexp literal. */ + lnk = njs_queue_next(&token->link); + continue; + } - if (lt->type == NJS_TOKEN_DIVISION || lt->type == NJS_TOKEN_END) { - break; - } + return token; + } - link = njs_queue_next(link); + do { + token = njs_lexer_next_token(lexer); + if (token == NULL) { + return NULL; + } - } else { + if (!with_end_line && token->type == NJS_TOKEN_LINE_END) { + lexer->prev_type = token->type; + continue; + } - lt = njs_lexer_token_push(vm, lexer); + break; - if (njs_slow_path(lt == NULL)) { - return NJS_TOKEN_ERROR; - } - } - } + } while (1); - return lt->type; + return token; } -njs_int_t -njs_lexer_rollback(njs_vm_t *vm, njs_lexer_t *lexer) +njs_lexer_token_t * +njs_lexer_peek_token(njs_lexer_t *lexer, njs_lexer_token_t *current, + njs_bool_t with_end_line) { - njs_lexer_token_t *lt; - - lt = njs_mp_zalloc(vm->mem_pool, sizeof(njs_lexer_token_t)); - if (njs_slow_path(lt == NULL)) { - return NJS_ERROR; - } - - *lt = *lexer->token; - - njs_queue_insert_head(&lexer->preread, <->link); + njs_queue_link_t *lnk; + njs_lexer_token_t *token; - return NJS_OK; -} + lnk = njs_queue_next(¤t->link); + while (lnk != njs_queue_head(&lexer->preread)) { + token = njs_queue_link_data(lnk, njs_lexer_token_t, link); -static njs_lexer_token_t * -njs_lexer_token_push(njs_vm_t *vm, njs_lexer_t *lexer) -{ - njs_int_t ret; - njs_lexer_token_t *token; + if (!with_end_line && token->type == NJS_TOKEN_LINE_END) { + lnk = njs_queue_next(&token->link); + continue; + } - token = njs_mp_zalloc(vm->mem_pool, sizeof(njs_lexer_token_t)); - if (njs_slow_path(token == NULL)) { - return NULL; + return token; } do { - ret = njs_lexer_next_token(lexer, token); - if (njs_slow_path(ret != NJS_OK)) { + token = njs_lexer_next_token(lexer); + if (token == NULL) { return NULL; } - } while (token->type == NJS_TOKEN_COMMENT); + if (!with_end_line && token->type == NJS_TOKEN_LINE_END) { + continue; + } - njs_queue_insert_tail(&lexer->preread, &token->link); + break; + + } while (1); return token; } -static njs_lexer_token_t * -njs_lexer_token_pop(njs_lexer_t *lexer) +void +njs_lexer_consume_token(njs_lexer_t *lexer, unsigned length) { - njs_queue_link_t *lnk; + njs_queue_link_t *lnk; + njs_lexer_token_t *token; - lnk = njs_queue_first(&lexer->preread); - njs_queue_remove(lnk); + while (length > 0) { + lnk = njs_queue_first(&lexer->preread); + token = njs_queue_link_data(lnk, njs_lexer_token_t, link); + + lexer->prev_type = token->type; + + if (token->type != NJS_TOKEN_LINE_END) { + length--; + } - return njs_queue_link_data(lnk, njs_lexer_token_t, link); + njs_queue_remove(lnk); + + njs_mp_free(lexer->mem_pool, token); + } } njs_int_t -njs_lexer_next_token(njs_lexer_t *lexer, njs_lexer_token_t *token) +njs_lexer_make_token(njs_lexer_t *lexer, njs_lexer_token_t *token) { u_char c, *p; @@ -446,7 +448,6 @@ njs_lexer_next_token(njs_lexer_t *lexer, njs_lexer_token_t *token) } } - lexer->keyword = 0; token->type = njs_tokens[c]; switch (token->type) { @@ -565,6 +566,7 @@ njs_lexer_next_token(njs_lexer_t *lexer, njs_lexer_token_t *token) /* Fall through. */ default: + token->line = lexer->line; token->text.start = lexer->start - 1; token->text.length = lexer->start - token->text.start; @@ -694,12 +696,14 @@ njs_lexer_word(njs_lexer_t *lexer, njs_lexer_token_t *token) } token->type = NJS_TOKEN_NAME; + token->keyword_type = NJS_KEYWORD_TYPE_UNDEF; } else { entry = &key_entry->value->entry; token->type = key_entry->value->type; - lexer->keyword = 1; + token->keyword_type = NJS_KEYWORD_TYPE_KEYWORD; + token->keyword_type |= key_entry->value->reserved; } token->unique_id = (uintptr_t) entry; diff --git a/src/njs_lexer.h b/src/njs_lexer.h index 670dbab3..6689deda 100644 --- a/src/njs_lexer.h +++ b/src/njs_lexer.h @@ -99,6 +99,7 @@ typedef enum { NJS_TOKEN_COALESCE, NJS_TOKEN_IN, + NJS_TOKEN_OF, NJS_TOKEN_INSTANCEOF, NJS_TOKEN_TYPEOF, NJS_TOKEN_VOID, @@ -178,7 +179,14 @@ typedef enum { NJS_TOKEN_IMPORT, NJS_TOKEN_EXPORT, + NJS_TOKEN_TARGET, + + NJS_TOKEN_FROM, + + NJS_TOKEN_META, + NJS_TOKEN_AWAIT, + NJS_TOKEN_ASYNC, NJS_TOKEN_CLASS, NJS_TOKEN_CONST, NJS_TOKEN_DEBUGGER, @@ -198,6 +206,13 @@ typedef enum { } njs_token_type_t; +typedef enum { + NJS_KEYWORD_TYPE_UNDEF = 0, + NJS_KEYWORD_TYPE_RESERVED = 1, + NJS_KEYWORD_TYPE_KEYWORD = 2 +} njs_keyword_type_t; + + typedef struct { njs_str_t name; } njs_lexer_entry_t; @@ -206,6 +221,7 @@ typedef struct { typedef struct { njs_lexer_entry_t entry; njs_token_type_t type; + njs_bool_t reserved; } njs_keyword_t; @@ -220,6 +236,7 @@ typedef struct { typedef struct { njs_token_type_t type:16; + njs_keyword_type_t keyword_type; uint32_t line; uintptr_t unique_id; njs_str_t text; @@ -231,10 +248,10 @@ typedef struct { typedef struct { njs_lexer_token_t *token; njs_queue_t preread; /* of njs_lexer_token_t */ - uint8_t keyword; u_char *prev_start; njs_token_type_t prev_type:16; + njs_token_type_t last_type:16; uint32_t line; njs_str_t file; @@ -251,12 +268,12 @@ typedef struct { njs_int_t njs_lexer_init(njs_vm_t *vm, njs_lexer_t *lexer, njs_str_t *file, u_char *start, u_char *end); -njs_token_type_t njs_lexer_token(njs_vm_t *vm, njs_lexer_t *lexer); -njs_token_type_t njs_lexer_peek_token(njs_vm_t *vm, njs_lexer_t *lexer, - size_t offset); -njs_int_t njs_lexer_rollback(njs_vm_t *vm, njs_lexer_t *lexer); - -njs_int_t njs_lexer_next_token(njs_lexer_t *lexer, njs_lexer_token_t *token); +njs_lexer_token_t *njs_lexer_token(njs_lexer_t *lexer, + njs_bool_t with_end_line); +njs_lexer_token_t *njs_lexer_peek_token(njs_lexer_t *lexer, + njs_lexer_token_t *current, njs_bool_t with_end_line); +void njs_lexer_consume_token(njs_lexer_t *lexer, unsigned length); +njs_int_t njs_lexer_make_token(njs_lexer_t *lexer, njs_lexer_token_t *token); const njs_lexer_keyword_entry_t *njs_lexer_keyword(const u_char *key, size_t length); @@ -270,6 +287,66 @@ njs_lexer_entry(uintptr_t unique_id) } +njs_inline njs_bool_t +njs_lexer_token_is_keyword(njs_lexer_token_t *token) +{ + return token->keyword_type & NJS_KEYWORD_TYPE_KEYWORD; +} + + +njs_inline njs_bool_t +njs_lexer_token_is_reserved(njs_lexer_token_t *token) +{ + return token->keyword_type & NJS_KEYWORD_TYPE_RESERVED; +} + + +njs_inline njs_bool_t +njs_lexer_token_is_name(njs_lexer_token_t *token) +{ + return token->type == NJS_TOKEN_NAME + || (!njs_lexer_token_is_reserved(token) + && njs_lexer_token_is_keyword(token)); +} + + +njs_inline njs_bool_t +njs_lexer_token_is_identifier_name(njs_lexer_token_t *token) +{ + return token->type == NJS_TOKEN_NAME || njs_lexer_token_is_keyword(token); +} + + +njs_inline njs_bool_t +njs_lexer_token_is_binding_identifier(njs_lexer_token_t *token) +{ + switch (token->type) { + case NJS_TOKEN_NAME: + case NJS_TOKEN_YIELD: + case NJS_TOKEN_AWAIT: + return 1; + + default: + return (!njs_lexer_token_is_reserved(token) + && njs_lexer_token_is_keyword(token)); + }; +} + + +njs_inline njs_bool_t +njs_lexer_token_is_label_identifier(njs_lexer_token_t *token) +{ + return njs_lexer_token_is_binding_identifier(token); +} + + +njs_inline njs_bool_t +njs_lexer_token_is_identifier_reference(njs_lexer_token_t *token) +{ + return njs_lexer_token_is_binding_identifier(token); +} + + extern const njs_lvlhsh_proto_t njs_lexer_hash_proto; diff --git a/src/njs_lexer_tables.h b/src/njs_lexer_tables.h index 8e1ff013..73f71678 100644 --- a/src/njs_lexer_tables.h +++ b/src/njs_lexer_tables.h @@ -10,135 +10,428 @@ #define _NJS_LEXER_TABLES_H_INCLUDED_ -static const njs_keyword_t njs_lexer_kws[48] = +static const njs_keyword_t njs_lexer_kws[53] = { - { .entry = { njs_str("null") }, .type = NJS_TOKEN_NULL }, - { .entry = { njs_str("false") }, .type = NJS_TOKEN_FALSE }, - { .entry = { njs_str("true") }, .type = NJS_TOKEN_TRUE }, - { .entry = { njs_str("in") }, .type = NJS_TOKEN_IN }, - { .entry = { njs_str("typeof") }, .type = NJS_TOKEN_TYPEOF }, - { .entry = { njs_str("instanceof") }, .type = NJS_TOKEN_INSTANCEOF }, - { .entry = { njs_str("void") }, .type = NJS_TOKEN_VOID }, - { .entry = { njs_str("new") }, .type = NJS_TOKEN_NEW }, - { .entry = { njs_str("delete") }, .type = NJS_TOKEN_DELETE }, - { .entry = { njs_str("yield") }, .type = NJS_TOKEN_YIELD }, - { .entry = { njs_str("var") }, .type = NJS_TOKEN_VAR }, - { .entry = { njs_str("if") }, .type = NJS_TOKEN_IF }, - { .entry = { njs_str("else") }, .type = NJS_TOKEN_ELSE }, - { .entry = { njs_str("while") }, .type = NJS_TOKEN_WHILE }, - { .entry = { njs_str("do") }, .type = NJS_TOKEN_DO }, - { .entry = { njs_str("for") }, .type = NJS_TOKEN_FOR }, - { .entry = { njs_str("break") }, .type = NJS_TOKEN_BREAK }, - { .entry = { njs_str("continue") }, .type = NJS_TOKEN_CONTINUE }, - { .entry = { njs_str("switch") }, .type = NJS_TOKEN_SWITCH }, - { .entry = { njs_str("case") }, .type = NJS_TOKEN_CASE }, - { .entry = { njs_str("default") }, .type = NJS_TOKEN_DEFAULT }, - { .entry = { njs_str("function") }, .type = NJS_TOKEN_FUNCTION }, - { .entry = { njs_str("return") }, .type = NJS_TOKEN_RETURN }, - { .entry = { njs_str("with") }, .type = NJS_TOKEN_WITH }, - { .entry = { njs_str("try") }, .type = NJS_TOKEN_TRY }, - { .entry = { njs_str("catch") }, .type = NJS_TOKEN_CATCH }, - { .entry = { njs_str("finally") }, .type = NJS_TOKEN_FINALLY }, - { .entry = { njs_str("throw") }, .type = NJS_TOKEN_THROW }, - { .entry = { njs_str("import") }, .type = NJS_TOKEN_IMPORT }, - { .entry = { njs_str("export") }, .type = NJS_TOKEN_EXPORT }, - { .entry = { njs_str("this") }, .type = NJS_TOKEN_THIS }, - { .entry = { njs_str("arguments") }, .type = NJS_TOKEN_ARGUMENTS }, - { .entry = { njs_str("eval") }, .type = NJS_TOKEN_EVAL }, - { .entry = { njs_str("await") }, .type = NJS_TOKEN_AWAIT }, - { .entry = { njs_str("class") }, .type = NJS_TOKEN_CLASS }, - { .entry = { njs_str("const") }, .type = NJS_TOKEN_CONST }, - { .entry = { njs_str("debugger") }, .type = NJS_TOKEN_DEBUGGER }, - { .entry = { njs_str("enum") }, .type = NJS_TOKEN_ENUM }, - { .entry = { njs_str("extends") }, .type = NJS_TOKEN_EXTENDS }, - { .entry = { njs_str("implements") }, .type = NJS_TOKEN_IMPLEMENTS }, - { .entry = { njs_str("interface") }, .type = NJS_TOKEN_INTERFACE }, - { .entry = { njs_str("let") }, .type = NJS_TOKEN_LET }, - { .entry = { njs_str("package") }, .type = NJS_TOKEN_PACKAGE }, - { .entry = { njs_str("private") }, .type = NJS_TOKEN_PRIVATE }, - { .entry = { njs_str("protected") }, .type = NJS_TOKEN_PROTECTED }, - { .entry = { njs_str("public") }, .type = NJS_TOKEN_PUBLIC }, - { .entry = { njs_str("static") }, .type = NJS_TOKEN_STATIC }, - { .entry = { njs_str("super") }, .type = NJS_TOKEN_SUPER }, + { + .entry = { njs_str("arguments") }, + .type = NJS_TOKEN_ARGUMENTS, + .reserved = 0 + }, + + { + .entry = { njs_str("async") }, + .type = NJS_TOKEN_ASYNC, + .reserved = 0 + }, + + { + .entry = { njs_str("await") }, + .type = NJS_TOKEN_AWAIT, + .reserved = 1 + }, + + { + .entry = { njs_str("break") }, + .type = NJS_TOKEN_BREAK, + .reserved = 1 + }, + + { + .entry = { njs_str("case") }, + .type = NJS_TOKEN_CASE, + .reserved = 1 + }, + + { + .entry = { njs_str("catch") }, + .type = NJS_TOKEN_CATCH, + .reserved = 1 + }, + + { + .entry = { njs_str("class") }, + .type = NJS_TOKEN_CLASS, + .reserved = 1 + }, + + { + .entry = { njs_str("const") }, + .type = NJS_TOKEN_CONST, + .reserved = 1 + }, + + { + .entry = { njs_str("continue") }, + .type = NJS_TOKEN_CONTINUE, + .reserved = 1 + }, + + { + .entry = { njs_str("debugger") }, + .type = NJS_TOKEN_DEBUGGER, + .reserved = 1 + }, + + { + .entry = { njs_str("default") }, + .type = NJS_TOKEN_DEFAULT, + .reserved = 1 + }, + + { + .entry = { njs_str("delete") }, + .type = NJS_TOKEN_DELETE, + .reserved = 1 + }, + + { + .entry = { njs_str("do") }, + .type = NJS_TOKEN_DO, + .reserved = 1 + }, + + { + .entry = { njs_str("else") }, + .type = NJS_TOKEN_ELSE, + .reserved = 1 + }, + + { + .entry = { njs_str("enum") }, + .type = NJS_TOKEN_ENUM, + .reserved = 1 + }, + + { + .entry = { njs_str("eval") }, + .type = NJS_TOKEN_EVAL, + .reserved = 0 + }, + + { + .entry = { njs_str("export") }, + .type = NJS_TOKEN_EXPORT, + .reserved = 1 + }, + + { + .entry = { njs_str("extends") }, + .type = NJS_TOKEN_EXTENDS, + .reserved = 1 + }, + + { + .entry = { njs_str("false") }, + .type = NJS_TOKEN_FALSE, + .reserved = 1 + }, + + { + .entry = { njs_str("finally") }, + .type = NJS_TOKEN_FINALLY, + .reserved = 1 + }, + + { + .entry = { njs_str("for") }, + .type = NJS_TOKEN_FOR, + .reserved = 1 + }, + + { + .entry = { njs_str("from") }, + .type = NJS_TOKEN_FROM, + .reserved = 0 + }, + + { + .entry = { njs_str("function") }, + .type = NJS_TOKEN_FUNCTION, + .reserved = 1 + }, + + { + .entry = { njs_str("if") }, + .type = NJS_TOKEN_IF, + .reserved = 1 + }, + + { + .entry = { njs_str("implements") }, + .type = NJS_TOKEN_IMPLEMENTS, + .reserved = 0 + }, + + { + .entry = { njs_str("import") }, + .type = NJS_TOKEN_IMPORT, + .reserved = 1 + }, + + { + .entry = { njs_str("in") }, + .type = NJS_TOKEN_IN, + .reserved = 1 + }, + + { + .entry = { njs_str("instanceof") }, + .type = NJS_TOKEN_INSTANCEOF, + .reserved = 1 + }, + + { + .entry = { njs_str("interface") }, + .type = NJS_TOKEN_INTERFACE, + .reserved = 0 + }, + + { + .entry = { njs_str("let") }, + .type = NJS_TOKEN_LET, + .reserved = 0 + }, + + { + .entry = { njs_str("meta") }, + .type = NJS_TOKEN_META, + .reserved = 0 + }, + + { + .entry = { njs_str("new") }, + .type = NJS_TOKEN_NEW, + .reserved = 1 + }, + + { + .entry = { njs_str("null") }, + .type = NJS_TOKEN_NULL, + .reserved = 1 + }, + + { + .entry = { njs_str("of") }, + .type = NJS_TOKEN_OF, + .reserved = 0 + }, + + { + .entry = { njs_str("package") }, + .type = NJS_TOKEN_PACKAGE, + .reserved = 0 + }, + + { + .entry = { njs_str("private") }, + .type = NJS_TOKEN_PRIVATE, + .reserved = 0 + }, + + { + .entry = { njs_str("protected") }, + .type = NJS_TOKEN_PROTECTED, + .reserved = 0 + }, + + { + .entry = { njs_str("public") }, + .type = NJS_TOKEN_PUBLIC, + .reserved = 0 + }, + + { + .entry = { njs_str("return") }, + .type = NJS_TOKEN_RETURN, + .reserved = 1 + }, + + { + .entry = { njs_str("static") }, + .type = NJS_TOKEN_STATIC, + .reserved = 0 + }, + + { + .entry = { njs_str("super") }, + .type = NJS_TOKEN_SUPER, + .reserved = 1 + }, + + { + .entry = { njs_str("switch") }, + .type = NJS_TOKEN_SWITCH, + .reserved = 1 + }, + + { + .entry = { njs_str("target") }, + .type = NJS_TOKEN_TARGET, + .reserved = 0 + }, + + { + .entry = { njs_str("this") }, + .type = NJS_TOKEN_THIS, + .reserved = 1 + }, + + { + .entry = { njs_str("throw") }, + .type = NJS_TOKEN_THROW, + .reserved = 1 + }, + + { + .entry = { njs_str("true") }, + .type = NJS_TOKEN_TRUE, + .reserved = 1 + }, + + { + .entry = { njs_str("try") }, + .type = NJS_TOKEN_TRY, + .reserved = 1 + }, + + { + .entry = { njs_str("typeof") }, + .type = NJS_TOKEN_TYPEOF, + .reserved = 1 + }, + + { + .entry = { njs_str("var") }, + .type = NJS_TOKEN_VAR, + .reserved = 1 + }, + + { + .entry = { njs_str("void") }, + .type = NJS_TOKEN_VOID, + .reserved = 1 + }, + + { + .entry = { njs_str("while") }, + .type = NJS_TOKEN_WHILE, + .reserved = 1 + }, + + { + .entry = { njs_str("with") }, + .type = NJS_TOKEN_WITH, + .reserved = 1 + }, + + { + .entry = { njs_str("yield") }, + .type = NJS_TOKEN_YIELD, + .reserved = 1 + }, }; -static const njs_lexer_keyword_entry_t njs_lexer_keyword_entries[75] = +static const njs_lexer_keyword_entry_t njs_lexer_keyword_entries[99] = { - { NULL, NULL, 74, 0 }, - { "case", &njs_lexer_kws[19], 4, 0 }, - { "continue", &njs_lexer_kws[17], 8, 0 }, - { "do", &njs_lexer_kws[14], 2, 0 }, - { "enum", &njs_lexer_kws[37], 4, 0 }, - { "extends", &njs_lexer_kws[38], 7, 0 }, - { "instanceof", &njs_lexer_kws[5], 10, 0 }, - { "public", &njs_lexer_kws[45], 6, 0 }, - { "static", &njs_lexer_kws[46], 6, 0 }, - { "in", &njs_lexer_kws[3], 2, 0 }, - { "await", &njs_lexer_kws[33], 5, 0 }, - { "private", &njs_lexer_kws[43], 7, 0 }, + { NULL, NULL, 98, 0 }, + { "continue", &njs_lexer_kws[8], 8, 0 }, + { "finally", &njs_lexer_kws[19], 7, 0 }, + { "return", &njs_lexer_kws[38], 6, 0 }, + { "static", &njs_lexer_kws[39], 6, 0 }, + { "async", &njs_lexer_kws[1], 5, 0 }, + { "break", &njs_lexer_kws[3], 5, 0 }, + { "interface", &njs_lexer_kws[28], 9, 0 }, + { "case", &njs_lexer_kws[4], 4, 0 }, + { "import", &njs_lexer_kws[25], 6, 0 }, + { "protected", &njs_lexer_kws[36], 9, 0 }, + { "switch", &njs_lexer_kws[41], 6, 0 }, + { "catch", &njs_lexer_kws[5], 5, 1 }, + { "delete", &njs_lexer_kws[11], 6, 0 }, + { "else", &njs_lexer_kws[13], 4, 0 }, + { "private", &njs_lexer_kws[35], 7, 0 }, + { "extends", &njs_lexer_kws[17], 7, 0 }, + { "this", &njs_lexer_kws[43], 4, 0 }, + { "false", &njs_lexer_kws[18], 5, 0 }, + { "await", &njs_lexer_kws[2], 5, 0 }, + { NULL, NULL, 0, 0 }, + { "public", &njs_lexer_kws[37], 6, 0 }, + { NULL, NULL, 0, 0 }, + { "class", &njs_lexer_kws[6], 5, 0 }, + { "const", &njs_lexer_kws[7], 5, 4 }, + { NULL, NULL, 0, 0 }, + { "try", &njs_lexer_kws[46], 3, 0 }, + { "null", &njs_lexer_kws[32], 4, 0 }, + { NULL, NULL, 0, 0 }, + { "do", &njs_lexer_kws[12], 2, 0 }, + { "var", &njs_lexer_kws[48], 3, 0 }, + { "if", &njs_lexer_kws[23], 2, 7 }, + { "implements", &njs_lexer_kws[24], 10, 0 }, + { "with", &njs_lexer_kws[51], 4, 0 }, + { NULL, NULL, 0, 0 }, + { "eval", &njs_lexer_kws[15], 4, 9 }, + { NULL, NULL, 0, 0 }, + { "target", &njs_lexer_kws[42], 6, 0 }, + { "enum", &njs_lexer_kws[14], 4, 10 }, + { "instanceof", &njs_lexer_kws[27], 10, 0 }, + { NULL, NULL, 0, 0 }, + { "debugger", &njs_lexer_kws[9], 8, 0 }, + { NULL, NULL, 0, 0 }, + { NULL, NULL, 0, 0 }, + { "default", &njs_lexer_kws[10], 7, 0 }, + { "void", &njs_lexer_kws[49], 4, 0 }, + { NULL, NULL, 0, 0 }, + { NULL, NULL, 0, 0 }, + { NULL, NULL, 0, 0 }, + { "from", &njs_lexer_kws[21], 4, 0 }, + { "package", &njs_lexer_kws[34], 7, 15 }, + { NULL, NULL, 0, 0 }, + { "yield", &njs_lexer_kws[52], 5, 0 }, + { NULL, NULL, 0, 0 }, + { NULL, NULL, 0, 0 }, + { "of", &njs_lexer_kws[33], 2, 0 }, + { NULL, NULL, 0, 0 }, + { "function", &njs_lexer_kws[22], 8, 0 }, + { NULL, NULL, 0, 0 }, + { "true", &njs_lexer_kws[45], 4, 16 }, + { "new", &njs_lexer_kws[31], 3, 0 }, + { "export", &njs_lexer_kws[16], 6, 0 }, + { NULL, NULL, 0, 0 }, + { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, - { "debugger", &njs_lexer_kws[36], 8, 0 }, - { "for", &njs_lexer_kws[15], 3, 1 }, { NULL, NULL, 0, 0 }, - { "catch", &njs_lexer_kws[25], 5, 0 }, { NULL, NULL, 0, 0 }, - { "super", &njs_lexer_kws[47], 5, 2 }, { NULL, NULL, 0, 0 }, - { "const", &njs_lexer_kws[35], 5, 0 }, + { "for", &njs_lexer_kws[20], 3, 0 }, + { "while", &njs_lexer_kws[50], 5, 0 }, { NULL, NULL, 0, 0 }, - { "false", &njs_lexer_kws[1], 5, 0 }, - { "with", &njs_lexer_kws[23], 4, 0 }, - { "implements", &njs_lexer_kws[39], 10, 0 }, - { "this", &njs_lexer_kws[30], 4, 0 }, - { "let", &njs_lexer_kws[41], 3, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, - { "true", &njs_lexer_kws[2], 4, 0 }, { NULL, NULL, 0, 0 }, - { "export", &njs_lexer_kws[29], 6, 0 }, { NULL, NULL, 0, 0 }, - { "interface", &njs_lexer_kws[40], 9, 0 }, { NULL, NULL, 0, 0 }, - { "eval", &njs_lexer_kws[32], 4, 0 }, - { "protected", &njs_lexer_kws[44], 9, 0 }, - { "while", &njs_lexer_kws[13], 5, 0 }, { NULL, NULL, 0, 0 }, - { "void", &njs_lexer_kws[6], 4, 0 }, { NULL, NULL, 0, 0 }, - { "return", &njs_lexer_kws[22], 6, 0 }, { NULL, NULL, 0, 0 }, - { "delete", &njs_lexer_kws[8], 6, 0 }, - { "yield", &njs_lexer_kws[9], 5, 0 }, - { "null", &njs_lexer_kws[0], 4, 0 }, - { "throw", &njs_lexer_kws[27], 5, 0 }, + { "typeof", &njs_lexer_kws[47], 6, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, + { "super", &njs_lexer_kws[40], 5, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, - { "import", &njs_lexer_kws[28], 6, 0 }, { NULL, NULL, 0, 0 }, - { "switch", &njs_lexer_kws[18], 6, 0 }, - { "try", &njs_lexer_kws[24], 3, 0 }, - { "function", &njs_lexer_kws[21], 8, 0 }, + { "let", &njs_lexer_kws[29], 3, 19 }, + { "in", &njs_lexer_kws[26], 2, 0 }, { NULL, NULL, 0, 0 }, - { "if", &njs_lexer_kws[11], 2, 0 }, - { "break", &njs_lexer_kws[16], 5, 0 }, { NULL, NULL, 0, 0 }, + { "throw", &njs_lexer_kws[44], 5, 0 }, + { "arguments", &njs_lexer_kws[0], 9, 0 }, + { "meta", &njs_lexer_kws[30], 4, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, - { "var", &njs_lexer_kws[10], 3, 4 }, { NULL, NULL, 0, 0 }, - { "default", &njs_lexer_kws[20], 7, 0 }, - { "arguments", &njs_lexer_kws[31], 9, 6 }, - { "finally", &njs_lexer_kws[26], 7, 0 }, { NULL, NULL, 0, 0 }, - { "else", &njs_lexer_kws[12], 4, 0 }, - { "class", &njs_lexer_kws[34], 5, 7 }, - { "new", &njs_lexer_kws[7], 3, 8 }, { NULL, NULL, 0, 0 }, - { "package", &njs_lexer_kws[42], 7, 11 }, - { "typeof", &njs_lexer_kws[4], 6, 0 }, { NULL, NULL, 0, 0 }, }; diff --git a/src/njs_module.c b/src/njs_module.c index 0cc20f90..31ea7ee0 100644 --- a/src/njs_module.c +++ b/src/njs_module.c @@ -15,6 +15,19 @@ typedef struct { } njs_module_info_t; +typedef struct { + njs_str_t text; + njs_module_info_t info; + njs_lexer_t *prev; + njs_lexer_t lexer; +} njs_module_temp_t; + + +static njs_int_t njs_parser_module_lambda_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_module_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + static njs_int_t njs_module_lookup(njs_vm_t *vm, const njs_str_t *cwd, njs_module_info_t *info); static njs_int_t njs_module_relative_path(njs_vm_t *vm, @@ -98,31 +111,32 @@ njs_module_reset(njs_vm_t *vm) njs_int_t -njs_parser_module(njs_vm_t *vm, njs_parser_t *parser) +njs_parser_module(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) { njs_int_t ret; njs_str_t name, text; - njs_lexer_t *prev, lexer; njs_module_t *module; - njs_token_type_t type; - njs_parser_node_t *node; + njs_module_temp_t *temp; njs_module_info_t info; - name = *njs_parser_text(parser); + name = token->text; parser->node = NULL; - module = njs_module_find(vm, &name, 0); + module = njs_module_find(parser->vm, &name, 0); if (module != NULL && module->function.native) { - goto found; - } + njs_lexer_consume_token(parser->lexer, 1); - prev = parser->lexer; + parser->target = (njs_parser_node_t *) module; + + return njs_parser_module_after(parser, token, current); + } njs_memzero(&text, sizeof(njs_str_t)); - if (vm->options.sandbox || name.length == 0) { - njs_parser_syntax_error(vm, parser, "Cannot find module \"%V\"", &name); + if (parser->vm->options.sandbox || name.length == 0) { + njs_parser_syntax_error(parser, "Cannot find module \"%V\"", &name); goto fail; } @@ -132,74 +146,134 @@ njs_parser_module(njs_vm_t *vm, njs_parser_t *parser) info.name = name; - ret = njs_module_lookup(vm, &parser->scope->cwd, &info); + ret = njs_module_lookup(parser->vm, &parser->scope->cwd, &info); if (njs_slow_path(ret != NJS_OK)) { - njs_parser_syntax_error(vm, parser, "Cannot find module \"%V\"", &name); + njs_parser_syntax_error(parser, "Cannot find module \"%V\"", &name); goto fail; } - module = njs_module_find(vm, &info.file, 0); + module = njs_module_find(parser->vm, &info.file, 0); if (module != NULL) { (void) close(info.fd); - goto found; + njs_lexer_consume_token(parser->lexer, 1); + + parser->target = (njs_parser_node_t *) module; + + return njs_parser_module_after(parser, token, current); } - ret = njs_module_read(vm, info.fd, &text); + ret = njs_module_read(parser->vm, info.fd, &text); (void) close(info.fd); if (njs_slow_path(ret != NJS_OK)) { - njs_internal_error(vm, "while reading \"%V\" module", &info.file); + njs_internal_error(parser->vm, "while reading \"%V\" module", + &info.file); goto fail; } - if (njs_module_realpath_equal(&prev->file, &info.file)) { - njs_parser_syntax_error(vm, parser, "Cannot import itself \"%V\"", + if (njs_module_realpath_equal(&parser->lexer->file, &info.file)) { + njs_parser_syntax_error(parser, "Cannot import itself \"%V\"", &info.file); goto fail; } - ret = njs_lexer_init(vm, &lexer, &info.file, text.start, + temp = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_module_temp_t)); + if (njs_slow_path(temp == NULL)) { + return NJS_ERROR; + } + + ret = njs_lexer_init(parser->vm, &temp->lexer, &info.file, text.start, text.start + text.length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } - parser->lexer = &lexer; + njs_lexer_consume_token(parser->lexer, 1); - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - goto fail; + temp->prev = parser->lexer; + temp->info = info; + temp->text = text; + + parser->lexer = &temp->lexer; + + njs_parser_next(parser, njs_parser_module_lambda); + + return njs_parser_after(parser, current, temp, 0, + njs_parser_module_lambda_after); + +fail: + + if (text.start != NULL) { + njs_mp_free(parser->vm->mem_pool, text.start); } - type = njs_parser_module_lambda(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - goto fail; + return NJS_ERROR; +} + + +njs_int_t +njs_parser_module_lambda_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_module_t *module; + njs_module_temp_t *temp; + + temp = (njs_module_temp_t *) parser->target; + + if (parser->ret != NJS_OK) { + njs_mp_free(parser->vm->mem_pool, temp->text.start); + njs_mp_free(parser->vm->mem_pool, temp); + + if (token->type == NJS_TOKEN_END) { + return njs_parser_stack_pop(parser); + } + + return njs_parser_failed(parser); } - module = njs_module_add(vm, &info.file); + module = njs_module_add(parser->vm, &temp->info.file); if (njs_slow_path(module == NULL)) { - goto fail; + parser->lexer = temp->prev; + + if (temp->text.start != NULL) { + njs_mp_free(parser->vm->mem_pool, temp->text.start); + } } module->function.args_offset = 1; module->function.u.lambda = parser->node->u.value.data.u.lambda; - njs_mp_free(vm->mem_pool, text.start); + njs_mp_free(parser->vm->mem_pool, temp->text.start); + + parser->lexer = temp->prev; + parser->target = (njs_parser_node_t *) module; + + njs_mp_free(parser->vm->mem_pool, temp); + + return njs_parser_module_after(parser, token, current); +} - parser->lexer = prev; -found: +njs_int_t +njs_parser_module_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + njs_module_t *module; + njs_parser_node_t *node; - node = njs_parser_node_new(vm, parser, 0); + node = njs_parser_node_new(parser, 0); if (njs_slow_path(node == NULL)) { return NJS_ERROR; } node->left = parser->node; + module = (njs_module_t *) parser->target; + if (module->index == 0) { - ret = njs_module_insert(vm, module); + ret = njs_module_insert(parser->vm, module); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -209,17 +283,7 @@ found: parser->node = node; - return NJS_OK; - -fail: - - parser->lexer = prev; - - if (text.start != NULL) { - njs_mp_free(vm->mem_pool, text.start); - } - - return NJS_ERROR; + return njs_parser_stack_pop(parser); } @@ -330,13 +394,9 @@ njs_module_relative_path(njs_vm_t *vm, const njs_str_t *dir, } -#define NJS_MODULE_START "function() {" -#define NJS_MODULE_END "}" - static njs_int_t njs_module_read(njs_vm_t *vm, int fd, njs_str_t *text) { - u_char *p; ssize_t n; struct stat sb; @@ -348,30 +408,19 @@ njs_module_read(njs_vm_t *vm, int fd, njs_str_t *text) goto fail; } - text->length = njs_length(NJS_MODULE_START) + sb.st_size - + njs_length(NJS_MODULE_END); + text->length = sb.st_size; text->start = njs_mp_alloc(vm->mem_pool, text->length); if (text->start == NULL) { goto fail; } - p = njs_cpymem(text->start, NJS_MODULE_START, njs_length(NJS_MODULE_START)); - - n = read(fd, p, sb.st_size); + n = read(fd, text->start, sb.st_size); - if (n < 0) { + if (n < 0 || n != sb.st_size) { goto fail; } - if (n != sb.st_size) { - goto fail; - } - - p += n; - - memcpy(p, NJS_MODULE_END, njs_length(NJS_MODULE_END)); - return NJS_OK; fail: diff --git a/src/njs_module.h b/src/njs_module.h index 955ef5b8..7da3a027 100644 --- a/src/njs_module.h +++ b/src/njs_module.h @@ -18,7 +18,8 @@ typedef struct { njs_int_t njs_module_load(njs_vm_t *vm); void njs_module_reset(njs_vm_t *vm); -njs_int_t njs_parser_module(njs_vm_t *vm, njs_parser_t *parser); +njs_int_t njs_parser_module(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current); njs_int_t njs_module_require(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused); diff --git a/src/njs_parser.c b/src/njs_parser.c index 2f953c35..13753845 100644 --- a/src/njs_parser.c +++ b/src/njs_parser.c @@ -1,6 +1,7 @@ /* * Copyright (C) Igor Sysoev + * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ @@ -8,83 +9,499 @@ #include -static njs_int_t njs_parser_scope_begin(njs_vm_t *vm, njs_parser_t *parser, - njs_scope_t type); -static void njs_parser_scope_end(njs_vm_t *vm, njs_parser_t *parser); -static njs_token_type_t njs_parser_statement_chain(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type, njs_bool_t top); -static njs_token_type_t njs_parser_statement(njs_vm_t *vm, njs_parser_t *parser, +static njs_int_t njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type); +static void njs_parser_scope_end(njs_parser_t *parser); + +static njs_int_t njs_parser_check_error_state(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_primary_expression_test(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_regexp_literal(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_template_literal(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_template_literal_string(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_template_literal_expression( + njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_cover_parenthesized_expression( + njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_binding_identifier_pattern(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_cover_parenthesized_expression_after( + njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_cover_parenthesized_expression_end( + njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_array_literal(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_array_element_list(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_array_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_array_spread_element(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_object_literal(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_object_literal_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_property_definition_list(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_property_definition_list_after( + njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_property_definition(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_property_definition_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_computed_property_name_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_initializer(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_initializer_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_initializer_assign(njs_parser_t *parser, njs_token_type_t type); -static njs_token_type_t njs_parser_block_statement(njs_vm_t *vm, - njs_parser_t *parser); -static njs_token_type_t njs_parser_block(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type); -static njs_token_type_t njs_parser_labelled_statement(njs_vm_t *vm, - njs_parser_t *parser); -static njs_token_type_t njs_parser_function_declaration(njs_vm_t *vm, - njs_parser_t *parser); -static njs_token_type_t njs_parser_lambda_arguments(njs_vm_t *vm, - njs_parser_t *parser, njs_function_lambda_t *lambda, njs_index_t index, - njs_token_type_t type); -static njs_token_type_t njs_parser_lambda_argument(njs_vm_t *vm, - njs_parser_t *parser, njs_index_t index); -static njs_token_type_t njs_parser_lambda_body(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type); -static njs_parser_node_t *njs_parser_return_set(njs_vm_t *vm, - njs_parser_t *parser, njs_parser_node_t *expr); -static njs_token_type_t njs_parser_return_statement(njs_vm_t *vm, - njs_parser_t *parser); -static njs_token_type_t njs_parser_var_statement(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t parent, njs_bool_t var_in); -static njs_token_type_t njs_parser_if_statement(njs_vm_t *vm, - njs_parser_t *parser); -static njs_token_type_t njs_parser_switch_statement(njs_vm_t *vm, - njs_parser_t *parser); -static njs_token_type_t njs_parser_while_statement(njs_vm_t *vm, - njs_parser_t *parser); -static njs_token_type_t njs_parser_do_while_statement(njs_vm_t *vm, - njs_parser_t *parser); -static njs_token_type_t njs_parser_for_statement(njs_vm_t *vm, - njs_parser_t *parser); -static njs_token_type_t njs_parser_var_in_statement(njs_vm_t *vm, - njs_parser_t *parser, njs_parser_node_t *name); -static njs_token_type_t njs_parser_for_in_statement(njs_vm_t *vm, - njs_parser_t *parser, njs_str_t *name, njs_token_type_t type); -static njs_token_type_t njs_parser_brk_statement(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type); -static njs_token_type_t njs_parser_try_statement(njs_vm_t *vm, - njs_parser_t *parser); -static njs_token_type_t njs_parser_try_block(njs_vm_t *vm, - njs_parser_t *parser); -static njs_token_type_t njs_parser_throw_statement(njs_vm_t *vm, - njs_parser_t *parser); -static njs_token_type_t njs_parser_import_statement(njs_vm_t *vm, - njs_parser_t *parser); -static njs_token_type_t njs_parser_export_statement(njs_vm_t *vm, - njs_parser_t *parser); -static njs_int_t njs_parser_export_sink(njs_vm_t *vm, njs_parser_t *parser); -static njs_token_type_t njs_parser_grouping_expression(njs_vm_t *vm, - njs_parser_t *parser); - - -#define njs_parser_chain_current(parser) \ - ((parser)->node) - -#define njs_parser_chain_top(parser) \ + +static njs_int_t njs_parser_member_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_member_expression_next(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_member_expression_bracket(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_member_expression_new_next(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_super_property(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_member_expression_import(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_member_expression_new(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_member_expression_new_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_member_expression_new_args(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_parser_node_t *njs_parser_create_call(njs_parser_t *parser, + njs_parser_node_t *node, uint8_t ctor); +static njs_int_t njs_parser_call_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_call_expression_args(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_call_expression_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_arguments(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_parenthesis_or_comma(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_argument_list(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_argument_list_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_optional_expression_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_optional_chain(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_optional_chain_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_new_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_new_expression_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_left_hand_side_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_left_hand_side_expression_after( + njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_left_hand_side_expression_node( + njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_left_hand_side_expression_optional( + njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_update_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_update_expression_post(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_update_expression_unary(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_unary_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_unary_expression_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_unary_expression_next(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_exponentiation_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_exponentiation_expression_match( + njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_multiplicative_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_multiplicative_expression_match( + njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_additive_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_additive_expression_match(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_shift_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_shift_expression_match(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_relational_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_relational_expression_match(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_equality_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_equality_expression_match(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_bitwise_AND_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_bitwise_AND_expression_and(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_bitwise_XOR_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_bitwise_XOR_expression_xor(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_bitwise_OR_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_bitwise_OR_expression_or(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_logical_AND_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_logical_AND_expression_and(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_logical_OR_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_logical_OR_expression_or(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_coalesce_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_short_circuit_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_conditional_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_conditional_question_mark(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_conditional_colon(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_conditional_colon_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_assignment_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_match_arrow_expression(njs_parser_t *parser, + njs_lexer_token_t *token); +static njs_int_t njs_parser_assignment_expression_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_assignment_operator(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_assignment_operator_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_expression_comma(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_statement_wo_node(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_declaration(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_hoistable_declaration(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_block_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_block_statement_open_brace(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_block_statement_close_brace(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_statement_list(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_statement_list_next(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_statement_list_item(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_variable_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_variable_declaration_list(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_variable_declaration_list_next( + njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_variable_declaration(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_binding_pattern(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_object_binding_pattern(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_array_binding_pattern(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_expression_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_expression_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_if_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_if_close_parenthesis(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_else_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_else_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_iteration_statement_do(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_iteration_statement_do_while(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_do_while_semicolon(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_iteration_statement_while(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_while_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_while_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_iteration_statement_for(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_iteration_statement_for_map(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_for_var_binding_or_var_list(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_for_var_in_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_for_var_in_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_for_var_in_of_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_for_in_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_for_in_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_for_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_for_expression_end(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_for_end(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_switch_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_switch_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_switch_block(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_switch_case(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_switch_case_wo_def(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_switch_case_def(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current, + njs_bool_t with_default); +static njs_int_t njs_parser_switch_case_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_switch_case_block(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_switch_case_after_wo_def(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_switch_case_block_wo_def(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_continue_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_break_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_break_continue(njs_parser_t *parser, + njs_lexer_token_t *token, njs_token_type_t type); + +static njs_int_t njs_parser_return_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_return_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_with_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_labelled_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_labelled_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_throw_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_throw_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_try_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_catch_or_finally(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_catch_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_catch_parenthesis(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_catch_finally(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_debugger_statement(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_function_declaration(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_function_declaration_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_function_parse(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_function_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_function_expression_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_unique_formal_parameters(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_formal_parameters(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_formal_parameters_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_arrow_function(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_arrow_function_args_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_arrow_function_arrow(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_arrow_function_body_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_method_definition(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_get_set(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_get_set_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_get_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_set_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_function_lambda(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_function_lambda_args_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_function_lambda_body_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_export(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_export_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + +static njs_int_t njs_parser_import(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_import_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_module_lambda_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); +static njs_int_t njs_parser_export_sink(njs_parser_t *parser); + +static njs_parser_node_t *njs_parser_return_set(njs_parser_t *parser, + njs_parser_node_t *expr); +static njs_parser_node_t *njs_parser_variable_node(njs_parser_t *parser, + uintptr_t unique_id, njs_variable_type_t type); + +static njs_parser_node_t *njs_parser_reference(njs_parser_t *parser, + njs_lexer_token_t *token); + +static njs_parser_node_t *njs_parser_argument(njs_parser_t *parser, + njs_parser_node_t *expr, njs_index_t index); + +static njs_int_t njs_parser_object_property(njs_parser_t *parser, + njs_parser_node_t *parent, njs_parser_node_t *property, + njs_parser_node_t *value, njs_bool_t proto_init); +static njs_int_t njs_parser_property_accessor(njs_parser_t *parser, + njs_parser_node_t *parent, njs_parser_node_t *property, + njs_parser_node_t *value, njs_token_type_t accessor); +static njs_int_t njs_parser_array_item(njs_parser_t *parser, + njs_parser_node_t *array, njs_parser_node_t *value); +static njs_int_t njs_parser_template_string(njs_parser_t *parser, + njs_lexer_token_t *token); +static njs_token_type_t njs_parser_escape_string_create(njs_parser_t *parser, + njs_lexer_token_t *token, njs_value_t *value); +static njs_int_t njs_parser_escape_string_calc_length(njs_parser_t *parser, + njs_lexer_token_t *token, size_t *out_size, size_t *out_length); + + +#define njs_parser_chain_top(parser) \ ((parser)->scope->top) -#define njs_parser_chain_top_set(parser, node) \ + +#define njs_parser_chain_top_set(parser, node) \ (parser)->scope->top = node +njs_inline njs_int_t +njs_parser_not_supported(njs_parser_t *parser, njs_lexer_token_t *token) +{ + if (token->type != NJS_TOKEN_END) { + njs_parser_syntax_error(parser, "Token \"%V\" not support " + "in this version", &token->text); + } else { + njs_parser_syntax_error(parser, "Not support in this version"); + } + + return NJS_DONE; +} + + +njs_inline njs_int_t +njs_parser_reject(njs_parser_t *parser) +{ + njs_parser_stack_entry_t *entry; + + while (!njs_queue_is_empty(&parser->stack)) { + entry = njs_queue_link_data(njs_queue_first(&parser->stack), + njs_parser_stack_entry_t, link); + + njs_queue_remove(njs_queue_first(&parser->stack)); + + if (!entry->optional) { + njs_parser_next(parser, entry->state); + parser->target = entry->node; + + return NJS_DECLINED; + } + } + + return njs_parser_failed(parser); +} + + njs_int_t -njs_parser(njs_vm_t *vm, njs_parser_t *parser, njs_parser_t *prev) +njs_parser(njs_parser_t *parser, njs_parser_t *prev) { njs_int_t ret; - njs_token_type_t type; - njs_parser_node_t *node; + njs_lexer_token_t *token; - ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_GLOBAL); + ret = njs_parser_scope_begin(parser, NJS_SCOPE_GLOBAL); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } @@ -94,54 +511,85 @@ njs_parser(njs_vm_t *vm, njs_parser_t *parser, njs_parser_t *prev) * Copy the global scope variables from the previous * iteration of the accumulative mode. */ - ret = njs_variables_copy(vm, &parser->scope->variables, + ret = njs_variables_copy(parser->vm, &parser->scope->variables, &prev->scope->variables); - if (njs_slow_path(ret != NJS_OK)) { + if (ret != NJS_OK) { return ret; } } - type = njs_parser_token(vm, parser); + njs_queue_init(&parser->stack); + + parser->target = NULL; + njs_parser_next(parser, njs_parser_statement_list); - while (type != NJS_TOKEN_END) { + ret = njs_parser_after(parser, njs_queue_first(&parser->stack), + NULL, 0, njs_parser_check_error_state); + if (ret != NJS_OK) { + return ret; + } - type = njs_parser_statement_chain(vm, parser, type, 1); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { + do { + token = njs_lexer_token(parser->lexer, 0); + if (njs_slow_path(token == NULL)) { return NJS_ERROR; } - if (type == NJS_TOKEN_CLOSE_BRACE && vm->options.trailer) { - parser->lexer->start--; - break; - } + parser->ret = parser->state(parser, token, + njs_queue_first(&parser->stack)); + + } while (parser->ret != NJS_DONE && parser->ret != NJS_ERROR); + + if (parser->ret != NJS_DONE) { + return NJS_ERROR; } - node = njs_parser_chain_top(parser); + if (njs_is_error(&parser->vm->retval)) { + return NJS_ERROR; + } - if (node == NULL) { + if (parser->node == NULL) { /* Empty string, just semicolons or variables declarations. */ - node = njs_parser_node_new(vm, parser, 0); - if (njs_slow_path(node == NULL)) { + parser->node = njs_parser_node_new(parser, 0); + if (njs_slow_path(parser->node == NULL)) { return NJS_ERROR; } - - njs_parser_chain_top_set(parser, node); } - node->token_type = NJS_TOKEN_END; + parser->node->token_type = NJS_TOKEN_END; - if (njs_slow_path(parser->count != 0)) { - njs_internal_error(vm, "parser->count != 0"); - return NJS_ERROR; - } + njs_parser_chain_top_set(parser, parser->node); return NJS_OK; } static njs_int_t -njs_parser_scope_begin(njs_vm_t *vm, njs_parser_t *parser, njs_scope_t type) +njs_parser_check_error_state(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + return njs_parser_failed(parser); +} + + +njs_int_t +njs_parser_failed_state(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_END) { + njs_parser_syntax_error(parser, "Unexpected token \"%V\"", + &token->text); + } else { + njs_parser_syntax_error(parser, "Unexpected end of input"); + } + + return NJS_DONE; +} + + +static njs_int_t +njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type) { njs_arr_t *values; njs_uint_t nesting; @@ -161,8 +609,7 @@ njs_parser_scope_begin(njs_vm_t *vm, njs_parser_t *parser, njs_scope_t type) break; } - njs_parser_syntax_error(vm, parser, - "The maximum function nesting " + njs_parser_syntax_error(parser, "The maximum function nesting " "level is \"%d\"", NJS_MAX_NESTING); return NJS_ERROR; @@ -170,7 +617,7 @@ njs_parser_scope_begin(njs_vm_t *vm, njs_parser_t *parser, njs_scope_t type) } } - scope = njs_mp_zalloc(vm->mem_pool, sizeof(njs_parser_scope_t)); + scope = njs_mp_zalloc(parser->vm->mem_pool, sizeof(njs_parser_scope_t)); if (njs_slow_path(scope == NULL)) { return NJS_ERROR; } @@ -202,7 +649,7 @@ njs_parser_scope_begin(njs_vm_t *vm, njs_parser_t *parser, njs_scope_t type) values = NULL; if (scope->type < NJS_SCOPE_BLOCK) { - values = njs_arr_create(vm->mem_pool, 4, sizeof(njs_value_t)); + values = njs_arr_create(parser->vm->mem_pool, 4, sizeof(njs_value_t)); if (njs_slow_path(values == NULL)) { return NJS_ERROR; } @@ -236,7 +683,7 @@ njs_parser_scope_begin(njs_vm_t *vm, njs_parser_t *parser, njs_scope_t type) static void -njs_parser_scope_end(njs_vm_t *vm, njs_parser_t *parser) +njs_parser_scope_end(njs_parser_t *parser) { njs_parser_scope_t *scope, *parent; @@ -268,1964 +715,7394 @@ njs_parser_scope_rbtree_compare(njs_rbtree_node_t *node1, } -static njs_token_type_t -njs_parser_statement_chain(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type, njs_bool_t top) +static njs_int_t +njs_parser_generator_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) { - njs_parser_node_t *stmt, *last, *node, *new_node, **child; + return njs_parser_not_supported(parser, token); +} - child = top ? &njs_parser_chain_top(parser) - : &njs_parser_chain_current(parser); - last = *child; +static njs_int_t +njs_parser_class_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + return njs_parser_not_supported(parser, token); +} - njs_parser_enter(vm, parser); - type = njs_parser_statement(vm, parser, type); +static njs_int_t +njs_parser_async_generator_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + return njs_parser_not_supported(parser, token); +} - njs_parser_leave(parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return njs_parser_unexpected_token(vm, parser, type); - } +static njs_int_t +njs_parser_async_function_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + return njs_parser_not_supported(parser, token); +} - if (parser->node == NULL) { - /* The statement is empty block or just semicolon. */ - return type; - } - new_node = parser->node; +static njs_int_t +njs_parser_generator_declaration(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + return njs_parser_not_supported(parser, token); +} - if (new_node->hoist) { - child = &njs_parser_chain_top(parser); - while (*child != NULL) { - node = *child; +static njs_int_t +njs_parser_class_declaration(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + return njs_parser_not_supported(parser, token); +} - if (node->hoist) { - break; - } - child = &node->left; - } +static njs_int_t +njs_parser_lexical_declaration(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + return njs_parser_not_supported(parser, token); +} - last = *child; - } - stmt = njs_parser_node_new(vm, parser, NJS_TOKEN_STATEMENT); - if (njs_slow_path(stmt == NULL)) { - return NJS_TOKEN_ERROR; +static njs_int_t +njs_parser_function_or_generator(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *node, *cur; + + if (token->type != NJS_TOKEN_FUNCTION) { + return NJS_DECLINED; } - stmt->hoist = new_node->hoist; - stmt->left = last; - stmt->right = new_node; + cur = parser->node; - *child = stmt; + if (token->type == NJS_TOKEN_MULTIPLICATION) { + njs_lexer_consume_token(parser->lexer, 1); + njs_parser_next(parser, njs_parser_generator_declaration); - while (type == NJS_TOKEN_SEMICOLON) { - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - break; + } else { + node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION); + if (node == NULL) { + return NJS_ERROR; } + + node->token_line = token->line; + parser->node = node; + + njs_lexer_consume_token(parser->lexer, 1); + njs_parser_next(parser, njs_parser_function_declaration); } - return type; + return njs_parser_after(parser, current, cur, 1, + njs_parser_statement_after); } -static njs_token_type_t -njs_parser_statement(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) +static njs_int_t +njs_parser_async_function_or_generator(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) { - size_t offset; - - parser->node = NULL; - - switch (type) { - - case NJS_TOKEN_FUNCTION: - return njs_parser_function_declaration(vm, parser); + if (token->type != NJS_TOKEN_ASYNC) { + return NJS_DECLINED; + } - case NJS_TOKEN_IF: - return njs_parser_if_statement(vm, parser); + token = njs_lexer_peek_token(parser->lexer, token, 1); + if (token == NULL) { + return NJS_ERROR; + } - case NJS_TOKEN_SWITCH: - return njs_parser_switch_statement(vm, parser); + if (token->type != NJS_TOKEN_FUNCTION) { + return NJS_DECLINED; + } - case NJS_TOKEN_WHILE: - return njs_parser_while_statement(vm, parser); + if (token->type == NJS_TOKEN_MULTIPLICATION) { + njs_parser_next(parser, njs_parser_generator_declaration); + } else { + njs_parser_next(parser, njs_parser_async_function_expression); + } - case NJS_TOKEN_DO: - return njs_parser_do_while_statement(vm, parser); + return NJS_OK; +} - case NJS_TOKEN_FOR: - return njs_parser_for_statement(vm, parser); - case NJS_TOKEN_TRY: - return njs_parser_try_statement(vm, parser); +njs_inline njs_int_t +njs_parser_expect_semicolon(njs_parser_t *parser, njs_lexer_token_t *token) +{ + if (token->type != NJS_TOKEN_SEMICOLON) { + if (parser->strict_semicolon + || (token->type != NJS_TOKEN_END + && token->type != NJS_TOKEN_CLOSE_BRACE + && parser->lexer->prev_type != NJS_TOKEN_LINE_END)) + { + return NJS_DECLINED; + } - case NJS_TOKEN_SEMICOLON: - return njs_parser_token(vm, parser); + return NJS_OK; + } - case NJS_TOKEN_OPEN_BRACE: - return njs_parser_block_statement(vm, parser); + njs_lexer_consume_token(parser->lexer, 1); - case NJS_TOKEN_CLOSE_BRACE: - if (vm->options.trailer) { - parser->node = NULL; - njs_thread_log_debug("BLOCK END"); - return type; - } + return NJS_OK; +} - /* Fall through. */ - default: +static njs_int_t +njs_parser_semicolon(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { + return njs_parser_failed(parser); + } - switch (type) { - case NJS_TOKEN_VAR: - type = njs_parser_var_statement(vm, parser, type, 0); - break; + return njs_parser_stack_pop(parser); +} - case NJS_TOKEN_RETURN: - type = njs_parser_return_statement(vm, parser); - break; - case NJS_TOKEN_THROW: - type = njs_parser_throw_statement(vm, parser); - break; +static njs_int_t +njs_parser_close_bracked(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_CLOSE_BRACKET) { + return njs_parser_failed(parser); + } - case NJS_TOKEN_CONTINUE: - case NJS_TOKEN_BREAK: - type = njs_parser_brk_statement(vm, parser, type); - break; + njs_lexer_consume_token(parser->lexer, 1); - case NJS_TOKEN_IMPORT: - type = njs_parser_import_statement(vm, parser); - break; + return njs_parser_stack_pop(parser); +} - case NJS_TOKEN_EXPORT: - type = njs_parser_export_statement(vm, parser); - break; - case NJS_TOKEN_NAME: - offset = 0; - if (njs_parser_peek_token(vm, parser, &offset) == NJS_TOKEN_COLON) { - return njs_parser_labelled_statement(vm, parser); - } +static njs_int_t +njs_parser_close_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { + return njs_parser_failed(parser); + } - /* Fall through. */ + njs_lexer_consume_token(parser->lexer, 1); - default: - type = njs_parser_expression(vm, parser, type); - break; - } + return njs_parser_stack_pop(parser); +} - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - /* - * An expression must be terminated by semicolon, - * or by a close curly brace or by the end of line. - */ - switch (type) { +static njs_int_t +njs_parser_expression_parenthesis(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { + return njs_parser_failed(parser); + } - case NJS_TOKEN_SEMICOLON: - return njs_parser_token(vm, parser); + njs_lexer_consume_token(parser->lexer, 1); - case NJS_TOKEN_CLOSE_BRACE: - case NJS_TOKEN_END: - return type; + parser->node = NULL; - default: - if (parser->lexer->prev_type == NJS_TOKEN_LINE_END) { - return type; - } + njs_parser_next(parser, njs_parser_expression); - return NJS_TOKEN_ILLEGAL; - } - } + return njs_parser_after(parser, current, NULL, 0, + njs_parser_close_parenthesis); } -static njs_token_type_t -njs_parser_block_statement(njs_vm_t *vm, njs_parser_t *parser) +/* + * 12.2 Primary Expression. + */ +static njs_int_t +njs_parser_primary_expression_test(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; - njs_token_type_t type; + njs_lexer_token_t *next; njs_parser_node_t *node; - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_BLOCK); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } - - parser->node = NULL; + switch (token->type) { + /* IdentifierReference */ + case NJS_TOKEN_THIS: + case NJS_TOKEN_NULL: + case NJS_TOKEN_NAME: + case NJS_TOKEN_YIELD: + case NJS_TOKEN_AWAIT: + goto reference; - while (type != NJS_TOKEN_CLOSE_BRACE) { - type = njs_parser_statement_chain(vm, parser, type, 0); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + case NJS_TOKEN_TRUE: + node = njs_parser_node_new(parser, token->type); + if (node == NULL) { + return NJS_ERROR; } - } - node = njs_parser_node_new(vm, parser, NJS_TOKEN_BLOCK); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } + node->u.value = njs_value_true; - node->left = parser->node; - node->right = NULL; - parser->node = node; + parser->node = node; + goto done; - njs_parser_scope_end(vm, parser); + case NJS_TOKEN_FALSE: + node = njs_parser_node_new(parser, token->type); + if (node == NULL) { + return NJS_ERROR; + } - return njs_parser_token(vm, parser); -} + node->u.value = njs_value_false; + parser->node = node; + goto done; -static njs_token_type_t -njs_parser_block(njs_vm_t *vm, njs_parser_t *parser, njs_token_type_t type) -{ - if (type == NJS_TOKEN_FUNCTION) { - njs_parser_syntax_error(vm, parser, - "Functions can only be declared at top level or inside a block"); - return NJS_TOKEN_ILLEGAL; - } + case NJS_TOKEN_NUMBER: + node = njs_parser_node_new(parser, NJS_TOKEN_NUMBER); + if (node == NULL) { + return NJS_ERROR; + } - return njs_parser_statement(vm, parser, type); -} + njs_set_number(&node->u.value, token->number); + parser->node = node; + goto done; -static njs_parser_node_t * -njs_parser_variable_node(njs_vm_t *vm, njs_parser_t *parser, - uintptr_t unique_id, njs_variable_type_t type) -{ - njs_int_t ret; - njs_variable_t *var; - njs_parser_node_t *node; + case NJS_TOKEN_STRING: + node = njs_parser_node_new(parser, NJS_TOKEN_STRING); + if (node == NULL) { + return NJS_ERROR; + } - var = njs_variable_add(vm, parser->scope, unique_id, type); - if (njs_slow_path(var == NULL)) { - return NULL; - } + ret = njs_parser_string_create(parser->vm, token, &node->u.value); + if (ret != NJS_OK) { + return NJS_ERROR; + } - if (njs_is_null(&var->value)) { + parser->node = node; + goto done; - switch (type) { + case NJS_TOKEN_ESCAPE_STRING: + /* Internal optimization. This is not in the specification. */ - case NJS_VARIABLE_VAR: - njs_set_undefined(&var->value); - break; + node = njs_parser_node_new(parser, NJS_TOKEN_STRING); + if (node == NULL) { + return NJS_ERROR; + } - default: - break; + ret = njs_parser_escape_string_create(parser, token, &node->u.value); + if (ret != NJS_TOKEN_STRING) { + return NJS_DONE; } - } - node = njs_parser_node_new(vm, parser, NJS_TOKEN_NAME); - if (njs_slow_path(node == NULL)) { - return NULL; - } + parser->node = node; + goto done; - ret = njs_variable_reference(vm, parser->scope, node, unique_id, - NJS_DECLARATION); - if (njs_slow_path(ret != NJS_OK)) { - return NULL; - } + case NJS_TOKEN_UNTERMINATED_STRING: + /* Internal optimization. This is not in the specification. */ - return node; -} + njs_parser_syntax_error(parser, "Unterminated string \"%V\"", + &token->text); + return NJS_DONE; + /* ArrayLiteral */ + case NJS_TOKEN_OPEN_BRACKET: + njs_parser_next(parser, njs_parser_array_literal); + break; -static njs_token_type_t -njs_parser_labelled_statement(njs_vm_t *vm, njs_parser_t *parser) -{ - uintptr_t unique_id; - njs_int_t ret; - njs_str_t name; - njs_variable_t *label; - njs_token_type_t type; + /* ObjectLiteral */ + case NJS_TOKEN_OPEN_BRACE: + njs_parser_next(parser, njs_parser_object_literal); + break; - name = *njs_parser_text(parser); - unique_id = njs_parser_key_hash(parser); + /* FunctionExpression */ + case NJS_TOKEN_FUNCTION: + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } - label = njs_label_find(vm, parser->scope, unique_id); - if (njs_slow_path(label != NULL)) { - njs_parser_syntax_error(vm, parser, "Label \"%V\" " - "has already been declared", &name); - return NJS_TOKEN_ILLEGAL; - } + /* GeneratorExpression */ + if (token->type == NJS_TOKEN_MULTIPLICATION) { + njs_parser_next(parser, njs_parser_generator_expression); - label = njs_label_add(vm, parser->scope, unique_id); - if (njs_slow_path(label == NULL)) { - return NJS_TOKEN_ERROR; - } + } else { + node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION); + if (node == NULL) { + return NJS_ERROR; + } - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + node->token_line = token->line; - type = njs_parser_match(vm, parser, type, NJS_TOKEN_COLON); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + parser->node = node; - type = njs_parser_statement(vm, parser, type); + njs_parser_next(parser, njs_parser_function_expression); + } - if (njs_fast_path(type > NJS_TOKEN_ILLEGAL)) { + break; - if (parser->node != NULL) { - /* The statement is not empty block or just semicolon. */ + /* ClassExpression */ + case NJS_TOKEN_CLASS: + njs_parser_next(parser, njs_parser_class_expression); + return NJS_OK; - ret = njs_name_copy(vm, &parser->node->name, &name); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } + /* AsyncFunctionExpression */ + case NJS_TOKEN_ASYNC: + next = njs_lexer_peek_token(parser->lexer, token, 1); + if (next == NULL) { + return NJS_ERROR; + } - ret = njs_label_remove(vm, parser->scope, unique_id); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } + if (next->type != NJS_TOKEN_FUNCTION) { + goto reference; } - } - return type; -} + next = njs_lexer_peek_token(parser->lexer, next, 0); + if (njs_slow_path(next == NULL)) { + return NJS_ERROR; + } + /* GeneratorExpression */ + if (next->type == NJS_TOKEN_MULTIPLICATION) { + njs_lexer_consume_token(parser->lexer, 1); + njs_parser_next(parser, njs_parser_async_generator_expression); -static njs_function_t * -njs_parser_function_alloc(njs_vm_t *vm, njs_parser_t *parser, - njs_variable_t *var) -{ - njs_value_t *value; - njs_function_t *function; - njs_function_lambda_t *lambda; + } else { + njs_parser_next(parser, njs_parser_async_function_expression); + } - lambda = njs_function_lambda_alloc(vm, 1); - if (njs_slow_path(lambda == NULL)) { - njs_memory_error(vm); - return NULL; - } + return NJS_OK; - /* TODO: - * njs_function_t is used to pass lambda to - * njs_generate_function_declaration() and is not actually needed. - * real njs_function_t is created by njs_vmcode_function() in runtime. - */ + /* RegularExpressionLiteral */ + case NJS_TOKEN_DIVISION: + node = njs_parser_node_new(parser, NJS_TOKEN_REGEXP); + if (node == NULL) { + return NJS_ERROR; + } - function = njs_function_alloc(vm, lambda, NULL, 1); - if (njs_slow_path(function == NULL)) { - return NULL; - } + parser->node = node; - njs_set_function(&var->value, function); + ret = njs_parser_regexp_literal(parser, token, current); + if (ret != NJS_OK) { + return NJS_DONE; + } - if (var->index != NJS_INDEX_NONE - && njs_scope_accumulative(vm, parser->scope)) - { - value = (njs_value_t *) var->index; - *value = var->value; - } + goto done; - return function; -} + /* TemplateLiteral */ + case NJS_TOKEN_GRAVE: + node = njs_parser_node_new(parser, NJS_TOKEN_TEMPLATE_LITERAL); + if (node == NULL) { + return NJS_ERROR; + } + parser->node = node; -static njs_token_type_t -njs_parser_function_declaration(njs_vm_t *vm, njs_parser_t *parser) -{ - njs_int_t ret; - njs_variable_t *var; - njs_function_t *function; - njs_token_type_t type; - njs_parser_node_t *node; + njs_parser_next(parser, njs_parser_template_literal); + break; - node = njs_parser_node_new(vm, parser, NJS_TOKEN_FUNCTION); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } + /* CoverParenthesizedExpressionAndArrowParameterList */ + case NJS_TOKEN_OPEN_PARENTHESIS: + njs_lexer_consume_token(parser->lexer, 1); - node->token_line = njs_parser_token_line(parser); + /* TODO: By specification. */ + (void) njs_parser_cover_parenthesized_expression; - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + parser->node = NULL; - if (type != NJS_TOKEN_NAME) { - if (njs_parser_restricted_identifier(type)) { - njs_parser_syntax_error(vm, parser, "Identifier \"%V\" " - "is forbidden in function declaration", - njs_parser_text(parser)); + njs_parser_next(parser, njs_parser_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_close_parenthesis); + + default: + if (njs_lexer_token_is_identifier_reference(token)) { + goto reference; } - return NJS_TOKEN_ILLEGAL; + return njs_parser_reject(parser); } - var = njs_parser_variable_add(vm, parser, NJS_VARIABLE_FUNCTION); - if (njs_slow_path(var == NULL)) { - return NJS_TOKEN_ERROR; - } + njs_lexer_consume_token(parser->lexer, 1); - ret = njs_parser_variable_reference(vm, parser, node, NJS_DECLARATION); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } + return NJS_OK; + +reference: - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + node = njs_parser_reference(parser, token); + if (node == NULL) { + return NJS_ERROR; } parser->node = node; - function = njs_parser_function_alloc(vm, parser, var); - if (njs_slow_path(function == NULL)) { - return NJS_TOKEN_ERROR; - } - - type = njs_parser_function_lambda(vm, parser, function->u.lambda, type); +done: - function->args_count = function->u.lambda->nargs - - function->u.lambda->rest_parameters; + njs_lexer_consume_token(parser->lexer, 1); - return type; + return NJS_DONE; } -njs_token_type_t -njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser) +static njs_int_t +njs_parser_regexp_literal(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) { - njs_int_t ret; - njs_variable_t *var; - njs_function_t *function; - njs_token_type_t type; - njs_parser_node_t *node; - njs_function_lambda_t *lambda; - - node = njs_parser_node_new(vm, parser, NJS_TOKEN_FUNCTION_EXPRESSION); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } + u_char *p; + njs_str_t text; + njs_lexer_t *lexer; + njs_value_t *value; + njs_regexp_flags_t flags; + njs_regexp_pattern_t *pattern; + + value = &parser->node->u.value; + lexer = parser->lexer; - node->token_line = njs_parser_token_line(parser); - parser->node = node; + for (p = lexer->start; p < lexer->end; p++) { - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + switch (*p) { + case '\n': + case '\r': + goto failed; - /* - * An optional function expression name is stored - * in intermediate shim scope. - */ - ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_SHIM); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } + case '[': + while (1) { + if (++p >= lexer->end) { + goto failed; + } - if (type == NJS_TOKEN_NAME) { - var = njs_parser_variable_add(vm, parser, NJS_VARIABLE_SHIM); - if (njs_slow_path(var == NULL)) { - return NJS_TOKEN_ERROR; - } + if (*p == ']') { + break; + } - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + switch (*p) { + case '\n': + case '\r': + goto failed; - function = njs_parser_function_alloc(vm, parser, var); - if (njs_slow_path(function == NULL)) { - return NJS_TOKEN_ERROR; - } + case '\\': + if (++p >= lexer->end || *p == '\n' || *p == '\r') { + goto failed; + } - lambda = function->u.lambda; + break; + } + } - } else { - /* Anonymous function. */ - lambda = njs_function_lambda_alloc(vm, 1); - if (njs_slow_path(lambda == NULL)) { - return NJS_TOKEN_ERROR; - } - } + break; - node->u.value.data.u.lambda = lambda; + case '\\': + if (++p >= lexer->end || *p == '\n' || *p == '\r') { + goto failed; + } - type = njs_parser_function_lambda(vm, parser, lambda, type); + break; - njs_parser_scope_end(vm, parser); + case '/': + text.start = lexer->start; + text.length = p - text.start; + p++; + lexer->start = p; - return type; -} + flags = njs_regexp_flags(&p, lexer->end, 0); + if (njs_slow_path(flags < 0)) { + njs_parser_syntax_error(parser, "Invalid RegExp flags \"%*s\"", + p - lexer->start, lexer->start); -njs_token_type_t -njs_parser_function_lambda(njs_vm_t *vm, njs_parser_t *parser, - njs_function_lambda_t *lambda, njs_token_type_t type) -{ - njs_int_t ret; - njs_index_t index; + return NJS_ERROR; + } - ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_FUNCTION); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } + lexer->start = p; - index = NJS_SCOPE_ARGUMENTS; + pattern = njs_regexp_pattern_create(parser->vm, text.start, + text.length, flags); - /* A "this" reservation. */ - index += sizeof(njs_value_t); + if (njs_slow_path(pattern == NULL)) { + return NJS_ERROR; + } - type = njs_parser_lambda_arguments(vm, parser, lambda, index, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + value->data.u.data = pattern; - type = njs_parser_lambda_body(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + return NJS_OK; + } } - njs_parser_scope_end(vm, parser); +failed: - return type; + njs_parser_syntax_error(parser, "Unterminated RegExp \"%*s\"", + p - (lexer->start - 1), lexer->start - 1); + return NJS_ERROR; } -static njs_token_type_t -njs_parser_lambda_arguments(njs_vm_t *vm, njs_parser_t *parser, - njs_function_lambda_t *lambda, njs_index_t index, njs_token_type_t type) +static njs_int_t +njs_parser_template_literal(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) { - type = njs_parser_match(vm, parser, type, NJS_TOKEN_OPEN_PARENTHESIS); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + njs_index_t index; + njs_parser_node_t *node, *array, *template, *temp; + + temp = njs_parser_node_new(parser, 0); + if (temp == NULL) { + return NJS_ERROR; } - lambda->nargs = 0; + array = njs_parser_node_new(parser, NJS_TOKEN_ARRAY); + if (array == NULL) { + return NJS_ERROR; + } - while (type != NJS_TOKEN_CLOSE_PARENTHESIS) { + template = parser->node; + index = NJS_SCOPE_CALLEE_ARGUMENTS; - if (njs_slow_path(lambda->rest_parameters)) { - return NJS_TOKEN_ILLEGAL; + if (template->token_type != NJS_TOKEN_TEMPLATE_LITERAL) { + node = njs_parser_argument(parser, array, index); + if (node == NULL) { + return NJS_ERROR; } - if (njs_slow_path(type == NJS_TOKEN_ELLIPSIS)) { - lambda->rest_parameters = 1; + template->right = node; + temp->right = node; - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return NJS_TOKEN_ILLEGAL; - } - } + index += sizeof(njs_value_t); - if (njs_slow_path(type != NJS_TOKEN_NAME)) { - return NJS_TOKEN_ILLEGAL; - } + } else { + template->left = array; + temp->right = template; + } - type = njs_parser_lambda_argument(vm, parser, index); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + temp->left = template; + temp->index = index; - if (type == NJS_TOKEN_COMMA) { - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - } + parser->target = temp; - lambda->nargs++; - index += sizeof(njs_value_t); - } + njs_parser_next(parser, njs_parser_template_literal_string); - return njs_parser_token(vm, parser); + return NJS_OK; } -static njs_token_type_t -njs_parser_lambda_argument(njs_vm_t *vm, njs_parser_t *parser, - njs_index_t index) +static njs_int_t +njs_parser_template_literal_string(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) { - njs_variable_t *arg; + njs_int_t ret, ret_item; + njs_parser_node_t *template; - arg = njs_parser_variable_add(vm, parser, NJS_VARIABLE_VAR); - if (njs_slow_path(arg == NULL)) { - return NJS_TOKEN_ERROR; - } + template = parser->target->left; - if (arg->index > 0) { - njs_parser_syntax_error(vm, parser, "Duplicate parameter names"); - - return NJS_TOKEN_ILLEGAL; + ret = njs_parser_template_string(parser, token); + if (ret == NJS_ERROR) { + njs_parser_syntax_error(parser, "Unterminated template literal"); + return NJS_DONE; } - arg->index = index; - arg->unique_id = njs_parser_key_hash(parser); + if (template->token_type != NJS_TOKEN_TEMPLATE_LITERAL) { + ret_item = njs_parser_array_item(parser, template->right->left, + parser->node); - return njs_parser_token(vm, parser); -} - - -static njs_token_type_t -njs_parser_lambda_body(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) -{ - njs_parser_node_t *body, *last, *parent; - - parent = parser->node; + } else { + ret_item = njs_parser_array_item(parser, template->left, parser->node); + } - type = njs_parser_lambda_statements(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + if (ret_item != NJS_OK) { + return NJS_ERROR; } - last = NULL; - body = njs_parser_chain_top(parser); + if (ret == NJS_DONE) { + parser->node = template; - if (body != NULL) { - /* Take the last function body statement. */ - last = body->right; + njs_parser_node_free(parser, parser->target); + njs_lexer_consume_token(parser->lexer, 1); - if (last == NULL) { - /* - * The last statement is terminated by semicolon. - * Take the last statement itself. - */ - last = body->left; - } + return njs_parser_stack_pop(parser); } - if (last == NULL || last->token_type != NJS_TOKEN_RETURN) { - /* - * There is no function body or the last function body - * body statement is not "return" statement. - */ - body = njs_parser_return_set(vm, parser, NULL); - if (njs_slow_path(body == NULL)) { - return NJS_TOKEN_ERROR; - } - } + parser->node = NULL; - parent->right = body; + njs_parser_next(parser, njs_parser_expression); - parser->node = parent; + njs_lexer_consume_token(parser->lexer, 1); - return type; + return njs_parser_after(parser, current, parser->target, 0, + njs_parser_template_literal_expression); } -static njs_parser_node_t * -njs_parser_return_set(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *expr) +static njs_int_t +njs_parser_template_literal_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) { - njs_parser_node_t *stmt, *node; + njs_int_t ret; + njs_parser_node_t *template, *node, *parent; - node = njs_parser_node_new(vm, parser, NJS_TOKEN_RETURN); - if (njs_slow_path(node == NULL)) { - return NULL; + if (parser->ret != NJS_OK) { + return njs_parser_failed(parser); } - node->right = expr; - - stmt = njs_parser_node_new(vm, parser, NJS_TOKEN_STATEMENT); - if (njs_slow_path(stmt == NULL)) { - return NULL; + if (token->type != NJS_TOKEN_CLOSE_BRACE) { + njs_parser_syntax_error(parser, "Missing \"}\" in template expression"); + return NJS_DONE; } - stmt->left = njs_parser_chain_top(parser); - stmt->right = node; + parent = parser->target->right; + template = parser->target->left; - njs_parser_chain_top_set(parser, stmt); + if (template->token_type != NJS_TOKEN_TEMPLATE_LITERAL) { + node = njs_parser_argument(parser, parser->node, parser->target->index); + if (node == NULL) { + return NJS_ERROR; + } - return stmt; -} + parent->right = node; + parent = node; + parser->target->index += sizeof(njs_value_t); -njs_token_type_t -njs_parser_lambda_statements(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) -{ - type = njs_parser_match(vm, parser, type, NJS_TOKEN_OPEN_BRACE); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + } else { + ret = njs_parser_array_item(parser, template->left, parser->node); + if (ret != NJS_OK) { + return NJS_ERROR; + } } + parser->target->right = parent; + parser->node = NULL; - while (type != NJS_TOKEN_CLOSE_BRACE) { - type = njs_parser_statement_chain(vm, parser, type, 1); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - } + njs_parser_next(parser, njs_parser_template_literal_string); + + token->text.length = 0; + token->text.start += 1; - return njs_parser_token(vm, parser); + return NJS_OK; } -static njs_token_type_t -njs_parser_return_statement(njs_vm_t *vm, njs_parser_t *parser) +static njs_int_t +njs_parser_cover_parenthesized_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) { - njs_token_type_t type; - njs_parser_node_t *node; - njs_parser_scope_t *scope; + switch (token->type) { + case NJS_TOKEN_CLOSE_PARENTHESIS: + (void) njs_parser_stack_pop(parser); + break; - for (scope = parser->scope; - scope != NULL; - scope = scope->parent) - { - if (scope->type == NJS_SCOPE_FUNCTION && !scope->module) { - break; - } + case NJS_TOKEN_ELLIPSIS: + njs_parser_next(parser, njs_parser_binding_identifier_pattern); + break; - if (scope->type == NJS_SCOPE_GLOBAL) { - njs_parser_syntax_error(vm, parser, "Illegal return statement"); + default: + parser->node = NULL; - return NJS_ERROR; - } - } + njs_parser_next(parser, njs_parser_expression); - node = njs_parser_node_new(vm, parser, NJS_TOKEN_RETURN); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; + return njs_parser_after(parser, current, NULL, 0, + njs_parser_cover_parenthesized_expression_after); } - parser->node = node; - - type = njs_lexer_token(vm, parser->lexer); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + njs_lexer_consume_token(parser->lexer, 1); - switch (type) { + return NJS_OK; +} - case NJS_TOKEN_LINE_END: - return njs_parser_token(vm, parser); - case NJS_TOKEN_SEMICOLON: - case NJS_TOKEN_CLOSE_BRACE: - case NJS_TOKEN_END: - return type; +static njs_int_t +njs_parser_binding_identifier_pattern(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + /* + * BindingIdentifier ) + * BindingPattern ) + */ - default: - type = njs_parser_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + switch (token->type) { - if (parser->node->token_type == NJS_TOKEN_FUNCTION) { - /* TODO: test closure */ - } + /* BindingIdentifier */ + case NJS_TOKEN_NAME: + njs_parser_next(parser, njs_parser_cover_parenthesized_expression_end); + break; - node->right = parser->node; - parser->node = node; + case NJS_TOKEN_YIELD: + njs_parser_next(parser, njs_parser_cover_parenthesized_expression_end); + break; - return type; - } -} + case NJS_TOKEN_AWAIT: + njs_parser_next(parser, njs_parser_cover_parenthesized_expression_end); + break; + /* BindingPattern */ + case NJS_TOKEN_OPEN_BRACKET: + njs_parser_next(parser, njs_parser_array_binding_pattern); -static njs_token_type_t -njs_parser_var_statement(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t parent, njs_bool_t var_in) -{ - njs_token_type_t token_type; - njs_parser_node_t *left, *stmt, *name, *assign, *expr; - njs_variable_type_t type; + njs_lexer_consume_token(parser->lexer, 1); + return njs_parser_after(parser, current, NULL, 0, + njs_parser_cover_parenthesized_expression_end); - type = NJS_VARIABLE_VAR; + case NJS_TOKEN_OPEN_BRACE: + njs_parser_next(parser, njs_parser_object_binding_pattern); - parser->node = NULL; - left = NULL; + njs_lexer_consume_token(parser->lexer, 1); + return njs_parser_after(parser, current, NULL, 0, + njs_parser_cover_parenthesized_expression_end); - do { - token_type = njs_parser_token(vm, parser); - if (njs_slow_path(token_type <= NJS_TOKEN_ILLEGAL)) { - return token_type; - } - - if (token_type != NJS_TOKEN_NAME) { - if (njs_parser_restricted_identifier(token_type)) { - njs_parser_syntax_error(vm, parser, "Identifier \"%V\" " - "is forbidden in var declaration", - njs_parser_text(parser)); - } - - return NJS_TOKEN_ILLEGAL; - } + default: + return NJS_ERROR; + } - name = njs_parser_variable_node(vm, parser, njs_parser_key_hash(parser), - type); - if (njs_slow_path(name == NULL)) { - return NJS_TOKEN_ERROR; - } + njs_lexer_consume_token(parser->lexer, 1); - token_type = njs_parser_token(vm, parser); - if (njs_slow_path(token_type <= NJS_TOKEN_ILLEGAL)) { - return token_type; - } + return NJS_OK; +} - if (var_in) { - if (token_type == NJS_TOKEN_IN) { - return njs_parser_var_in_statement(vm, parser, name); - } - var_in = 0; - } +static njs_int_t +njs_parser_cover_parenthesized_expression_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + /* + * ) + * ,) + * , ... BindingIdentifier ) + * , ... BindingPattern ) + */ - expr = NULL; + if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { + goto shift_stack; + } - if (token_type == NJS_TOKEN_ASSIGNMENT) { + if (token->type != NJS_TOKEN_COMMA) { + return njs_parser_failed(parser); + } - token_type = njs_parser_token(vm, parser); - if (njs_slow_path(token_type <= NJS_TOKEN_ILLEGAL)) { - return token_type; - } + njs_lexer_consume_token(parser->lexer, 1); - token_type = njs_parser_assignment_expression(vm, parser, - token_type); - if (njs_slow_path(token_type <= NJS_TOKEN_ILLEGAL)) { - return token_type; - } + token = njs_lexer_token(parser->lexer, 0); + if (njs_slow_path(token == NULL)) { + return NJS_ERROR; + } - expr = parser->node; - } + if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { + goto shift_stack; + } - assign = njs_parser_node_new(vm, parser, parent); - if (njs_slow_path(assign == NULL)) { - return NJS_TOKEN_ERROR; - } + if(token->type != NJS_TOKEN_ELLIPSIS) { + return njs_parser_failed(parser); + } - assign->u.operation = NJS_VMCODE_MOVE; - assign->left = name; - assign->right = expr; + njs_lexer_consume_token(parser->lexer, 1); - stmt = njs_parser_node_new(vm, parser, NJS_TOKEN_STATEMENT); - if (njs_slow_path(stmt == NULL)) { - return NJS_TOKEN_ERROR; - } + njs_parser_next(parser, njs_parser_binding_identifier_pattern); - stmt->left = left; - stmt->right = assign; - parser->node = stmt; + return NJS_OK; - left = stmt; +shift_stack: - } while (token_type == NJS_TOKEN_COMMA); + njs_lexer_consume_token(parser->lexer, 1); - return token_type; + return njs_parser_stack_pop(parser); } -static njs_token_type_t -njs_parser_if_statement(njs_vm_t *vm, njs_parser_t *parser) +static njs_int_t +njs_parser_cover_parenthesized_expression_end(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) { - njs_token_type_t type; - njs_parser_node_t *node, *cond, *stmt; - - type = njs_parser_grouping_expression(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - cond = parser->node; - - type = njs_parser_block(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { + return njs_parser_failed(parser); } - if (type == NJS_TOKEN_ELSE) { + njs_lexer_consume_token(parser->lexer, 1); - stmt = parser->node; - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + return njs_parser_stack_pop(parser); +} - type = njs_parser_block(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - node = njs_parser_node_new(vm, parser, NJS_TOKEN_BRANCHING); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } +/* + * 12.2.5 Array Initializer. + */ +static njs_int_t +njs_parser_array_literal(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *array; - node->left = stmt; - node->right = parser->node; - parser->node = node; + array = njs_parser_node_new(parser, NJS_TOKEN_ARRAY); + if (array == NULL) { + return NJS_ERROR; } - node = njs_parser_node_new(vm, parser, NJS_TOKEN_IF); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } + parser->target = array; - node->left = cond; - node->right = parser->node; - parser->node = node; + njs_parser_next(parser, njs_parser_array_element_list); - return type; + return NJS_OK; } -static njs_token_type_t -njs_parser_switch_statement(njs_vm_t *vm, njs_parser_t *parser) +static njs_int_t +njs_parser_array_element_list(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) { - njs_token_type_t type; - njs_parser_node_t *node, *swtch, *branch, *dflt, **last; - - node = NULL; - branch = NULL; - dflt = NULL; - - type = njs_parser_grouping_expression(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - swtch = njs_parser_node_new(vm, parser, NJS_TOKEN_SWITCH); - if (njs_slow_path(swtch == NULL)) { - return NJS_TOKEN_ERROR; - } + njs_parser_node_t *array; - swtch->left = parser->node; - last = &swtch->right; + array = parser->target; - type = njs_parser_match(vm, parser, type, NJS_TOKEN_OPEN_BRACE); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + switch (token->type) { + case NJS_TOKEN_CLOSE_BRACKET: + njs_lexer_consume_token(parser->lexer, 1); - while (type != NJS_TOKEN_CLOSE_BRACE) { + parser->node = array; - if (type == NJS_TOKEN_CASE || type == NJS_TOKEN_DEFAULT) { + return njs_parser_stack_pop(parser); - do { - node = njs_parser_node_new(vm, parser, 0); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } + case NJS_TOKEN_COMMA: + njs_lexer_consume_token(parser->lexer, 1); - if (type == NJS_TOKEN_CASE) { - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + array->ctor = 1; + array->u.length++; - type = njs_parser_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + return NJS_OK; - node->left = parser->node; + case NJS_TOKEN_ELLIPSIS: + njs_lexer_consume_token(parser->lexer, 1); - branch = njs_parser_node_new(vm, parser, 0); - if (njs_slow_path(branch == NULL)) { - return NJS_TOKEN_ERROR; - } + njs_parser_next(parser, njs_parser_assignment_expression); - branch->right = node; + return njs_parser_after(parser, current, array, 0, + njs_parser_array_spread_element); + default: + break; + } - } else { - if (dflt != NULL) { - njs_parser_syntax_error(vm, parser, - "More than one default clause " - "in switch statement"); + njs_parser_next(parser, njs_parser_assignment_expression); - return NJS_TOKEN_ILLEGAL; - } + return njs_parser_after(parser, current, array, 0, njs_parser_array_after); +} - branch = node; - branch->token_type = NJS_TOKEN_DEFAULT; - dflt = branch; - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - } +static njs_int_t +njs_parser_array_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; - *last = branch; - last = &branch->left; + ret = njs_parser_array_item(parser, parser->target, parser->node); + if (ret != NJS_OK) { + return NJS_ERROR; + } - type = njs_parser_match(vm, parser, type, NJS_TOKEN_COLON); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + switch (token->type) { + case NJS_TOKEN_COMMA: + njs_lexer_consume_token(parser->lexer, 1); - } while (type == NJS_TOKEN_CASE || type == NJS_TOKEN_DEFAULT); + /* Fall through. */ - parser->node = NULL; + case NJS_TOKEN_CLOSE_BRACKET: + njs_parser_next(parser, njs_parser_array_element_list); + break; - if (type == NJS_TOKEN_CLOSE_BRACE) { - break; - } + default: + return njs_parser_failed(parser); + } - } else if (branch == NULL) { - /* The first switch statment is not "case/default" keyword. */ - return NJS_TOKEN_ILLEGAL; - } + return NJS_OK; +} - type = njs_parser_statement_chain(vm, parser, type, 0); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - node->right = parser->node; +static njs_int_t +njs_parser_array_spread_element(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_CLOSE_BRACKET) { + return njs_parser_failed(parser); } - parser->node = swtch; + njs_lexer_consume_token(parser->lexer, 1); - return njs_parser_token(vm, parser); + parser->node = parser->target; + + return njs_parser_stack_pop(parser); } -static njs_token_type_t -njs_parser_while_statement(njs_vm_t *vm, njs_parser_t *parser) +/* + * 12.2.6 Object Initializer. + */ +static njs_int_t +njs_parser_object_literal(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) { - njs_token_type_t type; - njs_parser_node_t *node, *cond; + njs_parser_node_t *object, *node; - type = njs_parser_grouping_expression(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + object = njs_parser_node_new(parser, NJS_TOKEN_OBJECT); + if (object == NULL) { + return NJS_ERROR; } - cond = parser->node; - - type = njs_parser_block(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + node = njs_parser_node_new(parser, 0); + if (node == NULL) { + return NJS_ERROR; } - node = njs_parser_node_new(vm, parser, NJS_TOKEN_WHILE); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } + node->left = object; - node->left = parser->node; - node->right = cond; - parser->node = node; + parser->target = node; - return type; + njs_parser_next(parser, njs_parser_property_definition_list); + + return njs_parser_after(parser, current, node, 0, + njs_parser_object_literal_after); } -static njs_token_type_t -njs_parser_do_while_statement(njs_vm_t *vm, njs_parser_t *parser) +static njs_int_t +njs_parser_object_literal_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) { - njs_token_type_t type; - njs_parser_node_t *node, *stmt; - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + if (token->type == NJS_TOKEN_COMMA) { + njs_lexer_consume_token(parser->lexer, 1); - type = njs_parser_block(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + token = njs_lexer_token(parser->lexer, 1); + if (token == NULL) { + return NJS_ERROR; + } } - stmt = parser->node; - - if (njs_slow_path(type != NJS_TOKEN_WHILE)) { - return NJS_TOKEN_ILLEGAL; + if (token->type != NJS_TOKEN_CLOSE_BRACE) { + return njs_parser_failed(parser); } - type = njs_parser_grouping_expression(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + njs_lexer_consume_token(parser->lexer, 1); - node = njs_parser_node_new(vm, parser, NJS_TOKEN_DO); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } + parser->node = parser->target->left; - node->left = stmt; - node->right = parser->node; - parser->node = node; + njs_parser_node_free(parser, parser->target); + parser->target = NULL; - return type; + return njs_parser_stack_pop(parser); } -static njs_token_type_t -njs_parser_for_statement(njs_vm_t *vm, njs_parser_t *parser) +static njs_int_t +njs_parser_property_definition_list(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) { - njs_str_t name; - njs_token_type_t type; - njs_parser_node_t *node, *init, *condition, *update, *cond, *body; + njs_parser_next(parser, njs_parser_property_definition); - init = NULL; - condition = NULL; - update = NULL; + return njs_parser_after(parser, current, parser->target, 0, + njs_parser_property_definition_list_after); +} - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - type = njs_parser_match(vm, parser, type, NJS_TOKEN_OPEN_PARENTHESIS); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; +static njs_int_t +njs_parser_property_definition_list_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_COMMA) { + return njs_parser_stack_pop(parser); } - if (type != NJS_TOKEN_SEMICOLON) { + njs_lexer_consume_token(parser->lexer, 1); - if (type == NJS_TOKEN_VAR) { + njs_parser_next(parser, njs_parser_property_definition); - type = njs_parser_var_statement(vm, parser, type, 1); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + return njs_parser_after(parser, current, parser->target, 0, + njs_parser_property_definition_list_after); +} - init = parser->node; - if (init->token_type == NJS_TOKEN_FOR_IN) { - goto done; - } +njs_inline njs_parser_node_t * +njs_parser_property_name_node(njs_parser_t *parser, njs_lexer_token_t *token) +{ + njs_int_t ret; + njs_parser_node_t *property; - } else { + if (token->type == NJS_TOKEN_NUMBER) { + property = njs_parser_node_new(parser, NJS_TOKEN_NUMBER); + if (property != NULL) { + njs_set_number(&property->u.value, token->number); + } - name = *njs_parser_text(parser); + } else if (token->type == NJS_TOKEN_ESCAPE_STRING) { + property = njs_parser_node_new(parser, NJS_TOKEN_STRING); - type = njs_parser_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + if (property != NULL) { + ret = njs_parser_escape_string_create(parser, token, + &property->u.value); + if (ret != NJS_TOKEN_STRING) { + return NULL; } + } - init = parser->node; + } else { + property = njs_parser_node_string(parser->vm, token, parser); + } - if (init->token_type == NJS_TOKEN_IN) { - type = njs_parser_for_in_statement(vm, parser, &name, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + return property; +} - goto done; - } - } - } - type = njs_parser_match(vm, parser, type, NJS_TOKEN_SEMICOLON); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } +njs_inline njs_int_t +njs_parser_property_name(njs_parser_t *parser, njs_queue_link_t *current, + unsigned consume) +{ + njs_lexer_token_t *token; + njs_parser_node_t *property; - if (type != NJS_TOKEN_SEMICOLON) { + if (consume > 1) { + token = njs_lexer_token(parser->lexer, 0); - type = njs_parser_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + property = njs_parser_property_name_node(parser, token); + if (property == NULL) { + return NJS_ERROR; } - condition = parser->node; + parser->target->right = property; + parser->target->index = (token->type == NJS_TOKEN_NAME); } - type = njs_parser_match(vm, parser, type, NJS_TOKEN_SEMICOLON); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + njs_lexer_consume_token(parser->lexer, consume); - if (type != NJS_TOKEN_CLOSE_PARENTHESIS) { + parser->node = NULL; - type = njs_parser_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + njs_parser_next(parser, njs_parser_assignment_expression); - update = parser->node; - } + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_property_definition_after); +} - type = njs_parser_match(vm, parser, type, NJS_TOKEN_CLOSE_PARENTHESIS); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - type = njs_parser_block(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; +static njs_int_t +njs_parser_property_definition_ident(njs_parser_t *parser, + njs_lexer_token_t *token, njs_parser_node_t *temp) +{ + temp->right = njs_parser_node_string(parser->vm, token, parser); + if (temp->right == NULL) { + return NJS_ERROR; } - node = njs_parser_node_new(vm, parser, NJS_TOKEN_FOR); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; + temp->right->index = NJS_TOKEN_OPEN_BRACKET; + + parser->node = njs_parser_reference(parser, token); + if (parser->node == NULL) { + return NJS_ERROR; } - cond = njs_parser_node_new(vm, parser, 0); - if (njs_slow_path(cond == NULL)) { - return NJS_TOKEN_ERROR; + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; } - body = njs_parser_node_new(vm, parser, 0); - if (njs_slow_path(body == NULL)) { - return NJS_TOKEN_ERROR; + /* CoverInitializedName */ + if (token->type == NJS_TOKEN_ASSIGNMENT) { + return njs_parser_not_supported(parser, token); } - node->left = init; - node->right = cond; + njs_parser_next(parser, njs_parser_property_definition_after); - cond->left = condition; - cond->right = body; + return NJS_OK; +} - body->left = parser->node; - body->right = update; - parser->node = node; +static njs_int_t +njs_parser_property_definition(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_str_t *name; + njs_token_type_t accessor; + njs_lexer_token_t *next; + njs_parser_node_t *temp, *property; + + temp = parser->target; + + switch (token->type) { + /* PropertyName */ + case NJS_TOKEN_STRING: + case NJS_TOKEN_ESCAPE_STRING: + case NJS_TOKEN_NUMBER: + next = njs_lexer_peek_token(parser->lexer, token, 0); + if (next == NULL) { + return NJS_ERROR; + } -done: + if (next->type == NJS_TOKEN_COLON) { + return njs_parser_property_name(parser, current, 2); - return type; -} + } else if (next->type == NJS_TOKEN_OPEN_PARENTHESIS) { + goto method_definition; + } + njs_lexer_consume_token(parser->lexer, 1); -static njs_token_type_t -njs_parser_var_in_statement(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *name) -{ - njs_token_type_t type; - njs_parser_node_t *node, *foreach; + return njs_parser_failed(parser); - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + /* ComputedPropertyName */ + case NJS_TOKEN_OPEN_BRACKET: + njs_lexer_consume_token(parser->lexer, 1); - type = njs_parser_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + njs_parser_next(parser, njs_parser_assignment_expression); - node = njs_parser_node_new(vm, parser, NJS_TOKEN_IN); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } + return njs_parser_after(parser, current, temp, 1, + njs_parser_computed_property_name_after); - node->left = name; - node->right = parser->node; + case NJS_TOKEN_ELLIPSIS: + return njs_parser_not_supported(parser, token); - type = njs_parser_match(vm, parser, type, NJS_TOKEN_CLOSE_PARENTHESIS); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + default: + if (!njs_lexer_token_is_identifier_name(token)) { + return njs_parser_reject(parser); + } - type = njs_parser_block(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + next = njs_lexer_peek_token(parser->lexer, token, 0); + if (next == NULL) { + return NJS_ERROR; + } - foreach = njs_parser_node_new(vm, parser, NJS_TOKEN_FOR_IN); - if (njs_slow_path(foreach == NULL)) { - return NJS_TOKEN_ERROR; - } + /* PropertyName */ + if (next->type == NJS_TOKEN_COLON) { + return njs_parser_property_name(parser, current, 2); - foreach->left = node; - foreach->right = parser->node; + /* MethodDefinition */ + } else if (next->type == NJS_TOKEN_OPEN_PARENTHESIS) { + goto method_definition; - parser->node = foreach; + } else if (njs_lexer_token_is_reserved(token)) { + njs_lexer_consume_token(parser->lexer, 1); - return type; -} + return njs_parser_failed(parser); + } + name = &token->text; -static njs_token_type_t -njs_parser_for_in_statement(njs_vm_t *vm, njs_parser_t *parser, njs_str_t *name, - njs_token_type_t type) -{ - njs_parser_node_t *node; + if (name->length == 3 && (memcmp(name->start, "get", 3) == 0 + || memcmp(name->start, "set", 3) == 0)) + { + accessor = (name->start[0] == 'g') ? NJS_TOKEN_PROPERTY_GETTER + : NJS_TOKEN_PROPERTY_SETTER; - node = parser->node->left; + temp->right = (njs_parser_node_t *) (uintptr_t) accessor; - if (node->token_type != NJS_TOKEN_NAME) { - njs_parser_ref_error(vm, parser, "Invalid left-hand side \"%V\" " - "in for-in statement", name); + njs_parser_next(parser, njs_parser_get_set); - return NJS_TOKEN_ILLEGAL; - } + return NJS_OK; + } - node = njs_parser_node_new(vm, parser, NJS_TOKEN_FOR_IN); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; + return njs_parser_property_definition_ident(parser, token, temp); } - node->left = parser->node; +method_definition: - type = njs_parser_match(vm, parser, type, NJS_TOKEN_CLOSE_PARENTHESIS); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + property = njs_parser_property_name_node(parser, token); + if (property == NULL) { + return NJS_ERROR; } - type = njs_parser_block(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + temp->right = property; + temp->right->index = NJS_TOKEN_OPEN_BRACKET; - node->right = parser->node; - parser->node = node; + njs_parser_next(parser, njs_parser_method_definition); - return type; + return njs_parser_after(parser, current, temp, 1, + njs_parser_property_definition_after); } -static njs_token_type_t -njs_parser_brk_statement(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) +static njs_int_t +njs_parser_property_definition_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) { - uintptr_t unique_id; njs_int_t ret; njs_str_t name; - njs_parser_node_t *node; - - node = njs_parser_node_new(vm, parser, type); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } + njs_bool_t proto_init; + njs_parser_node_t *property, *temp; - node->token_line = njs_parser_token_line(parser); - parser->node = node; + static const njs_str_t proto_string = njs_str("__proto__"); - type = njs_lexer_token(vm, parser->lexer); + temp = parser->target; + property = temp->right; - switch (type) { + proto_init = 0; - case NJS_TOKEN_LINE_END: - return njs_parser_token(vm, parser); + if (property != NULL && property->index != NJS_TOKEN_OPEN_BRACKET + && njs_is_string(&property->u.value)) + { + njs_string_get(&property->u.value, &name); - case NJS_TOKEN_NAME: - name = *njs_parser_text(parser); - unique_id = njs_parser_key_hash(parser); + if (njs_slow_path(njs_strstr_eq(&name, &proto_string))) { + if (temp->token_type == NJS_TOKEN_PROTO_INIT) { + njs_parser_syntax_error(parser, + "Duplicate __proto__ fields are not allowed " + "in object literals"); + return NJS_ERROR; + } - if (njs_label_find(vm, parser->scope, unique_id) == NULL) { - njs_parser_syntax_error(vm, parser, "Undefined label \"%V\"", - &name); - return NJS_TOKEN_ILLEGAL; + temp->token_type = NJS_TOKEN_PROTO_INIT; + proto_init = 1; } + } - ret = njs_name_copy(vm, &parser->node->name, &name); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } + if (property->index != 0) { + property->index = 0; + } - return njs_parser_token(vm, parser); + ret = njs_parser_object_property(parser, temp->left, property, + parser->node, proto_init); + if (ret != NJS_OK) { + return NJS_ERROR; + } - case NJS_TOKEN_SEMICOLON: - case NJS_TOKEN_CLOSE_BRACE: - case NJS_TOKEN_END: - return type; + temp->right = NULL; - default: - /* TODO: LABEL */ - return NJS_TOKEN_ILLEGAL; - } + return njs_parser_stack_pop(parser); } -static njs_token_type_t -njs_parser_try_statement(njs_vm_t *vm, njs_parser_t *parser) +static njs_int_t +njs_parser_computed_property_name_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) { - njs_int_t ret; - njs_token_type_t type; - njs_parser_node_t *node, *try, *catch; + njs_parser_node_t *expr; - type = njs_parser_try_block(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + if (token->type != NJS_TOKEN_CLOSE_BRACKET) { + return njs_parser_failed(parser); } - try = njs_parser_node_new(vm, parser, NJS_TOKEN_TRY); - if (njs_slow_path(try == NULL)) { - return NJS_TOKEN_ERROR; + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; } - try->left = parser->node; + /* + * For further identification. + * In njs_parser_property_definition_after() index will be reset to zero. + */ + parser->node->index = NJS_TOKEN_OPEN_BRACKET; - if (type == NJS_TOKEN_CATCH) { - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + parser->target->right = parser->node; - type = njs_parser_match(vm, parser, type, NJS_TOKEN_OPEN_PARENTHESIS); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + if (token->type == NJS_TOKEN_COLON) { + return njs_parser_property_name(parser, current, 1); - if (type != NJS_TOKEN_NAME) { - return NJS_TOKEN_ILLEGAL; + /* MethodDefinition */ + } else if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { + expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION); + if (expr == NULL) { + return NJS_ERROR; } - catch = njs_parser_node_new(vm, parser, NJS_TOKEN_CATCH); - if (njs_slow_path(catch == NULL)) { - return NJS_TOKEN_ERROR; + expr->token_line = token->line; + + parser->node = expr; + + njs_lexer_consume_token(parser->lexer, 1); + njs_parser_next(parser, njs_parser_function_lambda); + + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_property_definition_after); + } + + return njs_parser_failed(parser); +} + + +static njs_int_t +njs_parser_initializer(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + if (token->type != NJS_TOKEN_ASSIGNMENT) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + node = parser->node; + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_assignment_expression); + + return njs_parser_after(parser, current, node, 1, + njs_parser_initializer_after); +} + + +static njs_int_t +njs_parser_initializer_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *stmt; + + stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); + if (stmt == NULL) { + return NJS_ERROR; + } + + stmt->left = NULL; + stmt->right = parser->target; + + parser->target->right = parser->node; + parser->node = stmt; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_initializer_assign(njs_parser_t *parser, njs_token_type_t type) +{ + njs_parser_node_t *assign; + + assign = njs_parser_node_new(parser, type); + if (assign == NULL) { + return NJS_ERROR; + } + + assign->u.operation = NJS_VMCODE_MOVE; + assign->left = parser->node; + + /* assign->right in njs_parser_initializer_after. */ + + parser->node = assign; + + return NJS_OK; +} + + +/* + * 12.3 Left-Hand-Side Expressions. + */ +static njs_int_t +njs_parser_property(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node, *prop_node; + + /* + * [ Expression ] + * . IdentifierName + * TemplateLiteral + */ + + switch (token->type) { + case NJS_TOKEN_OPEN_BRACKET: + njs_lexer_consume_token(parser->lexer, 1); + + node = njs_parser_node_new(parser, NJS_TOKEN_PROPERTY); + if (node == NULL) { + return NJS_ERROR; } - try->right = catch; + node->u.operation = NJS_VMCODE_PROPERTY_GET; + node->left = parser->node; - /* - * The "catch" clause creates a block scope for single variable - * which receives exception value. - */ - ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_BLOCK); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; + parser->node = NULL; + + njs_parser_next(parser, njs_parser_expression); + + return njs_parser_after(parser, current, node, 1, + njs_parser_member_expression_bracket); + + case NJS_TOKEN_DOT: + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; } - node = njs_parser_variable_node(vm, parser, njs_parser_key_hash(parser), - NJS_VARIABLE_CATCH); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; + if (njs_lexer_token_is_identifier_name(token)) { + node = njs_parser_node_new(parser, NJS_TOKEN_PROPERTY); + if (node == NULL) { + return NJS_ERROR; + } + + node->u.operation = NJS_VMCODE_PROPERTY_GET; + + prop_node = njs_parser_node_string(parser->vm, token, parser); + if (prop_node == NULL) { + return NJS_ERROR; + } + + node->left = parser->node; + node->right = prop_node; + + parser->node = node; + + njs_lexer_consume_token(parser->lexer, 2); + return NJS_AGAIN; } - catch->left = node; + njs_lexer_consume_token(parser->lexer, 1); + + return NJS_DECLINED; - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + case NJS_TOKEN_GRAVE: + njs_lexer_consume_token(parser->lexer, 1); + + node = njs_parser_create_call(parser, parser->node, 0); + if (node == NULL) { + return NJS_ERROR; } - if (njs_slow_path(type != NJS_TOKEN_CLOSE_PARENTHESIS)) { - return NJS_TOKEN_ILLEGAL; + parser->node = node; + + njs_parser_next(parser, njs_parser_template_literal); + + break; + + default: + return NJS_DONE; + } + + return NJS_OK; +} + + +static njs_int_t +njs_parser_member_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + + /* + * PrimaryExpression + * MemberExpression [ Expression ] + * MemberExpression . IdentifierName + * MemberExpression TemplateLiteral + * SuperProperty + * MetaProperty + * new MemberExpression Arguments + */ + + switch (token->type) { + /* SuperProperty */ + case NJS_TOKEN_SUPER: + /* TODO: By specification. */ + (void) njs_parser_super_property; + return njs_parser_not_supported(parser, token); + + /* MetaProperty */ + case NJS_TOKEN_IMPORT: + /* TODO: By specification. */ + (void) njs_parser_member_expression_import; + return njs_parser_not_supported(parser, token); + + case NJS_TOKEN_NEW: + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_member_expression_new); + break; + + default: + ret = njs_parser_primary_expression_test(parser, token, current); + + if (ret != NJS_OK) { + if (ret == NJS_DONE) { + njs_parser_next(parser, njs_parser_member_expression_next); + return NJS_OK; + } + + return ret; } - type = njs_parser_try_block(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + break; + } + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_member_expression_next); +} + + +static njs_int_t +njs_parser_member_expression_next(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_int_t ret; + + /* + * [ Expression ] + * . IdentifierName + * TemplateLiteral + */ + + ret = njs_parser_property(parser, token, current); + + switch (ret) { + case NJS_AGAIN: + return NJS_OK; + + case NJS_DONE: + return njs_parser_stack_pop(parser); + + case NJS_DECLINED: + return njs_parser_failed(parser); + + default: + break; + } + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_member_expression_next); +} + + +static njs_int_t +njs_parser_member_expression_bracket(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_CLOSE_BRACKET) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + parser->target->right = parser->node; + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_member_expression_new_next(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_int_t ret; + + /* + * PrimaryExpression + * SuperProperty + * MetaProperty + * Arguments + */ + + switch (token->type) { + /* SuperProperty */ + case NJS_TOKEN_SUPER: + (void) njs_parser_super_property; + return njs_parser_not_supported(parser, token); + + /* MetaProperty */ + case NJS_TOKEN_IMPORT: + (void) njs_parser_member_expression_import; + return njs_parser_not_supported(parser, token); + + default: + ret = njs_parser_primary_expression_test(parser, token, current); + + if (ret != NJS_OK) { + if (ret == NJS_DONE) { + njs_parser_next(parser, njs_parser_member_expression_next); + return NJS_OK; + } + + return ret; } - catch->right = parser->node; + return njs_parser_after(parser, current, NULL, 1, + njs_parser_member_expression_next); + } +} + + +static njs_int_t +njs_parser_super_property(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + /* + * [ Expression ] + * . IdentifierName + */ + + if (token->type == NJS_TOKEN_OPEN_BRACKET) { + parser->node = NULL; + + njs_parser_next(parser, njs_parser_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_close_bracked); + } + + if (token->type != NJS_TOKEN_DOT) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (!njs_lexer_token_is_identifier_name(token)) { + return njs_parser_failed(parser); + } + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_member_expression_import(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + /* + * import . meta + */ + + if (token->type != NJS_TOKEN_DOT) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type != NJS_TOKEN_META) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_member_expression_new(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + /* + * new MemberExpression Arguments + * new . target + */ + + if (token->type != NJS_TOKEN_DOT) { + njs_parser_next(parser, njs_parser_member_expression_new_next); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_member_expression_new_after); + } + + /* njs_lexer_consume_token(parser->lexer, 1); */ + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } - njs_parser_scope_end(vm, parser); + if (token->type != NJS_TOKEN_TARGET) { + return njs_parser_failed(parser); } - if (type == NJS_TOKEN_FINALLY) { + /* njs_lexer_consume_token(parser->lexer, 1); */ + + /* return njs_parser_stack_pop(parser); */ + return njs_parser_not_supported(parser, token); +} + - type = njs_parser_try_block(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; +static njs_int_t +njs_parser_member_expression_new_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *func; + + if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { + parser->node = njs_parser_create_call(parser, parser->node, 1); + if (parser->node == NULL) { + return NJS_ERROR; } - node = njs_parser_node_new(vm, parser, NJS_TOKEN_FINALLY); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; + return njs_parser_stack_pop(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + func = njs_parser_create_call(parser, parser->node, 1); + if (func == NULL) { + return NJS_ERROR; + } + + parser->node = func; + + njs_parser_next(parser, njs_parser_arguments); + + return njs_parser_after(parser, current, func, 1, + njs_parser_member_expression_new_args); +} + + +static njs_int_t +njs_parser_member_expression_new_args(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +static njs_parser_node_t * +njs_parser_create_call(njs_parser_t *parser, njs_parser_node_t *node, + uint8_t ctor) +{ + njs_parser_node_t *func; + + switch (node->token_type) { + case NJS_TOKEN_NAME: + func = node; + func->token_type = NJS_TOKEN_FUNCTION_CALL; + + break; + + case NJS_TOKEN_PROPERTY: + func = njs_parser_node_new(parser, NJS_TOKEN_METHOD_CALL); + if (func == NULL) { + return NULL; } - node->right = parser->node; + func->left = node; + break; - if (try->right != NULL) { - node->left = try->right; + default: + /* + * NJS_TOKEN_METHOD_CALL, + * NJS_TOKEN_FUNCTION_CALL, + * NJS_TOKEN_FUNCTION_EXPRESSION, + * NJS_TOKEN_OPEN_PARENTHESIS, + * NJS_TOKEN_EVAL. + */ + func = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_CALL); + if (func == NULL) { + return NULL; } - try->right = node; + func->left = node; + break; } - if (try->right == NULL) { - njs_parser_syntax_error(vm, parser, - "Missing catch or finally after try"); + func->ctor = ctor; - return NJS_TOKEN_ILLEGAL; + return func; +} + + +static njs_int_t +njs_parser_call_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + + /* + * CoverCallExpressionAndAsyncArrowHead + * SuperCall + * ImportCall + * CallExpression Arguments + * CallExpression [ Expression ] + * CallExpression . IdentifierName + * CallExpression TemplateLiteral + */ + + switch (token->type) { + /* MemberExpression or SuperCall */ + case NJS_TOKEN_SUPER: + return njs_parser_not_supported(parser, token); + + case NJS_TOKEN_IMPORT: + return njs_parser_not_supported(parser, token); + + default: + break; + } + + njs_parser_next(parser, njs_parser_member_expression); + + ret = njs_parser_after(parser, current, NULL, 1, + njs_parser_call_expression_args); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_call_expression_after); +} + + +static njs_int_t +njs_parser_call_expression_args(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *func; + + if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + func = njs_parser_create_call(parser, parser->node, 0); + if (func == NULL) { + return NJS_ERROR; + } + + parser->node = func; + + njs_parser_next(parser, njs_parser_arguments); + + return njs_parser_after(parser, current, func, 1, + njs_parser_left_hand_side_expression_node); +} + + +static njs_int_t +njs_parser_call_expression_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_int_t ret; + njs_parser_node_t *func; + + /* + * Arguments + * [ Expression ] + * . IdentifierName + * TemplateLiteral + */ + + switch (token->type) { + case NJS_TOKEN_OPEN_PARENTHESIS: + njs_lexer_consume_token(parser->lexer, 1); + + func = njs_parser_create_call(parser, parser->node, 0); + if (func == NULL) { + return NJS_ERROR; + } + + parser->node = func; + + njs_parser_next(parser, njs_parser_arguments); + + ret = njs_parser_after(parser, current, func, 1, + njs_parser_left_hand_side_expression_node); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + break; + + default: + ret = njs_parser_property(parser, token, current); + + switch (ret) { + case NJS_AGAIN: + return NJS_OK; + + case NJS_DONE: + return njs_parser_stack_pop(parser); + + case NJS_DECLINED: + return njs_parser_failed(parser); + + default: + break; + } + + break; + } + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_call_expression_after); +} + + +static njs_int_t +njs_parser_arguments(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + /* + * ) + * ArgumentList ) + * ArgumentList , ) + */ + + if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { + njs_lexer_consume_token(parser->lexer, 1); + return njs_parser_stack_pop(parser); + } + + njs_parser_next(parser, njs_parser_argument_list); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_parenthesis_or_comma); +} + + +static njs_int_t +njs_parser_parenthesis_or_comma(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { + njs_lexer_consume_token(parser->lexer, 1); + return njs_parser_stack_pop(parser); + } + + if (token->type != NJS_TOKEN_COMMA) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (njs_slow_path(token == NULL)) { + return NJS_ERROR; + } + + if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_argument_list(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + /* + * AssignmentExpression + * ... AssignmentExpression + * ArgumentList , AssignmentExpression + * ArgumentList , ... AssignmentExpression + */ + + if (token->type == NJS_TOKEN_ELLIPSIS) { + njs_lexer_consume_token(parser->lexer, 1); + } + + njs_parser_next(parser, njs_parser_assignment_expression); + + return njs_parser_after(parser, current, parser->node, 1, + njs_parser_argument_list_after); +} + + +static njs_int_t +njs_parser_argument_list_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + node = njs_parser_node_new(parser, NJS_TOKEN_ARGUMENT); + if (node == NULL) { + return NJS_ERROR; + } + + if (parser->target->index == 0) { + node->index = NJS_SCOPE_CALLEE_ARGUMENTS; + + } else { + node->index = parser->target->index + sizeof(njs_value_t); + } + + node->left = parser->node; + parser->node->dest = node; + + parser->target->right = node; + parser->node = node; + + if (token->type == NJS_TOKEN_COMMA) { + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { + return njs_parser_stack_pop(parser); + } + + return njs_parser_argument_list(parser, token, current); + } + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_optional_expression_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_CONDITIONAL) { + return njs_parser_stack_pop(parser); + } + + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type != NJS_TOKEN_DOT) { + return njs_parser_stack_pop(parser); + } + + njs_parser_next(parser, njs_parser_optional_chain); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_optional_expression_after); +} + + +static njs_int_t +njs_parser_optional_chain(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + njs_parser_node_t *func; + + /* + * ? . Arguments + * ? . [ Expression ] + * ? . IdentifierName + * ? . TemplateLiteral + * OptionalChain Arguments + * OptionalChain [ Expression ] + * OptionalChain . IdentifierName + * OptionalChain TemplateLiteral + */ + + if (token->type != NJS_TOKEN_CONDITIONAL) { + return njs_parser_failed(parser); + } + + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type != NJS_TOKEN_DOT) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + switch (token->type) { + case NJS_TOKEN_OPEN_PARENTHESIS: + njs_lexer_consume_token(parser->lexer, 2); + + func = njs_parser_create_call(parser, parser->node, 0); + if (func == NULL) { + return NJS_ERROR; + } + + parser->node = func; + + njs_parser_next(parser, njs_parser_arguments); + + ret = njs_parser_after(parser, current, func, 1, + njs_parser_left_hand_side_expression_node); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + break; + + default: + if (!njs_lexer_token_is_identifier_name(token)) { + njs_lexer_consume_token(parser->lexer, 1); + } + + ret = njs_parser_property(parser, token, current); + + switch (ret) { + case NJS_DONE: + case NJS_DECLINED: + return njs_parser_failed(parser); + + default: + break; + } + + break; + } + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_optional_chain_after); +} + + +static njs_int_t +njs_parser_optional_chain_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + njs_parser_node_t *func; + + /* + * OptionalChain Arguments + * OptionalChain [ Expression ] + * OptionalChain . IdentifierName + * OptionalChain TemplateLiteral + */ + + switch (token->type) { + case NJS_TOKEN_OPEN_PARENTHESIS: + njs_lexer_consume_token(parser->lexer, 1); + + func = njs_parser_create_call(parser, parser->node, 0); + if (func == NULL) { + return NJS_ERROR; + } + + parser->node = func; + + njs_parser_next(parser, njs_parser_arguments); + + ret = njs_parser_after(parser, current, func, 1, + njs_parser_left_hand_side_expression_node); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + break; + + default: + ret = njs_parser_property(parser, token, current); + + switch (ret) { + case NJS_AGAIN: + return NJS_OK; + + case NJS_DONE: + return njs_parser_stack_pop(parser); + + case NJS_DECLINED: + return njs_parser_failed(parser); + + default: + break; + } + + break; + } + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_optional_chain_after); +} + + +static njs_int_t +njs_parser_new_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_NEW) { + parser->node = NULL; + + njs_parser_next(parser, njs_parser_member_expression_new); + + return NJS_OK; + } + + njs_lexer_consume_token(parser->lexer, 1); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_new_expression_after); +} + + +static njs_int_t +njs_parser_new_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *func; + + if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { + njs_parser_next(parser, njs_parser_member_expression_new_after); + return NJS_OK; + } + + func = njs_parser_create_call(parser, parser->node, 1); + if (func == NULL) { + return NJS_ERROR; + } + + parser->node = func; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_left_hand_side_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + /* + * NewExpression = new MemberExpression + * CallExpression = MemberExpression Arguments + * OptionalExpression = MemberExpression OptionalChain + */ + + switch (token->type) { + /* NewExpression or MemberExpression */ + case NJS_TOKEN_NEW: + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type == NJS_TOKEN_NEW) { + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_new_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_left_hand_side_expression_after); + } + + break; + + /* CallExpression or MemberExpression */ + case NJS_TOKEN_SUPER: + case NJS_TOKEN_IMPORT: + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { + njs_parser_next(parser, njs_parser_call_expression); + return NJS_OK; + } + + break; + + default: + break; + } + + njs_parser_next(parser, njs_parser_member_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_left_hand_side_expression_after); +} + + +static njs_int_t +njs_parser_left_hand_side_expression_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_int_t ret; + njs_parser_node_t *func; + + switch (token->type) { + /* CallExpression */ + case NJS_TOKEN_OPEN_PARENTHESIS: + njs_lexer_consume_token(parser->lexer, 1); + + func = njs_parser_create_call(parser, parser->node, 0); + if (func == NULL) { + return NJS_ERROR; + } + + parser->node = func; + + njs_parser_next(parser, njs_parser_arguments); + + ret = njs_parser_after(parser, current, func, 1, + njs_parser_left_hand_side_expression_node); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_left_hand_side_expression_optional); + + /* OptionalExpression */ + case NJS_TOKEN_CONDITIONAL: + njs_parser_next(parser, njs_parser_optional_expression_after); + break; + + default: + return njs_parser_stack_pop(parser); + } + + return NJS_OK; +} + + +static njs_int_t +njs_parser_left_hand_side_expression_node(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_left_hand_side_expression_optional(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + /* OptionalExpression */ + if (token->type == NJS_TOKEN_CONDITIONAL) { + njs_parser_next(parser, njs_parser_optional_expression_after); + return NJS_OK; + } + + njs_parser_next(parser, njs_parser_optional_chain_after); + + return NJS_OK; +} + + +static njs_int_t +njs_parser_expression_node(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current, njs_token_type_t type, + njs_vmcode_operation_t operation, njs_parser_state_func_t after) +{ + njs_parser_node_t *node; + + if (parser->target != NULL) { + parser->target->right = parser->node; + parser->target->right->dest = parser->target; + parser->node = parser->target; + } + + if (token->type != type) { + return njs_parser_stack_pop(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + node = njs_parser_node_new(parser, type); + if (node == NULL) { + return NJS_ERROR; + } + + node->u.operation = operation; + node->left = parser->node; + node->left->dest = node; + + return njs_parser_after(parser, current, node, 1, after); +} + + +/* + * 12.4 Update Expressions. + */ +static njs_int_t +njs_parser_update_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node; + njs_vmcode_operation_t operation; + + switch (token->type) { + case NJS_TOKEN_INCREMENT: + operation = NJS_VMCODE_INCREMENT; + break; + + case NJS_TOKEN_DECREMENT: + operation = NJS_VMCODE_DECREMENT; + break; + + default: + njs_parser_next(parser, njs_parser_left_hand_side_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_update_expression_post); + } + + node = njs_parser_node_new(parser, token->type); + if (node == NULL) { + return NJS_ERROR; + } + + node->u.operation = operation; + + njs_lexer_consume_token(parser->lexer, 1); + njs_parser_next(parser, njs_parser_left_hand_side_expression); + + return njs_parser_after(parser, current, node, 0, + njs_parser_update_expression_unary); +} + + +static njs_int_t +njs_parser_update_expression_post(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_token_type_t type; + njs_parser_node_t *node; + njs_vmcode_operation_t operation; + + /* [no LineTerminator here] */ + + switch (token->type) { + case NJS_TOKEN_INCREMENT: + type = NJS_TOKEN_POST_INCREMENT; + operation = NJS_VMCODE_POST_INCREMENT; + break; + + case NJS_TOKEN_DECREMENT: + type = NJS_TOKEN_POST_DECREMENT; + operation = NJS_VMCODE_POST_DECREMENT; + break; + + default: + return njs_parser_stack_pop(parser); + } + + if (parser->lexer->prev_type == NJS_TOKEN_LINE_END) { + return njs_parser_stack_pop(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + if (!njs_parser_is_lvalue(parser->node)) { + njs_parser_ref_error(parser, + "Invalid left-hand side in postfix operation"); + return NJS_DONE; + } + + node = njs_parser_node_new(parser, type); + if (node == NULL) { + return NJS_ERROR; + } + + node->u.operation = operation; + node->left = parser->node; + parser->node = node; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_update_expression_unary(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (parser->ret != NJS_OK) { + return njs_parser_failed(parser); + } + + if (!njs_parser_is_lvalue(parser->node)) { + njs_parser_ref_error(parser, + "Invalid left-hand side in prefix operation"); + return NJS_DONE; + } + + parser->target->left = parser->node; + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +/* + * 12.5 Unary Operators. + */ +static njs_int_t +njs_parser_unary_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_token_type_t type; + njs_parser_node_t *node; + njs_vmcode_operation_t operation; + + switch (token->type) { + case NJS_TOKEN_DELETE: + type = NJS_TOKEN_DELETE; + operation = NJS_VMCODE_DELETE; + break; + + case NJS_TOKEN_VOID: + type = NJS_TOKEN_VOID; + operation = NJS_VMCODE_VOID; + break; + + case NJS_TOKEN_TYPEOF: + type = NJS_TOKEN_TYPEOF; + operation = NJS_VMCODE_TYPEOF; + break; + + case NJS_TOKEN_ADDITION: + type = NJS_TOKEN_UNARY_PLUS; + operation = NJS_VMCODE_UNARY_PLUS; + break; + + case NJS_TOKEN_SUBSTRACTION: + type = NJS_TOKEN_UNARY_NEGATION; + operation = NJS_VMCODE_UNARY_NEGATION; + break; + + case NJS_TOKEN_BITWISE_NOT: + type = NJS_TOKEN_BITWISE_NOT; + operation = NJS_VMCODE_BITWISE_NOT; + break; + + case NJS_TOKEN_LOGICAL_NOT: + type = NJS_TOKEN_LOGICAL_NOT; + operation = NJS_VMCODE_LOGICAL_NOT; + break; + + /* AwaitExpression */ + case NJS_TOKEN_AWAIT: + return njs_parser_not_supported(parser, token); + + default: + njs_parser_next(parser, njs_parser_update_expression); + + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_unary_expression_after); + } + + njs_lexer_consume_token(parser->lexer, 1); + + node = njs_parser_node_new(parser, type); + if (node == NULL) { + return NJS_ERROR; + } + + node->u.operation = operation; + + parser->target = node; + + return njs_parser_after(parser, current, node, 1, + njs_parser_unary_expression_next); +} + + +static njs_int_t +njs_parser_unary_expression_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (parser->target == NULL && + token->type == NJS_TOKEN_EXPONENTIATION) + { + return njs_parser_exponentiation_expression_match(parser, token, + current); + } + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_unary_expression_next(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + double num; + njs_token_type_t type; + njs_parser_node_t *node; + + type = parser->target->token_type; + node = parser->node; + + if (token->type == NJS_TOKEN_EXPONENTIATION) { + njs_parser_syntax_error(parser, "Either left-hand side or entire " + "exponentiation must be parenthesized"); + return NJS_DONE; + } + + if (node->token_type == NJS_TOKEN_NUMBER) { + if (type == NJS_TOKEN_UNARY_PLUS) { + /* Skip the unary plus of number. */ + return njs_parser_stack_pop(parser); + } + + if (type == NJS_TOKEN_UNARY_NEGATION) { + /* Optimization of common negative number. */ + num = -njs_number(&node->u.value); + njs_set_number(&node->u.value, num); + + return njs_parser_stack_pop(parser); + } + } + + if (type == NJS_TOKEN_DELETE) { + switch (node->token_type) { + + case NJS_TOKEN_PROPERTY: + node->token_type = NJS_TOKEN_PROPERTY_DELETE; + node->u.operation = NJS_VMCODE_PROPERTY_DELETE; + + return njs_parser_stack_pop(parser); + + case NJS_TOKEN_NAME: + njs_parser_syntax_error(parser, + "Delete of an unqualified identifier"); + return NJS_DONE; + + default: + break; + } + } + + if (type == NJS_TOKEN_TYPEOF && node->token_type == NJS_TOKEN_NAME) { + node->u.reference.type = NJS_TYPEOF; + } + + parser->target->left = parser->node; + parser->target->left->dest = parser->target; + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +/* + * 12.6 Exponentiation Operator. + */ +static njs_int_t +njs_parser_exponentiation_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + parser->target = NULL; + + njs_parser_next(parser, njs_parser_unary_expression); + + /* For UpdateExpression, see njs_parser_unary_expression_after. */ + + return NJS_OK; +} + + +static njs_int_t +njs_parser_exponentiation_expression_match(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + if (parser->target != NULL) { + parser->target->right = parser->node; + parser->target->right->dest = parser->target; + parser->node = parser->target; + + return njs_parser_stack_pop(parser); + } + + if (token->type != NJS_TOKEN_EXPONENTIATION) { + return njs_parser_stack_pop(parser); + } + + node = njs_parser_node_new(parser, token->type); + if (node == NULL) { + return NJS_ERROR; + } + + node->u.operation = NJS_VMCODE_EXPONENTIATION; + node->left = parser->node; + node->left->dest = node; + + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_exponentiation_expression); + + return njs_parser_after(parser, current, node, 1, + njs_parser_exponentiation_expression_match); +} + + +/* + * 12.7 Multiplicative Operators. + */ +static njs_int_t +njs_parser_multiplicative_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_exponentiation_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_multiplicative_expression_match); +} + + +static njs_int_t +njs_parser_multiplicative_expression_match(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *node; + njs_vmcode_operation_t operation; + + if (parser->target != NULL) { + parser->target->right = parser->node; + parser->target->right->dest = parser->target; + parser->node = parser->target; + } + + switch (token->type) { + case NJS_TOKEN_MULTIPLICATION: + operation = NJS_VMCODE_MULTIPLICATION; + break; + + case NJS_TOKEN_DIVISION: + operation = NJS_VMCODE_DIVISION; + break; + + case NJS_TOKEN_REMAINDER: + operation = NJS_VMCODE_REMAINDER; + break; + + default: + return njs_parser_stack_pop(parser); + } + + node = njs_parser_node_new(parser, token->type); + if (node == NULL) { + return NJS_ERROR; + } + + node->u.operation = operation; + node->left = parser->node; + node->left->dest = node; + + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_exponentiation_expression); + + return njs_parser_after(parser, current, node, 1, + njs_parser_multiplicative_expression_match); +} + + +/* + * 12.8 Additive Operators. + */ +static njs_int_t +njs_parser_additive_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_multiplicative_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_additive_expression_match); +} + + +static njs_int_t +njs_parser_additive_expression_match(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *node; + njs_vmcode_operation_t operation; + + if (parser->target != NULL) { + parser->target->right = parser->node; + parser->target->right->dest = parser->target; + parser->node = parser->target; + } + + switch (token->type) { + case NJS_TOKEN_ADDITION: + operation = NJS_VMCODE_ADDITION; + break; + + case NJS_TOKEN_SUBSTRACTION: + operation = NJS_VMCODE_SUBSTRACTION; + break; + + default: + return njs_parser_stack_pop(parser); + } + + node = njs_parser_node_new(parser, token->type); + if (node == NULL) { + return NJS_ERROR; + } + + node->u.operation = operation; + node->left = parser->node; + node->left->dest = node; + + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_multiplicative_expression); + + return njs_parser_after(parser, current, node, 1, + njs_parser_additive_expression_match); +} + + +/* + * 12.9 Bitwise Shift Operators + */ +static njs_int_t +njs_parser_shift_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_additive_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_shift_expression_match); +} + + +static njs_int_t +njs_parser_shift_expression_match(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *node; + njs_vmcode_operation_t operation; + + if (parser->target != NULL) { + parser->target->right = parser->node; + parser->target->right->dest = parser->target; + parser->node = parser->target; + } + + switch (token->type) { + case NJS_TOKEN_RIGHT_SHIFT: + operation = NJS_VMCODE_RIGHT_SHIFT; + break; + + case NJS_TOKEN_LEFT_SHIFT: + operation = NJS_VMCODE_LEFT_SHIFT; + break; + + case NJS_TOKEN_UNSIGNED_RIGHT_SHIFT: + operation = NJS_VMCODE_UNSIGNED_RIGHT_SHIFT; + break; + + default: + return njs_parser_stack_pop(parser); + } + + node = njs_parser_node_new(parser, token->type); + if (node == NULL) { + return NJS_ERROR; + } + + node->u.operation = operation; + node->left = parser->node; + node->left->dest = node; + + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_additive_expression); + + return njs_parser_after(parser, current, node, 1, + njs_parser_shift_expression_match); +} + + +/* + * 12.10 Relational Operators. + */ +static njs_int_t +njs_parser_relational_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_shift_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_relational_expression_match); +} + + +static njs_int_t +njs_parser_relational_expression_match(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *node; + njs_vmcode_operation_t operation; + + if (parser->target != NULL) { + parser->target->right = parser->node; + parser->target->right->dest = parser->target; + parser->node = parser->target; + } + + switch (token->type) { + case NJS_TOKEN_LESS: + operation = NJS_VMCODE_LESS; + break; + + case NJS_TOKEN_GREATER: + operation = NJS_VMCODE_GREATER; + break; + + case NJS_TOKEN_LESS_OR_EQUAL: + operation = NJS_VMCODE_LESS_OR_EQUAL; + break; + + case NJS_TOKEN_GREATER_OR_EQUAL: + operation = NJS_VMCODE_GREATER_OR_EQUAL; + break; + + case NJS_TOKEN_INSTANCEOF: + operation = NJS_VMCODE_INSTANCE_OF; + break; + + case NJS_TOKEN_IN: + operation = NJS_VMCODE_PROPERTY_IN; + break; + + default: + return njs_parser_stack_pop(parser); + } + + node = njs_parser_node_new(parser, token->type); + if (node == NULL) { + return NJS_ERROR; + } + + node->u.operation = operation; + node->left = parser->node; + node->left->dest = node; + + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_shift_expression); + + return njs_parser_after(parser, current, node, 1, + njs_parser_relational_expression_match); +} + + +/* + * 12.11 Equality Operators. + */ +static njs_int_t +njs_parser_equality_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_relational_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_equality_expression_match); +} + + +static njs_int_t +njs_parser_equality_expression_match(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *node; + njs_vmcode_operation_t operation; + + if (parser->target != NULL) { + parser->target->right = parser->node; + parser->target->right->dest = parser->target; + parser->node = parser->target; + } + + switch (token->type) { + case NJS_TOKEN_EQUAL: + operation = NJS_VMCODE_EQUAL; + break; + + case NJS_TOKEN_NOT_EQUAL: + operation = NJS_VMCODE_NOT_EQUAL; + break; + + case NJS_TOKEN_STRICT_EQUAL: + operation = NJS_VMCODE_STRICT_EQUAL; + break; + + case NJS_TOKEN_STRICT_NOT_EQUAL: + operation = NJS_VMCODE_STRICT_NOT_EQUAL; + break; + + default: + return njs_parser_stack_pop(parser); + } + + node = njs_parser_node_new(parser, token->type); + if (node == NULL) { + return NJS_ERROR; + } + + node->u.operation = operation; + node->left = parser->node; + node->left->dest = node; + + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_relational_expression); + + return njs_parser_after(parser, current, node, 1, + njs_parser_equality_expression_match); +} + + +/* + * 12.12 Binary Bitwise Operators. + */ +static njs_int_t +njs_parser_bitwise_AND_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_equality_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_bitwise_AND_expression_and); +} + + +static njs_int_t +njs_parser_bitwise_AND_expression_and(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_equality_expression); + + return njs_parser_expression_node(parser, token, current, + NJS_TOKEN_BITWISE_AND, + NJS_VMCODE_BITWISE_AND, + njs_parser_bitwise_AND_expression_and); +} + + +static njs_int_t +njs_parser_bitwise_XOR_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_bitwise_AND_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_bitwise_XOR_expression_xor); +} + + +static njs_int_t +njs_parser_bitwise_XOR_expression_xor(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_bitwise_AND_expression); + + return njs_parser_expression_node(parser, token, current, + NJS_TOKEN_BITWISE_XOR, + NJS_VMCODE_BITWISE_XOR, + njs_parser_bitwise_XOR_expression_xor); +} + + +static njs_int_t +njs_parser_bitwise_OR_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_bitwise_XOR_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_bitwise_OR_expression_or); +} + + +static njs_int_t +njs_parser_bitwise_OR_expression_or(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_bitwise_XOR_expression); + + return njs_parser_expression_node(parser, token, current, + NJS_TOKEN_BITWISE_OR, + NJS_VMCODE_BITWISE_OR, + njs_parser_bitwise_OR_expression_or); +} + + +/* + * 12.13 Binary Logical Operators. + */ +static njs_int_t +njs_parser_logical_AND_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_bitwise_OR_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_logical_AND_expression_and); +} + + +static njs_int_t +njs_parser_logical_AND_expression_and(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_bitwise_OR_expression); + + return njs_parser_expression_node(parser, token, current, + NJS_TOKEN_LOGICAL_AND, + NJS_VMCODE_TEST_IF_FALSE, + njs_parser_logical_AND_expression_and); +} + + +static njs_int_t +njs_parser_logical_OR_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_logical_AND_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_logical_OR_expression_or); +} + + +static njs_int_t +njs_parser_logical_OR_expression_or(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_logical_AND_expression); + + return njs_parser_expression_node(parser, token, current, + NJS_TOKEN_LOGICAL_OR, + NJS_VMCODE_TEST_IF_TRUE, + njs_parser_logical_OR_expression_or); +} + + +static njs_int_t +njs_parser_coalesce_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_token_type_t type; + njs_parser_node_t *node; + + node = parser->node; + + if (parser->target != NULL) { + parser->target->right = node; + parser->target->right->dest = parser->target; + parser->node = parser->target; + } + + if (token->type != NJS_TOKEN_COALESCE) { + return njs_parser_stack_pop(parser); + } + + if (node != NULL) { + type = node->token_type; + + if (parser->lexer->prev_type != NJS_TOKEN_CLOSE_PARENTHESIS + && (type == NJS_TOKEN_LOGICAL_OR || type == NJS_TOKEN_LOGICAL_AND)) + { + njs_parser_syntax_error(parser, "Either \"??\" or \"%s\" " + "expression must be parenthesized", + (type == NJS_TOKEN_LOGICAL_OR) ? "||" + : "&&"); + return NJS_DONE; + } + } + + njs_lexer_consume_token(parser->lexer, 1); + + node = njs_parser_node_new(parser, NJS_TOKEN_COALESCE); + if (node == NULL) { + return NJS_ERROR; + } + + node->u.operation = NJS_VMCODE_COALESCE; + node->left = parser->node; + node->left->dest = node; + + njs_parser_next(parser, njs_parser_bitwise_OR_expression); + + return njs_parser_after(parser, current, node, 1, + njs_parser_coalesce_expression); +} + + +static njs_int_t +njs_parser_short_circuit_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_logical_OR_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_coalesce_expression); +} + + +/* + * 12.14 Conditional Operator ( ? : ). + */ +static njs_int_t +njs_parser_conditional_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_short_circuit_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_conditional_question_mark); +} + + +static njs_int_t +njs_parser_conditional_question_mark(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *node, *cond; + + if (token->type != NJS_TOKEN_CONDITIONAL) { + return njs_parser_stack_pop(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + cond = njs_parser_node_new(parser, NJS_TOKEN_CONDITIONAL); + if (cond == NULL) { + return NJS_ERROR; + } + + cond->left = parser->node; + + node = njs_parser_node_new(parser, NJS_TOKEN_BRANCHING); + if (node == NULL) { + return NJS_ERROR; + } + + cond->right = node; + + njs_parser_next(parser, njs_parser_assignment_expression); + + return njs_parser_after(parser, current, cond, 1, + njs_parser_conditional_colon); +} + + +static njs_int_t +njs_parser_conditional_colon(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + if (token->type != NJS_TOKEN_COLON) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + node = parser->target->right; + + node->left = parser->node; + node->left->dest = parser->target; + + njs_parser_next(parser, njs_parser_assignment_expression); + + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_conditional_colon_after); +} + + +static njs_int_t +njs_parser_conditional_colon_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + node = parser->target->right; + + node->right = parser->node; + node->right->dest = parser->target; + + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +/* + * 12.15 Assignment Operators. + */ +static njs_int_t +njs_parser_assignment_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_int_t ret; + + ret = njs_parser_match_arrow_expression(parser, token); + if (ret == NJS_OK) { + njs_parser_next(parser, njs_parser_arrow_function); + + return NJS_OK; + + } else if (ret == NJS_ERROR) { + return NJS_ERROR; + } + + njs_parser_next(parser, njs_parser_conditional_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_assignment_expression_after); +} + + +/* + * TODO: this function is a crutch. + * See NJS_TOKEN_OPEN_PARENTHESIS in njs_parser_primary_expression_test. + * and look CoverParenthesizedExpressionAndArrowParameterList in spec. + */ +static njs_int_t +njs_parser_match_arrow_expression(njs_parser_t *parser, + njs_lexer_token_t *token) +{ + njs_bool_t rest_parameters; + + if (token->type != NJS_TOKEN_OPEN_PARENTHESIS + && !njs_lexer_token_is_binding_identifier(token)) + { + return NJS_DECLINED; + } + + if (njs_lexer_token_is_binding_identifier(token)) { + goto arrow; + } + + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + + rest_parameters = 0; + + while (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { + + if (rest_parameters) { + return NJS_DECLINED; + } + + if (token->type == NJS_TOKEN_ELLIPSIS) { + rest_parameters = 1; + + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + } + + if (!njs_lexer_token_is_binding_identifier(token)) { + return NJS_DECLINED; + } + + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type == NJS_TOKEN_COMMA) { + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + } + } + +arrow: + + token = njs_lexer_peek_token(parser->lexer, token, 1); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type == NJS_TOKEN_LINE_END) { + return NJS_DECLINED; + } + + if (token->type != NJS_TOKEN_ARROW) { + return NJS_DECLINED; + } + + return NJS_OK; +} + + +static njs_int_t +njs_parser_assignment_expression_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + return njs_parser_assignment_operator(parser, token, current); +} + + +static njs_int_t +njs_parser_assignment_operator(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_token_type_t type; + njs_parser_node_t *node; + njs_vmcode_operation_t operation; + + switch (token->type) { + case NJS_TOKEN_ASSIGNMENT: + njs_thread_log_debug("JS: ="); + operation = NJS_VMCODE_MOVE; + break; + + case NJS_TOKEN_MULTIPLICATION_ASSIGNMENT: + njs_thread_log_debug("JS: *="); + operation = NJS_VMCODE_MULTIPLICATION; + break; + + case NJS_TOKEN_DIVISION_ASSIGNMENT: + njs_thread_log_debug("JS: /="); + operation = NJS_VMCODE_DIVISION; + break; + + case NJS_TOKEN_REMAINDER_ASSIGNMENT: + njs_thread_log_debug("JS: %="); + operation = NJS_VMCODE_REMAINDER; + break; + + case NJS_TOKEN_ADDITION_ASSIGNMENT: + njs_thread_log_debug("JS: +="); + operation = NJS_VMCODE_ADDITION; + break; + + case NJS_TOKEN_SUBSTRACTION_ASSIGNMENT: + njs_thread_log_debug("JS: -="); + operation = NJS_VMCODE_SUBSTRACTION; + break; + + case NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT: + njs_thread_log_debug("JS: <<="); + operation = NJS_VMCODE_LEFT_SHIFT; + break; + + case NJS_TOKEN_RIGHT_SHIFT_ASSIGNMENT: + njs_thread_log_debug("JS: >>="); + operation = NJS_VMCODE_RIGHT_SHIFT; + break; + + case NJS_TOKEN_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: + njs_thread_log_debug("JS: >>>="); + operation = NJS_VMCODE_UNSIGNED_RIGHT_SHIFT; + break; + + case NJS_TOKEN_BITWISE_AND_ASSIGNMENT: + njs_thread_log_debug("JS: &="); + operation = NJS_VMCODE_BITWISE_AND; + break; + + case NJS_TOKEN_BITWISE_XOR_ASSIGNMENT: + njs_thread_log_debug("JS: ^="); + operation = NJS_VMCODE_BITWISE_XOR; + break; + + case NJS_TOKEN_BITWISE_OR_ASSIGNMENT: + njs_thread_log_debug("JS: |="); + operation = NJS_VMCODE_BITWISE_OR; + break; + + case NJS_TOKEN_EXPONENTIATION_ASSIGNMENT: + njs_thread_log_debug("JS: **="); + operation = NJS_VMCODE_EXPONENTIATION; + break; + + default: + return njs_parser_stack_pop(parser); + } + + if (!njs_parser_is_lvalue(parser->node)) { + type = parser->node->token_type; + + if (njs_parser_restricted_identifier(type)) { + njs_parser_syntax_error(parser, "Identifier \"%s\" " + "is forbidden as left-hand in assignment", + (type == NJS_TOKEN_EVAL) ? "eval" + : "arguments"); + + } else { + njs_parser_ref_error(parser, + "Invalid left-hand side in assignment"); + } + + return NJS_DONE; + } + + node = njs_parser_node_new(parser, token->type); + if (node == NULL) { + return NJS_ERROR; + } + + node->u.operation = operation; + node->left = parser->node; + + njs_lexer_consume_token(parser->lexer, 1); + njs_parser_next(parser, njs_parser_assignment_expression); + + return njs_parser_after(parser, current, node, 1, + njs_parser_assignment_operator_after); +} + + +static njs_int_t +njs_parser_assignment_operator_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + parser->target->right = parser->node; + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +/* + * 12.16 Comma Operator ( , ). + */ +static njs_int_t +njs_parser_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_assignment_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_expression_comma); +} + + +static njs_int_t +njs_parser_expression_comma(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_assignment_expression); + + return njs_parser_expression_node(parser, token, current, NJS_TOKEN_COMMA, + NJS_VMCODE_NOP, + njs_parser_expression_comma); +} + + +/* + * 13 Statements and Declarations. + */ +static njs_int_t +njs_parser_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + njs_queue_link_t *lnk; + njs_parser_stack_entry_t *entry; + + if (token->type == NJS_TOKEN_END) { + lnk = njs_queue_next(njs_queue_first(&parser->stack)); + + if (lnk == njs_queue_head(&parser->stack)) { + return njs_parser_reject(parser); + } + + entry = njs_queue_link_data(lnk, njs_parser_stack_entry_t, link); + + if (entry->state == njs_parser_check_error_state) { + return NJS_DONE; + } + + return njs_parser_reject(parser); + } + + switch (token->type) { + case NJS_TOKEN_SEMICOLON: + njs_lexer_consume_token(parser->lexer, 1); + return njs_parser_stack_pop(parser); + + case NJS_TOKEN_EXPORT: + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_export); + + return njs_parser_after(parser, current, parser->node, 1, + njs_parser_statement_after); + + case NJS_TOKEN_IMPORT: + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_import); + + return njs_parser_after(parser, current, parser->node, 1, + njs_parser_statement_after); + default: + break; + } + + ret = njs_parser_statement_wo_node(parser, token, current); + if (ret != NJS_OK) { + return ret; + } + + return njs_parser_after(parser, current, parser->node, 1, + njs_parser_statement_after); +} + + +static njs_int_t +njs_parser_statement_wo_node(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + switch (token->type) { + case NJS_TOKEN_OPEN_BRACE: + njs_parser_next(parser, njs_parser_block_statement); + break; + + case NJS_TOKEN_VAR: + njs_lexer_consume_token(parser->lexer, 1); + return njs_parser_variable_statement(parser, token, current); + + case NJS_TOKEN_SEMICOLON: + njs_lexer_consume_token(parser->lexer, 1); + return njs_parser_stack_pop(parser); + + case NJS_TOKEN_IF: + njs_parser_next(parser, njs_parser_if_statement); + break; + + /* BreakableStatement */ + case NJS_TOKEN_DO: + njs_parser_next(parser, njs_parser_iteration_statement_do); + break; + + case NJS_TOKEN_WHILE: + njs_parser_next(parser, njs_parser_iteration_statement_while); + break; + + case NJS_TOKEN_FOR: + njs_parser_next(parser, njs_parser_iteration_statement_for); + break; + + case NJS_TOKEN_SWITCH: + njs_parser_next(parser, njs_parser_switch_statement); + break; + + case NJS_TOKEN_CONTINUE: + parser->line = token->line; + njs_parser_next(parser, njs_parser_continue_statement); + break; + + case NJS_TOKEN_BREAK: + parser->line = token->line; + njs_parser_next(parser, njs_parser_break_statement); + break; + + case NJS_TOKEN_RETURN: + njs_parser_next(parser, njs_parser_return_statement); + break; + + case NJS_TOKEN_WITH: + njs_parser_next(parser, njs_parser_with_statement); + break; + + case NJS_TOKEN_THROW: + njs_parser_next(parser, njs_parser_throw_statement); + break; + + case NJS_TOKEN_TRY: + njs_parser_next(parser, njs_parser_try_statement); + break; + + case NJS_TOKEN_DEBUGGER: + njs_parser_next(parser, njs_parser_debugger_statement); + break; + + case NJS_TOKEN_END: + return njs_parser_failed(parser); + + default: + if (njs_lexer_token_is_identifier_reference(token)) { + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type == NJS_TOKEN_COLON) { + njs_parser_next(parser, njs_parser_labelled_statement); + return NJS_OK; + } + } + + njs_parser_next(parser, njs_parser_expression_statement); + return NJS_OK; + } + + njs_lexer_consume_token(parser->lexer, 1); + + return NJS_OK; +} + + +static njs_int_t +njs_parser_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *stmt; + + stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); + if (njs_slow_path(stmt == NULL)) { + return NJS_ERROR; + } + + stmt->left = parser->target; + stmt->right = parser->node; + + parser->node = stmt; + + njs_parser_chain_top_set(parser, stmt); + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_declaration(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + + ret = njs_parser_hoistable_declaration(parser, token, current); + if (ret == NJS_OK) { + return NJS_OK; + } + + switch (token->type) { + case NJS_TOKEN_CLASS: + njs_parser_next(parser, njs_parser_class_declaration); + break; + + case NJS_TOKEN_LET: + case NJS_TOKEN_CONST: + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + + switch (token->type) { + case NJS_TOKEN_OPEN_BRACE: + case NJS_TOKEN_OPEN_BRACKET: + njs_parser_next(parser, njs_parser_lexical_declaration); + break; + + default: + if (njs_lexer_token_is_binding_identifier(token)) { + njs_parser_next(parser, njs_parser_lexical_declaration); + break; + } + + return NJS_DECLINED; + } + + break; + + default: + return NJS_DECLINED; + } + + return NJS_OK; +} + + +static njs_int_t +njs_parser_hoistable_declaration(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_int_t ret; + + ret = njs_parser_function_or_generator(parser, token, current); + if (ret == NJS_OK) { + return NJS_OK; + } + + ret = njs_parser_async_function_or_generator(parser, token, current); + if (ret == NJS_OK) { + return NJS_OK; + } + + return NJS_DECLINED; +} + + +/* + * 13.2 Block. + */ +static njs_int_t +njs_parser_block_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + + ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + parser->node = NULL; + + if (token->type == NJS_TOKEN_CLOSE_BRACE) { + njs_parser_next(parser, njs_parser_block_statement_close_brace); + return NJS_OK; + } + + njs_parser_next(parser, njs_parser_statement_list); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_block_statement_close_brace); +} + + +static njs_int_t +njs_parser_block_statement_open_brace(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_OPEN_BRACE) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + return njs_parser_block_statement(parser, token, current); +} + + +static njs_int_t +njs_parser_block_statement_close_brace(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + if (token->type != NJS_TOKEN_CLOSE_BRACE) { + return njs_parser_failed(parser); + } + + node = njs_parser_node_new(parser, NJS_TOKEN_BLOCK); + if (node == NULL) { + return NJS_ERROR; + } + + node->left = parser->node; + node->right = NULL; + parser->node = node; + + njs_parser_scope_end(parser); + + njs_lexer_consume_token(parser->lexer, 1); + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_statement_list(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_statement_list_item); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_statement_list_next); +} + + +static njs_int_t +njs_parser_statement_list_next(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + if (parser->ret != NJS_OK) { + parser->node = parser->target; + return njs_parser_stack_pop(parser); + } + + njs_parser_next(parser, njs_parser_statement_list_item); + + return njs_parser_after(parser, current, parser->node, 0, + njs_parser_statement_list_next); +} + + +static njs_int_t +njs_parser_statement_list_item(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + + ret = njs_parser_declaration(parser, token, current); + if (ret == NJS_OK) { + return NJS_OK; + } + + njs_parser_next(parser, njs_parser_statement); + + return NJS_OK; +} + + +/* + * 13.3.2 Variable Statement + */ +static njs_int_t +njs_parser_variable_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_variable_declaration_list); + + return njs_parser_after(parser, current, NULL, 1, njs_parser_semicolon); +} + + +static njs_int_t +njs_parser_variable_declaration_list(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_variable_declaration); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_variable_declaration_list_next); +} + + +static njs_int_t +njs_parser_variable_declaration_list_next(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + if (parser->target != NULL) { + parser->node->left = parser->target; + } + + if (token->type != NJS_TOKEN_COMMA) { + return njs_parser_stack_pop(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + node = parser->node; + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_variable_declaration); + + return njs_parser_after(parser, current, node, 1, + njs_parser_variable_declaration_list_next); +} + + +static njs_int_t +njs_parser_variable_declaration(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_int_t ret; + njs_parser_node_t *name; + + ret = njs_parser_binding_pattern(parser, token, current); + if (ret == NJS_OK) { + return njs_parser_after(parser, current, NULL, 1, + njs_parser_initializer); + } + + if (!njs_lexer_token_is_binding_identifier(token)) { + return njs_parser_failed(parser); + } + + if (njs_parser_restricted_identifier(token->type)) { + njs_parser_syntax_error(parser, "Identifier \"%V\" is forbidden in" + " var declaration", &token->text); + return NJS_DONE; + } + + name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR); + if (name == NULL) { + return NJS_ERROR; + } + + parser->node = name; + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + ret = njs_parser_initializer_assign(parser, NJS_TOKEN_VAR); + + if (token->type == NJS_TOKEN_ASSIGNMENT) { + njs_parser_next(parser, njs_parser_initializer); + + return ret; + } + + parser->target = parser->node; + parser->node = NULL; + + njs_parser_next(parser, njs_parser_initializer_after); + + return ret; +} + + +/* + * 13.3.3 Destructuring Binding Patterns. + */ +static njs_int_t +njs_parser_binding_pattern(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + if (token->type == NJS_TOKEN_OPEN_BRACE) { + njs_parser_next(parser, njs_parser_object_binding_pattern); + + } else if (token->type == NJS_TOKEN_OPEN_BRACKET) { + njs_parser_next(parser, njs_parser_array_binding_pattern); + + } else { + return NJS_DECLINED; + } + + njs_lexer_consume_token(parser->lexer, 1); + + return NJS_OK; +} + +static njs_int_t +njs_parser_object_binding_pattern(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + return njs_parser_not_supported(parser, token); +} + + +static njs_int_t +njs_parser_array_binding_pattern(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + return njs_parser_not_supported(parser, token); +} + + +/* + * 13.5 Expression Statement. + */ +static njs_int_t +njs_parser_expression_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_lexer_token_t *next; + + switch (token->type) { + case NJS_TOKEN_FUNCTION: + njs_parser_syntax_error(parser, "Functions can only be declared " + "at top level or inside a block"); + return NJS_DONE; + + case NJS_TOKEN_CLASS: + njs_parser_syntax_error(parser, "Class can only be declared " + "at top level or inside a block"); + return NJS_DONE; + + case NJS_TOKEN_OPEN_BRACE: + return njs_parser_reject(parser); + + case NJS_TOKEN_ASYNC: + next = njs_lexer_peek_token(parser->lexer, token, 1); + if (next == NULL) { + return NJS_ERROR; + } + + if (next->type == NJS_TOKEN_FUNCTION) { + return njs_parser_not_supported(parser, token); + } + + break; + + case NJS_TOKEN_LET: + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type == NJS_TOKEN_OPEN_BRACKET) { + return njs_parser_failed(parser); + } + + break; + + default: + break; + } + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_expression); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_expression_statement_after); +} + + +static njs_int_t +njs_parser_expression_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { + return njs_parser_failed(parser); + } + + return njs_parser_stack_pop(parser); +} + + +/* + * 13.6 if Statement. + */ +static njs_int_t +njs_parser_if_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + njs_parser_node_t *node; + + if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + node = njs_parser_node_new(parser, NJS_TOKEN_IF); + if (node == NULL) { + return NJS_ERROR; + } + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_expression); + + ret = njs_parser_after(parser, current, node, 1, + njs_parser_if_close_parenthesis); + if (ret != NJS_OK) { + return ret; + } + + ret = njs_parser_after(parser, current, NULL, 1, + njs_parser_statement_wo_node); + if (ret != NJS_OK) { + return ret; + } + + return njs_parser_after(parser, current, node, 1, + njs_parser_else_statement); +} + + +static njs_int_t +njs_parser_if_close_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + parser->target->left = parser->node; + parser->node = NULL; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_else_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + parser->target->right = parser->node; + parser->node = NULL; + + if (token->type == NJS_TOKEN_ELSE) { + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_statement_wo_node); + + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_else_statement_after); + } + + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_else_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + node = njs_parser_node_new(parser, NJS_TOKEN_BRANCHING); + if (node == NULL) { + return NJS_ERROR; + } + + node->left = parser->target->right; + node->right = parser->node; + + parser->target->right = node; + + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_iteration_statement_do(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + parser->node = NULL; + + njs_parser_next(parser, njs_parser_statement_wo_node); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_iteration_statement_do_while); +} + + +static njs_int_t +njs_parser_iteration_statement_do_while(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_WHILE) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_expression_parenthesis); + + return njs_parser_after(parser, current, parser->node, 1, + njs_parser_do_while_semicolon); +} + + +static njs_int_t +njs_parser_do_while_semicolon(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + if (parser->strict_semicolon) { + return njs_parser_failed(parser); + } + + node = njs_parser_node_new(parser, NJS_TOKEN_DO); + if (node == NULL) { + return NJS_ERROR; + } + + node->left = parser->target; + node->right = parser->node; + parser->node = node; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_iteration_statement_while(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_next(parser, njs_parser_expression_parenthesis); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_while_statement); +} + + +static njs_int_t +njs_parser_while_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + node = parser->node; + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_statement_wo_node); + + return njs_parser_after(parser, current, node, 1, njs_parser_while_after); +} + + +static njs_int_t +njs_parser_while_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + node = njs_parser_node_new(parser, NJS_TOKEN_WHILE); + if (node == NULL) { + return NJS_ERROR; + } + + node->left = parser->node; + node->right = parser->target; + parser->node = node; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_iteration_statement_for(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_iteration_statement_for_map); + + return NJS_OK; + } + + if (token->type == NJS_TOKEN_AWAIT) { + return njs_parser_not_supported(parser, token); + } + + return njs_parser_failed(parser); +} + + +static njs_int_t +njs_parser_iteration_statement_for_map(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_int_t ret; + njs_str_t *text; + + /* + * "var" ";" ? ";" ? ")" + * + * "var" "in" ")" + * "var" "of" ")" + + * "in" ")" + * "of" ")" + + * ? ";" ? ";" ? ")" + * ? ";" ? ")" + + * "in" ")" + * "of" ")" + */ + + parser->node = NULL; + + switch (token->type) { + case NJS_TOKEN_SEMICOLON: + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type != NJS_TOKEN_SEMICOLON) { + njs_lexer_consume_token(parser->lexer, 1); + + parser->target = NULL; + + njs_parser_next(parser, njs_parser_expression); + + return njs_parser_after(parser, current, NULL, 0, + njs_parser_for_expression); + } + + parser->node = NULL; + parser->target = NULL; + + njs_lexer_consume_token(parser->lexer, 1); + njs_parser_next(parser, njs_parser_for_expression); + + return NJS_OK; + + case NJS_TOKEN_VAR: + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + + njs_lexer_consume_token(parser->lexer, 1); + + ret = njs_parser_for_var_binding_or_var_list(parser, token, current); + if (ret != NJS_OK) { + if (ret == NJS_DONE) { + return NJS_OK; + } + + return ret; + } + + break; + + case NJS_TOKEN_LET: + case NJS_TOKEN_CONST: + return njs_parser_not_supported(parser, token); + + default: + njs_parser_next(parser, njs_parser_expression); + break; + } + + /* + * Here we pass not a node, but a token, this is important. + * This is necessary for correct error output. + */ + + text = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_str_t)); + if (text == NULL) { + return NJS_ERROR; + } + + *text = token->text; + + return njs_parser_after(parser, current, text, 1, + njs_parser_for_var_in_of_expression); +} + + +static njs_int_t +njs_parser_for_var_binding_or_var_list(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_int_t ret; + njs_lexer_token_t *next; + njs_parser_node_t *node; + + switch (token->type) { + /* BindingPattern */ + case NJS_TOKEN_OPEN_BRACE: + njs_parser_next(parser, njs_parser_object_binding_pattern); + return NJS_DONE; + + case NJS_TOKEN_OPEN_BRACKET: + njs_parser_next(parser, njs_parser_array_binding_pattern); + return NJS_DONE; + + default: + if (njs_lexer_token_is_binding_identifier(token)) { + if (njs_parser_restricted_identifier(token->type)) { + njs_parser_syntax_error(parser, "Identifier \"%V\" is forbidden" + " in var declaration", &token->text); + return NJS_DONE; + } + + next = njs_lexer_peek_token(parser->lexer, token, 0); + if (next == NULL) { + return NJS_ERROR; + } + + if (next->type != NJS_TOKEN_IN) { + njs_parser_next(parser, njs_parser_variable_declaration_list); + return NJS_OK; + } + + node = njs_parser_variable_node(parser, token->unique_id, + NJS_VARIABLE_VAR); + if (node == NULL) { + return NJS_ERROR; + } + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_expression); + + ret = njs_parser_after(parser, current, node, 1, + njs_parser_for_var_in_statement); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + njs_lexer_consume_token(parser->lexer, 2); + + return NJS_DONE; + } + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_expression); + return NJS_OK; + } +} + + +static njs_int_t +njs_parser_for_var_in_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + node = njs_parser_node_new(parser, NJS_TOKEN_IN); + if (node == NULL) { + return NJS_ERROR; + } + + node->left = parser->target; + node->right = parser->node; + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_statement_wo_node); + + return njs_parser_after(parser, current, node, 1, + njs_parser_for_var_in_statement_after); +} + + +static njs_int_t +njs_parser_for_var_in_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *foreach; + + foreach = njs_parser_node_new(parser, NJS_TOKEN_FOR_IN); + if (foreach == NULL) { + return NJS_ERROR; + } + + foreach->left = parser->target; + foreach->right = parser->node; + + parser->node = foreach; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_for_var_in_of_expression(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_str_t *text; + njs_parser_node_t *node; + + /* + * ";" ? ";" ? ")" + * "in" ")" + * "of" ")" + */ + + if (parser->node->token_type == NJS_TOKEN_IN) { + node = parser->node->left; + + if (node->token_type != NJS_TOKEN_NAME) { + text = (njs_str_t *) parser->target; + + njs_parser_ref_error(parser, "Invalid left-hand side \"%V\" " + "in for-in statement", text); + + njs_mp_free(parser->vm->mem_pool, text); + + return NJS_DONE; + } + + njs_parser_next(parser, njs_parser_for_in_statement); + return NJS_OK; + } + + if (parser->target != NULL) { + text = (njs_str_t *) parser->target; + + njs_mp_free(parser->vm->mem_pool, text); + } + + switch (token->type) { + case NJS_TOKEN_SEMICOLON: + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + + node = parser->node; + parser->node = NULL; + + if (token->type != NJS_TOKEN_SEMICOLON) { + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_expression); + + return njs_parser_after(parser, current, node, 1, + njs_parser_for_expression); + } + + parser->target = node; + + njs_lexer_consume_token(parser->lexer, 1); + njs_parser_next(parser, njs_parser_for_expression); + + return NJS_OK; + + case NJS_TOKEN_OF: + return njs_parser_not_supported(parser, token); + + default: + return njs_parser_failed(parser); + } +} + + +static njs_int_t +njs_parser_for_in_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node, *forin; + + if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + node = parser->node; + + if (node->right != NULL && node->right->token_type == NJS_TOKEN_VAR) { + return NJS_ERROR; + } + + forin = njs_parser_node_new(parser, NJS_TOKEN_FOR_IN); + if (node == NULL) { + return NJS_ERROR; + } + + forin->left = parser->node; + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_statement_wo_node); + + return njs_parser_after(parser, current, forin, 1, + njs_parser_for_in_statement_after); +} + + +static njs_int_t +njs_parser_for_in_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + parser->target->right = parser->node; + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_for_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *body, *cond, *for_node; + + if (token->type != NJS_TOKEN_SEMICOLON) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + for_node = njs_parser_node_new(parser, NJS_TOKEN_FOR); + if (for_node == NULL) { + return NJS_ERROR; + } + + cond = njs_parser_node_new(parser, 0); + if (cond == NULL) { + return NJS_ERROR; + } + + body = njs_parser_node_new(parser, 0); + if (body == NULL) { + return NJS_ERROR; + } + + for_node->left = parser->target; + for_node->right = cond; + + cond->left = parser->node; + cond->right = body; + + parser->node = NULL; + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { + parser->target = for_node; + + njs_parser_next(parser, njs_parser_for_expression_end); + + return NJS_OK; + } + + njs_parser_next(parser, njs_parser_expression); + + return njs_parser_after(parser, current, for_node, 1, + njs_parser_for_expression_end); +} + + +static njs_int_t +njs_parser_for_expression_end(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *body; + + if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + body = parser->target->right->right; + + body->right = parser->node; + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_statement_wo_node); + + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_for_end); +} + + +static njs_int_t +njs_parser_for_end(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *body; + + body = parser->target->right->right; + + body->left = parser->node; + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +/* + * 13.8 continue Statement. + */ +static njs_int_t +njs_parser_continue_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + return njs_parser_break_continue(parser, token, NJS_TOKEN_CONTINUE); +} + + +/* + * 13.9 break Statement. + */ +static njs_int_t +njs_parser_break_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + return njs_parser_break_continue(parser, token, NJS_TOKEN_BREAK); +} + + +static njs_int_t +njs_parser_break_continue(njs_parser_t *parser, njs_lexer_token_t *token, + njs_token_type_t type) +{ + njs_int_t ret; + + parser->node = njs_parser_node_new(parser, type); + if (parser->node == NULL) { + return NJS_ERROR; + } + + parser->node->token_line = parser->line; + + switch (token->type) { + case NJS_TOKEN_SEMICOLON: + break; + + case NJS_TOKEN_LINE_END: + return njs_parser_failed(parser); + + default: + if (!parser->strict_semicolon + && parser->lexer->prev_type == NJS_TOKEN_LINE_END) + { + break; + } + + if (njs_lexer_token_is_label_identifier(token)) { + if (njs_label_find(parser->vm, parser->scope, + token->unique_id) == NULL) + { + njs_parser_syntax_error(parser, "Undefined label \"%V\"", + &token->text); + return NJS_DONE; + } + + ret = njs_name_copy(parser->vm, &parser->node->name, &token->text); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + break; + } + + if (parser->strict_semicolon + || (token->type != NJS_TOKEN_END + && token->type != NJS_TOKEN_CLOSE_BRACE + && parser->lexer->prev_type != NJS_TOKEN_LINE_END)) + { + return njs_parser_failed(parser); + } + + return njs_parser_stack_pop(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + return njs_parser_stack_pop(parser); +} + + +/* + * 13.10 return Statement. + */ +static njs_int_t +njs_parser_return_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node; + njs_parser_scope_t *scope; + + for (scope = parser->scope; + scope != NULL; + scope = scope->parent) + { + if (scope->type == NJS_SCOPE_FUNCTION && !scope->module) { + break; + } + + if (scope->type == NJS_SCOPE_GLOBAL) { + njs_parser_syntax_error(parser, "Illegal return statement"); + return NJS_ERROR; + } + } + + node = njs_parser_node_new(parser, NJS_TOKEN_RETURN); + if (njs_slow_path(node == NULL)) { + return NJS_ERROR; + } + + switch (token->type) { + case NJS_TOKEN_SEMICOLON: + break; + + case NJS_TOKEN_LINE_END: + return njs_parser_failed(parser); + + default: + if (!parser->strict_semicolon + && parser->lexer->prev_type == NJS_TOKEN_LINE_END) + { + break; + } + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_expression); + + return njs_parser_after(parser, current, node, 0, + njs_parser_return_statement_after); + } + + parser->node = node; + + njs_lexer_consume_token(parser->lexer, 1); + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_return_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (parser->ret != NJS_OK) { + parser->node = parser->target; + return njs_parser_stack_pop(parser); + } + + if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { + return njs_parser_failed(parser); + } + + parser->target->right = parser->node; + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +/* + * 13.11 with Statement + */ +static njs_int_t +njs_parser_with_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + return njs_parser_not_supported(parser, token); +} + + +/* + * 13.12 switch Statement + */ +static njs_int_t +njs_parser_switch_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + njs_parser_node_t *swtch; + + swtch = njs_parser_node_new(parser, NJS_TOKEN_SWITCH); + if (swtch == NULL) { + return NJS_ERROR; + } + + njs_parser_next(parser, njs_parser_expression_parenthesis); + + ret = njs_parser_after(parser, current, swtch, 1, + njs_parser_switch_block); + if (ret != NJS_OK) { + return ret; + } + + return njs_parser_after(parser, current, swtch, 1, + njs_parser_switch_statement_after); +} + + +static njs_int_t +njs_parser_switch_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_switch_block(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_OPEN_BRACE) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + parser->target->left = parser->node; + + njs_parser_next(parser, njs_parser_switch_case); + + return NJS_OK; +} + + +static njs_int_t +njs_parser_switch_case(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + return njs_parser_switch_case_def(parser, token, current, 1); +} + + +static njs_int_t +njs_parser_switch_case_wo_def(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + return njs_parser_switch_case_def(parser, token, current, 0); +} + + +static njs_int_t +njs_parser_switch_case_def(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current, njs_bool_t with_default) +{ + njs_parser_node_t *node, *branch; + + node = njs_parser_node_new(parser, 0); + if (node == NULL) { + return NJS_ERROR; + } + + parser->node = NULL; + + switch (token->type) { + case NJS_TOKEN_CASE: + branch = njs_parser_node_new(parser, 0); + if (branch == NULL) { + return NJS_ERROR; + } + + branch->right = node; + + njs_parser_next(parser, njs_parser_expression); + + njs_lexer_consume_token(parser->lexer, 1); + + if (parser->target->token_type == NJS_TOKEN_SWITCH) { + parser->target->right = branch; + + } else { + parser->target->left = branch; + } + + if (with_default) { + return njs_parser_after(parser, current, branch, 1, + njs_parser_switch_case_after); + + } else { + return njs_parser_after(parser, current, branch, 1, + njs_parser_switch_case_after_wo_def); + } + + case NJS_TOKEN_DEFAULT: + if (!with_default) { + njs_parser_syntax_error(parser, "More than one default clause " + "in switch statement"); + return NJS_DONE; + } + + njs_lexer_consume_token(parser->lexer, 1); + + if (parser->target->token_type == NJS_TOKEN_SWITCH) { + parser->target->right = node; + + } else { + parser->target->left = node; + } + + node->token_type = NJS_TOKEN_DEFAULT; + + parser->target = node; + + njs_parser_next(parser, njs_parser_switch_case_after_wo_def); + + break; + + case NJS_TOKEN_CLOSE_BRACE: + njs_lexer_consume_token(parser->lexer, 1); + return njs_parser_stack_pop(parser); + + default: + return njs_parser_failed(parser); + } + + return NJS_OK; +} + + +static njs_int_t +njs_parser_switch_case_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_COLON) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + parser->target->right->left = parser->node; + parser->node = NULL; + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + switch (token->type) { + case NJS_TOKEN_CASE: + case NJS_TOKEN_DEFAULT: + case NJS_TOKEN_CLOSE_BRACE: + njs_parser_next(parser, njs_parser_switch_case_block); + return NJS_OK; + + default: + njs_parser_next(parser, njs_parser_statement_list); + break; + } + + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_switch_case_block); +} + + +static njs_int_t +njs_parser_switch_case_block(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + parser->target->right->right = parser->node; + + njs_parser_next(parser, njs_parser_switch_case); + + return NJS_OK; +} + + +static njs_int_t +njs_parser_switch_case_after_wo_def(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_COLON) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + if (parser->target->right != NULL) { + parser->target->right->left = parser->node; + } + + parser->node = NULL; + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + switch (token->type) { + case NJS_TOKEN_CASE: + case NJS_TOKEN_DEFAULT: + case NJS_TOKEN_CLOSE_BRACE: + njs_parser_next(parser, njs_parser_switch_case_block_wo_def); + return NJS_OK; + + default: + njs_parser_next(parser, njs_parser_statement_list); + break; + } + + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_switch_case_block_wo_def); +} + + +static njs_int_t +njs_parser_switch_case_block_wo_def(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (parser->target->right != NULL) { + parser->target->right->right = parser->node; + + } else { + parser->target->right = parser->node; + } + + njs_parser_next(parser, njs_parser_switch_case_wo_def); + + return NJS_OK; +} + + +/* + * 13.13 Labelled Statements + */ +static njs_int_t +njs_parser_labelled_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + uintptr_t unique_id; + njs_variable_t *label; + njs_parser_node_t *node; + + unique_id = token->unique_id; + + label = njs_label_find(parser->vm, parser->scope, unique_id); + if (label != NULL) { + njs_parser_syntax_error(parser, "Label \"%V\" " + "has already been declared", &token->text); + return NJS_DONE; + } + + label = njs_label_add(parser->vm, parser->scope, unique_id); + if (label == NULL) { + return NJS_ERROR; + } + + njs_lexer_consume_token(parser->lexer, 2); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + parser->node = NULL; + + if (token->type == NJS_TOKEN_FUNCTION) { + node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION); + if (node == NULL) { + return NJS_ERROR; + } + + node->token_line = token->line; + + njs_lexer_consume_token(parser->lexer, 1); + njs_parser_next(parser, njs_parser_function_declaration); + + } else { + njs_parser_next(parser, njs_parser_statement_wo_node); + } + + return njs_parser_after(parser, current, (void *) unique_id, 1, + njs_parser_labelled_statement_after); +} + + +static njs_int_t +njs_parser_labelled_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_int_t ret; + uintptr_t unique_id; + const njs_lexer_entry_t *entry; + + if (parser->node != NULL) { + /* The statement is not empty block or just semicolon. */ + + unique_id = (uintptr_t) parser->target; + entry = (const njs_lexer_entry_t *) unique_id; + + ret = njs_name_copy(parser->vm, &parser->node->name, &entry->name); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + ret = njs_label_remove(parser->vm, parser->scope, unique_id); + if (ret != NJS_OK) { + return NJS_ERROR; + } + } + + return njs_parser_stack_pop(parser); +} + + +/* + * 13.14 throw Statement + */ +static njs_int_t +njs_parser_throw_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + node = njs_parser_node_new(parser, NJS_TOKEN_THROW); + if (node == NULL) { + return NJS_ERROR; + } + + if (parser->lexer->prev_type == NJS_TOKEN_LINE_END) { + njs_parser_syntax_error(parser, "Illegal newline after throw"); + return NJS_DONE; + } + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_expression); + + return njs_parser_after(parser, current, node, 1, + njs_parser_throw_statement_after); +} + + +static njs_int_t +njs_parser_throw_statement_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (parser->ret != NJS_OK) { + parser->node = parser->target; + return njs_parser_reject(parser); + } + + if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { + return njs_parser_failed(parser); + } + + parser->target->right = parser->node; + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +/* + * 13.15 try Statement. + */ +static njs_int_t +njs_parser_try_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *try; + + try = njs_parser_node_new(parser, NJS_TOKEN_TRY); + if (try == NULL) { + return NJS_ERROR; + } + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_block_statement_open_brace); + + return njs_parser_after(parser, current, try, 1, + njs_parser_catch_or_finally); +} + + +static njs_int_t +njs_parser_catch_or_finally(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + njs_parser_node_t *catch, *node, *try; + + try = parser->target; + + try->left = parser->node; + + if (token->type == NJS_TOKEN_FINALLY) { + njs_lexer_consume_token(parser->lexer, 1); + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_block_statement_open_brace); + + return njs_parser_after(parser, current, try, 0, + njs_parser_catch_finally); + + } else if (token->type != NJS_TOKEN_CATCH) { + njs_parser_syntax_error(parser, "Missing catch or finally after try"); + return NJS_DONE; + } + + njs_lexer_consume_token(parser->lexer, 1); + + catch = njs_parser_node_new(parser, NJS_TOKEN_CATCH); + if (catch == NULL) { + return NJS_ERROR; + } + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { + parser->node = NULL; + + njs_parser_next(parser, njs_parser_block_statement_open_brace); + + try->right = catch; + + /* TODO: it is necessary to change the generator. */ + + return njs_parser_not_supported(parser, token); + + /* + * return njs_parser_after(parser, current, parser->target, 0, + * njs_parser_catch_after); + */ + } + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + try->right = catch; + + if (njs_lexer_token_is_binding_identifier(token)) { + node = njs_parser_variable_node(parser, token->unique_id, + NJS_VARIABLE_CATCH); + if (node == NULL) { + return NJS_ERROR; + } + + catch->left = node; + + njs_lexer_consume_token(parser->lexer, 1); + njs_parser_next(parser, njs_parser_catch_parenthesis); + return NJS_OK; + } + + if (token->type != NJS_TOKEN_OPEN_BRACE) { + return njs_parser_failed(parser); + } + + /* + * njs_parser_next(parser, njs_parser_block_statement_open_brace); + * + * return njs_parser_after(parser, current, try, 1, + * njs_parser_catch_parenthesis); + */ + + return njs_parser_not_supported(parser, token); +} + + +static njs_int_t +njs_parser_catch_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_scope_end(parser); + + parser->target->right->right = parser->node; + + if (token->type == NJS_TOKEN_FINALLY) { + njs_lexer_consume_token(parser->lexer, 1); + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_block_statement_open_brace); + + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_catch_finally); + } + + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_catch_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + parser->target->right->right = parser->node; + parser->node = NULL; + + njs_parser_next(parser, njs_parser_block_statement_open_brace); + + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_catch_after); +} + + +static njs_int_t +njs_parser_catch_finally(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + node = njs_parser_node_new(parser, NJS_TOKEN_FINALLY); + if (node == NULL) { + return NJS_ERROR; + } + + node->right = parser->node; + + if (parser->target->right != NULL) { + node->left = parser->target->right; + } + + parser->target->right = node; + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_debugger_statement(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + return njs_parser_not_supported(parser, token); +} + + +/* + * 14.1 Function Definitions. + */ +static njs_function_t * +njs_parser_function_alloc(njs_parser_t *parser, njs_variable_t *var) +{ + njs_value_t *value; + njs_function_t *function; + njs_function_lambda_t *lambda; + + lambda = njs_function_lambda_alloc(parser->vm, 1); + if (lambda == NULL) { + njs_memory_error(parser->vm); + return NULL; + } + + /* TODO: + * njs_function_t is used to pass lambda to + * njs_generate_function_declaration() and is not actually needed. + * real njs_function_t is created by njs_vmcode_function() in runtime. + */ + + function = njs_function_alloc(parser->vm, lambda, NULL, 1); + if (function == NULL) { + return NULL; + } + + njs_set_function(&var->value, function); + + if (var->index != NJS_INDEX_NONE + && njs_scope_accumulative(parser->vm, parser->scope)) + { + value = (njs_value_t *) var->index; + *value = var->value; + } + + return function; +} + + +static njs_int_t +njs_parser_function_declaration(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + uintptr_t unique_id; + njs_variable_t *var; + njs_function_t *function; + njs_parser_node_t *node, *temp; + + if (!njs_lexer_token_is_binding_identifier(token)) { + return njs_parser_failed(parser); + } + + if (njs_parser_restricted_identifier(token->type)) { + njs_parser_syntax_error(parser, "Identifier \"%V\" is forbidden" + " in function declaration", &token->text); + return NJS_DONE; + } + + node = parser->node; + unique_id = token->unique_id; + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + var = njs_variable_add(parser->vm, parser->scope, unique_id, + NJS_VARIABLE_FUNCTION); + if (var == NULL) { + return NJS_ERROR; + } + + ret = njs_variable_reference(parser->vm, parser->scope, node, + unique_id, NJS_DECLARATION); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + function = njs_parser_function_alloc(parser, var); + if (function == NULL) { + return NJS_ERROR; + } + + temp = njs_parser_node_new(parser, 0); + if (temp == NULL) { + return NJS_ERROR; + } + + temp->left = node; + temp->u.value.data.u.lambda = function->u.lambda; + + node->left = (njs_parser_node_t *) function; + + parser->node = temp; + + njs_parser_next(parser, njs_parser_function_parse); + + return njs_parser_after(parser, current, temp, 1, + njs_parser_function_declaration_after); +} + + +static njs_int_t +njs_parser_function_declaration_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_function_t *function; + + parser->node = parser->target->left; + + function = (njs_function_t *) parser->node->left; + + function->args_count = function->u.lambda->nargs + - function->u.lambda->rest_parameters; + + parser->node->right = parser->target->right; + parser->node->left = NULL; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_function_parse(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + + ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + parser->target = parser->node; + parser->node = NULL; + + njs_parser_next(parser, njs_parser_formal_parameters); + + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_function_lambda_args_after); +} + + +static njs_int_t +njs_parser_function_expression(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + njs_variable_t *var; + njs_function_t *function; + njs_function_lambda_t *lambda; + + ret = njs_parser_scope_begin(parser, NJS_SCOPE_SHIM); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + if (njs_lexer_token_is_binding_identifier(token)) { + var = njs_variable_add(parser->vm, parser->scope, token->unique_id, + NJS_VARIABLE_SHIM); + if (var == NULL) { + return NJS_ERROR; + } + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + function = njs_parser_function_alloc(parser, var); + if (function == NULL) { + return NJS_ERROR; + } + + lambda = function->u.lambda; + + } else { + /* Anonymous function. */ + lambda = njs_function_lambda_alloc(parser->vm, 1); + if (lambda == NULL) { + return NJS_ERROR; + } + } + + if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + parser->node->u.value.data.u.lambda = lambda; + + njs_parser_next(parser, njs_parser_function_parse); + + return njs_parser_after(parser, current, NULL, 1, + njs_parser_function_expression_after); +} + + +static njs_int_t +njs_parser_function_expression_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_scope_end(parser); + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_unique_formal_parameters(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + parser->node = NULL; + + njs_parser_next(parser, njs_parser_formal_parameters); + + return NJS_OK; +} + + +static njs_int_t +njs_parser_formal_parameters(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_variable_t *arg, *cur_arg; + njs_function_lambda_t *lambda; + + lambda = parser->target->u.value.data.u.lambda; + + switch (token->type) { + /* BindingRestElement */ + case NJS_TOKEN_ELLIPSIS: + if (lambda->rest_parameters != 0) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + lambda->rest_parameters = 1; + + return NJS_OK; + + /* BindingElement */ + case NJS_TOKEN_OPEN_BRACE: + return njs_parser_not_supported(parser, token); + + case NJS_TOKEN_OPEN_BRACKET: + return njs_parser_not_supported(parser, token); + + default: + /* SingleNameBinding */ + if (njs_lexer_token_is_binding_identifier(token)) { + arg = njs_variable_add(parser->vm, parser->scope, + token->unique_id, NJS_VARIABLE_VAR); + if (arg == NULL) { + return NJS_ERROR; + } + + if (arg->index > 0) { + njs_parser_syntax_error(parser, "Duplicate parameter names"); + return NJS_DONE; + } + + cur_arg = (njs_variable_t *) parser->node; + + if (cur_arg == NULL) { + arg->index = NJS_SCOPE_ARGUMENTS; + + /* A "this" reservation. */ + arg->index += sizeof(njs_value_t); + + } else { + arg->index = cur_arg->index + sizeof(njs_value_t); + } + + lambda->nargs++; + + /* Crutch for temporary storage. */ + parser->node = (njs_parser_node_t *) arg; + + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_formal_parameters_after); + break; + } + + return njs_parser_stack_pop(parser); + } + + return NJS_OK; +} + + +static njs_int_t +njs_parser_formal_parameters_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_function_lambda_t *lambda; + + if (token->type != NJS_TOKEN_COMMA) { + return njs_parser_stack_pop(parser); + } + + lambda = parser->target->u.value.data.u.lambda; + + if (lambda->rest_parameters) { + njs_parser_syntax_error(parser, "Rest parameter must be " + "last formal parameter"); + return NJS_DONE; + } + + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_formal_parameters); + + return NJS_OK; +} + + +/* + * 14.2 Arrow Function Definitions + * + * TODO: implement according to specification. + */ +static njs_int_t +njs_parser_arrow_function(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + njs_variable_t *arg; + njs_parser_node_t *node; + njs_function_lambda_t *lambda; + + node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION); + if (node == NULL) { + return NJS_ERROR; + } + + node->token_line = token->line; + parser->node = node; + + lambda = njs_function_lambda_alloc(parser->vm, 0); + if (lambda == NULL) { + return NJS_ERROR; + } + + node->u.value.data.u.lambda = lambda; + + ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + parser->scope->arrow_function = 1; + + if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { + njs_lexer_consume_token(parser->lexer, 1); + + parser->node = NULL; + parser->target = node; + + njs_parser_next(parser, njs_parser_formal_parameters); + + return njs_parser_after(parser, current, node, 1, + njs_parser_arrow_function_args_after); + + } else if (njs_lexer_token_is_binding_identifier(token)) { + arg = njs_variable_add(parser->vm, parser->scope, + token->unique_id, NJS_VARIABLE_VAR); + if (arg == NULL) { + return NJS_ERROR; + } + + arg->index = NJS_SCOPE_ARGUMENTS; + + /* A "this" reservation. */ + arg->index += sizeof(njs_value_t); + + lambda->nargs = 1; + + njs_lexer_consume_token(parser->lexer, 1); + + parser->target = node; + + njs_parser_next(parser, njs_parser_arrow_function_arrow); + + } else { + return njs_parser_failed(parser); + } + + return NJS_OK; +} + + +static njs_int_t +njs_parser_arrow_function_args_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + njs_parser_next(parser, njs_parser_arrow_function_arrow); + + return NJS_OK; +} + + +static njs_int_t +njs_parser_arrow_function_arrow(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_ARROW) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type == NJS_TOKEN_OPEN_BRACE) { + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type == NJS_TOKEN_CLOSE_BRACE) { + parser->node = NULL; + + njs_parser_next(parser, njs_parser_function_lambda_body_after); + + return NJS_OK; + } + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_statement_list); + + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_function_lambda_body_after); + } + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_assignment_expression); + + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_arrow_function_body_after); +} + + +static njs_int_t +njs_parser_arrow_function_body_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *body; + + body = njs_parser_return_set(parser, parser->node); + if (body == NULL) { + return NJS_ERROR; + } + + parser->target->right = body; + parser->node = parser->target; + + njs_parser_scope_end(parser); + + return njs_parser_stack_pop(parser); +} + + +/* + * 14.3 Method Definitions. + */ +static njs_int_t +njs_parser_method_definition(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_lexer_token_t *next; + njs_parser_node_t *expr; + + switch (token->type) { + /* PropertyName */ + case NJS_TOKEN_STRING: + case NJS_TOKEN_ESCAPE_STRING: + case NJS_TOKEN_NUMBER: + break; + + default: + if (njs_lexer_token_is_identifier_name(token)) { + break; + } + + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + next = njs_lexer_token(parser->lexer, 0); + if (next == NULL) { + return NJS_ERROR; + } + + if (next->type != NJS_TOKEN_OPEN_PARENTHESIS) { + return njs_parser_failed(parser); + } + + expr = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION); + if (expr == NULL) { + return NJS_ERROR; + } + + expr->token_line = next->line; + + parser->node = expr; + + njs_lexer_consume_token(parser->lexer, 1); + njs_parser_next(parser, njs_parser_function_lambda); + + return NJS_OK; +} + + +static njs_int_t +njs_parser_get_set(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_token_type_t accessor; + njs_lexer_token_t *name; + njs_parser_node_t *property, *expression, *temp; + + temp = parser->target; + accessor = (njs_token_type_t) temp->right; + + name = token; + + token = njs_lexer_peek_token(parser->lexer, token, 0); + if (token == NULL) { + return NJS_ERROR; + } + + switch (token->type) { + /* IdentifierReference */ + case NJS_TOKEN_NAME: + case NJS_TOKEN_STRING: + case NJS_TOKEN_ESCAPE_STRING: + case NJS_TOKEN_NUMBER: + break; + + case NJS_TOKEN_OPEN_BRACKET: + njs_lexer_consume_token(parser->lexer, 2); + + njs_parser_next(parser, njs_parser_assignment_expression); + + return njs_parser_after(parser, current, temp, 1, + njs_parser_get_set_after); + + default: + if (njs_lexer_token_is_identifier_name(token)) { + break; + } + + return njs_parser_property_definition_ident(parser, name, temp); + } + + property = njs_parser_property_name_node(parser, token); + if (property == NULL) { + return NJS_ERROR; + } + + njs_lexer_consume_token(parser->lexer, 2); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { + return njs_parser_failed(parser); + } + + expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION); + if (expression == NULL) { + return NJS_ERROR; + } + + expression->token_line = token->line; + + temp->right = property; + + parser->node = expression; + + njs_lexer_consume_token(parser->lexer, 1); + njs_parser_next(parser, njs_parser_function_lambda); + + if (accessor == NJS_TOKEN_PROPERTY_GETTER) { + return njs_parser_after(parser, current, temp, 1, njs_parser_get_after); + } + + return njs_parser_after(parser, current, temp, 1, njs_parser_set_after); +} + + +static njs_int_t +njs_parser_get_set_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_token_type_t accessor; + njs_parser_node_t *expression, *temp; + + if (token->type != NJS_TOKEN_CLOSE_BRACKET) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { + return njs_parser_failed(parser); + } + + expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION); + if (expression == NULL) { + return NJS_ERROR; + } + + expression->token_line = token->line; + + temp = parser->target; + + accessor = (njs_token_type_t) temp->right; + temp->right = parser->node; + + parser->node = expression; + + njs_lexer_consume_token(parser->lexer, 1); + njs_parser_next(parser, njs_parser_function_lambda); + + if (accessor == NJS_TOKEN_PROPERTY_GETTER) { + return njs_parser_after(parser, current, temp, 1, njs_parser_get_after); + } + + return njs_parser_after(parser, current, temp, 1, njs_parser_set_after); +} + + +static njs_int_t +njs_parser_get_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + njs_parser_node_t *expression, *temp; + njs_function_lambda_t *lambda; + + temp = parser->target; + + expression = parser->node; + lambda = expression->u.value.data.u.lambda; + + if (lambda->nargs != 0) { + njs_parser_syntax_error(parser, + "Getter must not have any formal parameters"); + return NJS_DONE; + } + + ret = njs_parser_property_accessor(parser, temp->left, temp->right, + expression, NJS_TOKEN_PROPERTY_GETTER); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + parser->node = temp->left; + parser->target = NULL; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_set_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + njs_parser_node_t *expression, *temp; + njs_function_lambda_t *lambda; + + temp = parser->target; + + expression = parser->node; + lambda = expression->u.value.data.u.lambda; + + if (lambda->nargs != 1) { + njs_parser_syntax_error(parser, + "Setter must have exactly one formal parameter"); + return NJS_DONE; + } + + ret = njs_parser_property_accessor(parser, temp->left, temp->right, + expression, NJS_TOKEN_PROPERTY_SETTER); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + parser->node = temp->left; + parser->target = NULL; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_function_lambda(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_int_t ret; + njs_parser_node_t *expr; + njs_function_lambda_t *lambda; + + lambda = njs_function_lambda_alloc(parser->vm, 0); + if (lambda == NULL) { + return NJS_ERROR; + } + + expr = parser->node; + expr->u.value.data.u.lambda = lambda; + + ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + parser->node = NULL; + parser->target = expr; + + njs_parser_next(parser, njs_parser_unique_formal_parameters); + + return njs_parser_after(parser, current, expr, 1, + njs_parser_function_lambda_args_after); +} + + +static njs_int_t +njs_parser_function_lambda_args_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type != NJS_TOKEN_OPEN_BRACE) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type == NJS_TOKEN_CLOSE_BRACE) { + parser->node = NULL; + + njs_parser_next(parser, njs_parser_function_lambda_body_after); + + return NJS_OK; + } + + parser->node = NULL; + + njs_parser_next(parser, njs_parser_statement_list); + + return njs_parser_after(parser, current, parser->target, 1, + njs_parser_function_lambda_body_after); +} + + +static njs_int_t +njs_parser_function_lambda_body_after(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current) +{ + njs_parser_node_t *body, *last, *parent; + + if (token->type != NJS_TOKEN_CLOSE_BRACE) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + parent = parser->target; + + last = NULL; + body = njs_parser_chain_top(parser); + + if (body != NULL) { + /* Take the last function body statement. */ + last = body->right; + + if (last == NULL) { + /* + * The last statement is terminated by semicolon. + * Take the last statement itself. + */ + last = body->left; + } + } + + if (last == NULL || last->token_type != NJS_TOKEN_RETURN) { + /* + * There is no function body or the last function body + * body statement is not "return" statement. + */ + body = njs_parser_return_set(parser, NULL); + if (body == NULL) { + return NJS_ERROR; + } + } + + parent->right = body; + parser->node = parent; + + njs_parser_scope_end(parser); + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_export(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *node; + + if (!parser->scope->module) { + njs_parser_syntax_error(parser, "Illegal export statement"); + return NJS_DONE; + } + + if (token->type != NJS_TOKEN_DEFAULT) { + njs_parser_syntax_error(parser, "Non-default export is not supported"); + return NJS_DONE; + } + + njs_lexer_consume_token(parser->lexer, 1); + + node = njs_parser_node_new(parser, NJS_TOKEN_EXPORT); + if (node == NULL) { + return NJS_ERROR; + } + + parser->node = node; + + njs_parser_next(parser, njs_parser_expression); + + return njs_parser_after(parser, current, node, 1, njs_parser_export_after); +} + + +static njs_int_t +njs_parser_export_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + parser->target->right = parser->node; + parser->node = parser->target; + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_import(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *name; + + if (parser->scope->type != NJS_SCOPE_GLOBAL && !parser->scope->module) { + njs_parser_syntax_error(parser, "Illegal import statement"); + return NJS_DONE; + } + + if (token->type != NJS_TOKEN_NAME) { + njs_parser_syntax_error(parser, "Non-default import is not supported"); + return NJS_DONE; + } + + name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_VAR); + if (name == NULL) { + return NJS_ERROR; + } + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type != NJS_TOKEN_FROM) { + return njs_parser_failed(parser); + } + + njs_lexer_consume_token(parser->lexer, 1); + + token = njs_lexer_token(parser->lexer, 0); + if (token == NULL) { + return NJS_ERROR; + } + + if (token->type != NJS_TOKEN_STRING) { + return njs_parser_failed(parser); + } + + njs_parser_next(parser, njs_parser_module); + + return njs_parser_after(parser, current, name, 1, njs_parser_import_after); +} + + +static njs_int_t +njs_parser_import_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_parser_node_t *import; + + if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { + return njs_parser_failed(parser); + } + + import = njs_parser_node_new(parser, NJS_TOKEN_IMPORT); + if (import == NULL) { + return NJS_ERROR; + } + + import->left = parser->target; + import->right = parser->node; + + parser->node = import; + parser->node->hoist = 1; + + return njs_parser_stack_pop(parser); +} + + +njs_int_t +njs_parser_module_lambda(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + njs_parser_node_t *node, *parent; + njs_function_lambda_t *lambda; + + node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION); + if (node == NULL) { + return NJS_ERROR; + } + + node->token_line = token->line; + + lambda = njs_function_lambda_alloc(parser->vm, 1); + if (lambda == NULL) { + return NJS_ERROR; + } + + node->u.value.data.u.lambda = lambda; + parser->node = node; + + ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + parser->scope->module = 1; + + parent = parser->node; + parser->node = NULL; + + njs_parser_next(parser, njs_parser_statement_list); + + return njs_parser_after(parser, current, parent, 0, + njs_parser_module_lambda_after); +} + + +static njs_int_t +njs_parser_module_lambda_after(njs_parser_t *parser, njs_lexer_token_t *token, + njs_queue_link_t *current) +{ + njs_int_t ret; + + ret = njs_parser_export_sink(parser); + if (ret != NJS_OK) { + return NJS_ERROR; + } + + parser->target->right = njs_parser_chain_top(parser); + parser->target->right->token_line = 1; + + parser->node = parser->target; + + njs_parser_scope_end(parser); + + return njs_parser_stack_pop(parser); +} + + +static njs_int_t +njs_parser_export_sink(njs_parser_t *parser) +{ + njs_uint_t n; + njs_parser_node_t *node, *prev; + + n = 0; + + for (node = njs_parser_chain_top(parser); + node != NULL; + node = node->left) + { + if (node->right != NULL + && node->right->token_type == NJS_TOKEN_EXPORT) + { + n++; + } + } + + if (n != 1) { + njs_parser_syntax_error(parser, + (n == 0) ? "export statement is required" + : "Identifier \"default\" has already been declared"); + return NJS_ERROR; + } + + node = njs_parser_chain_top(parser); + + if (node->right && node->right->token_type == NJS_TOKEN_EXPORT) { + return NJS_OK; + } + + prev = njs_parser_chain_top(parser); + + while (prev->left != NULL) { + node = prev->left; + + if (node->right != NULL + && node->right->token_type == NJS_TOKEN_EXPORT) + { + prev->left = node->left; + break; + } + + prev = prev->left; + } + + node->left = njs_parser_chain_top(parser); + njs_parser_chain_top_set(parser, node); + + return NJS_OK; +} + + +static njs_parser_node_t * +njs_parser_return_set(njs_parser_t *parser, njs_parser_node_t *expr) +{ + njs_parser_node_t *stmt, *node; + + node = njs_parser_node_new(parser, NJS_TOKEN_RETURN); + if (njs_slow_path(node == NULL)) { + return NULL; + } + + node->right = expr; + + stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); + if (njs_slow_path(stmt == NULL)) { + return NULL; + } + + stmt->left = njs_parser_chain_top(parser); + stmt->right = node; + + njs_parser_chain_top_set(parser, stmt); + + return stmt; +} + + +static njs_parser_node_t * +njs_parser_variable_node(njs_parser_t *parser, uintptr_t unique_id, + njs_variable_type_t type) +{ + njs_int_t ret; + njs_variable_t *var; + njs_parser_node_t *node; + + var = njs_variable_add(parser->vm, parser->scope, unique_id, type); + if (njs_slow_path(var == NULL)) { + return NULL; + } + + if (njs_is_null(&var->value)) { + + switch (type) { + + case NJS_VARIABLE_VAR: + njs_set_undefined(&var->value); + break; + + default: + break; + } + } + + node = njs_parser_node_new(parser, NJS_TOKEN_NAME); + if (njs_slow_path(node == NULL)) { + return NULL; + } + + ret = njs_variable_reference(parser->vm, parser->scope, node, unique_id, + NJS_DECLARATION); + if (njs_slow_path(ret != NJS_OK)) { + return NULL; + } + + return node; +} + + +static njs_parser_node_t * +njs_parser_reference(njs_parser_t *parser, njs_lexer_token_t *token) +{ + njs_int_t ret; + njs_variable_t *var; + njs_parser_node_t *node; + njs_parser_scope_t *scope; + + node = njs_parser_node_new(parser, token->type); + if (njs_slow_path(node == NULL)) { + return NULL; + } + + switch (token->type) { + + case NJS_TOKEN_NULL: + njs_thread_log_debug("JS: null"); + + node->u.value = njs_value_null; + break; + + case NJS_TOKEN_THIS: + njs_thread_log_debug("JS: this"); + + scope = njs_function_scope(parser->scope, 0); + + if (scope != NULL) { + if (scope == njs_function_scope(parser->scope, 1)) { + node->index = NJS_INDEX_THIS; + + } else { + node->token_type = NJS_TOKEN_NON_LOCAL_THIS; + + node->token_line = token->line; + + ret = njs_variable_reference(parser->vm, scope, node, + token->unique_id, NJS_REFERENCE); + if (njs_slow_path(ret != NJS_OK)) { + return NULL; + } + + var = njs_variable_add(parser->vm, scope, token->unique_id, + NJS_VARIABLE_VAR); + if (njs_slow_path(var == NULL)) { + return NULL; + } + + var->this_object = 1; + } + + break; + } + + node->token_type = NJS_TOKEN_GLOBAL_OBJECT; + + break; + + case NJS_TOKEN_ARGUMENTS: + njs_thread_log_debug("JS: arguments"); + + scope = njs_function_scope(parser->scope, 0); + + if (scope == NULL) { + njs_parser_syntax_error(parser, "\"%V\" object in global scope", + &token->text); + return NULL; + } + + node->token_line = token->line; + + ret = njs_variable_reference(parser->vm, scope, node, token->unique_id, + NJS_REFERENCE); + if (njs_slow_path(ret != NJS_OK)) { + return NULL; + } + + var = njs_variable_add(parser->vm, scope, token->unique_id, + NJS_VARIABLE_VAR); + if (njs_slow_path(var == NULL)) { + return NULL; + } + + var->arguments_object = 1; + + break; + + default: + if (token->type == NJS_TOKEN_EVAL + || njs_lexer_token_is_identifier_reference(token)) + { + njs_thread_log_debug("JS: %V", name); + + if (token->type != NJS_TOKEN_EVAL) { + node->token_type = NJS_TOKEN_NAME; + } + + node->token_line = token->line; + + ret = njs_variable_reference(parser->vm, parser->scope, node, + token->unique_id, NJS_REFERENCE); + if (njs_slow_path(ret != NJS_OK)) { + return NULL; + } + + break; + } + + (void) njs_parser_unexpected_token(parser->vm, parser, &token->text, + token->type); + return NULL; + } + + return node; +} + + +static njs_parser_node_t * +njs_parser_argument(njs_parser_t *parser, njs_parser_node_t *expr, + njs_index_t index) +{ + njs_parser_node_t *node; + + node = njs_parser_node_new(parser, NJS_TOKEN_ARGUMENT); + if (njs_slow_path(node == NULL)) { + return NULL; + } + + node->index = index; + + node->left = expr; + expr->dest = node; + + return node; +} + + +static njs_int_t +njs_parser_object_property(njs_parser_t *parser, njs_parser_node_t *parent, + njs_parser_node_t *property, njs_parser_node_t *value, + njs_bool_t proto_init) +{ + njs_token_type_t type; + njs_parser_node_t *stmt, *assign, *object, *propref; + + object = njs_parser_node_new(parser, NJS_TOKEN_OBJECT_VALUE); + if (njs_slow_path(object == NULL)) { + return NJS_TOKEN_ERROR; + } + + object->u.object = parent; + + type = proto_init ? NJS_TOKEN_PROTO_INIT : NJS_TOKEN_PROPERTY_INIT; + + propref = njs_parser_node_new(parser, type); + if (njs_slow_path(propref == NULL)) { + return NJS_ERROR; + } + + propref->left = object; + propref->right = property; + + assign = njs_parser_node_new(parser, NJS_TOKEN_ASSIGNMENT); + if (njs_slow_path(assign == NULL)) { + return NJS_ERROR; + } + + assign->u.operation = NJS_VMCODE_MOVE; + assign->left = propref; + assign->right = value; + + stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); + if (njs_slow_path(stmt == NULL)) { + return NJS_ERROR; + } + + stmt->right = assign; + stmt->left = parent->left; + parent->left = stmt; + + return NJS_OK; +} + + +static njs_int_t +njs_parser_property_accessor(njs_parser_t *parser, njs_parser_node_t *parent, + njs_parser_node_t *property, njs_parser_node_t *value, + njs_token_type_t accessor) +{ + njs_parser_node_t *node, *stmt, *object, *propref; + + object = njs_parser_node_new(parser, NJS_TOKEN_OBJECT_VALUE); + if (njs_slow_path(object == NULL)) { + return NJS_TOKEN_ERROR; + } + + object->u.object = parent; + + propref = njs_parser_node_new(parser, 0); + if (njs_slow_path(propref == NULL)) { + return NJS_ERROR; + } + + propref->left = object; + propref->right = property; + + node = njs_parser_node_new(parser, accessor); + if (njs_slow_path(node == NULL)) { + return NJS_ERROR; + } + + node->left = propref; + node->right = value; + + stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); + if (njs_slow_path(stmt == NULL)) { + return NJS_ERROR; + } + + stmt->right = node; + stmt->left = parent->left; + parent->left = stmt; + + return NJS_OK; +} + + +static njs_int_t +njs_parser_array_item(njs_parser_t *parser, njs_parser_node_t *array, + njs_parser_node_t *value) +{ + njs_int_t ret; + njs_parser_node_t *number; + + number = njs_parser_node_new(parser, NJS_TOKEN_NUMBER); + if (njs_slow_path(number == NULL)) { + return NJS_ERROR; + } + + njs_set_number(&number->u.value, array->u.length); + + ret = njs_parser_object_property(parser, array, number, value, 0); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; } - parser->node = try; + array->ctor = 0; + array->u.length++; - return type; + return NJS_OK; } -static njs_token_type_t -njs_parser_try_block(njs_vm_t *vm, njs_parser_t *parser) +static njs_int_t +njs_parser_template_string(njs_parser_t *parser, njs_lexer_token_t *token) { - njs_token_type_t type; + u_char *p, c; + njs_int_t ret; + njs_str_t *text; + njs_bool_t escape; + njs_lexer_t *lexer; njs_parser_node_t *node; - type = njs_parser_token(vm, parser); - if (njs_slow_path(type != NJS_TOKEN_OPEN_BRACE)) { - return NJS_TOKEN_ILLEGAL; - } + lexer = parser->lexer; + text = &token->text; - type = njs_parser_block_statement(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + escape = 0; + p = text->start; + + if (p == NULL) { + return NJS_ERROR; } - node = parser->node; + while (p < lexer->end) { - if (node != NULL && node->token_type == NJS_TOKEN_BLOCK) { - parser->node = node->left; + c = *p++; - njs_mp_free(vm->mem_pool, node); - } + if (c == '\\') { + if (p == lexer->end) { + break; + } - return type; -} + p++; + escape = 1; + continue; + } -static njs_token_type_t -njs_parser_throw_statement(njs_vm_t *vm, njs_parser_t *parser) -{ - njs_token_type_t type; - njs_parser_node_t *node; + if (c == '`') { + text->length = p - text->start - 1; + goto done; + } - node = njs_parser_node_new(vm, parser, NJS_TOKEN_THROW); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; + if (c == '$') { + if (p < lexer->end && *p == '{') { + p++; + text->length = p - text->start - 2; + goto done; + } + } } - type = njs_lexer_token(vm, parser->lexer); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + return NJS_ERROR; - switch (type) { +done: - case NJS_TOKEN_LINE_END: - njs_parser_syntax_error(vm, parser, "Illegal newline after throw"); - return NJS_TOKEN_ILLEGAL; + node = njs_parser_node_new(parser, NJS_TOKEN_STRING); + if (njs_slow_path(node == NULL)) { + return NJS_ERROR; + } - default: - type = njs_parser_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + if (escape) { + ret = njs_parser_escape_string_create(parser, token, &node->u.value); + if (njs_slow_path(ret != NJS_TOKEN_STRING)) { + return NJS_ERROR; } - node->right = parser->node; - parser->node = node; - - return type; + } else { + ret = njs_parser_string_create(parser->vm, token, &node->u.value); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } } + + lexer->start = p; + parser->node = node; + + return c == '`' ? NJS_DONE : NJS_OK; } -static njs_token_type_t -njs_parser_import_statement(njs_vm_t *vm, njs_parser_t *parser) +njs_int_t +njs_parser_string_create(njs_vm_t *vm, njs_lexer_token_t *token, + njs_value_t *value) { - njs_int_t ret; - njs_token_type_t type; - njs_parser_node_t *name, *import; + u_char *dst; + ssize_t size, length; + uint32_t cp; + njs_str_t *src; + const u_char *p, *end; - if (parser->scope->type != NJS_SCOPE_GLOBAL - && !parser->scope->module) - { - njs_parser_syntax_error(vm, parser, "Illegal import statement"); + src = &token->text; - return NJS_TOKEN_ERROR; - } + length = njs_utf8_safe_length(src->start, src->length, &size); - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + dst = njs_string_alloc(vm, value, size, length); + if (njs_slow_path(dst == NULL)) { + return NJS_ERROR; } - if (type != NJS_TOKEN_NAME) { - njs_parser_syntax_error(vm, parser, - "Non-default import is not supported"); - return NJS_TOKEN_ILLEGAL; - } + p = src->start; + end = src->start + src->length; - name = njs_parser_variable_node(vm, parser, njs_parser_key_hash(parser), - NJS_VARIABLE_VAR); - if (njs_slow_path(name == NULL)) { - return NJS_TOKEN_ERROR; - } + while (p < end) { + cp = njs_utf8_safe_decode(&p, end); - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + dst = njs_utf8_encode(dst, cp); } - type = njs_parser_match_name(vm, parser, type, "from"); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + if (length > NJS_STRING_MAP_STRIDE && size != length) { + njs_string_offset_map_init(value->long_string.data->start, size); } - if (type != NJS_TOKEN_STRING) { - return NJS_TOKEN_ILLEGAL; - } + return NJS_OK; +} - ret = njs_parser_module(vm, parser); + +static njs_token_type_t +njs_parser_escape_string_create(njs_parser_t *parser, njs_lexer_token_t *token, + njs_value_t *value) +{ + u_char c, *start, *dst; + size_t size, length, hex_length; + uint64_t cp, cp_pair; + njs_int_t ret; + njs_str_t *string; + const u_char *src, *end, *hex_end; + + ret = njs_parser_escape_string_calc_length(parser, token, &size, &length); if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; + return NJS_TOKEN_ILLEGAL; } - import = njs_parser_node_new(vm, parser, NJS_TOKEN_IMPORT); - if (njs_slow_path(import == NULL)) { + start = njs_string_alloc(parser->vm, value, size, length); + if (njs_slow_path(start == NULL)) { return NJS_TOKEN_ERROR; } - import->left = name; - import->right = parser->node; - - parser->node = import; - parser->node->hoist = 1; + dst = start; + cp_pair = 0; - return njs_parser_token(vm, parser); -} + string = &token->text; + src = string->start; + end = src + string->length; + while (src < end) { + c = *src++; -njs_token_type_t -njs_parser_module_lambda(njs_vm_t *vm, njs_parser_t *parser) -{ - njs_int_t ret; - njs_token_type_t type; - njs_parser_node_t *node, *parent; - njs_function_lambda_t *lambda; + if (c == '\\') { + /* + * Testing "src == end" is not required here + * since this has been already tested by lexer. + */ - node = njs_parser_node_new(vm, parser, NJS_TOKEN_FUNCTION_EXPRESSION); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } + c = *src++; - node->token_line = njs_parser_token_line(parser); + switch (c) { + case 'u': + /* + * A character after "u" can be safely tested here + * because there is always a closing quote at the + * end of string: ...\u". + */ - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + if (*src != '{') { + hex_length = 4; + goto hex_length; + } - lambda = njs_function_lambda_alloc(vm, 1); - if (njs_slow_path(lambda == NULL)) { - return NJS_TOKEN_ERROR; - } + src++; + hex_length = 0; + hex_end = end; - node->u.value.data.u.lambda = lambda; - parser->node = node; + goto hex; - ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_FUNCTION); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } + case 'x': + hex_length = 2; + goto hex_length; - parser->scope->module = 1; + case '0': + c = '\0'; + break; - type = njs_parser_match(vm, parser, type, NJS_TOKEN_OPEN_PARENTHESIS); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + case 'b': + c = '\b'; + break; - parent = parser->node; + case 'f': + c = '\f'; + break; - type = njs_parser_match(vm, parser, type, NJS_TOKEN_CLOSE_PARENTHESIS); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + case 'n': + c = '\n'; + break; - type = njs_parser_lambda_statements(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + case 'r': + c = '\r'; + break; - ret = njs_parser_export_sink(vm, parser); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } + case 't': + c = '\t'; + break; - parent->right = njs_parser_chain_top(parser); - parent->right->token_line = 1; + case 'v': + c = '\v'; + break; - parser->node = parent; + case '\r': + /* + * A character after "\r" can be safely tested here + * because there is always a closing quote at the + * end of string: ...\\r". + */ - njs_parser_scope_end(vm, parser); + if (*src == '\n') { + src++; + } - return type; -} + continue; + case '\n': + continue; -static njs_token_type_t -njs_parser_export_statement(njs_vm_t *vm, njs_parser_t *parser) -{ - njs_token_type_t type; - njs_parser_node_t *node; + default: + if (c >= 0x80) { + goto utf8_copy; + } - if (!parser->scope->module) { - njs_parser_syntax_error(vm, parser, "Illegal export statement"); - return NJS_ERROR; - } + break; + } + } - node = njs_parser_node_new(vm, parser, NJS_TOKEN_EXPORT); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } + if (c < 0x80) { + *dst++ = c; - parser->node = node; + continue; + } - type = njs_parser_token(vm, parser); - if (njs_slow_path(type != NJS_TOKEN_DEFAULT)) { - njs_parser_syntax_error(vm, parser, - "Non-default export is not supported"); - return NJS_TOKEN_ILLEGAL; - } + utf8_copy: - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + src--; - type = njs_parser_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + cp = njs_utf8_safe_decode2(&src, end); + dst = njs_utf8_encode(dst, cp); - node->right = parser->node; - parser->node = node; + continue; - return type; -} + hex_length: + hex_end = src + hex_length; -static njs_int_t -njs_parser_export_sink(njs_vm_t *vm, njs_parser_t *parser) -{ - njs_uint_t n; - njs_parser_node_t *node, *prev; + hex: + cp = njs_number_hex_parse(&src, hex_end); - n = 0; + /* Skip '}' character. */ - for (node = njs_parser_chain_top(parser); - node != NULL; - node = node->left) - { - if (node->right != NULL - && node->right->token_type == NJS_TOKEN_EXPORT) - { - n++; + if (hex_length == 0) { + src++; } - } - if (n != 1) { - njs_parser_syntax_error(vm, parser, - (n == 0) ? "export statement is required" - : "Identifier \"default\" has already been declared"); - return NJS_ERROR; - } + if (cp_pair != 0) { + if (njs_fast_path(njs_surrogate_trailing(cp))) { + cp = njs_string_surrogate_pair(cp_pair, cp); - node = njs_parser_chain_top(parser); + } else if (njs_slow_path(njs_surrogate_leading(cp))) { + cp = NJS_UTF8_REPLACEMENT; - if (node->right && node->right->token_type == NJS_TOKEN_EXPORT) { - return NJS_OK; - } + dst = njs_utf8_encode(dst, (uint32_t) cp); - prev = njs_parser_chain_top(parser); + } else { + dst = njs_utf8_encode(dst, NJS_UTF8_REPLACEMENT); + } - while (prev->left != NULL) { - node = prev->left; + cp_pair = 0; - if (node->right != NULL - && node->right->token_type == NJS_TOKEN_EXPORT) - { - prev->left = node->left; - break; + } else if (njs_surrogate_any(cp)) { + if (cp <= 0xdbff && src[0] == '\\' && src[1] == 'u') { + cp_pair = cp; + continue; + } + + cp = NJS_UTF8_REPLACEMENT; } - prev = prev->left; + dst = njs_utf8_encode(dst, (uint32_t) cp); + if (njs_slow_path(dst == NULL)) { + njs_parser_syntax_error(parser, "Invalid Unicode code point \"%V\"", + &token->text); + + return NJS_TOKEN_ILLEGAL; + } } - node->left = njs_parser_chain_top(parser); - njs_parser_chain_top_set(parser, node); + if (length > NJS_STRING_MAP_STRIDE && length != size) { + njs_string_offset_map_init(start, size); + } - return NJS_OK; + return NJS_TOKEN_STRING; } -static njs_token_type_t -njs_parser_grouping_expression(njs_vm_t *vm, njs_parser_t *parser) +static njs_int_t +njs_parser_escape_string_calc_length(njs_parser_t *parser, + njs_lexer_token_t *token, size_t *out_size, size_t *out_length) { - njs_token_type_t type; - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + size_t size, length, hex_length; + uint64_t cp, cp_pair; + njs_str_t *string; + const u_char *ptr, *src, *end, *hex_end; - type = njs_parser_match(vm, parser, type, NJS_TOKEN_OPEN_PARENTHESIS); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + size = 0; + length = 0; + cp_pair = 0; - type = njs_parser_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + string = &token->text; + src = string->start; + end = src + string->length; - return njs_parser_match(vm, parser, type, NJS_TOKEN_CLOSE_PARENTHESIS); -} + while (src < end) { + if (*src == '\\') { + src++; -njs_int_t -njs_parser_match_arrow_expression(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) -{ - size_t offset; - njs_bool_t rest_parameters; + switch (*src) { + case 'u': + src++; - if (type != NJS_TOKEN_OPEN_PARENTHESIS && type != NJS_TOKEN_NAME) { - return NJS_DECLINED; - } + if (*src != '{') { + hex_length = 4; + goto hex_length; + } - offset = 0; + src++; + hex_length = 0; + hex_end = end; - if (type == NJS_TOKEN_NAME) { - goto arrow; - } + goto hex; - type = njs_parser_peek_token(vm, parser, &offset); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return NJS_DECLINED; - } + case 'x': + src++; + hex_length = 2; + goto hex_length; - rest_parameters = 0; + case '\r': + src++; - while (type != NJS_TOKEN_CLOSE_PARENTHESIS) { + if (*src == '\n') { + src++; + } - if (rest_parameters) { - return NJS_DECLINED; - } + continue; - if (njs_slow_path(type == NJS_TOKEN_ELLIPSIS)) { - rest_parameters = 1; + case '\n': + src++; + continue; - type = njs_parser_peek_token(vm, parser, &offset); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return NJS_DECLINED; + default: + break; } } - if (njs_slow_path(type != NJS_TOKEN_NAME)) { - return NJS_DECLINED; - } + if (*src >= 0x80) { + cp = njs_utf8_safe_decode2(&src, end); - type = njs_parser_peek_token(vm, parser, &offset); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + size += njs_utf8_size(cp); + length++; - if (type == NJS_TOKEN_COMMA) { - type = njs_parser_peek_token(vm, parser, &offset); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return NJS_DECLINED; - } + continue; } - } -arrow: + src++; + size++; + length++; - type = njs_parser_peek_token(vm, parser, &offset); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return NJS_DECLINED; - } + continue; - if (parser->lexer->prev_type == NJS_TOKEN_LINE_END) { - return NJS_DECLINED; - } + hex_length: - if (njs_slow_path(type != NJS_TOKEN_ARROW)) { - return NJS_DECLINED; - } + hex_end = src + hex_length; - return NJS_OK; -} + if (njs_slow_path(hex_end > end)) { + goto invalid; + } + hex: -njs_token_type_t -njs_parser_arrow_expression(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) -{ - njs_int_t ret; - njs_index_t index; - njs_parser_node_t *node, *body, *parent; - njs_function_lambda_t *lambda; + ptr = src; + cp = njs_number_hex_parse(&src, hex_end); - node = njs_parser_node_new(vm, parser, NJS_TOKEN_FUNCTION_EXPRESSION); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } + if (hex_length != 0) { + if (src != hex_end) { + goto invalid; + } - node->token_line = njs_parser_token_line(parser); - parser->node = node; + } else { + if (src == ptr || (src - ptr) > 6) { + goto invalid; + } - lambda = njs_function_lambda_alloc(vm, 0); - if (njs_slow_path(lambda == NULL)) { - return NJS_TOKEN_ERROR; - } + if (src == end || *src++ != '}') { + goto invalid; + } + } - node->u.value.data.u.lambda = lambda; + if (cp_pair != 0) { + if (njs_fast_path(njs_surrogate_trailing(cp))) { + cp = njs_string_surrogate_pair(cp_pair, cp); - ret = njs_parser_scope_begin(vm, parser, NJS_SCOPE_FUNCTION); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } + } else if (njs_slow_path(njs_surrogate_leading(cp))) { + cp = NJS_UTF8_REPLACEMENT; - parser->scope->arrow_function = 1; + size += njs_utf8_size(cp); + length++; - index = NJS_SCOPE_ARGUMENTS; + } else { + size += njs_utf8_size(NJS_UTF8_REPLACEMENT); + length++; + } - /* A "this" reservation. */ - index += sizeof(njs_value_t); + cp_pair = 0; - if (type == NJS_TOKEN_OPEN_PARENTHESIS) { - type = njs_parser_lambda_arguments(vm, parser, lambda, index, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } + } else if (njs_surrogate_any(cp)) { + if (cp <= 0xdbff && src[0] == '\\' && src[1] == 'u') { + cp_pair = cp; + continue; + } - } else { - type = njs_parser_lambda_argument(vm, parser, index); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + cp = NJS_UTF8_REPLACEMENT; } - lambda->nargs = 1; - } - - if (parser->lexer->prev_type == NJS_TOKEN_LINE_END) { - return NJS_TOKEN_ILLEGAL; - } - - type = njs_parser_match(vm, parser, type, NJS_TOKEN_ARROW); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; + size += njs_utf8_size(cp); + length++; } - if (type == NJS_TOKEN_OPEN_BRACE) { - type = njs_parser_lambda_body(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - } else { - parent = parser->node; - - type = njs_parser_assignment_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - body = njs_parser_return_set(vm, parser, parser->node); - if (njs_slow_path(body == NULL)) { - return NJS_TOKEN_ERROR; - } + *out_size = size; + *out_length = length; - parent->right = body; - parser->node = parent; - } + return NJS_OK; - njs_parser_scope_end(vm, parser); +invalid: - return type; + njs_parser_syntax_error(parser, "Invalid Unicode code point \"%V\"", + &token->text); + return NJS_ERROR; } @@ -2262,17 +8139,16 @@ njs_parser_has_side_effect(njs_parser_node_t *node) njs_token_type_t njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) + njs_str_t *name, njs_token_type_t type) { if (type != NJS_TOKEN_END) { - njs_parser_syntax_error(vm, parser, "Unexpected token \"%V\"", - njs_parser_text(parser)); + njs_parser_syntax_error(parser, "Unexpected token \"%V\"", name); } else { - njs_parser_syntax_error(vm, parser, "Unexpected end of input"); + njs_parser_syntax_error(parser, "Unexpected end of input"); } - return NJS_TOKEN_ILLEGAL; + return NJS_DONE; } @@ -2302,10 +8178,10 @@ njs_parser_trace_handler(njs_trace_t *trace, njs_trace_data_t *td, if (lexer->file.length != 0) { njs_internal_error(vm, "%s in %V:%uD", start, &lexer->file, - njs_parser_token_line(parser)); + parser->lexer->token->line); } else { njs_internal_error(vm, "%s in %uD", start, - njs_parser_token_line(parser)); + parser->lexer->token->line); } } else { @@ -2367,18 +8243,18 @@ njs_parser_scope_error(njs_vm_t *vm, njs_parser_scope_t *scope, void -njs_parser_lexer_error(njs_vm_t *vm, njs_parser_t *parser, - njs_object_type_t type, const char *fmt, ...) +njs_parser_lexer_error(njs_parser_t *parser, njs_object_type_t type, + const char *fmt, ...) { va_list args; - if (njs_is_error(&vm->retval)) { + if (njs_is_error(&parser->vm->retval)) { return; } va_start(args, fmt); - njs_parser_scope_error(vm, parser->scope, type, parser->lexer->line, fmt, - args); + njs_parser_scope_error(parser->vm, parser->scope, type, + parser->lexer->line, fmt, args); va_end(args); } diff --git a/src/njs_parser.h b/src/njs_parser.h index 541eeaec..e507feb5 100644 --- a/src/njs_parser.h +++ b/src/njs_parser.h @@ -1,6 +1,7 @@ /* * Copyright (C) Igor Sysoev + * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ @@ -69,14 +70,34 @@ struct njs_parser_node_s { }; +typedef njs_int_t (*njs_parser_state_func_t)(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + + struct njs_parser_s { + njs_parser_state_func_t state; + njs_queue_t stack; njs_lexer_t *lexer; + njs_vm_t *vm; njs_parser_node_t *node; + njs_parser_node_t *target; njs_parser_scope_t *scope; - njs_uint_t count; + njs_int_t ret; + njs_bool_t strict_semicolon; + uint32_t line; }; +typedef struct { + njs_parser_state_func_t state; + njs_queue_link_t link; + + njs_parser_node_t *node; + + njs_bool_t optional; +} njs_parser_stack_entry_t; + + typedef struct { NJS_RBTREE_NODE (node); uintptr_t key; @@ -84,58 +105,30 @@ typedef struct { } njs_parser_rbtree_node_t; +njs_int_t njs_parser_failed_state(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); + intptr_t njs_parser_scope_rbtree_compare(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2); -njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_t *prev); -njs_token_type_t njs_parser_expression(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type); -njs_token_type_t njs_parser_assignment_expression(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type); -njs_token_type_t njs_parser_function_expression(njs_vm_t *vm, - njs_parser_t *parser); -njs_int_t njs_parser_match_arrow_expression(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type); -njs_token_type_t njs_parser_arrow_expression(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type); -njs_token_type_t njs_parser_module_lambda(njs_vm_t *vm, njs_parser_t *parser); -njs_token_type_t njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type); -njs_token_type_t njs_parser_template_literal(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *parent); -njs_parser_node_t *njs_parser_argument(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *expr, njs_index_t index); -njs_int_t njs_parser_string_create(njs_vm_t *vm, njs_value_t *value); -njs_token_type_t njs_parser_function_lambda(njs_vm_t *vm, njs_parser_t *parser, - njs_function_lambda_t *lambda, njs_token_type_t type); -njs_token_type_t njs_parser_lambda_statements(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type); +njs_int_t njs_parser(njs_parser_t *parser, njs_parser_t *prev); + +njs_int_t njs_parser_module_lambda(njs_parser_t *parser, + njs_lexer_token_t *token, njs_queue_link_t *current); njs_variable_t *njs_variable_resolve(njs_vm_t *vm, njs_parser_node_t *node); -njs_index_t njs_variable_typeof(njs_vm_t *vm, njs_parser_node_t *node); njs_index_t njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node); njs_bool_t njs_parser_has_side_effect(njs_parser_node_t *node); njs_token_type_t njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type); + njs_str_t *name, njs_token_type_t type); +njs_int_t njs_parser_string_create(njs_vm_t *vm, njs_lexer_token_t *token, + njs_value_t *value); u_char *njs_parser_trace_handler(njs_trace_t *trace, njs_trace_data_t *td, u_char *start); -void njs_parser_lexer_error(njs_vm_t *vm, njs_parser_t *parser, +void njs_parser_lexer_error(njs_parser_t *parser, njs_object_type_t type, const char *fmt, ...); void njs_parser_node_error(njs_vm_t *vm, njs_parser_node_t *node, njs_object_type_t type, const char *fmt, ...); -#define njs_parser_enter(vm, parser) \ - do { \ - if (njs_slow_path((parser)->count++ > 4096)) { \ - njs_range_error(vm, "Maximum call stack size exceeded"); \ - return NJS_TOKEN_ERROR; \ - } \ - } while (0) - - -#define njs_parser_leave(parser) ((parser)->count--) - - #define njs_parser_restricted_identifier(token) \ (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) @@ -149,64 +142,22 @@ void njs_parser_node_error(njs_vm_t *vm, njs_parser_node_t *node, ((vm)->options.accumulative && (scope)->type == NJS_SCOPE_GLOBAL) -#define njs_parser_text(parser) \ - &(parser)->lexer->token->text - - -#define njs_parser_key_hash(parser) \ - (parser)->lexer->token->unique_id - - -#define njs_parser_number(parser) \ - (parser)->lexer->token->number - - -#define njs_parser_token_line(parser) \ - (parser)->lexer->token->line - - -#define njs_parser_syntax_error(vm, parser, fmt, ...) \ - njs_parser_lexer_error(vm, parser, NJS_OBJ_TYPE_SYNTAX_ERROR, fmt, \ +#define njs_parser_syntax_error(parser, fmt, ...) \ + njs_parser_lexer_error(parser, NJS_OBJ_TYPE_SYNTAX_ERROR, fmt, \ ##__VA_ARGS__) -#define njs_parser_ref_error(vm, parser, fmt, ...) \ - njs_parser_lexer_error(vm, parser, NJS_OBJ_TYPE_REF_ERROR, fmt, \ +#define njs_parser_ref_error(parser, fmt, ...) \ + njs_parser_lexer_error(parser, NJS_OBJ_TYPE_REF_ERROR, fmt, \ ##__VA_ARGS__) -njs_inline njs_token_type_t -njs_parser_token(njs_vm_t *vm, njs_parser_t *parser) -{ - njs_token_type_t type; - - do { - type = njs_lexer_token(vm, parser->lexer); - } while (njs_slow_path(type == NJS_TOKEN_LINE_END)); - - return type; -} - - -njs_inline njs_token_type_t -njs_parser_peek_token(njs_vm_t *vm, njs_parser_t *parser, size_t *offset) -{ - njs_token_type_t type; - - do { - type = njs_lexer_peek_token(vm, parser->lexer, (*offset)++); - } while (njs_slow_path(type == NJS_TOKEN_LINE_END)); - - return type; -} - - njs_inline njs_parser_node_t * -njs_parser_node_new(njs_vm_t *vm, njs_parser_t *parser, njs_token_type_t type) +njs_parser_node_new(njs_parser_t *parser, njs_token_type_t type) { njs_parser_node_t *node; - node = njs_mp_zalloc(vm->mem_pool, sizeof(njs_parser_node_t)); + node = njs_mp_zalloc(parser->vm->mem_pool, sizeof(njs_parser_node_t)); if (njs_fast_path(node != NULL)) { node->token_type = type; @@ -217,18 +168,26 @@ njs_parser_node_new(njs_vm_t *vm, njs_parser_t *parser, njs_token_type_t type) } +njs_inline void +njs_parser_node_free(njs_parser_t *parser, njs_parser_node_t *node) +{ + njs_mp_free(parser->vm->mem_pool, node); +} + + njs_inline njs_parser_node_t * -njs_parser_node_string(njs_vm_t *vm, njs_parser_t *parser) +njs_parser_node_string(njs_vm_t *vm, njs_lexer_token_t *token, + njs_parser_t *parser) { njs_int_t ret; njs_parser_node_t *node; - node = njs_parser_node_new(vm, parser, NJS_TOKEN_STRING); + node = njs_parser_node_new(parser, NJS_TOKEN_STRING); if (njs_slow_path(node == NULL)) { return NULL; } - ret = njs_parser_string_create(vm, &node->u.value); + ret = njs_parser_string_create(vm, token, &node->u.value); if (njs_slow_path(ret != NJS_OK)) { return NULL; } @@ -237,86 +196,157 @@ njs_parser_node_string(njs_vm_t *vm, njs_parser_t *parser) } -njs_inline njs_token_type_t -njs_parser_match(njs_vm_t *vm, njs_parser_t *parser, njs_token_type_t type, - njs_token_type_t match) +njs_inline njs_parser_scope_t * +njs_parser_global_scope(njs_vm_t *vm) { - if (njs_fast_path(type == match)) { - return njs_parser_token(vm, parser); + njs_parser_scope_t *scope; + + scope = vm->parser->scope; + + while (scope->type != NJS_SCOPE_GLOBAL) { + scope = scope->parent; } - return njs_parser_unexpected_token(vm, parser, type); + return scope; } -njs_inline njs_token_type_t -njs_parser_match_name(njs_vm_t *vm, njs_parser_t *parser, njs_token_type_t type, - const char *name) +njs_inline njs_parser_scope_t * +njs_function_scope(njs_parser_scope_t *scope, njs_bool_t any) { - size_t len; - njs_str_t *text; - - len = njs_strlen(name); - text = njs_parser_text(parser); + while (scope->type != NJS_SCOPE_GLOBAL) { + if (scope->type == NJS_SCOPE_FUNCTION + && (any || !scope->arrow_function)) + { + return scope; + } - if (njs_fast_path(type == NJS_TOKEN_NAME - && text->length == len - && memcmp(text->start, name, len) == 0)) - { - return njs_parser_token(vm, parser); + scope = scope->parent; } - return njs_parser_unexpected_token(vm, parser, type); + return NULL; } +#ifndef NJS_PARSER_DEBUG -njs_inline njs_variable_t * -njs_parser_variable_add(njs_vm_t *vm, njs_parser_t *parser, - njs_variable_type_t type) +njs_inline void +njs_parser_next(njs_parser_t *parser, njs_parser_state_func_t state) { - return njs_variable_add(vm, parser->scope, - njs_parser_key_hash(parser), type); + parser->state = state; } +#else -njs_inline njs_int_t -njs_parser_variable_reference(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *node, njs_reference_type_t type) +njs_inline int +njs_parser_height(njs_parser_t *parser, njs_queue_link_t *link) { - return njs_variable_reference(vm, parser->scope, node, - njs_parser_key_hash(parser), type); + int height; + njs_queue_link_t *lnk; + + height = 0; + + for (lnk = njs_queue_first(&parser->stack); + lnk != njs_queue_tail(&parser->stack); + lnk = njs_queue_next(lnk)) + { + if (link != lnk) { + height++; + continue; + } + + return height; + } + + return -1; } +#define njs_parser_next(parser, _state) \ + do { \ + const char *name = njs_stringify(_state); \ + if (memcmp(name, "entry->state", njs_min(njs_strlen(name), 12))) { \ + njs_printf("next(%s)\n", name + njs_length("njs_parser_")); \ + } \ + \ + parser->state = _state; \ + } while(0) -njs_inline njs_parser_scope_t * -njs_parser_global_scope(njs_vm_t *vm) +#endif + + +njs_inline njs_int_t +njs_parser_stack_pop(njs_parser_t *parser) { - njs_parser_scope_t *scope; + njs_parser_stack_entry_t *entry; - scope = vm->parser->scope; - while (scope->type != NJS_SCOPE_GLOBAL) { - scope = scope->parent; - } + entry = njs_queue_link_data(njs_queue_first(&parser->stack), + njs_parser_stack_entry_t, link); - return scope; + njs_queue_remove(njs_queue_first(&parser->stack)); + +#ifdef NJS_PARSER_DEBUG + njs_printf(" stack_pop(%d)\n", + njs_parser_height(parser, njs_queue_last(&parser->stack))); +#endif + + njs_parser_next(parser, entry->state); + + parser->target = entry->node; + + njs_mp_free(parser->vm->mem_pool, entry); + + return NJS_OK; } -njs_inline njs_parser_scope_t * -njs_function_scope(njs_parser_scope_t *scope, njs_bool_t any) +#ifndef NJS_PARSER_DEBUG + +#define njs_parser_after(_p, _l, _n, _opt, _state) \ + _njs_parser_after(_p, _l, _n, _opt, _state) + +#else + +#define njs_parser_after(__p, _l, _n, _opt, _state) \ + ( \ + njs_printf(" after(%s, link:%d, height:%d)\n", \ + &njs_stringify(_state)[njs_min(njs_strlen(_state), 11)], \ + njs_parser_height(__p, _l), \ + njs_parser_height(__p, njs_queue_last(&(__p)->stack))), \ + _njs_parser_after(__p, _l, _n, _opt, _state) \ + ) + +#endif + +njs_inline njs_int_t +_njs_parser_after(njs_parser_t *parser, njs_queue_link_t *link, void *node, + njs_bool_t is_optional, njs_parser_state_func_t state) { - while (scope->type != NJS_SCOPE_GLOBAL) { - if (scope->type == NJS_SCOPE_FUNCTION - && (any || !scope->arrow_function)) - { - return scope; - } + njs_parser_stack_entry_t *entry; - scope = scope->parent; + entry = njs_mp_alloc(parser->vm->mem_pool, + sizeof(njs_parser_stack_entry_t)); + if (njs_slow_path(entry == NULL)) { + return NJS_ERROR; } - return NULL; + entry->state = state; + entry->node = node; + entry->optional = is_optional; + + njs_queue_insert_before(link, &entry->link); + + return NJS_OK; +} + + +njs_inline njs_int_t +njs_parser_failed(njs_parser_t *parser) +{ + njs_parser_next(parser, njs_parser_failed_state); + + parser->target = NULL; + + return NJS_DECLINED; } diff --git a/src/njs_parser_expression.c b/src/njs_parser_expression.c deleted file mode 100644 index 349b809b..00000000 --- a/src/njs_parser_expression.c +++ /dev/null @@ -1,1121 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - * Copyright (C) NGINX, Inc. - */ - - -#include - - -typedef struct { - njs_token_type_t type; - njs_vmcode_operation_t operation; -} njs_parser_operation_t; - - -typedef struct njs_parser_expression_s njs_parser_expression_t; - -struct njs_parser_expression_s { - njs_token_type_t (*next)(njs_vm_t *, - njs_parser_t *, - const njs_parser_expression_t *, - njs_token_type_t); - const njs_parser_expression_t *expression; - njs_uint_t count; - -#if (NJS_SUNC) - /* - * SunC supports C99 flexible array members but does not allow - * static struct's initialization with arbitrary number of members. - */ - njs_parser_operation_t op[6]; -#else - njs_parser_operation_t op[]; -#endif -}; - - -static njs_token_type_t njs_parser_any_expression(njs_vm_t *vm, - njs_parser_t *parser, const njs_parser_expression_t *expr, - njs_token_type_t type); -static njs_token_type_t njs_parser_conditional_expression(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type); -static njs_token_type_t njs_parser_coalesce_expression(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type); -static njs_token_type_t njs_parser_binary_expression(njs_vm_t *vm, - njs_parser_t *parser, const njs_parser_expression_t *expr, - njs_token_type_t type); -static njs_token_type_t njs_parser_exponential_expression(njs_vm_t *vm, - njs_parser_t *parser, const njs_parser_expression_t *expr, - njs_token_type_t type); -static njs_token_type_t njs_parser_unary_expression(njs_vm_t *vm, - njs_parser_t *parser, const njs_parser_expression_t *expr, - njs_token_type_t type); -static njs_token_type_t njs_parser_inc_dec_expression(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type); -static njs_token_type_t njs_parser_post_inc_dec_expression(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type); -static njs_token_type_t njs_parser_call_expression(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type); -static njs_token_type_t njs_parser_new_expression(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type); -static njs_token_type_t njs_parser_property_expression(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type); -static njs_token_type_t njs_parser_property_brackets(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type); -static njs_token_type_t njs_parser_call(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type, uint8_t ctor); -static njs_token_type_t njs_parser_arguments(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *parent); - - -static const njs_parser_expression_t - njs_parser_factor_expression = -{ - njs_parser_exponential_expression, - NULL, - 3, { - { NJS_TOKEN_MULTIPLICATION, NJS_VMCODE_MULTIPLICATION }, - { NJS_TOKEN_DIVISION, NJS_VMCODE_DIVISION }, - { NJS_TOKEN_REMAINDER, NJS_VMCODE_REMAINDER }, - } -}; - - -static const njs_parser_expression_t - njs_parser_addition_expression = -{ - njs_parser_binary_expression, - &njs_parser_factor_expression, - 2, { - { NJS_TOKEN_ADDITION, NJS_VMCODE_ADDITION }, - { NJS_TOKEN_SUBSTRACTION, NJS_VMCODE_SUBSTRACTION }, - } -}; - - -static const njs_parser_expression_t - njs_parser_bitwise_shift_expression = -{ - njs_parser_binary_expression, - &njs_parser_addition_expression, - 3, { - { NJS_TOKEN_LEFT_SHIFT, NJS_VMCODE_LEFT_SHIFT }, - { NJS_TOKEN_RIGHT_SHIFT, NJS_VMCODE_RIGHT_SHIFT }, - { NJS_TOKEN_UNSIGNED_RIGHT_SHIFT, NJS_VMCODE_UNSIGNED_RIGHT_SHIFT }, - } -}; - - -static const njs_parser_expression_t - njs_parser_relational_expression = -{ - njs_parser_binary_expression, - &njs_parser_bitwise_shift_expression, - 6, { - { NJS_TOKEN_LESS, NJS_VMCODE_LESS }, - { NJS_TOKEN_LESS_OR_EQUAL, NJS_VMCODE_LESS_OR_EQUAL }, - { NJS_TOKEN_GREATER, NJS_VMCODE_GREATER }, - { NJS_TOKEN_GREATER_OR_EQUAL, NJS_VMCODE_GREATER_OR_EQUAL }, - { NJS_TOKEN_IN, NJS_VMCODE_PROPERTY_IN }, - { NJS_TOKEN_INSTANCEOF, NJS_VMCODE_INSTANCE_OF }, - } -}; - - -static const njs_parser_expression_t - njs_parser_equality_expression = -{ - njs_parser_binary_expression, - &njs_parser_relational_expression, - 4, { - { NJS_TOKEN_EQUAL, NJS_VMCODE_EQUAL }, - { NJS_TOKEN_NOT_EQUAL, NJS_VMCODE_NOT_EQUAL }, - { NJS_TOKEN_STRICT_EQUAL, NJS_VMCODE_STRICT_EQUAL }, - { NJS_TOKEN_STRICT_NOT_EQUAL, NJS_VMCODE_STRICT_NOT_EQUAL }, - } -}; - - -static const njs_parser_expression_t - njs_parser_bitwise_and_expression = -{ - njs_parser_binary_expression, - &njs_parser_equality_expression, - 1, { - { NJS_TOKEN_BITWISE_AND, NJS_VMCODE_BITWISE_AND }, - } -}; - - -static const njs_parser_expression_t - njs_parser_bitwise_xor_expression = -{ - njs_parser_binary_expression, - &njs_parser_bitwise_and_expression, - 1, { - { NJS_TOKEN_BITWISE_XOR, NJS_VMCODE_BITWISE_XOR }, - } -}; - - -static const njs_parser_expression_t - njs_parser_bitwise_or_expression = -{ - njs_parser_binary_expression, - &njs_parser_bitwise_xor_expression, - 1, { - { NJS_TOKEN_BITWISE_OR, NJS_VMCODE_BITWISE_OR }, - } -}; - - -static const njs_parser_expression_t - njs_parser_logical_and_expression = -{ - njs_parser_binary_expression, - &njs_parser_bitwise_or_expression, - 1, { - { NJS_TOKEN_LOGICAL_AND, NJS_VMCODE_TEST_IF_FALSE }, - } -}; - - -static const njs_parser_expression_t - njs_parser_logical_or_expression = -{ - njs_parser_binary_expression, - &njs_parser_logical_and_expression, - 1, { - { NJS_TOKEN_LOGICAL_OR, NJS_VMCODE_TEST_IF_TRUE }, - } -}; - - -static const njs_parser_expression_t - njs_parser_comma_expression = -{ - njs_parser_any_expression, - NULL, - 1, { - { NJS_TOKEN_COMMA, NJS_VMCODE_NOP }, - } -}; - - -njs_token_type_t -njs_parser_expression(njs_vm_t *vm, njs_parser_t *parser, njs_token_type_t type) -{ - return njs_parser_binary_expression(vm, parser, - &njs_parser_comma_expression, type); -} - - -static njs_token_type_t -njs_parser_any_expression(njs_vm_t *vm, njs_parser_t *parser, - const njs_parser_expression_t *expr, njs_token_type_t type) -{ - return njs_parser_assignment_expression(vm, parser, type); -} - - -njs_token_type_t -njs_parser_assignment_expression(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) -{ - njs_parser_node_t *node; - njs_vmcode_operation_t operation; - - njs_parser_enter(vm, parser); - - type = njs_parser_conditional_expression(vm, parser, type); - - njs_parser_leave(parser); - - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - for ( ;; ) { - switch (type) { - - case NJS_TOKEN_ASSIGNMENT: - njs_thread_log_debug("JS: ="); - operation = NJS_VMCODE_MOVE; - break; - - case NJS_TOKEN_ADDITION_ASSIGNMENT: - njs_thread_log_debug("JS: +="); - operation = NJS_VMCODE_ADDITION; - break; - - case NJS_TOKEN_SUBSTRACTION_ASSIGNMENT: - njs_thread_log_debug("JS: -="); - operation = NJS_VMCODE_SUBSTRACTION; - break; - - case NJS_TOKEN_MULTIPLICATION_ASSIGNMENT: - njs_thread_log_debug("JS: *="); - operation = NJS_VMCODE_MULTIPLICATION; - break; - - case NJS_TOKEN_EXPONENTIATION_ASSIGNMENT: - njs_thread_log_debug("JS: **="); - operation = NJS_VMCODE_EXPONENTIATION; - break; - - case NJS_TOKEN_DIVISION_ASSIGNMENT: - njs_thread_log_debug("JS: /="); - operation = NJS_VMCODE_DIVISION; - break; - - case NJS_TOKEN_REMAINDER_ASSIGNMENT: - njs_thread_log_debug("JS: %="); - operation = NJS_VMCODE_REMAINDER; - break; - - case NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT: - njs_thread_log_debug("JS: <<="); - operation = NJS_VMCODE_LEFT_SHIFT; - break; - - case NJS_TOKEN_RIGHT_SHIFT_ASSIGNMENT: - njs_thread_log_debug("JS: >>="); - operation = NJS_VMCODE_RIGHT_SHIFT; - break; - - case NJS_TOKEN_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: - njs_thread_log_debug("JS: >>="); - operation = NJS_VMCODE_UNSIGNED_RIGHT_SHIFT; - break; - - case NJS_TOKEN_BITWISE_AND_ASSIGNMENT: - njs_thread_log_debug("JS: &="); - operation = NJS_VMCODE_BITWISE_AND; - break; - - case NJS_TOKEN_BITWISE_XOR_ASSIGNMENT: - njs_thread_log_debug("JS: ^="); - operation = NJS_VMCODE_BITWISE_XOR; - break; - - case NJS_TOKEN_BITWISE_OR_ASSIGNMENT: - njs_thread_log_debug("JS: |="); - operation = NJS_VMCODE_BITWISE_OR; - break; - - default: - return type; - } - - if (!njs_parser_is_lvalue(parser->node)) { - type = parser->node->token_type; - - if (njs_parser_restricted_identifier(type)) { - njs_parser_syntax_error(vm, parser, "Identifier \"%s\" " - "is forbidden as left-hand in assignment", - (type == NJS_TOKEN_EVAL) ? "eval" - : "arguments"); - - } else { - njs_parser_ref_error(vm, parser, - "Invalid left-hand side in assignment"); - } - - return NJS_TOKEN_ILLEGAL; - } - - node = njs_parser_node_new(vm, parser, type); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - node->u.operation = operation; - node->left = parser->node; - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - njs_parser_enter(vm, parser); - - type = njs_parser_assignment_expression(vm, parser, type); - - njs_parser_leave(parser); - - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - node->right = parser->node; - parser->node = node; - } -} - - -njs_token_type_t -njs_parser_conditional_expression(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) -{ - njs_parser_node_t *node, *cond; - - type = njs_parser_coalesce_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - for ( ;; ) { - if (type != NJS_TOKEN_CONDITIONAL) { - return type; - } - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - cond = njs_parser_node_new(vm, parser, NJS_TOKEN_CONDITIONAL); - if (njs_slow_path(cond == NULL)) { - return NJS_TOKEN_ERROR; - } - - cond->left = parser->node; - - node = njs_parser_node_new(vm, parser, NJS_TOKEN_BRANCHING); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - cond->right = node; - - type = njs_parser_assignment_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - if (njs_slow_path(type != NJS_TOKEN_COLON)) { - return NJS_TOKEN_ILLEGAL; - } - - node->left = parser->node; - node->left->dest = cond; - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - type = njs_parser_assignment_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - node->right = parser->node; - node->right->dest = cond; - - parser->node = cond; - } -} - - -static njs_token_type_t -njs_parser_coalesce_expression(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) -{ - njs_token_type_t prev_token, next_token; - njs_parser_node_t *node; - - type = njs_parser_binary_expression(vm, parser, - &njs_parser_logical_or_expression, - type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - for ( ;; ) { - if (type != NJS_TOKEN_COALESCE) { - return type; - } - - prev_token = parser->lexer->prev_type; - - node = njs_parser_node_new(vm, parser, NJS_TOKEN_COALESCE); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - node->u.operation = NJS_VMCODE_COALESCE; - node->left = parser->node; - node->left->dest = node; - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - next_token = type; - - type = njs_parser_binary_expression(vm, parser, - &njs_parser_logical_or_expression, - type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - node->right = parser->node; - node->right->dest = node; - parser->node = node; - - if (prev_token != NJS_TOKEN_CLOSE_PARENTHESIS - && njs_slow_path(node->left->token_type == NJS_TOKEN_LOGICAL_OR - || node->left->token_type == NJS_TOKEN_LOGICAL_AND)) - { - type = node->left->token_type; - goto require_parentheses; - } - - if (next_token != NJS_TOKEN_OPEN_PARENTHESIS - && njs_slow_path(node->right->token_type == NJS_TOKEN_LOGICAL_OR - || node->right->token_type == NJS_TOKEN_LOGICAL_AND)) - { - type = node->right->token_type; - goto require_parentheses; - } - } - -require_parentheses: - - njs_parser_syntax_error(vm, parser, "Either \"??\" or \"%s\" expression " - "must be parenthesized", - (type == NJS_TOKEN_LOGICAL_OR) ? "||" - : "&&"); - return NJS_TOKEN_ILLEGAL; -} - - -static njs_token_type_t -njs_parser_binary_expression(njs_vm_t *vm, njs_parser_t *parser, - const njs_parser_expression_t *expr, njs_token_type_t type) -{ - njs_int_t n; - njs_parser_node_t *node; - const njs_parser_operation_t *op; - - type = expr->next(vm, parser, expr->expression, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - for ( ;; ) { - n = expr->count; - op = expr->op; - - do { - if (op->type == type) { - goto found; - } - - op++; - n--; - - } while (n != 0); - - return type; - - found: - - node = njs_parser_node_new(vm, parser, type); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - node->u.operation = op->operation; - node->left = parser->node; - node->left->dest = node; - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - type = expr->next(vm, parser, expr->expression, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - node->right = parser->node; - node->right->dest = node; - parser->node = node; - } -} - - -static njs_token_type_t -njs_parser_exponential_expression(njs_vm_t *vm, njs_parser_t *parser, - const njs_parser_expression_t *expr, njs_token_type_t type) -{ - njs_parser_node_t *node; - - type = njs_parser_unary_expression(vm, parser, NULL, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - if (type == NJS_TOKEN_EXPONENTIATION) { - - node = njs_parser_node_new(vm, parser, type); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - node->u.operation = NJS_VMCODE_EXPONENTIATION; - node->left = parser->node; - node->left->dest = node; - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - njs_parser_enter(vm, parser); - - type = njs_parser_exponential_expression(vm, parser, NULL, type); - - njs_parser_leave(parser); - - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - node->right = parser->node; - node->right->dest = node; - parser->node = node; - } - - return type; -} - - -static njs_token_type_t -njs_parser_unary_expression(njs_vm_t *vm, njs_parser_t *parser, - const njs_parser_expression_t *expr, njs_token_type_t type) -{ - double num; - njs_token_type_t next; - njs_parser_node_t *node; - njs_vmcode_operation_t operation; - - switch (type) { - - case NJS_TOKEN_ADDITION: - type = NJS_TOKEN_UNARY_PLUS; - operation = NJS_VMCODE_UNARY_PLUS; - break; - - case NJS_TOKEN_SUBSTRACTION: - type = NJS_TOKEN_UNARY_NEGATION; - operation = NJS_VMCODE_UNARY_NEGATION; - break; - - case NJS_TOKEN_LOGICAL_NOT: - operation = NJS_VMCODE_LOGICAL_NOT; - break; - - case NJS_TOKEN_BITWISE_NOT: - operation = NJS_VMCODE_BITWISE_NOT; - break; - - case NJS_TOKEN_TYPEOF: - operation = NJS_VMCODE_TYPEOF; - break; - - case NJS_TOKEN_VOID: - operation = NJS_VMCODE_VOID; - break; - - case NJS_TOKEN_DELETE: - operation = NJS_VMCODE_DELETE; - break; - - default: - return njs_parser_inc_dec_expression(vm, parser, type); - } - - next = njs_parser_token(vm, parser); - if (njs_slow_path(next <= NJS_TOKEN_ILLEGAL)) { - return next; - } - - njs_parser_enter(vm, parser); - - next = njs_parser_unary_expression(vm, parser, NULL, next); - - njs_parser_leave(parser); - - if (njs_slow_path(next <= NJS_TOKEN_ILLEGAL)) { - return next; - } - - if (next == NJS_TOKEN_EXPONENTIATION) { - njs_parser_syntax_error(vm, parser, "Either left-hand side or entire " - "exponentiation must be parenthesized"); - - return NJS_TOKEN_ILLEGAL; - } - - node = parser->node; - - if (type == NJS_TOKEN_UNARY_PLUS && node->token_type == NJS_TOKEN_NUMBER) { - /* Skip the unary plus of number. */ - return next; - } - - if (type == NJS_TOKEN_UNARY_NEGATION - && node->token_type == NJS_TOKEN_NUMBER) - { - /* Optimization of common negative number. */ - num = -njs_number(&node->u.value); - njs_set_number(&node->u.value, num); - - return next; - } - - if (type == NJS_TOKEN_DELETE) { - - switch (node->token_type) { - - case NJS_TOKEN_PROPERTY: - node->token_type = NJS_TOKEN_PROPERTY_DELETE; - node->u.operation = NJS_VMCODE_PROPERTY_DELETE; - - return next; - - case NJS_TOKEN_NAME: - njs_parser_syntax_error(vm, parser, - "Delete of an unqualified identifier"); - - return NJS_TOKEN_ILLEGAL; - - default: - break; - } - } - - if (type == NJS_TOKEN_TYPEOF && node->token_type == NJS_TOKEN_NAME) { - node->u.reference.type = NJS_TYPEOF; - } - - node = njs_parser_node_new(vm, parser, type); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - node->u.operation = operation; - node->left = parser->node; - node->left->dest = node; - parser->node = node; - - return next; -} - - -static njs_token_type_t -njs_parser_inc_dec_expression(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) -{ - njs_token_type_t next; - njs_parser_node_t *node; - njs_vmcode_operation_t operation; - - switch (type) { - - case NJS_TOKEN_INCREMENT: - operation = NJS_VMCODE_INCREMENT; - break; - - case NJS_TOKEN_DECREMENT: - operation = NJS_VMCODE_DECREMENT; - break; - - default: - return njs_parser_post_inc_dec_expression(vm, parser, type); - } - - next = njs_parser_token(vm, parser); - if (njs_slow_path(next <= NJS_TOKEN_ILLEGAL)) { - return next; - } - - next = njs_parser_call_expression(vm, parser, next); - if (njs_slow_path(next <= NJS_TOKEN_ILLEGAL)) { - return next; - } - - if (!njs_parser_is_lvalue(parser->node)) { - njs_parser_ref_error(vm, parser, - "Invalid left-hand side in prefix operation"); - return NJS_TOKEN_ILLEGAL; - } - - node = njs_parser_node_new(vm, parser, type); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - node->u.operation = operation; - node->left = parser->node; - parser->node = node; - - return next; -} - - -static njs_token_type_t -njs_parser_post_inc_dec_expression(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) -{ - njs_int_t ret; - njs_parser_node_t *node; - njs_vmcode_operation_t operation; - - type = njs_parser_call_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - switch (type) { - - case NJS_TOKEN_INCREMENT: - type = NJS_TOKEN_POST_INCREMENT; - operation = NJS_VMCODE_POST_INCREMENT; - break; - - case NJS_TOKEN_DECREMENT: - type = NJS_TOKEN_POST_DECREMENT; - operation = NJS_VMCODE_POST_DECREMENT; - break; - - default: - return type; - } - - /* Automatic semicolon insertion. */ - - if (parser->lexer->prev_type == NJS_TOKEN_LINE_END) { - ret = njs_lexer_rollback(vm, parser->lexer); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } - - return NJS_TOKEN_SEMICOLON; - } - - if (!njs_parser_is_lvalue(parser->node)) { - njs_parser_ref_error(vm, parser, - "Invalid left-hand side in postfix operation"); - return NJS_TOKEN_ILLEGAL; - } - - node = njs_parser_node_new(vm, parser, type); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - node->u.operation = operation; - node->left = parser->node; - parser->node = node; - - return njs_parser_token(vm, parser); -} - - -static njs_token_type_t -njs_parser_call_expression(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) -{ - njs_parser_enter(vm, parser); - - if (type == NJS_TOKEN_NEW) { - type = njs_parser_new_expression(vm, parser, type); - - } else { - type = njs_parser_terminal(vm, parser, type); - } - - njs_parser_leave(parser); - - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - for ( ;; ) { - type = njs_parser_property_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - if (type != NJS_TOKEN_OPEN_PARENTHESIS && type != NJS_TOKEN_GRAVE) { - return type; - } - - njs_parser_enter(vm, parser); - - type = njs_parser_call(vm, parser, type, 0); - - njs_parser_leave(parser); - - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - } -} - - -static njs_token_type_t -njs_parser_call(njs_vm_t *vm, njs_parser_t *parser, njs_token_type_t type, - uint8_t ctor) -{ - njs_parser_node_t *func, *node; - - node = parser->node; - - switch (node->token_type) { - - case NJS_TOKEN_NAME: - func = node; - func->token_type = NJS_TOKEN_FUNCTION_CALL; - - break; - - case NJS_TOKEN_PROPERTY: - func = njs_parser_node_new(vm, parser, NJS_TOKEN_METHOD_CALL); - if (njs_slow_path(func == NULL)) { - return NJS_TOKEN_ERROR; - } - - func->left = node; - - break; - - default: - /* - * NJS_TOKEN_METHOD_CALL, - * NJS_TOKEN_FUNCTION_CALL, - * NJS_TOKEN_FUNCTION_EXPRESSION, - * NJS_TOKEN_OPEN_PARENTHESIS, - * NJS_TOKEN_EVAL. - */ - func = njs_parser_node_new(vm, parser, NJS_TOKEN_FUNCTION_CALL); - if (njs_slow_path(func == NULL)) { - return NJS_TOKEN_ERROR; - } - - func->left = node; - - break; - } - - func->ctor = ctor; - - switch (type) { - - case NJS_TOKEN_OPEN_PARENTHESIS: - type = njs_parser_arguments(vm, parser, func); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - break; - - case NJS_TOKEN_GRAVE: - type = njs_parser_template_literal(vm, parser, func); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - break; - - default: - break; - } - - parser->node = func; - - return type; -} - - -static njs_token_type_t -njs_parser_new_expression(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) -{ - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - njs_parser_enter(vm, parser); - - if (type == NJS_TOKEN_NEW) { - type = njs_parser_new_expression(vm, parser, type); - - } else { - type = njs_parser_terminal(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - njs_parser_leave(parser); - return type; - } - - type = njs_parser_property_expression(vm, parser, type); - } - - njs_parser_leave(parser); - - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - return njs_parser_call(vm, parser, type, 1); -} - - -static njs_token_type_t -njs_parser_property_expression(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) -{ - njs_parser_node_t *node, *prop_node; - - for ( ;; ) { - if (type != NJS_TOKEN_DOT - && type != NJS_TOKEN_OPEN_BRACKET) - { - return type; - } - - node = njs_parser_node_new(vm, parser, NJS_TOKEN_PROPERTY); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - node->u.operation = NJS_VMCODE_PROPERTY_GET; - node->left = parser->node; - - if (type == NJS_TOKEN_DOT) { - type = njs_parser_token(vm, parser); - - if (type != NJS_TOKEN_NAME && !parser->lexer->keyword) { - return NJS_TOKEN_ILLEGAL; - } - - prop_node = njs_parser_node_string(vm, parser); - if (njs_slow_path(prop_node == NULL)) { - return NJS_TOKEN_ERROR; - } - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - } else { - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - type = njs_parser_property_brackets(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - prop_node = parser->node; - } - - node->right = prop_node; - parser->node = node; - } -} - - -static njs_token_type_t -njs_parser_property_brackets(njs_vm_t *vm, njs_parser_t *parser, - njs_token_type_t type) -{ - type = njs_parser_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - if (njs_slow_path(type != NJS_TOKEN_CLOSE_BRACKET)) { - return NJS_TOKEN_ERROR; - } - - return njs_parser_token(vm, parser); -} - - -static njs_token_type_t -njs_parser_arguments(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *parent) -{ - njs_index_t index; - njs_token_type_t type; - njs_parser_node_t *node; - - index = NJS_SCOPE_CALLEE_ARGUMENTS; - - do { - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - if (type == NJS_TOKEN_CLOSE_PARENTHESIS) { - break; - } - - type = njs_parser_assignment_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - node = njs_parser_argument(vm, parser, parser->node, index); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - parent->right = node; - parent = node; - - index += sizeof(njs_value_t); - - } while (type == NJS_TOKEN_COMMA); - - if (njs_slow_path(type != NJS_TOKEN_CLOSE_PARENTHESIS)) { - return NJS_TOKEN_ILLEGAL; - } - - return njs_parser_token(vm, parser); -} - - -njs_parser_node_t * -njs_parser_argument(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *expr, njs_index_t index) -{ - njs_parser_node_t *node; - - node = njs_parser_node_new(vm, parser, NJS_TOKEN_ARGUMENT); - if (njs_slow_path(node == NULL)) { - return NULL; - } - - node->index = index; - - node->left = expr; - expr->dest = node; - - return node; -} diff --git a/src/njs_parser_terminal.c b/src/njs_parser_terminal.c deleted file mode 100644 index 47e64b5e..00000000 --- a/src/njs_parser_terminal.c +++ /dev/null @@ -1,1296 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - * Copyright (C) NGINX, Inc. - */ - - -#include - - -static njs_parser_node_t *njs_parser_reference(njs_vm_t *vm, - njs_parser_t *parser, njs_token_type_t type, njs_str_t *name, - uintptr_t hash, uint32_t token_line); -static njs_token_type_t njs_parser_object(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *obj); -static njs_int_t njs_parser_object_property(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *parent, njs_parser_node_t *property, - njs_parser_node_t *value, njs_bool_t proto_init); -static njs_int_t njs_parser_property_accessor(njs_vm_t *vm, - njs_parser_t *parser, njs_parser_node_t *parent, - njs_parser_node_t *property, njs_parser_node_t *value, - njs_token_type_t accessor); -static njs_token_type_t njs_parser_array(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *array); -static njs_int_t njs_parser_array_item(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *array, njs_parser_node_t *value); -static njs_int_t njs_parser_template_expression(njs_vm_t *vm, - njs_parser_t *parser); -static njs_int_t njs_parser_template_string(njs_vm_t *vm, - njs_parser_t *parser); -static njs_int_t njs_parser_escape_string_calc_length(njs_vm_t *vm, - njs_parser_t *parser, size_t *out_size, size_t *out_length); -static njs_token_type_t njs_parser_escape_string_create(njs_vm_t *vm, - njs_parser_t *parser, njs_value_t *value); - - -njs_token_type_t -njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_type_t type) -{ - double num; - njs_int_t ret; - njs_parser_node_t *node; - - ret = njs_parser_match_arrow_expression(vm, parser, type); - if (ret == NJS_OK) { - return njs_parser_arrow_expression(vm, parser, type); - } - - if (type == NJS_TOKEN_OPEN_PARENTHESIS) { - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - type = njs_parser_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - return njs_parser_match(vm, parser, type, NJS_TOKEN_CLOSE_PARENTHESIS); - } - - if (type == NJS_TOKEN_FUNCTION) { - return njs_parser_function_expression(vm, parser); - } - - switch (type) { - - case NJS_TOKEN_OPEN_BRACE: - njs_thread_log_debug("JS: OBJECT"); - - node = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - return njs_parser_object(vm, parser, node); - - case NJS_TOKEN_OPEN_BRACKET: - njs_thread_log_debug("JS: ARRAY"); - - node = njs_parser_node_new(vm, parser, NJS_TOKEN_ARRAY); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - return njs_parser_array(vm, parser, node); - - case NJS_TOKEN_GRAVE: - njs_thread_log_debug("JS: TEMPLATE LITERAL"); - - node = njs_parser_node_new(vm, parser, NJS_TOKEN_TEMPLATE_LITERAL); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - return njs_parser_template_literal(vm, parser, node); - - case NJS_TOKEN_DIVISION: - node = njs_parser_node_new(vm, parser, NJS_TOKEN_REGEXP); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - type = njs_regexp_literal(vm, parser, &node->u.value); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - njs_thread_log_debug("REGEX: '%V'", njs_parser_text(parser)); - - break; - - case NJS_TOKEN_STRING: - njs_thread_log_debug("JS: '%V'", njs_parser_text(parser)); - - node = njs_parser_node_new(vm, parser, NJS_TOKEN_STRING); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - ret = njs_parser_string_create(vm, &node->u.value); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } - - break; - - case NJS_TOKEN_ESCAPE_STRING: - njs_thread_log_debug("JS: '%V'", njs_parser_text(parser)); - - node = njs_parser_node_new(vm, parser, NJS_TOKEN_STRING); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - ret = njs_parser_escape_string_create(vm, parser, &node->u.value); - if (njs_slow_path(ret != NJS_TOKEN_STRING)) { - return ret; - } - - break; - - case NJS_TOKEN_UNTERMINATED_STRING: - njs_parser_syntax_error(vm, parser, "Unterminated string \"%V\"", - njs_parser_text(parser)); - - return NJS_TOKEN_ILLEGAL; - - case NJS_TOKEN_NUMBER: - num = njs_parser_number(parser); - njs_thread_log_debug("JS: %f", num); - - node = njs_parser_node_new(vm, parser, NJS_TOKEN_NUMBER); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - njs_set_number(&node->u.value, num); - - break; - - case NJS_TOKEN_TRUE: - case NJS_TOKEN_FALSE: - njs_thread_log_debug("JS: boolean: %V", njs_parser_text(parser)); - - node = njs_parser_node_new(vm, parser, parser->lexer->token->type); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - if (parser->lexer->token->type == NJS_TOKEN_FALSE) { - node->u.value = njs_value_false; - - } else { - node->u.value = njs_value_true; - } - - break; - - default: - node = njs_parser_reference(vm, parser, type, njs_parser_text(parser), - njs_parser_key_hash(parser), - njs_parser_token_line(parser)); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - break; - } - - parser->node = node; - - return njs_parser_token(vm, parser); -} - - -static njs_parser_node_t * -njs_parser_reference(njs_vm_t *vm, njs_parser_t *parser, njs_token_type_t type, - njs_str_t *name, uintptr_t unique_id, uint32_t token_line) -{ - njs_int_t ret; - njs_variable_t *var; - njs_parser_node_t *node; - njs_parser_scope_t *scope; - - node = njs_parser_node_new(vm, parser, type); - if (njs_slow_path(node == NULL)) { - return NULL; - } - - switch (type) { - - case NJS_TOKEN_NULL: - njs_thread_log_debug("JS: null"); - - node->u.value = njs_value_null; - break; - - case NJS_TOKEN_THIS: - njs_thread_log_debug("JS: this"); - - scope = njs_function_scope(parser->scope, 0); - - if (scope != NULL) { - if (scope == njs_function_scope(parser->scope, 1)) { - node->index = NJS_INDEX_THIS; - - } else { - node->token_type = NJS_TOKEN_NON_LOCAL_THIS; - - node->token_line = token_line; - - ret = njs_variable_reference(vm, scope, node, unique_id, - NJS_REFERENCE); - if (njs_slow_path(ret != NJS_OK)) { - return NULL; - } - - var = njs_variable_add(vm, scope, unique_id, NJS_VARIABLE_VAR); - if (njs_slow_path(var == NULL)) { - return NULL; - } - - var->this_object = 1; - } - - break; - } - - node->token_type = NJS_TOKEN_GLOBAL_OBJECT; - - break; - - case NJS_TOKEN_ARGUMENTS: - njs_thread_log_debug("JS: arguments"); - - scope = njs_function_scope(parser->scope, 0); - - if (scope == NULL) { - njs_parser_syntax_error(vm, parser, "\"%V\" object " - "in global scope", name); - - return NULL; - } - - node->token_line = token_line; - - ret = njs_variable_reference(vm, scope, node, unique_id, NJS_REFERENCE); - if (njs_slow_path(ret != NJS_OK)) { - return NULL; - } - - var = njs_variable_add(vm, scope, unique_id, NJS_VARIABLE_VAR); - if (njs_slow_path(var == NULL)) { - return NULL; - } - - var->arguments_object = 1; - - break; - - case NJS_TOKEN_NAME: - case NJS_TOKEN_EVAL: - njs_thread_log_debug("JS: %V", name); - - node->token_line = token_line; - - ret = njs_variable_reference(vm, parser->scope, node, unique_id, - NJS_REFERENCE); - if (njs_slow_path(ret != NJS_OK)) { - return NULL; - } - - break; - - default: - (void) njs_parser_unexpected_token(vm, parser, type); - return NULL; - } - - return node; -} - - -static njs_token_type_t -njs_parser_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj) -{ - uintptr_t unique_id; - uint32_t token_line; - njs_int_t ret, __proto__; - njs_str_t name; - njs_bool_t computed, proto_init; - njs_token_type_t type, accessor; - njs_lexer_t *lexer; - njs_parser_node_t *object, *property, *expression; - njs_function_lambda_t *lambda; - - const njs_str_t proto_string = njs_str("__proto__"); - - lexer = parser->lexer; - - /* GCC and Clang complain about uninitialized values. */ - unique_id = 0; - token_line = 0; - __proto__ = 0; - property = NULL; - - object = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT_VALUE); - if (njs_slow_path(object == NULL)) { - return NJS_TOKEN_ERROR; - } - - object->u.object = obj; - - for ( ;; ) { - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - accessor = 0; - computed = 0; - proto_init = 0; - njs_memzero(&name, sizeof(njs_str_t)); - - if (type == NJS_TOKEN_NAME || lexer->keyword) { - name = *njs_parser_text(parser); - unique_id = njs_parser_key_hash(parser); - token_line = njs_parser_token_line(parser); - - property = njs_parser_node_string(vm, parser); - if (njs_slow_path(property == NULL)) { - return NJS_TOKEN_ERROR; - } - - if (type == NJS_TOKEN_NAME && name.length == 3 - && (memcmp(name.start, "get", 3) == 0 - || memcmp(name.start, "set", 3) == 0)) - { - accessor = (name.start[0] == 'g') ? NJS_TOKEN_PROPERTY_GETTER - : NJS_TOKEN_PROPERTY_SETTER; - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - } - } - - switch (type) { - - case NJS_TOKEN_CLOSE_BRACE: - if (accessor) { - accessor = 0; - break; - } - - goto done; - - case NJS_TOKEN_OPEN_BRACKET: - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - if (type == NJS_TOKEN_CLOSE_BRACKET) { - return NJS_TOKEN_ILLEGAL; - } - - type = njs_parser_assignment_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - property = parser->node; - - type = njs_parser_match(vm, parser, type, NJS_TOKEN_CLOSE_BRACKET); - - computed = 1; - - break; - - case NJS_TOKEN_NUMBER: - case NJS_TOKEN_STRING: - case NJS_TOKEN_ESCAPE_STRING: - type = njs_parser_terminal(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - property = parser->node; - break; - - default: - if (type != NJS_TOKEN_NAME && !lexer->keyword) { - if (name.length == 0) { - return NJS_TOKEN_ILLEGAL; - } - - accessor = 0; - break; - } - - if (accessor) { - property = njs_parser_node_string(vm, parser); - if (njs_slow_path(property == NULL)) { - return NJS_TOKEN_ERROR; - } - } - - type = njs_parser_token(vm, parser); - break; - } - - if (accessor) { - expression = njs_parser_node_new(vm, parser, - NJS_TOKEN_FUNCTION_EXPRESSION); - if (njs_slow_path(expression == NULL)) { - return NJS_TOKEN_ERROR; - } - - expression->token_line = njs_parser_token_line(parser); - parser->node = expression; - - lambda = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_lambda_t)); - if (njs_slow_path(lambda == NULL)) { - return NJS_TOKEN_ERROR; - } - - expression->u.value.data.u.lambda = lambda; - - type = njs_parser_function_lambda(vm, parser, lambda, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - if (accessor == NJS_TOKEN_PROPERTY_GETTER) { - if (lambda->nargs != 0) { - njs_parser_syntax_error(vm, parser, - "Getter must not have any formal parameters"); - return NJS_TOKEN_ILLEGAL; - } - - } else { - if (lambda->nargs != 1) { - njs_parser_syntax_error(vm, parser, - "Setter must have exactly one formal parameter"); - return NJS_TOKEN_ILLEGAL; - } - } - - ret = njs_parser_property_accessor(vm, parser, obj, property, - expression, accessor); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } - - } else { - switch (type) { - - case NJS_TOKEN_COMMA: - case NJS_TOKEN_CLOSE_BRACE: - - if (name.length == 0 - || lexer->prev_type == NJS_TOKEN_THIS - || lexer->prev_type == NJS_TOKEN_GLOBAL_OBJECT) - { - return NJS_TOKEN_ILLEGAL; - } - - expression = njs_parser_reference(vm, parser, lexer->prev_type, - &name, unique_id, token_line); - if (njs_slow_path(expression == NULL)) { - return NJS_TOKEN_ERROR; - } - - break; - - case NJS_TOKEN_COLON: - if (!computed) { - if (njs_is_string(&property->u.value)) { - njs_string_get(&property->u.value, &name); - } - - if (njs_slow_path(njs_strstr_eq(&name, &proto_string))) { - if (++__proto__ > 1) { - njs_parser_syntax_error(vm, parser, - "Duplicate __proto__ fields are not allowed " - "in object literals"); - return NJS_TOKEN_ILLEGAL; - } - - proto_init = 1; - } - } - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - type = njs_parser_assignment_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - expression = parser->node; - break; - - case NJS_TOKEN_OPEN_PARENTHESIS: - expression = njs_parser_node_new(vm, parser, - NJS_TOKEN_FUNCTION_EXPRESSION); - if (njs_slow_path(expression == NULL)) { - return NJS_TOKEN_ERROR; - } - - expression->token_line = njs_parser_token_line(parser); - parser->node = expression; - - lambda = njs_function_lambda_alloc(vm, 0); - if (njs_slow_path(lambda == NULL)) { - return NJS_TOKEN_ERROR; - } - - expression->u.value.data.u.lambda = lambda; - - type = njs_parser_function_lambda(vm, parser, lambda, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - break; - - default: - return NJS_TOKEN_ILLEGAL; - } - - ret = njs_parser_object_property(vm, parser, obj, property, - expression, proto_init); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } - } - - if (type == NJS_TOKEN_CLOSE_BRACE) { - break; - } - - if (njs_slow_path(type != NJS_TOKEN_COMMA)) { - return NJS_TOKEN_ILLEGAL; - } - } - -done: - - parser->node = obj; - - return njs_parser_token(vm, parser); -} - - -static njs_int_t -njs_parser_object_property(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *parent, njs_parser_node_t *property, - njs_parser_node_t *value, njs_bool_t proto_init) -{ - njs_token_type_t type; - njs_parser_node_t *stmt, *assign, *object, *propref; - - object = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT_VALUE); - if (njs_slow_path(object == NULL)) { - return NJS_TOKEN_ERROR; - } - - object->u.object = parent; - - type = proto_init ? NJS_TOKEN_PROTO_INIT : NJS_TOKEN_PROPERTY_INIT; - - propref = njs_parser_node_new(vm, parser, type); - if (njs_slow_path(propref == NULL)) { - return NJS_ERROR; - } - - propref->left = object; - propref->right = property; - - assign = njs_parser_node_new(vm, parser, NJS_TOKEN_ASSIGNMENT); - if (njs_slow_path(assign == NULL)) { - return NJS_ERROR; - } - - assign->u.operation = NJS_VMCODE_MOVE; - assign->left = propref; - assign->right = value; - - stmt = njs_parser_node_new(vm, parser, NJS_TOKEN_STATEMENT); - if (njs_slow_path(stmt == NULL)) { - return NJS_ERROR; - } - - stmt->right = assign; - stmt->left = parent->left; - parent->left = stmt; - - return NJS_OK; -} - - -static njs_int_t -njs_parser_property_accessor(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *parent, njs_parser_node_t *property, - njs_parser_node_t *value, njs_token_type_t accessor) -{ - njs_parser_node_t *node, *stmt, *object, *propref; - - object = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT_VALUE); - if (njs_slow_path(object == NULL)) { - return NJS_TOKEN_ERROR; - } - - object->u.object = parent; - - propref = njs_parser_node_new(vm, parser, 0); - if (njs_slow_path(propref == NULL)) { - return NJS_ERROR; - } - - propref->left = object; - propref->right = property; - - node = njs_parser_node_new(vm, parser, accessor); - if (njs_slow_path(node == NULL)) { - return NJS_ERROR; - } - - node->left = propref; - node->right = value; - - stmt = njs_parser_node_new(vm, parser, NJS_TOKEN_STATEMENT); - if (njs_slow_path(stmt == NULL)) { - return NJS_ERROR; - } - - stmt->right = node; - stmt->left = parent->left; - parent->left = stmt; - - return NJS_OK; -} - - -static njs_token_type_t -njs_parser_array(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *array) -{ - njs_int_t ret; - njs_token_type_t type; - - for ( ;; ) { - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - if (type == NJS_TOKEN_CLOSE_BRACKET) { - break; - } - - if (type == NJS_TOKEN_COMMA) { - array->ctor = 1; - array->u.length++; - continue; - } - - type = njs_parser_assignment_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return type; - } - - ret = njs_parser_array_item(vm, parser, array, parser->node); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } - - if (type == NJS_TOKEN_CLOSE_BRACKET) { - break; - } - - if (njs_slow_path(type != NJS_TOKEN_COMMA)) { - return NJS_TOKEN_ILLEGAL; - } - } - - parser->node = array; - - return njs_parser_token(vm, parser); -} - - -static njs_int_t -njs_parser_array_item(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *array, njs_parser_node_t *value) -{ - njs_int_t ret; - njs_parser_node_t *number; - - number = njs_parser_node_new(vm, parser, NJS_TOKEN_NUMBER); - if (njs_slow_path(number == NULL)) { - return NJS_ERROR; - } - - njs_set_number(&number->u.value, array->u.length); - - ret = njs_parser_object_property(vm, parser, array, number, value, 0); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; - } - - array->ctor = 0; - array->u.length++; - - return NJS_OK; -} - - -njs_token_type_t -njs_parser_template_literal(njs_vm_t *vm, njs_parser_t *parser, - njs_parser_node_t *parent) -{ - uint8_t tagged_template; - njs_int_t ret; - njs_bool_t expression; - njs_index_t index; - njs_parser_node_t *node, *array; - - tagged_template = (parent->token_type != NJS_TOKEN_TEMPLATE_LITERAL); - - index = NJS_SCOPE_CALLEE_ARGUMENTS; - - array = njs_parser_node_new(vm, parser, NJS_TOKEN_ARRAY); - if (njs_slow_path(array == NULL)) { - return NJS_TOKEN_ERROR; - } - - if (tagged_template) { - node = njs_parser_argument(vm, parser, array, index); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - parent->right = node; - parent = node; - - index += sizeof(njs_value_t); - - } else { - parent->left = array; - } - - expression = 0; - - for ( ;; ) { - ret = expression ? njs_parser_template_expression(vm, parser) - : njs_parser_template_string(vm, parser); - - if (ret == NJS_ERROR) { - njs_parser_syntax_error(vm, parser, - "Unterminated template literal"); - return NJS_TOKEN_ILLEGAL; - } - - node = parser->node; - - if (ret == NJS_DONE) { - ret = njs_parser_array_item(vm, parser, array, node); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } - - parser->node = parent; - - return njs_parser_token(vm, parser); - } - - /* NJS_OK */ - - if (tagged_template && expression) { - node = njs_parser_argument(vm, parser, node, index); - if (njs_slow_path(node == NULL)) { - return NJS_TOKEN_ERROR; - } - - parent->right = node; - parent = node; - - index += sizeof(njs_value_t); - - } else { - ret = njs_parser_array_item(vm, parser, array, node); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ERROR; - } - } - - expression = !expression; - } -} - - -static njs_int_t -njs_parser_template_expression(njs_vm_t *vm, njs_parser_t *parser) -{ - njs_token_type_t type; - - type = njs_parser_token(vm, parser); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return NJS_ERROR; - } - - type = njs_parser_expression(vm, parser, type); - if (njs_slow_path(type <= NJS_TOKEN_ILLEGAL)) { - return NJS_ERROR; - } - - if (type != NJS_TOKEN_CLOSE_BRACE) { - njs_parser_syntax_error(vm, parser, - "Missing \"}\" in template expression"); - return NJS_ERROR; - } - - return NJS_OK; -} - - -static njs_int_t -njs_parser_template_string(njs_vm_t *vm, njs_parser_t *parser) -{ - u_char *p, c; - njs_int_t ret; - njs_str_t *text; - njs_bool_t escape; - njs_lexer_t *lexer; - njs_parser_node_t *node; - - lexer = parser->lexer; - text = &lexer->token->text; - - text->start = lexer->start; - - escape = 0; - p = lexer->start; - - while (p < lexer->end) { - - c = *p++; - - if (c == '\\') { - if (p == lexer->end) { - break; - } - - p++; - escape = 1; - - continue; - } - - if (c == '`') { - text->length = p - text->start - 1; - goto done; - } - - if (c == '$') { - if (p < lexer->end && *p == '{') { - p++; - text->length = p - text->start - 2; - goto done; - } - } - } - - return NJS_ERROR; - -done: - - node = njs_parser_node_new(vm, parser, NJS_TOKEN_STRING); - if (njs_slow_path(node == NULL)) { - return NJS_ERROR; - } - - if (escape) { - ret = njs_parser_escape_string_create(vm, parser, &node->u.value); - if (njs_slow_path(ret != NJS_TOKEN_STRING)) { - return NJS_ERROR; - } - - } else { - ret = njs_parser_string_create(vm, &node->u.value); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_ERROR; - } - } - - lexer->start = p; - parser->node = node; - - return c == '`' ? NJS_DONE : NJS_OK; -} - - -njs_int_t -njs_parser_string_create(njs_vm_t *vm, njs_value_t *value) -{ - u_char *dst; - ssize_t size, length; - uint32_t cp; - njs_str_t *src; - const u_char *p, *end; - - src = njs_parser_text(vm->parser); - - length = njs_utf8_safe_length(src->start, src->length, &size); - - dst = njs_string_alloc(vm, value, size, length); - if (njs_slow_path(dst == NULL)) { - return NJS_ERROR; - } - - p = src->start; - end = src->start + src->length; - - while (p < end) { - cp = njs_utf8_safe_decode(&p, end); - - dst = njs_utf8_encode(dst, cp); - } - - if (length > NJS_STRING_MAP_STRIDE && size != length) { - njs_string_offset_map_init(value->long_string.data->start, size); - } - - return NJS_OK; -} - - -static njs_token_type_t -njs_parser_escape_string_create(njs_vm_t *vm, njs_parser_t *parser, - njs_value_t *value) -{ - u_char c, *start, *dst; - size_t size, length, hex_length; - uint64_t cp, cp_pair; - njs_int_t ret; - njs_str_t *string; - const u_char *src, *end, *hex_end; - - ret = njs_parser_escape_string_calc_length(vm, parser, &size, &length); - if (njs_slow_path(ret != NJS_OK)) { - return NJS_TOKEN_ILLEGAL; - } - - start = njs_string_alloc(vm, value, size, length); - if (njs_slow_path(start == NULL)) { - return NJS_TOKEN_ERROR; - } - - dst = start; - cp_pair = 0; - - string = njs_parser_text(parser); - src = string->start; - end = src + string->length; - - while (src < end) { - c = *src++; - - if (c == '\\') { - /* - * Testing "src == end" is not required here - * since this has been already tested by lexer. - */ - - c = *src++; - - switch (c) { - case 'u': - /* - * A character after "u" can be safely tested here - * because there is always a closing quote at the - * end of string: ...\u". - */ - - if (*src != '{') { - hex_length = 4; - goto hex_length; - } - - src++; - hex_length = 0; - hex_end = end; - - goto hex; - - case 'x': - hex_length = 2; - goto hex_length; - - case '0': - c = '\0'; - break; - - case 'b': - c = '\b'; - break; - - case 'f': - c = '\f'; - break; - - case 'n': - c = '\n'; - break; - - case 'r': - c = '\r'; - break; - - case 't': - c = '\t'; - break; - - case 'v': - c = '\v'; - break; - - case '\r': - /* - * A character after "\r" can be safely tested here - * because there is always a closing quote at the - * end of string: ...\\r". - */ - - if (*src == '\n') { - src++; - } - - continue; - - case '\n': - continue; - - default: - if (c >= 0x80) { - goto utf8_copy; - } - - break; - } - } - - if (c < 0x80) { - *dst++ = c; - - continue; - } - - utf8_copy: - - src--; - - cp = njs_utf8_safe_decode2(&src, end); - dst = njs_utf8_encode(dst, cp); - - continue; - - hex_length: - - hex_end = src + hex_length; - - hex: - cp = njs_number_hex_parse(&src, hex_end); - - /* Skip '}' character. */ - - if (hex_length == 0) { - src++; - } - - if (cp_pair != 0) { - if (njs_fast_path(njs_surrogate_trailing(cp))) { - cp = njs_string_surrogate_pair(cp_pair, cp); - - } else if (njs_slow_path(njs_surrogate_leading(cp))) { - cp = NJS_UTF8_REPLACEMENT; - - dst = njs_utf8_encode(dst, (uint32_t) cp); - - } else { - dst = njs_utf8_encode(dst, NJS_UTF8_REPLACEMENT); - } - - cp_pair = 0; - - } else if (njs_surrogate_any(cp)) { - if (cp <= 0xdbff && src[0] == '\\' && src[1] == 'u') { - cp_pair = cp; - continue; - } - - cp = NJS_UTF8_REPLACEMENT; - } - - dst = njs_utf8_encode(dst, (uint32_t) cp); - if (njs_slow_path(dst == NULL)) { - njs_parser_syntax_error(vm, parser, - "Invalid Unicode code point \"%V\"", - njs_parser_text(parser)); - - return NJS_TOKEN_ILLEGAL; - } - } - - if (length > NJS_STRING_MAP_STRIDE && length != size) { - njs_string_offset_map_init(start, size); - } - - return NJS_TOKEN_STRING; -} - - -static njs_int_t -njs_parser_escape_string_calc_length(njs_vm_t *vm, njs_parser_t *parser, - size_t *out_size, size_t *out_length) -{ - size_t size, length, hex_length; - uint64_t cp, cp_pair; - njs_str_t *string; - const u_char *ptr, *src, *end, *hex_end; - - size = 0; - length = 0; - cp_pair = 0; - - string = njs_parser_text(parser); - src = string->start; - end = src + string->length; - - while (src < end) { - - if (*src == '\\') { - src++; - - switch (*src) { - case 'u': - src++; - - if (*src != '{') { - hex_length = 4; - goto hex_length; - } - - src++; - hex_length = 0; - hex_end = end; - - goto hex; - - case 'x': - src++; - hex_length = 2; - goto hex_length; - - case '\r': - src++; - - if (*src == '\n') { - src++; - } - - continue; - - case '\n': - src++; - continue; - - default: - break; - } - } - - if (*src >= 0x80) { - cp = njs_utf8_safe_decode2(&src, end); - - size += njs_utf8_size(cp); - length++; - - continue; - } - - src++; - size++; - length++; - - continue; - - hex_length: - - hex_end = src + hex_length; - - if (njs_slow_path(hex_end > end)) { - goto invalid; - } - - hex: - - ptr = src; - cp = njs_number_hex_parse(&src, hex_end); - - if (hex_length != 0) { - if (src != hex_end) { - goto invalid; - } - - } else { - if (src == ptr || (src - ptr) > 6) { - goto invalid; - } - - if (src == end || *src++ != '}') { - goto invalid; - } - } - - if (cp_pair != 0) { - if (njs_fast_path(njs_surrogate_trailing(cp))) { - cp = njs_string_surrogate_pair(cp_pair, cp); - - } else if (njs_slow_path(njs_surrogate_leading(cp))) { - cp = NJS_UTF8_REPLACEMENT; - - size += njs_utf8_size(cp); - length++; - - } else { - size += njs_utf8_size(NJS_UTF8_REPLACEMENT); - length++; - } - - cp_pair = 0; - - } else if (njs_surrogate_any(cp)) { - if (cp <= 0xdbff && src[0] == '\\' && src[1] == 'u') { - cp_pair = cp; - continue; - } - - cp = NJS_UTF8_REPLACEMENT; - } - - size += njs_utf8_size(cp); - length++; - } - - *out_size = size; - *out_length = length; - - return NJS_OK; - -invalid: - - njs_parser_syntax_error(vm, parser, "Invalid Unicode code point \"%V\"", - njs_parser_text(parser)); - - return NJS_ERROR; -} diff --git a/src/njs_regexp.c b/src/njs_regexp.c index 8e1dec05..09603227 100644 --- a/src/njs_regexp.c +++ b/src/njs_regexp.c @@ -17,8 +17,6 @@ struct njs_regexp_group_s { static void *njs_regexp_malloc(size_t size, void *memory_data); static void njs_regexp_free(void *p, void *memory_data); -static njs_regexp_flags_t njs_regexp_flags(u_char **start, u_char *end, - njs_bool_t bound); static njs_int_t njs_regexp_prototype_source(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); @@ -306,98 +304,7 @@ done: } -njs_token_type_t -njs_regexp_literal(njs_vm_t *vm, njs_parser_t *parser, njs_value_t *value) -{ - u_char *p; - njs_str_t text; - njs_lexer_t *lexer; - njs_regexp_flags_t flags; - njs_regexp_pattern_t *pattern; - - lexer = parser->lexer; - - for (p = lexer->start; p < lexer->end; p++) { - - switch (*p) { - case '\n': - case '\r': - goto failed; - - case '[': - while (1) { - if (++p >= lexer->end) { - goto failed; - } - - if (*p == ']') { - break; - } - - switch (*p) { - case '\n': - case '\r': - goto failed; - - case '\\': - if (++p >= lexer->end || *p == '\n' || *p == '\r') { - goto failed; - } - - break; - } - } - - break; - - case '\\': - if (++p >= lexer->end || *p == '\n' || *p == '\r') { - goto failed; - } - - break; - - case '/': - text.start = lexer->start; - text.length = p - text.start; - p++; - lexer->start = p; - - flags = njs_regexp_flags(&p, lexer->end, 0); - - if (njs_slow_path(flags < 0)) { - njs_parser_syntax_error(vm, parser, - "Invalid RegExp flags \"%*s\"", - p - lexer->start, lexer->start); - - return NJS_TOKEN_ILLEGAL; - } - - lexer->start = p; - - pattern = njs_regexp_pattern_create(vm, text.start, text.length, - flags); - - if (njs_slow_path(pattern == NULL)) { - return NJS_TOKEN_ILLEGAL; - } - - value->data.u.data = pattern; - - return NJS_TOKEN_REGEXP; - } - } - -failed: - - njs_parser_syntax_error(vm, parser, "Unterminated RegExp \"%*s\"", - p - (lexer->start - 1), lexer->start - 1); - - return NJS_TOKEN_ILLEGAL; -} - - -static njs_regexp_flags_t +njs_regexp_flags_t njs_regexp_flags(u_char **start, u_char *end, njs_bool_t bound) { u_char *p; diff --git a/src/njs_regexp.h b/src/njs_regexp.h index afdf34ac..d284e299 100644 --- a/src/njs_regexp.h +++ b/src/njs_regexp.h @@ -19,8 +19,8 @@ typedef enum { njs_int_t njs_regexp_init(njs_vm_t *vm); njs_int_t njs_regexp_create(njs_vm_t *vm, njs_value_t *value, u_char *start, size_t length, njs_regexp_flags_t flags); -njs_token_type_t njs_regexp_literal(njs_vm_t *vm, njs_parser_t *parser, - njs_value_t *value); +njs_regexp_flags_t njs_regexp_flags(u_char **start, u_char *end, + njs_bool_t bound); njs_regexp_pattern_t *njs_regexp_pattern_create(njs_vm_t *vm, u_char *string, size_t length, njs_regexp_flags_t flags); njs_int_t njs_regexp_match(njs_vm_t *vm, njs_regex_t *regex, diff --git a/src/njs_variable.c b/src/njs_variable.c index a52b0f5e..57d43b57 100644 --- a/src/njs_variable.c +++ b/src/njs_variable.c @@ -140,7 +140,7 @@ fail: entry = njs_lexer_entry(unique_id); - njs_parser_syntax_error(vm, vm->parser, + njs_parser_syntax_error(vm->parser, "\"%V\" has already been declared", &entry->name); return NULL; @@ -316,28 +316,6 @@ njs_variables_scope_reference(njs_vm_t *vm, njs_parser_scope_t *scope) } -njs_index_t -njs_variable_typeof(njs_vm_t *vm, njs_parser_node_t *node) -{ - njs_int_t ret; - njs_variable_reference_t *vr; - - if (node->index != NJS_INDEX_NONE) { - return node->index; - } - - vr = &node->u.reference; - - ret = njs_variable_reference_resolve(vm, vr, node->scope); - - if (njs_fast_path(ret == NJS_OK)) { - return vr->variable->index; - } - - return NJS_INDEX_NONE; -} - - njs_index_t njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node) { diff --git a/src/njs_vm.c b/src/njs_vm.c index 56618306..e0eadc3d 100644 --- a/src/njs_vm.c +++ b/src/njs_vm.c @@ -151,11 +151,12 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end) return NJS_ERROR; } + parser->vm = vm; parser->lexer = &lexer; njs_set_undefined(&vm->retval); - ret = njs_parser(vm, parser, prev); + ret = njs_parser(parser, prev); if (njs_slow_path(ret != NJS_OK)) { goto fail; } diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 1e6be077..358dbd08 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -275,7 +275,7 @@ static njs_unit_test_t njs_test[] = njs_str("SyntaxError: Unexpected token \"0x\" in 1") }, { njs_str("0xffff."), - njs_str("SyntaxError: Unexpected token \"\" in 1") }, + njs_str("SyntaxError: Unexpected end of input in 1") }, { njs_str("0x12g"), njs_str("SyntaxError: Unexpected token \"g\" in 1") }, @@ -1348,8 +1348,7 @@ static njs_unit_test_t njs_test[] = "must be parenthesized in 1") }, { njs_str("null ?? 0 || 1"), - njs_str("SyntaxError: Either \"??\" or \"||\" expression " - "must be parenthesized in 1") }, + njs_str("SyntaxError: Unexpected token \"||\" in 1") }, { njs_str("var a = true; a = -~!a"), njs_str("1") }, @@ -9125,7 +9124,7 @@ static njs_unit_test_t njs_test[] = njs_str("3,4,5") }, { njs_str("function myFoo(a,b,...other, c) { return other };"), - njs_str("SyntaxError: Unexpected token \"c\" in 1") }, + njs_str("SyntaxError: Rest parameter must be last formal parameter in 1") }, { njs_str("function sum(a, b, c, ...other) { return a+b+c+other[2] };" "sum(\"one \",2,\" three \",\"four \",\"five \",\"the long enough sixth argument \");"), @@ -11532,34 +11531,34 @@ static njs_unit_test_t njs_test[] = #if NJS_HAVE_LARGE_STACK { njs_str("new Function(\"(\".repeat(2**13));"), - njs_str("RangeError: Maximum call stack size exceeded") }, + njs_str("SyntaxError: Unexpected token \"}\" in runtime:1") }, { njs_str("new Function(\"{\".repeat(2**13));"), - njs_str("RangeError: Maximum call stack size exceeded") }, + njs_str("SyntaxError: Unexpected token \")\" in runtime:1") }, { njs_str("new Function(\"[\".repeat(2**13));"), - njs_str("RangeError: Maximum call stack size exceeded") }, + njs_str("SyntaxError: Unexpected token \"}\" in runtime:1") }, { njs_str("new Function(\"`\".repeat(2**13));"), njs_str("RangeError: Maximum call stack size exceeded") }, { njs_str("new Function(\"{[\".repeat(2**13));"), - njs_str("RangeError: Maximum call stack size exceeded") }, + njs_str("SyntaxError: Unexpected token \")\" in runtime:1") }, { njs_str("new Function(\"{;\".repeat(2**13));"), - njs_str("RangeError: Maximum call stack size exceeded") }, + njs_str("SyntaxError: Unexpected token \")\" in runtime:1") }, { njs_str("new Function(\"1;\".repeat(2**13));"), njs_str("RangeError: Maximum call stack size exceeded") }, { njs_str("new Function(\"~\".repeat(2**13));"), - njs_str("RangeError: Maximum call stack size exceeded") }, + njs_str("SyntaxError: Unexpected token \"}\" in runtime:1") }, { njs_str("new Function(\"new \".repeat(2**13));"), - njs_str("RangeError: Maximum call stack size exceeded") }, + njs_str("SyntaxError: Unexpected token \"}\" in runtime:1") }, { njs_str("new Function(\"typeof \".repeat(2**13));"), - njs_str("RangeError: Maximum call stack size exceeded") }, + njs_str("SyntaxError: Unexpected token \"}\" in runtime:1") }, { njs_str("new Function(\"1\" + \"** 1\".repeat(2**13));"), njs_str("RangeError: Maximum call stack size exceeded") }, @@ -16621,6 +16620,40 @@ static njs_unit_test_t njs_test[] = "return 1}, enumerable:1}); a.b =1;" "var x = Object.assign({}, a);x.b;"), njs_str("undefined") }, + + /* let and const */ + + { njs_str("var let = 123;" + "let"), + njs_str("123") }, + + { njs_str("var const = 123"), + njs_str("SyntaxError: Unexpected token \"const\" in 1") }, + + /* Async */ + + { njs_str("var async;" + "function f() {" + " async\n" + " function foo() {}" + "}" + "f()"), + njs_str("undefined") }, + + { njs_str("var async;" + "function f() {" + " async;" + " function foo() {}" + "}" + "f()"), + njs_str("undefined") }, + + { njs_str("var async;" + "async\n" + "function foo() {}"), + njs_str("undefined") }, + + }; diff --git a/test/njs_expect_test.exp b/test/njs_expect_test.exp index f7091a9a..6d26c37b 100644 --- a/test/njs_expect_test.exp +++ b/test/njs_expect_test.exp @@ -364,7 +364,7 @@ njs_test { {"JSON.parse(Error())\r\n" "JSON.parse(Error())\r\nThrown:\r\nSyntaxError: Unexpected token at position 0*at JSON.parse (native)"} {"JSON.parse(Error()\r\n" - "JSON.parse(Error()\r\nThrown:\r\nSyntaxError: Unexpected token \"\" in shell:1"} + "JSON.parse(Error()\r\nThrown:\r\nSyntaxError: Unexpected end of input in shell:1"} } njs_test { @@ -762,7 +762,7 @@ ReferenceError: \"ref\" is not defined in string:1 njs_run {"-c" "setTimeout(() => {ref}, 0); setTimeout(() => {console.log('A'.repeat(1024))}, 0)"} \ "^Thrown: ReferenceError: \"ref\" is not defined in string:1 - at anonymous \\\(native\\\) + at anonymous \\\(string:1\\\) at main \\\(native\\\)\n$" # Modules diff --git a/utils/lexer_keyword.py b/utils/lexer_keyword.py index 535dd011..78b3ddc7 100755 --- a/utils/lexer_keyword.py +++ b/utils/lexer_keyword.py @@ -1,70 +1,78 @@ import re, os -global_keywords = [ +global_keywords = { # Values. - "null", - "false", - "true", + "null": 1, + "false": 1, + "true": 1, # Operators. - "in", - "typeof", - "instanceof", - "void", - "new", - "delete", - "yield", + "in": 1, + "of": 0, + "typeof": 1, + "instanceof": 1, + "void": 1, + "new": 1, + "delete": 1, + "yield": 1, # Statements. - "var", - "if", - "else", - "while", - "do", - "for", - "break", - "continue", - "switch", - "case", - "default", - "function", - "return", - "with", - "try", - "catch", - "finally", - "throw", + "var": 1, + "if": 1, + "else": 1, + "while": 1, + "do": 1, + "for": 1, + "break": 1, + "continue": 1, + "switch": 1, + "case": 1, + "default": 1, + "function": 1, + "return": 1, + "with": 1, + "try": 1, + "catch": 1, + "finally": 1, + "throw": 1, # Module. - "import", - "export", + "import": 1, + "export": 1, # Reserved words. - "this", - "arguments", - "eval", - - "await", - "class", - "const", - "debugger", - "enum", - "extends", - "implements", - "interface", - "let", - "package", - "private", - "protected", - "public", - "static", - "super" -] + "meta": 0, + + "from": 0, + + "this": 1, + "arguments": 0, + "eval": 0, + + "target": 0, + + "await": 1, + "async": 0, + "class": 1, + "const": 1, + "debugger": 1, + "enum": 1, + "extends": 1, + "implements": 0, + "interface": 0, + "let": 0, + "package": 0, + "private": 0, + "protected": 0, + "public": 0, + "static": 0, + "super": 1 +} class Table: @@ -75,15 +83,18 @@ class Table: def add(self, data): self.buffer.append(data) - def create(self): + def create(self, newlines = False): result = [] data = self.buffer result.append("static const {}[{}] =\n{{\n".format(self.header, len(data))) + last = len(data) - 1 + for idx in range(len(data)): - result.append(" {},\n".format(data[idx])) + result.append(" {},\n{}".format(data[idx], + ("\n" if newlines and idx != last else ""))) result.append("};") @@ -191,16 +202,20 @@ if __name__ == "__main__": def kw_create(): t = Table("njs_keyword_t " + data_name) - for kw in global_keywords: - t.add("{{ .entry = {{ njs_str(\"{}\") }}, .type = {} }}" - .format(kw, enum(kw))) + for k, v in sorted(global_keywords.items()): + t.add("{{\n" + " .entry = {{ njs_str(\"{}\") }},\n" + " .type = {},\n" + " .reserved = {}\n" + " }}" + .format(k, enum(k), v)) - return t.create() + return t.create(True) def entries_create(): shs = SHS([{ "key": kw, "value": "&{}[{}]".format(data_name, i) } - for i, kw in enumerate(global_keywords)]) + for i, kw in enumerate(sorted(global_keywords.keys()))]) best_size = shs.test(5, 128) shs.make(best_size) -- 2.47.3