]> git.kaiwu.me - njs.git/commitdiff
Parser refactoring.
authorAlexander Borisov <alexander.borisov@nginx.com>
Fri, 29 May 2020 17:00:32 +0000 (20:00 +0300)
committerAlexander Borisov <alexander.borisov@nginx.com>
Fri, 29 May 2020 17:00:32 +0000 (20:00 +0300)
18 files changed:
auto/sources
src/njs_function.c
src/njs_lexer.c
src/njs_lexer.h
src/njs_lexer_tables.h
src/njs_module.c
src/njs_module.h
src/njs_parser.c
src/njs_parser.h
src/njs_parser_expression.c [deleted file]
src/njs_parser_terminal.c [deleted file]
src/njs_regexp.c
src/njs_regexp.h
src/njs_variable.c
src/njs_vm.c
src/test/njs_unit_test.c
test/njs_expect_test.exp
utils/lexer_keyword.py

index a14cd0ca6f50464dbfb33addc0c9c339426adffd..66381b5772f117bb445226673052105646607356 100644 (file)
@@ -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 \
index 1fc44b37bd7651ca549b9bae0c28cf4964e03302..a50cde4339534044ce59afbc1f72780a04d01002 100644 (file)
@@ -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;
     }
index 6f8308f5cc058129fc2cfffe25f18881219bf633..215f005ecfb468465ca5fa49ea46d1829fa649ea 100644 (file)
@@ -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, &lt->link);
+    njs_queue_link_t   *lnk;
+    njs_lexer_token_t  *token;
 
-    return NJS_OK;
-}
+    lnk = njs_queue_next(&current->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;
index 670dbab31af134ba78a98e1ff98e0d217643d721..6689dedaffa0f01702b3ddf7e3a82ac8bb19331f 100644 (file)
@@ -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;
 
 
index 8e1ff013feeb212ae6fd3cb42a654e0ea26e27d8..73f71678680883607b27b6ab4bae86d2f7330b2d 100644 (file)
 #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 },
 };
 
index 0cc20f90c8746f63bddc28219f91b65106bcf33e..31ea7ee0dd9814f7e6b040a5ce7115fe6ba3ae23 100644 (file)
@@ -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:
index 955ef5b890f0e80acb7ce99c187e00d2cab71608..7da3a027864771cf825342bf5dde4ce20e3bb5e7 100644 (file)
@@ -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);
 
index 2f953c35a9c88acad3459ff6000d847ef7de41c2..137538452ef20f2ff73ddac25d7e3e28b23f84a2 100644 (file)
@@ -1,6 +1,7 @@
 
 /*
  * Copyright (C) Igor Sysoev
+ * Copyright (C) Alexander Borisov
  * Copyright (C) NGINX, Inc.
  */
 
 #include <njs_main.h>
 
 
-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" <VariableDeclarationList> ";" <Expression>? ";" <Expression>? ")"
+     *     <Statement>
+     * "var" <ForBinding> "in" <Expression> ")" <Statement>
+     * "var" <ForBinding> "of" <AssignmentExpression> ")" <Statement>
+
+     * <ForDeclaration> "in" <Expression> ")" <Statement>
+     * <ForDeclaration> "of" <AssignmentExpression> ")" <Statement>
+
+     * <Expression>? ";" <Expression>? ";" <Expression>? ")" <Statement>
+     * <LexicalDeclaration> <Expression>? ";" <Expression>? ")" <Statement>
+
+     * <LeftHandSideExpression> "in" <Expression> ")" <Statement>
+     * <LeftHandSideExpression> "of" <AssignmentExpression> ")" <Statement>
+     */
+
+    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;
+
+    /*
+     * ";" <Expression>? ";" <Expression>? ")" <Statement>
+     * "in" <Expression> ")" <Statement>
+     * "of" <AssignmentExpression> ")" <Statement>
+     */
+
+    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);
 }
 
index 541eeaeccbf1a80e172bea9345696d500d4ee35e..e507feb5a16174951624d129b637c272e6059cf8 100644 (file)
@@ -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 (file)
index 349b809..0000000
+++ /dev/null
@@ -1,1121 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- * Copyright (C) NGINX, Inc.
- */
-
-
-#include <njs_main.h>
-
-
-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 (file)
index 47e64b5..0000000
+++ /dev/null
@@ -1,1296 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- * Copyright (C) NGINX, Inc.
- */
-
-
-#include <njs_main.h>
-
-
-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;
-}
index 8e1dec05ce42684d313a81e85b4e5d58b20976a2..0960322792d5c23ad2ebf5b7058e97614e0556fb 100644 (file)
@@ -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;
index afdf34acfe123df0d34129e2a3d25e86b462f33b..d284e299f5c497bc678b1b45d79fe2cd9715fd96 100644 (file)
@@ -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,
index a52b0f5ee02ff80545751ea03089715c874fc295..57d43b579cff94acb2a0ad5787c66b1f1536ec51 100644 (file)
@@ -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)
 {
index 56618306cdbd37b78f8392e11cdb6e59321264ec..e0eadc3dc05172929281e410f858a3a662783803 100644 (file)
@@ -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;
     }
index 1e6be077851d69ebb65feb6f55a60be808f12589..358dbd08374d4be0ea10939c554171fccd330b8c 100644 (file)
@@ -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") },
+
+
 };
 
 
index f7091a9a33409e0ee548baa468554c26813de838..6d26c37bc6c0c29c29db5820bbfd8764d1431b0d 100644 (file)
@@ -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
index 535dd0117bf3dea6dc65ab4d10a83a50beb79cee..78b3ddc788122a3e458104406849d383874387a1 100755 (executable)
@@ -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)