From: hongzhidao Date: Sun, 31 Mar 2019 14:59:04 +0000 (+0800) Subject: Refactored out terminal tokens into njs_parser_terminal.c. X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/static/gitweb.js?a=commitdiff_plain;h=28437348f08fb0d114fb93d36c47ce093977eeff;p=njs.git Refactored out terminal tokens into njs_parser_terminal.c. --- diff --git a/auto/sources b/auto/sources index 525e5467..4b925fac 100644 --- a/auto/sources +++ b/auto/sources @@ -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 \ diff --git a/njs/njs_parser.c b/njs/njs_parser.c index 6b2f14db..eff28888 100644 --- a/njs/njs_parser.c +++ b/njs/njs_parser.c @@ -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) { diff --git a/njs/njs_parser.h b/njs/njs_parser.h index 505d3452..ad67f99a 100644 --- a/njs/njs_parser.h +++ b/njs/njs_parser.h @@ -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) { diff --git a/njs/njs_parser_expression.c b/njs/njs_parser_expression.c index 746ffc8f..7e21c445 100644 --- a/njs/njs_parser_expression.c +++ b/njs/njs_parser_expression.c @@ -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 index 00000000..186fe290 --- /dev/null +++ b/njs/njs_parser_terminal.c @@ -0,0 +1,866 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include +#include +#include + + +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; +}