]> git.kaiwu.me - njs.git/commitdiff
Refactored out terminal tokens into njs_parser_terminal.c.
authorhongzhidao <hongzhidao@gmail.com>
Sun, 31 Mar 2019 14:59:04 +0000 (22:59 +0800)
committerhongzhidao <hongzhidao@gmail.com>
Sun, 31 Mar 2019 14:59:04 +0000 (22:59 +0800)
auto/sources
njs/njs_parser.c
njs/njs_parser.h
njs/njs_parser_expression.c
njs/njs_parser_terminal.c [new file with mode: 0644]

index 525e546754f22ec9a6ab2024fa50d7235cea2bc3..4b925fac85bbf84279a1a97b88d6ba90f260b10e 100644 (file)
@@ -53,6 +53,7 @@ NJS_LIB_SRCS=" \
    njs/njs_lexer.c \
    njs/njs_lexer_keyword.c \
    njs/njs_parser.c \
+   njs/njs_parser_terminal.c \
    njs/njs_parser_expression.c \
    njs/njs_generator.c \
    njs/njs_disassembler.c \
index 6b2f14db4b4db561fa67ae169fbf6d6875848e6e..eff288888dc87587d7d3007548e9376e739a715c 100644 (file)
@@ -67,21 +67,6 @@ static nxt_int_t njs_parser_import_hoist(njs_vm_t *vm, njs_parser_t *parser,
 static nxt_int_t njs_parser_export_sink(njs_vm_t *vm, njs_parser_t *parser);
 static njs_token_t njs_parser_grouping_expression(njs_vm_t *vm,
     njs_parser_t *parser);
-static njs_parser_node_t *njs_parser_reference(njs_vm_t *vm,
-    njs_parser_t *parser, njs_token_t token, nxt_str_t *name, uint32_t hash,
-    uint32_t token_line);
-static nxt_int_t njs_parser_builtin(njs_vm_t *vm, njs_parser_t *parser,
-    njs_parser_node_t *node, njs_value_type_t type, nxt_str_t *name,
-    uint32_t hash);
-static njs_token_t njs_parser_object(njs_vm_t *vm, njs_parser_t *parser,
-    njs_parser_node_t *obj);
-static njs_token_t njs_parser_array(njs_vm_t *vm, njs_parser_t *parser,
-    njs_parser_node_t *obj);
-static nxt_int_t njs_parser_string_create(njs_vm_t *vm, njs_value_t *value);
-static njs_token_t njs_parser_escape_string_create(njs_vm_t *vm,
-    njs_parser_t *parser, njs_value_t *value);
-static njs_token_t njs_parser_unexpected_token(njs_vm_t *vm,
-    njs_parser_t *parser, njs_token_t token);
 
 
 #define njs_parser_chain_current(parser)                            \
@@ -93,15 +78,6 @@ static njs_token_t njs_parser_unexpected_token(njs_vm_t *vm,
 #define njs_parser_chain_top_set(parser, node)                      \
     (parser)->scope->top = node
 
-#define njs_parser_key_hash(parser)                                 \
-    (parser)->lexer->lexer_token->key_hash
-
-#define njs_parser_number(parser)                                 \
-    (parser)->lexer->lexer_token->number
-
-#define njs_parser_token_line(parser)                                 \
-    (parser)->lexer->lexer_token->token_line
-
 
 nxt_int_t
 njs_parser(njs_vm_t *vm, njs_parser_t *parser, njs_parser_t *prev)
@@ -515,36 +491,6 @@ njs_parser_block(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
 }
 
 
-nxt_inline njs_token_t
-njs_parser_match(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token,
-    njs_token_t match)
-{
-    if (nxt_fast_path(token == match)) {
-        return njs_parser_token(vm, parser);
-    }
-
-    return njs_parser_unexpected_token(vm, parser, token);
-}
-
-
-nxt_inline njs_variable_t *
-njs_parser_variable_add(njs_vm_t *vm, njs_parser_t *parser,
-    njs_variable_type_t type)
-{
-    return njs_variable_add(vm, parser->scope, njs_parser_text(parser),
-                            njs_parser_key_hash(parser), type);
-}
-
-nxt_inline njs_ret_t
-njs_parser_variable_reference(njs_vm_t *vm, njs_parser_t *parser,
-    njs_parser_node_t *node, njs_reference_type_t type)
-{
-    return njs_variable_reference(vm, parser->scope, node,
-                                  njs_parser_text(parser),
-                                  njs_parser_key_hash(parser), type);
-}
-
-
 static njs_token_t
 njs_parser_labelled_statement(njs_vm_t *vm, njs_parser_t *parser)
 {
@@ -688,7 +634,7 @@ njs_parser_function_declaration(njs_vm_t *vm, njs_parser_t *parser)
 }
 
 
-static njs_token_t
+njs_token_t
 njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser)
 {
     njs_ret_t              ret;
@@ -2226,849 +2172,6 @@ njs_parser_property_token(njs_vm_t *vm, njs_parser_t *parser)
 }
 
 
-njs_token_t
-njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
-{
-    double             num;
-    njs_ret_t          ret;
-    njs_parser_node_t  *node;
-
-    if (token == NJS_TOKEN_OPEN_PARENTHESIS) {
-
-        token = njs_parser_token(vm, parser);
-        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
-            return token;
-        }
-
-        token = njs_parser_expression(vm, parser, token);
-        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
-            return token;
-        }
-
-        return njs_parser_match(vm, parser, token, NJS_TOKEN_CLOSE_PARENTHESIS);
-    }
-
-    if (token == NJS_TOKEN_FUNCTION) {
-        return njs_parser_function_expression(vm, parser);
-    }
-
-    switch (token) {
-
-    case NJS_TOKEN_OPEN_BRACE:
-        nxt_thread_log_debug("JS: OBJECT");
-
-        node = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT);
-        if (nxt_slow_path(node == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        parser->node = node;
-
-        token = njs_parser_object(vm, parser, node);
-
-        if (parser->node != node) {
-            /* The object is not empty. */
-            node->left = parser->node;
-            parser->node = node;
-        }
-
-        return token;
-
-    case NJS_TOKEN_OPEN_BRACKET:
-        nxt_thread_log_debug("JS: ARRAY");
-
-        node = njs_parser_node_new(vm, parser, NJS_TOKEN_ARRAY);
-        if (nxt_slow_path(node == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        parser->node = node;
-
-        token = njs_parser_array(vm, parser, node);
-
-        if (parser->node != node) {
-            /* The array is not empty. */
-            node->left = parser->node;
-            parser->node = node;
-        }
-
-        return token;
-
-    case NJS_TOKEN_DIVISION:
-        node = njs_parser_node_new(vm, parser, NJS_TOKEN_REGEXP);
-        if (nxt_slow_path(node == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        token = njs_regexp_literal(vm, parser, &node->u.value);
-        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
-            return token;
-        }
-
-        nxt_thread_log_debug("REGEX: '%V'", njs_parser_text(parser));
-
-        break;
-
-    case NJS_TOKEN_STRING:
-        nxt_thread_log_debug("JS: '%V'", njs_parser_text(parser));
-
-        node = njs_parser_node_new(vm, parser, NJS_TOKEN_STRING);
-        if (nxt_slow_path(node == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        ret = njs_parser_string_create(vm, &node->u.value);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        break;
-
-    case NJS_TOKEN_ESCAPE_STRING:
-        nxt_thread_log_debug("JS: '%V'", njs_parser_text(parser));
-
-        node = njs_parser_node_new(vm, parser, NJS_TOKEN_STRING);
-        if (nxt_slow_path(node == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        ret = njs_parser_escape_string_create(vm, parser, &node->u.value);
-        if (nxt_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);
-        nxt_thread_log_debug("JS: %f", num);
-
-        node = njs_parser_node_new(vm, parser, NJS_TOKEN_NUMBER);
-        if (nxt_slow_path(node == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        node->u.value.data.u.number = num;
-        node->u.value.type = NJS_NUMBER;
-        node->u.value.data.truth = njs_is_number_true(num);
-
-        break;
-
-    case NJS_TOKEN_BOOLEAN:
-        num = njs_parser_number(parser);
-        nxt_thread_log_debug("JS: boolean: %V", njs_parser_text(parser));
-
-        node = njs_parser_node_new(vm, parser, NJS_TOKEN_BOOLEAN);
-        if (nxt_slow_path(node == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        if (num == 0) {
-            node->u.value = njs_value_false;
-
-        } else {
-            node->u.value = njs_value_true;
-        }
-
-        break;
-
-    default:
-        node = njs_parser_reference(vm, parser, token,
-                                    njs_parser_text(parser),
-                                    njs_parser_key_hash(parser),
-                                    njs_parser_token_line(parser));
-
-        if (nxt_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_t token,
-    nxt_str_t *name, uint32_t hash, uint32_t token_line)
-{
-    njs_ret_t           ret;
-    njs_value_t         *ext;
-    njs_parser_node_t   *node;
-    njs_parser_scope_t  *scope;
-
-    node = njs_parser_node_new(vm, parser, token);
-    if (nxt_slow_path(node == NULL)) {
-        return NULL;
-    }
-
-    switch (token) {
-
-    case NJS_TOKEN_NULL:
-        nxt_thread_log_debug("JS: null");
-
-        node->u.value = njs_value_null;
-        break;
-
-    case NJS_TOKEN_UNDEFINED:
-        nxt_thread_log_debug("JS: undefined");
-
-        node->u.value = njs_value_undefined;
-        break;
-
-    case NJS_TOKEN_THIS:
-        nxt_thread_log_debug("JS: this");
-
-        scope = parser->scope;
-
-        while (scope->type != NJS_SCOPE_GLOBAL) {
-            if (scope->type == NJS_SCOPE_FUNCTION) {
-                node->index = NJS_INDEX_THIS;
-                break;
-            }
-
-            scope = scope->parent;
-        }
-
-        if (node->index == NJS_INDEX_THIS) {
-            break;
-        }
-
-        node->token = NJS_TOKEN_GLOBAL_THIS;
-
-        /* Fall through. */
-
-    case NJS_TOKEN_NJS:
-    case NJS_TOKEN_MATH:
-    case NJS_TOKEN_JSON:
-        ret = njs_parser_builtin(vm, parser, node, NJS_OBJECT, name, hash);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return NULL;
-        }
-
-        break;
-
-    case NJS_TOKEN_ARGUMENTS:
-        nxt_thread_log_debug("JS: arguments");
-
-        if (parser->scope->type <= NJS_SCOPE_GLOBAL) {
-            njs_parser_syntax_error(vm, parser, "\"%V\" object "
-                                    "in global scope", name);
-
-            return NULL;
-        }
-
-        parser->scope->arguments_object = 1;
-
-        break;
-
-    case NJS_TOKEN_OBJECT_CONSTRUCTOR:
-        node->index = NJS_INDEX_OBJECT;
-        break;
-
-    case NJS_TOKEN_ARRAY_CONSTRUCTOR:
-        node->index = NJS_INDEX_ARRAY;
-        break;
-
-    case NJS_TOKEN_BOOLEAN_CONSTRUCTOR:
-        node->index = NJS_INDEX_BOOLEAN;
-        break;
-
-    case NJS_TOKEN_NUMBER_CONSTRUCTOR:
-        node->index = NJS_INDEX_NUMBER;
-        break;
-
-    case NJS_TOKEN_STRING_CONSTRUCTOR:
-        node->index = NJS_INDEX_STRING;
-        break;
-
-    case NJS_TOKEN_FUNCTION_CONSTRUCTOR:
-        node->index = NJS_INDEX_FUNCTION;
-        break;
-
-    case NJS_TOKEN_REGEXP_CONSTRUCTOR:
-        node->index = NJS_INDEX_REGEXP;
-        break;
-
-    case NJS_TOKEN_DATE_CONSTRUCTOR:
-        node->index = NJS_INDEX_DATE;
-        break;
-
-    case NJS_TOKEN_ERROR_CONSTRUCTOR:
-        node->index = NJS_INDEX_OBJECT_ERROR;
-        break;
-
-    case NJS_TOKEN_EVAL_ERROR_CONSTRUCTOR:
-        node->index = NJS_INDEX_OBJECT_EVAL_ERROR;
-        break;
-
-    case NJS_TOKEN_INTERNAL_ERROR_CONSTRUCTOR:
-        node->index = NJS_INDEX_OBJECT_INTERNAL_ERROR;
-        break;
-
-    case NJS_TOKEN_RANGE_ERROR_CONSTRUCTOR:
-        node->index = NJS_INDEX_OBJECT_RANGE_ERROR;
-        break;
-
-    case NJS_TOKEN_REF_ERROR_CONSTRUCTOR:
-        node->index = NJS_INDEX_OBJECT_REF_ERROR;
-        break;
-
-    case NJS_TOKEN_SYNTAX_ERROR_CONSTRUCTOR:
-        node->index = NJS_INDEX_OBJECT_SYNTAX_ERROR;
-        break;
-
-    case NJS_TOKEN_TYPE_ERROR_CONSTRUCTOR:
-        node->index = NJS_INDEX_OBJECT_TYPE_ERROR;
-        break;
-
-    case NJS_TOKEN_URI_ERROR_CONSTRUCTOR:
-        node->index = NJS_INDEX_OBJECT_URI_ERROR;
-        break;
-
-    case NJS_TOKEN_MEMORY_ERROR_CONSTRUCTOR:
-        node->index = NJS_INDEX_OBJECT_MEMORY_ERROR;
-        break;
-
-    case NJS_TOKEN_EVAL:
-    case NJS_TOKEN_TO_STRING:
-    case NJS_TOKEN_IS_NAN:
-    case NJS_TOKEN_IS_FINITE:
-    case NJS_TOKEN_PARSE_INT:
-    case NJS_TOKEN_PARSE_FLOAT:
-    case NJS_TOKEN_ENCODE_URI:
-    case NJS_TOKEN_ENCODE_URI_COMPONENT:
-    case NJS_TOKEN_DECODE_URI:
-    case NJS_TOKEN_DECODE_URI_COMPONENT:
-    case NJS_TOKEN_REQUIRE:
-    case NJS_TOKEN_SET_TIMEOUT:
-    case NJS_TOKEN_SET_IMMEDIATE:
-    case NJS_TOKEN_CLEAR_TIMEOUT:
-        ret = njs_parser_builtin(vm, parser, node, NJS_FUNCTION, name, hash);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return NULL;
-        }
-
-        break;
-
-    case NJS_TOKEN_NAME:
-        nxt_thread_log_debug("JS: %V", name);
-
-        node->token_line = token_line;
-
-        ext = njs_external_lookup(vm, name, hash);
-
-        if (ext != NULL) {
-            node->token = NJS_TOKEN_EXTERNAL;
-            node->u.value = *ext;
-            node->index = (njs_index_t) ext;
-            break;
-        }
-
-        ret = njs_variable_reference(vm, parser->scope, node, name, hash,
-                                     NJS_REFERENCE);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return NULL;
-        }
-
-        break;
-
-    default:
-        (void) njs_parser_unexpected_token(vm, parser, token);
-        return NULL;
-    }
-
-    return node;
-}
-
-
-static nxt_int_t
-njs_parser_builtin(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node,
-    njs_value_type_t type, nxt_str_t *name, uint32_t hash)
-{
-    njs_ret_t           ret;
-    nxt_uint_t          index;
-    njs_variable_t      *var;
-    njs_parser_scope_t  *scope;
-
-    scope = njs_parser_global_scope(vm);
-
-    var = njs_variable_add(vm, scope, name, hash, NJS_VARIABLE_VAR);
-    if (nxt_slow_path(var == NULL)) {
-        return NXT_ERROR;
-    }
-
-    /* TODO: once */
-    switch (type) {
-    case NJS_OBJECT:
-        index = node->token - NJS_TOKEN_FIRST_OBJECT;
-        var->value.data.u.object = &vm->shared->objects[index];
-        break;
-
-    case NJS_FUNCTION:
-        index = node->token - NJS_TOKEN_FIRST_FUNCTION;
-        var->value.data.u.function = &vm->shared->functions[index];
-        break;
-
-    default:
-        return NXT_ERROR;
-    }
-
-    var->value.type = type;
-    var->value.data.truth = 1;
-
-    ret = njs_variable_reference(vm, scope, node, name, hash, NJS_REFERENCE);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        return NXT_ERROR;
-    }
-
-    return NXT_OK;
-}
-
-
-/*
- * ES6: 12.2.6 Object Initializer
- * Supported syntax:
- *   PropertyDefinition:
- *     PropertyName : AssignmentExpression
- *     IdentifierReference
- *   PropertyName:
- *    IdentifierName, StringLiteral, NumericLiteral.
- */
-static njs_token_t
-njs_parser_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj)
-{
-    uint32_t           hash, token_line;
-    nxt_str_t          name;
-    njs_token_t        token;
-    njs_lexer_t        *lexer;
-    njs_parser_node_t  *stmt, *assign, *object, *propref, *left, *expression;
-
-    left = NULL;
-    lexer = parser->lexer;
-
-    /* GCC and Clang complain about uninitialized hash. */
-    hash = 0;
-    token_line = 0;
-
-    object = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT_VALUE);
-    if (nxt_slow_path(object == NULL)) {
-        return NJS_TOKEN_ERROR;
-    }
-
-    object->u.object = obj;
-
-    for ( ;; ) {
-        token = njs_parser_property_token(vm, parser);
-
-        name.start = NULL;
-
-        switch (token) {
-
-        case NJS_TOKEN_CLOSE_BRACE:
-            return njs_parser_token(vm, parser);
-
-        case NJS_TOKEN_NAME:
-            name = *njs_parser_text(parser);
-
-            hash = njs_parser_key_hash(parser);
-            token_line = njs_parser_token_line(parser);
-
-            token = njs_parser_token(vm, parser);
-            break;
-
-        case NJS_TOKEN_NUMBER:
-        case NJS_TOKEN_STRING:
-        case NJS_TOKEN_ESCAPE_STRING:
-            token = njs_parser_terminal(vm, parser, token);
-            break;
-
-        default:
-            return NJS_TOKEN_ILLEGAL;
-        }
-
-        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
-            return token;
-        }
-
-        propref = njs_parser_node_new(vm, parser, NJS_TOKEN_PROPERTY);
-        if (nxt_slow_path(propref == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        propref->left = object;
-        propref->right = parser->node;
-
-        if (name.start != NULL
-            && (token == NJS_TOKEN_COMMA || token == NJS_TOKEN_CLOSE_BRACE)
-            && lexer->property_token != NJS_TOKEN_THIS
-            && lexer->property_token != NJS_TOKEN_GLOBAL_THIS)
-        {
-            expression = njs_parser_reference(vm, parser, lexer->property_token,
-                                              &name, hash, token_line);
-            if (nxt_slow_path(expression == NULL)) {
-                return NJS_TOKEN_ERROR;
-            }
-
-        } else {
-            token = njs_parser_match(vm, parser, token, NJS_TOKEN_COLON);
-            if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
-                return token;
-            }
-
-            token = njs_parser_assignment_expression(vm, parser, token);
-            if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
-                return token;
-            }
-
-            expression = parser->node;
-        }
-
-        assign = njs_parser_node_new(vm, parser, NJS_TOKEN_ASSIGNMENT);
-        if (nxt_slow_path(assign == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        assign->u.operation = njs_vmcode_move;
-        assign->left = propref;
-        assign->right = expression;
-
-        stmt = njs_parser_node_new(vm, parser, NJS_TOKEN_STATEMENT);
-        if (nxt_slow_path(stmt == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        stmt->left = left;
-        stmt->right = assign;
-
-        parser->node = stmt;
-
-        left = stmt;
-
-        if (token == NJS_TOKEN_CLOSE_BRACE) {
-            return njs_parser_token(vm, parser);
-        }
-
-        if (nxt_slow_path(token != NJS_TOKEN_COMMA)) {
-            return NJS_TOKEN_ILLEGAL;
-        }
-    }
-}
-
-
-static njs_token_t
-njs_parser_array(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj)
-{
-    nxt_uint_t         index;
-    njs_token_t        token;
-    njs_parser_node_t  *stmt, *assign, *object, *propref, *left, *node;
-
-    index = 0;
-    left = NULL;
-
-    for ( ;; ) {
-        token = njs_parser_token(vm, parser);
-        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
-            return token;
-        }
-
-        if (token == NJS_TOKEN_CLOSE_BRACKET) {
-            break;
-        }
-
-        if (token == NJS_TOKEN_COMMA) {
-            obj->ctor = 1;
-            index++;
-            continue;
-        }
-
-        node = njs_parser_node_new(vm, parser, NJS_TOKEN_NUMBER);
-        if (nxt_slow_path(node == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        node->u.value.data.u.number = index;
-        node->u.value.type = NJS_NUMBER;
-        node->u.value.data.truth = (index != 0);
-        index++;
-
-        object = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT_VALUE);
-        if (nxt_slow_path(object == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        object->u.object = obj;
-
-        propref = njs_parser_node_new(vm, parser, NJS_TOKEN_PROPERTY);
-        if (nxt_slow_path(propref == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        propref->left = object;
-        propref->right = node;
-
-        token = njs_parser_assignment_expression(vm, parser, token);
-        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
-            return token;
-        }
-
-        assign = njs_parser_node_new(vm, parser, NJS_TOKEN_ASSIGNMENT);
-        if (nxt_slow_path(assign == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        assign->u.operation = njs_vmcode_move;
-        assign->left = propref;
-        assign->right = parser->node;
-
-        stmt = njs_parser_node_new(vm, parser, NJS_TOKEN_STATEMENT);
-        if (nxt_slow_path(stmt == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        stmt->left = left;
-        stmt->right = assign;
-
-        parser->node = stmt;
-        left = stmt;
-
-        obj->ctor = 0;
-
-        if (token == NJS_TOKEN_CLOSE_BRACKET) {
-            break;
-        }
-
-        if (nxt_slow_path(token != NJS_TOKEN_COMMA)) {
-            return NJS_TOKEN_ILLEGAL;
-        }
-    }
-
-    obj->u.length = index;
-
-    return njs_parser_token(vm, parser);
-}
-
-
-static nxt_int_t
-njs_parser_string_create(njs_vm_t *vm, njs_value_t *value)
-{
-    u_char     *p;
-    ssize_t    length;
-    nxt_str_t  *src;
-
-    src = njs_parser_text(vm->parser);
-
-    length = nxt_utf8_length(src->start, src->length);
-
-    if (nxt_slow_path(length < 0)) {
-        length = 0;
-    }
-
-    p = njs_string_alloc(vm, value, src->length, length);
-
-    if (nxt_fast_path(p != NULL)) {
-        memcpy(p, src->start, src->length);
-
-        if (length > NJS_STRING_MAP_STRIDE && (size_t) length != src->length) {
-            njs_string_offset_map_init(p, src->length);
-        }
-
-        return NXT_OK;
-    }
-
-    return NXT_ERROR;
-}
-
-
-static njs_token_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      u;
-    nxt_str_t     *string;
-    const u_char  *p, *src, *end, *hex_end;
-
-    start = NULL;
-    dst = NULL;
-
-    for ( ;; ) {
-        /*
-         * The loop runs twice: at the first step string size and
-         * UTF-8 length are evaluated.  Then the string is allocated
-         * and at the second step string content is copied.
-         */
-        size = 0;
-        length = 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':
-                    hex_length = 4;
-                    /*
-                     * A character after "u" can be safely tested here
-                     * because there is always a closing quote at the
-                     * end of string: ...\u".
-                     */
-                    if (*src != '{') {
-                        goto hex_length_test;
-                    }
-
-                    src++;
-                    hex_length = 0;
-                    hex_end = end;
-
-                    goto hex;
-
-                case 'x':
-                    hex_length = 2;
-                    goto hex_length_test;
-
-                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:
-                    break;
-                }
-            }
-
-            size++;
-            length++;
-
-            if (dst != NULL) {
-                *dst++ = c;
-            }
-
-            continue;
-
-        hex_length_test:
-
-            hex_end = src + hex_length;
-
-            if (hex_end > end) {
-                goto invalid;
-            }
-
-        hex:
-
-            p = src;
-            u = njs_number_hex_parse(&src, hex_end);
-
-            if (hex_length != 0) {
-                if (src != hex_end) {
-                    goto invalid;
-                }
-
-            } else {
-                if (src == p || (src - p) > 6) {
-                    goto invalid;
-                }
-
-                if (src == end || *src++ != '}') {
-                    goto invalid;
-                }
-            }
-
-            size += nxt_utf8_size(u);
-            length++;
-
-            if (dst != NULL) {
-                dst = nxt_utf8_encode(dst, (uint32_t) u);
-                if (dst == NULL) {
-                    goto invalid;
-                }
-            }
-        }
-
-        if (start != NULL) {
-            if (length > NJS_STRING_MAP_STRIDE && length != size) {
-                njs_string_offset_map_init(start, size);
-            }
-
-            return NJS_TOKEN_STRING;
-        }
-
-        start = njs_string_alloc(vm, value, size, length);
-        if (nxt_slow_path(start == NULL)) {
-            return NJS_TOKEN_ERROR;
-        }
-
-        dst = start;
-    }
-
-invalid:
-
-    njs_parser_syntax_error(vm, parser, "Invalid Unicode code point \"%V\"",
-                            njs_parser_text(parser));
-
-    return NJS_TOKEN_ILLEGAL;
-}
-
-
 nxt_bool_t
 njs_parser_has_side_effect(njs_parser_node_t *node)
 {
@@ -3100,7 +2203,7 @@ njs_parser_has_side_effect(njs_parser_node_t *node)
 }
 
 
-static njs_token_t
+njs_token_t
 njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser,
     njs_token_t token)
 {
index 505d3452732c6c69a45c4fe6a5a1bad0c8141577..ad67f99a7c743069c0c5239d33df57a5d2a4d5e5 100644 (file)
@@ -77,24 +77,26 @@ struct njs_parser_s {
 
 nxt_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_t *prev);
-njs_token_t njs_parser_arguments(njs_vm_t *vm, njs_parser_t *parser,
-    njs_parser_node_t *parent);
 njs_token_t njs_parser_expression(njs_vm_t *vm, njs_parser_t *parser,
     njs_token_t token);
 njs_token_t njs_parser_var_expression(njs_vm_t *vm, njs_parser_t *parser,
     njs_token_t token);
 njs_token_t njs_parser_assignment_expression(njs_vm_t *vm,
     njs_parser_t *parser, njs_token_t token);
+njs_token_t njs_parser_function_expression(njs_vm_t *vm, njs_parser_t *parser);
 njs_token_t njs_parser_module_lambda(njs_vm_t *vm, njs_parser_t *parser);
 njs_token_t njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser,
     njs_token_t token);
 njs_token_t njs_parser_property_token(njs_vm_t *vm, njs_parser_t *parser);
+nxt_int_t njs_parser_string_create(njs_vm_t *vm, njs_value_t *value);
 njs_token_t njs_parser_lambda_statements(njs_vm_t *vm, njs_parser_t *parser,
     njs_token_t token);
 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);
 nxt_bool_t njs_parser_has_side_effect(njs_parser_node_t *node);
+njs_token_t njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser,
+    njs_token_t token);
 u_char *njs_parser_trace_handler(nxt_trace_t *trace, nxt_trace_data_t *td,
     u_char *start);
 void njs_parser_lexer_error(njs_vm_t *vm, njs_parser_t *parser,
@@ -115,6 +117,18 @@ void njs_parser_node_error(njs_vm_t *vm, njs_parser_node_t *node,
     &(parser)->lexer->lexer_token->text
 
 
+#define njs_parser_key_hash(parser)                                           \
+    (parser)->lexer->lexer_token->key_hash
+
+
+#define njs_parser_number(parser)                                             \
+    (parser)->lexer->lexer_token->number
+
+
+#define njs_parser_token_line(parser)                                         \
+    (parser)->lexer->lexer_token->token_line
+
+
 #define njs_parser_syntax_error(vm, parser, fmt, ...)                         \
     njs_parser_lexer_error(vm, parser, NJS_OBJECT_SYNTAX_ERROR, fmt,          \
                            ##__VA_ARGS__)
@@ -166,6 +180,37 @@ njs_parser_node_new(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
 }
 
 
+nxt_inline njs_token_t
+njs_parser_match(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token,
+    njs_token_t match)
+{
+    if (nxt_fast_path(token == match)) {
+        return njs_parser_token(vm, parser);
+    }
+
+    return njs_parser_unexpected_token(vm, parser, token);
+}
+
+
+nxt_inline njs_variable_t *
+njs_parser_variable_add(njs_vm_t *vm, njs_parser_t *parser,
+    njs_variable_type_t type)
+{
+    return njs_variable_add(vm, parser->scope, njs_parser_text(parser),
+                            njs_parser_key_hash(parser), type);
+}
+
+
+nxt_inline njs_ret_t
+njs_parser_variable_reference(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node, njs_reference_type_t type)
+{
+    return njs_variable_reference(vm, parser->scope, node,
+                                  njs_parser_text(parser),
+                                  njs_parser_key_hash(parser), type);
+}
+
+
 nxt_inline njs_parser_scope_t *
 njs_parser_global_scope(njs_vm_t *vm)
 {
index 746ffc8f746325cee9b3b66b10bd0e53869b4468..7e21c445ed8edd0fdcffe97c34fd504bee0e11da 100644 (file)
@@ -62,6 +62,8 @@ static njs_token_t njs_parser_property_expression(njs_vm_t *vm,
     njs_parser_t *parser, njs_token_t token);
 static njs_token_t njs_parser_property_brackets(njs_vm_t *vm,
     njs_parser_t *parser, njs_token_t token);
+static njs_token_t njs_parser_arguments(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *parent);
 
 
 static const njs_parser_expression_t
@@ -1031,7 +1033,7 @@ njs_parser_property_brackets(njs_vm_t *vm, njs_parser_t *parser,
 }
 
 
-njs_token_t
+static njs_token_t
 njs_parser_arguments(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *parent)
 {
diff --git a/njs/njs_parser_terminal.c b/njs/njs_parser_terminal.c
new file mode 100644 (file)
index 0000000..186fe29
--- /dev/null
@@ -0,0 +1,866 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <njs_core.h>
+#include <njs_regexp.h>
+#include <string.h>
+
+
+static njs_parser_node_t *njs_parser_reference(njs_vm_t *vm,
+    njs_parser_t *parser, njs_token_t token, nxt_str_t *name, uint32_t hash,
+    uint32_t token_line);
+static nxt_int_t njs_parser_builtin(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node, njs_value_type_t type, nxt_str_t *name,
+    uint32_t hash);
+static njs_token_t njs_parser_object(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *obj);
+static njs_token_t njs_parser_array(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *obj);
+static njs_token_t njs_parser_escape_string_create(njs_vm_t *vm,
+    njs_parser_t *parser, njs_value_t *value);
+
+
+njs_token_t
+njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
+{
+    double             num;
+    njs_ret_t          ret;
+    njs_parser_node_t  *node;
+
+    if (token == NJS_TOKEN_OPEN_PARENTHESIS) {
+
+        token = njs_parser_token(vm, parser);
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
+        }
+
+        token = njs_parser_expression(vm, parser, token);
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
+        }
+
+        return njs_parser_match(vm, parser, token, NJS_TOKEN_CLOSE_PARENTHESIS);
+    }
+
+    if (token == NJS_TOKEN_FUNCTION) {
+        return njs_parser_function_expression(vm, parser);
+    }
+
+    switch (token) {
+
+    case NJS_TOKEN_OPEN_BRACE:
+        nxt_thread_log_debug("JS: OBJECT");
+
+        node = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT);
+        if (nxt_slow_path(node == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        parser->node = node;
+
+        token = njs_parser_object(vm, parser, node);
+
+        if (parser->node != node) {
+            /* The object is not empty. */
+            node->left = parser->node;
+            parser->node = node;
+        }
+
+        return token;
+
+    case NJS_TOKEN_OPEN_BRACKET:
+        nxt_thread_log_debug("JS: ARRAY");
+
+        node = njs_parser_node_new(vm, parser, NJS_TOKEN_ARRAY);
+        if (nxt_slow_path(node == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        parser->node = node;
+
+        token = njs_parser_array(vm, parser, node);
+
+        if (parser->node != node) {
+            /* The array is not empty. */
+            node->left = parser->node;
+            parser->node = node;
+        }
+
+        return token;
+
+    case NJS_TOKEN_DIVISION:
+        node = njs_parser_node_new(vm, parser, NJS_TOKEN_REGEXP);
+        if (nxt_slow_path(node == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        token = njs_regexp_literal(vm, parser, &node->u.value);
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
+        }
+
+        nxt_thread_log_debug("REGEX: '%V'", njs_parser_text(parser));
+
+        break;
+
+    case NJS_TOKEN_STRING:
+        nxt_thread_log_debug("JS: '%V'", njs_parser_text(parser));
+
+        node = njs_parser_node_new(vm, parser, NJS_TOKEN_STRING);
+        if (nxt_slow_path(node == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        ret = njs_parser_string_create(vm, &node->u.value);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        break;
+
+    case NJS_TOKEN_ESCAPE_STRING:
+        nxt_thread_log_debug("JS: '%V'", njs_parser_text(parser));
+
+        node = njs_parser_node_new(vm, parser, NJS_TOKEN_STRING);
+        if (nxt_slow_path(node == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        ret = njs_parser_escape_string_create(vm, parser, &node->u.value);
+        if (nxt_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);
+        nxt_thread_log_debug("JS: %f", num);
+
+        node = njs_parser_node_new(vm, parser, NJS_TOKEN_NUMBER);
+        if (nxt_slow_path(node == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        node->u.value.data.u.number = num;
+        node->u.value.type = NJS_NUMBER;
+        node->u.value.data.truth = njs_is_number_true(num);
+
+        break;
+
+    case NJS_TOKEN_BOOLEAN:
+        num = njs_parser_number(parser);
+        nxt_thread_log_debug("JS: boolean: %V", njs_parser_text(parser));
+
+        node = njs_parser_node_new(vm, parser, NJS_TOKEN_BOOLEAN);
+        if (nxt_slow_path(node == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        if (num == 0) {
+            node->u.value = njs_value_false;
+
+        } else {
+            node->u.value = njs_value_true;
+        }
+
+        break;
+
+    default:
+        node = njs_parser_reference(vm, parser, token,
+                                    njs_parser_text(parser),
+                                    njs_parser_key_hash(parser),
+                                    njs_parser_token_line(parser));
+
+        if (nxt_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_t token,
+    nxt_str_t *name, uint32_t hash, uint32_t token_line)
+{
+    njs_ret_t           ret;
+    njs_value_t         *ext;
+    njs_parser_node_t   *node;
+    njs_parser_scope_t  *scope;
+
+    node = njs_parser_node_new(vm, parser, token);
+    if (nxt_slow_path(node == NULL)) {
+        return NULL;
+    }
+
+    switch (token) {
+
+    case NJS_TOKEN_NULL:
+        nxt_thread_log_debug("JS: null");
+
+        node->u.value = njs_value_null;
+        break;
+
+    case NJS_TOKEN_UNDEFINED:
+        nxt_thread_log_debug("JS: undefined");
+
+        node->u.value = njs_value_undefined;
+        break;
+
+    case NJS_TOKEN_THIS:
+        nxt_thread_log_debug("JS: this");
+
+        scope = parser->scope;
+
+        while (scope->type != NJS_SCOPE_GLOBAL) {
+            if (scope->type == NJS_SCOPE_FUNCTION) {
+                node->index = NJS_INDEX_THIS;
+                break;
+            }
+
+            scope = scope->parent;
+        }
+
+        if (node->index == NJS_INDEX_THIS) {
+            break;
+        }
+
+        node->token = NJS_TOKEN_GLOBAL_THIS;
+
+        /* Fall through. */
+
+    case NJS_TOKEN_NJS:
+    case NJS_TOKEN_MATH:
+    case NJS_TOKEN_JSON:
+        ret = njs_parser_builtin(vm, parser, node, NJS_OBJECT, name, hash);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NULL;
+        }
+
+        break;
+
+    case NJS_TOKEN_ARGUMENTS:
+        nxt_thread_log_debug("JS: arguments");
+
+        if (parser->scope->type <= NJS_SCOPE_GLOBAL) {
+            njs_parser_syntax_error(vm, parser, "\"%V\" object "
+                                    "in global scope", name);
+
+            return NULL;
+        }
+
+        parser->scope->arguments_object = 1;
+
+        break;
+
+    case NJS_TOKEN_OBJECT_CONSTRUCTOR:
+        node->index = NJS_INDEX_OBJECT;
+        break;
+
+    case NJS_TOKEN_ARRAY_CONSTRUCTOR:
+        node->index = NJS_INDEX_ARRAY;
+        break;
+
+    case NJS_TOKEN_BOOLEAN_CONSTRUCTOR:
+        node->index = NJS_INDEX_BOOLEAN;
+        break;
+
+    case NJS_TOKEN_NUMBER_CONSTRUCTOR:
+        node->index = NJS_INDEX_NUMBER;
+        break;
+
+    case NJS_TOKEN_STRING_CONSTRUCTOR:
+        node->index = NJS_INDEX_STRING;
+        break;
+
+    case NJS_TOKEN_FUNCTION_CONSTRUCTOR:
+        node->index = NJS_INDEX_FUNCTION;
+        break;
+
+    case NJS_TOKEN_REGEXP_CONSTRUCTOR:
+        node->index = NJS_INDEX_REGEXP;
+        break;
+
+    case NJS_TOKEN_DATE_CONSTRUCTOR:
+        node->index = NJS_INDEX_DATE;
+        break;
+
+    case NJS_TOKEN_ERROR_CONSTRUCTOR:
+        node->index = NJS_INDEX_OBJECT_ERROR;
+        break;
+
+    case NJS_TOKEN_EVAL_ERROR_CONSTRUCTOR:
+        node->index = NJS_INDEX_OBJECT_EVAL_ERROR;
+        break;
+
+    case NJS_TOKEN_INTERNAL_ERROR_CONSTRUCTOR:
+        node->index = NJS_INDEX_OBJECT_INTERNAL_ERROR;
+        break;
+
+    case NJS_TOKEN_RANGE_ERROR_CONSTRUCTOR:
+        node->index = NJS_INDEX_OBJECT_RANGE_ERROR;
+        break;
+
+    case NJS_TOKEN_REF_ERROR_CONSTRUCTOR:
+        node->index = NJS_INDEX_OBJECT_REF_ERROR;
+        break;
+
+    case NJS_TOKEN_SYNTAX_ERROR_CONSTRUCTOR:
+        node->index = NJS_INDEX_OBJECT_SYNTAX_ERROR;
+        break;
+
+    case NJS_TOKEN_TYPE_ERROR_CONSTRUCTOR:
+        node->index = NJS_INDEX_OBJECT_TYPE_ERROR;
+        break;
+
+    case NJS_TOKEN_URI_ERROR_CONSTRUCTOR:
+        node->index = NJS_INDEX_OBJECT_URI_ERROR;
+        break;
+
+    case NJS_TOKEN_MEMORY_ERROR_CONSTRUCTOR:
+        node->index = NJS_INDEX_OBJECT_MEMORY_ERROR;
+        break;
+
+    case NJS_TOKEN_EVAL:
+    case NJS_TOKEN_TO_STRING:
+    case NJS_TOKEN_IS_NAN:
+    case NJS_TOKEN_IS_FINITE:
+    case NJS_TOKEN_PARSE_INT:
+    case NJS_TOKEN_PARSE_FLOAT:
+    case NJS_TOKEN_ENCODE_URI:
+    case NJS_TOKEN_ENCODE_URI_COMPONENT:
+    case NJS_TOKEN_DECODE_URI:
+    case NJS_TOKEN_DECODE_URI_COMPONENT:
+    case NJS_TOKEN_REQUIRE:
+    case NJS_TOKEN_SET_TIMEOUT:
+    case NJS_TOKEN_SET_IMMEDIATE:
+    case NJS_TOKEN_CLEAR_TIMEOUT:
+        ret = njs_parser_builtin(vm, parser, node, NJS_FUNCTION, name, hash);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NULL;
+        }
+
+        break;
+
+    case NJS_TOKEN_NAME:
+        nxt_thread_log_debug("JS: %V", name);
+
+        node->token_line = token_line;
+
+        ext = njs_external_lookup(vm, name, hash);
+
+        if (ext != NULL) {
+            node->token = NJS_TOKEN_EXTERNAL;
+            node->u.value = *ext;
+            node->index = (njs_index_t) ext;
+            break;
+        }
+
+        ret = njs_variable_reference(vm, parser->scope, node, name, hash,
+                                     NJS_REFERENCE);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NULL;
+        }
+
+        break;
+
+    default:
+        (void) njs_parser_unexpected_token(vm, parser, token);
+        return NULL;
+    }
+
+    return node;
+}
+
+
+static nxt_int_t
+njs_parser_builtin(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node,
+    njs_value_type_t type, nxt_str_t *name, uint32_t hash)
+{
+    njs_ret_t           ret;
+    nxt_uint_t          index;
+    njs_variable_t      *var;
+    njs_parser_scope_t  *scope;
+
+    scope = njs_parser_global_scope(vm);
+
+    var = njs_variable_add(vm, scope, name, hash, NJS_VARIABLE_VAR);
+    if (nxt_slow_path(var == NULL)) {
+        return NXT_ERROR;
+    }
+
+    /* TODO: once */
+    switch (type) {
+    case NJS_OBJECT:
+        index = node->token - NJS_TOKEN_FIRST_OBJECT;
+        var->value.data.u.object = &vm->shared->objects[index];
+        break;
+
+    case NJS_FUNCTION:
+        index = node->token - NJS_TOKEN_FIRST_FUNCTION;
+        var->value.data.u.function = &vm->shared->functions[index];
+        break;
+
+    default:
+        return NXT_ERROR;
+    }
+
+    var->value.type = type;
+    var->value.data.truth = 1;
+
+    ret = njs_variable_reference(vm, scope, node, name, hash, NJS_REFERENCE);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return NXT_ERROR;
+    }
+
+    return NXT_OK;
+}
+
+
+/*
+ * ES6: 12.2.6 Object Initializer
+ * Supported syntax:
+ *   PropertyDefinition:
+ *     PropertyName : AssignmentExpression
+ *     IdentifierReference
+ *   PropertyName:
+ *    IdentifierName, StringLiteral, NumericLiteral.
+ */
+static njs_token_t
+njs_parser_object(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj)
+{
+    uint32_t           hash, token_line;
+    nxt_str_t          name;
+    njs_token_t        token;
+    njs_lexer_t        *lexer;
+    njs_parser_node_t  *stmt, *assign, *object, *propref, *left, *expression;
+
+    left = NULL;
+    lexer = parser->lexer;
+
+    /* GCC and Clang complain about uninitialized hash. */
+    hash = 0;
+    token_line = 0;
+
+    object = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT_VALUE);
+    if (nxt_slow_path(object == NULL)) {
+        return NJS_TOKEN_ERROR;
+    }
+
+    object->u.object = obj;
+
+    for ( ;; ) {
+        token = njs_parser_property_token(vm, parser);
+
+        name.start = NULL;
+
+        switch (token) {
+
+        case NJS_TOKEN_CLOSE_BRACE:
+            return njs_parser_token(vm, parser);
+
+        case NJS_TOKEN_NAME:
+            name = *njs_parser_text(parser);
+
+            hash = njs_parser_key_hash(parser);
+            token_line = njs_parser_token_line(parser);
+
+            token = njs_parser_token(vm, parser);
+            break;
+
+        case NJS_TOKEN_NUMBER:
+        case NJS_TOKEN_STRING:
+        case NJS_TOKEN_ESCAPE_STRING:
+            token = njs_parser_terminal(vm, parser, token);
+            break;
+
+        default:
+            return NJS_TOKEN_ILLEGAL;
+        }
+
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
+        }
+
+        propref = njs_parser_node_new(vm, parser, NJS_TOKEN_PROPERTY);
+        if (nxt_slow_path(propref == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        propref->left = object;
+        propref->right = parser->node;
+
+        if (name.start != NULL
+            && (token == NJS_TOKEN_COMMA || token == NJS_TOKEN_CLOSE_BRACE)
+            && lexer->property_token != NJS_TOKEN_THIS
+            && lexer->property_token != NJS_TOKEN_GLOBAL_THIS)
+        {
+            expression = njs_parser_reference(vm, parser, lexer->property_token,
+                                              &name, hash, token_line);
+            if (nxt_slow_path(expression == NULL)) {
+                return NJS_TOKEN_ERROR;
+            }
+
+        } else {
+            token = njs_parser_match(vm, parser, token, NJS_TOKEN_COLON);
+            if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                return token;
+            }
+
+            token = njs_parser_assignment_expression(vm, parser, token);
+            if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                return token;
+            }
+
+            expression = parser->node;
+        }
+
+        assign = njs_parser_node_new(vm, parser, NJS_TOKEN_ASSIGNMENT);
+        if (nxt_slow_path(assign == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        assign->u.operation = njs_vmcode_move;
+        assign->left = propref;
+        assign->right = expression;
+
+        stmt = njs_parser_node_new(vm, parser, NJS_TOKEN_STATEMENT);
+        if (nxt_slow_path(stmt == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        stmt->left = left;
+        stmt->right = assign;
+
+        parser->node = stmt;
+
+        left = stmt;
+
+        if (token == NJS_TOKEN_CLOSE_BRACE) {
+            return njs_parser_token(vm, parser);
+        }
+
+        if (nxt_slow_path(token != NJS_TOKEN_COMMA)) {
+            return NJS_TOKEN_ILLEGAL;
+        }
+    }
+}
+
+
+static njs_token_t
+njs_parser_array(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *obj)
+{
+    nxt_uint_t         index;
+    njs_token_t        token;
+    njs_parser_node_t  *stmt, *assign, *object, *propref, *left, *node;
+
+    index = 0;
+    left = NULL;
+
+    for ( ;; ) {
+        token = njs_parser_token(vm, parser);
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
+        }
+
+        if (token == NJS_TOKEN_CLOSE_BRACKET) {
+            break;
+        }
+
+        if (token == NJS_TOKEN_COMMA) {
+            obj->ctor = 1;
+            index++;
+            continue;
+        }
+
+        node = njs_parser_node_new(vm, parser, NJS_TOKEN_NUMBER);
+        if (nxt_slow_path(node == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        node->u.value.data.u.number = index;
+        node->u.value.type = NJS_NUMBER;
+        node->u.value.data.truth = (index != 0);
+        index++;
+
+        object = njs_parser_node_new(vm, parser, NJS_TOKEN_OBJECT_VALUE);
+        if (nxt_slow_path(object == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        object->u.object = obj;
+
+        propref = njs_parser_node_new(vm, parser, NJS_TOKEN_PROPERTY);
+        if (nxt_slow_path(propref == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        propref->left = object;
+        propref->right = node;
+
+        token = njs_parser_assignment_expression(vm, parser, token);
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
+        }
+
+        assign = njs_parser_node_new(vm, parser, NJS_TOKEN_ASSIGNMENT);
+        if (nxt_slow_path(assign == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        assign->u.operation = njs_vmcode_move;
+        assign->left = propref;
+        assign->right = parser->node;
+
+        stmt = njs_parser_node_new(vm, parser, NJS_TOKEN_STATEMENT);
+        if (nxt_slow_path(stmt == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        stmt->left = left;
+        stmt->right = assign;
+
+        parser->node = stmt;
+        left = stmt;
+
+        obj->ctor = 0;
+
+        if (token == NJS_TOKEN_CLOSE_BRACKET) {
+            break;
+        }
+
+        if (nxt_slow_path(token != NJS_TOKEN_COMMA)) {
+            return NJS_TOKEN_ILLEGAL;
+        }
+    }
+
+    obj->u.length = index;
+
+    return njs_parser_token(vm, parser);
+}
+
+
+nxt_int_t
+njs_parser_string_create(njs_vm_t *vm, njs_value_t *value)
+{
+    u_char     *p;
+    ssize_t    length;
+    nxt_str_t  *src;
+
+    src = njs_parser_text(vm->parser);
+
+    length = nxt_utf8_length(src->start, src->length);
+
+    if (nxt_slow_path(length < 0)) {
+        length = 0;
+    }
+
+    p = njs_string_alloc(vm, value, src->length, length);
+
+    if (nxt_fast_path(p != NULL)) {
+        memcpy(p, src->start, src->length);
+
+        if (length > NJS_STRING_MAP_STRIDE && (size_t) length != src->length) {
+            njs_string_offset_map_init(p, src->length);
+        }
+
+        return NXT_OK;
+    }
+
+    return NXT_ERROR;
+}
+
+
+static njs_token_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      u;
+    nxt_str_t     *string;
+    const u_char  *p, *src, *end, *hex_end;
+
+    start = NULL;
+    dst = NULL;
+
+    for ( ;; ) {
+        /*
+         * The loop runs twice: at the first step string size and
+         * UTF-8 length are evaluated.  Then the string is allocated
+         * and at the second step string content is copied.
+         */
+        size = 0;
+        length = 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':
+                    hex_length = 4;
+                    /*
+                     * A character after "u" can be safely tested here
+                     * because there is always a closing quote at the
+                     * end of string: ...\u".
+                     */
+                    if (*src != '{') {
+                        goto hex_length_test;
+                    }
+
+                    src++;
+                    hex_length = 0;
+                    hex_end = end;
+
+                    goto hex;
+
+                case 'x':
+                    hex_length = 2;
+                    goto hex_length_test;
+
+                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:
+                    break;
+                }
+            }
+
+            size++;
+            length++;
+
+            if (dst != NULL) {
+                *dst++ = c;
+            }
+
+            continue;
+
+        hex_length_test:
+
+            hex_end = src + hex_length;
+
+            if (hex_end > end) {
+                goto invalid;
+            }
+
+        hex:
+
+            p = src;
+            u = njs_number_hex_parse(&src, hex_end);
+
+            if (hex_length != 0) {
+                if (src != hex_end) {
+                    goto invalid;
+                }
+
+            } else {
+                if (src == p || (src - p) > 6) {
+                    goto invalid;
+                }
+
+                if (src == end || *src++ != '}') {
+                    goto invalid;
+                }
+            }
+
+            size += nxt_utf8_size(u);
+            length++;
+
+            if (dst != NULL) {
+                dst = nxt_utf8_encode(dst, (uint32_t) u);
+                if (dst == NULL) {
+                    goto invalid;
+                }
+            }
+        }
+
+        if (start != NULL) {
+            if (length > NJS_STRING_MAP_STRIDE && length != size) {
+                njs_string_offset_map_init(start, size);
+            }
+
+            return NJS_TOKEN_STRING;
+        }
+
+        start = njs_string_alloc(vm, value, size, length);
+        if (nxt_slow_path(start == NULL)) {
+            return NJS_TOKEN_ERROR;
+        }
+
+        dst = start;
+    }
+
+invalid:
+
+    njs_parser_syntax_error(vm, parser, "Invalid Unicode code point \"%V\"",
+                            njs_parser_text(parser));
+
+    return NJS_TOKEN_ILLEGAL;
+}