}
+int64_t
+njs_hex_number_parse(u_char *p, u_char *end)
+{
+ int8_t d;
+ uint32_t n;
+
+ static const int8_t hex[256]
+ nxt_aligned(32) =
+ {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+
+ n = 0;
+
+ while (p < end) {
+ d = hex[*p++];
+
+ if (nxt_slow_path(d < 0)) {
+ return -1;
+ }
+
+ n = (n << 4) + d;
+ }
+
+ return n;
+}
+
+
njs_ret_t
njs_number_to_string(njs_vm_t *vm, njs_value_t *string,
const njs_value_t *number)
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_value_t *value);
njs_parser_node_t *
break;
+ case NJS_TOKEN_ESCAPE_STRING:
+ node->token = NJS_TOKEN_STRING;
+
+ nxt_thread_log_debug("JS: '%V'", &parser->lexer->text);
+
+ ret = njs_parser_escape_string_create(vm, &node->u.value);
+ if (nxt_slow_path(ret != NJS_TOKEN_STRING)) {
+ return ret;
+ }
+
+ break;
+
case NJS_TOKEN_NUMBER:
nxt_thread_log_debug("JS: %f", parser->lexer->number);
}
+static njs_token_t
+njs_parser_escape_string_create(njs_vm_t *vm, njs_value_t *value)
+{
+ u_char c, *p, *start, *dst, *src, *end, *hex_end;
+ size_t size, length, hex_length, skip;
+ int64_t u;
+
+ 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;
+
+ src = vm->parser->lexer->text.data;
+ end = src + vm->parser->lexer->text.len;
+
+ 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':
+ skip = 0;
+ 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 == '{') {
+ hex_length = 0;
+ src++;
+
+ for (p = src; p < end && *p != '}'; p++) {
+ hex_length++;
+ }
+
+ if (hex_length == 0 || hex_length > 6) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ skip = 1;
+ }
+
+ goto hex;
+
+ case 'x':
+ skip = 0;
+ hex_length = 2;
+ goto hex;
+
+ 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:
+
+ hex_end = src + hex_length;
+
+ if (hex_end > end) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ u = njs_hex_number_parse(src, hex_end);
+ if (nxt_slow_path(u < 0)) {
+ return NJS_TOKEN_ILLEGAL;
+ }
+
+ src = hex_end + skip;
+ size += nxt_utf8_size(u);
+ length++;
+
+ if (dst != NULL) {
+ dst = nxt_utf8_encode(dst, (uint32_t) u);
+ }
+ }
+
+ if (start != NULL) {
+ if (length > NJS_STRING_MAP_OFFSET && 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;
+ }
+}
+
+
njs_index_t
njs_parser_index(njs_parser_t *parser, uint32_t scope)
{
nxt_string("1.7976931348623157e+308") },
#endif
-#if 0
- { nxt_string("var a = 'a\\'b'"),
- nxt_string("a'b") },
-#endif
-
{ nxt_string("+1"),
nxt_string("1") },
" a = b"),
nxt_string("abcdefghijklmnop") },
+ /* Escape strings. */
+
+ { nxt_string("'\\a \\' \\\" \\\\ \\0 \\b \\f \\n \\r \\t \\v'"),
+ nxt_string("a ' \" \\ \0 \b \f \n \r \t \v") },
+
+ { nxt_string("'a\\\nb'"),
+ nxt_string("ab") },
+
+ { nxt_string("'a\\\rb'"),
+ nxt_string("ab") },
+
+ { nxt_string("'a\\\r\nb'"),
+ nxt_string("ab") },
+
+ { nxt_string("'\\'"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("'\\u03B1'"),
+ nxt_string("α") },
+
+ { nxt_string("'\\u'"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("'\\u03B'"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("'\\u{61}\\u{3B1}\\u{20AC}'"),
+ nxt_string("aα€") },
+
+ { nxt_string("'\\u'"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("'\\u{'"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("'\\u{}'"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("'\\u{1234567}'"),
+ nxt_string("SyntaxError") },
+
+ { nxt_string("'\\x61'"),
+ nxt_string("a") },
+
{ nxt_string("''.length"),
nxt_string("0") },