Implement tagged template literals (#3456)

Missing features: snapshot support

JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
This commit is contained in:
Robert Fancsik
2019-12-19 19:10:45 +01:00
committed by GitHub
parent 7bfbc701d8
commit 9596a7e1d6
28 changed files with 1229 additions and 314 deletions
+9
View File
@@ -662,6 +662,14 @@
VM_OC_YIELD | VM_OC_GET_STACK) \
CBC_OPCODE (CBC_EXT_RETURN, CBC_NO_FLAG, -1, \
VM_OC_EXT_RETURN | VM_OC_GET_STACK) \
CBC_OPCODE (CBC_EXT_STRING_CONCAT, CBC_NO_FLAG, -1, \
VM_OC_STRING_CONCAT | VM_OC_GET_STACK_STACK | VM_OC_PUT_STACK) \
CBC_OPCODE (CBC_EXT_STRING_CONCAT_RIGHT_LITERAL, CBC_HAS_LITERAL_ARG, 0, \
VM_OC_STRING_CONCAT | VM_OC_GET_STACK_LITERAL | VM_OC_PUT_STACK) \
CBC_OPCODE (CBC_EXT_STRING_CONCAT_TWO_LITERALS, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2, 1, \
VM_OC_STRING_CONCAT | VM_OC_GET_LITERAL_LITERAL | VM_OC_PUT_STACK) \
CBC_OPCODE (CBC_EXT_GET_TAGGED_TEMPLATE_LITERAL, CBC_HAS_BYTE_ARG, 1, \
VM_OC_GET_TEMPLATE_OBJECT | VM_OC_PUT_STACK) \
\
/* Last opcode (not a real opcode). */ \
CBC_OPCODE (CBC_EXT_END, CBC_NO_FLAG, 0, \
@@ -753,6 +761,7 @@ typedef enum
CBC_CODE_FLAGS_CONSTRUCTOR = (1u << 10), /**< this function is a constructor */
CBC_CODE_FLAGS_GENERATOR = (1u << 11), /**< this function is a generator */
CBC_CODE_FLAGS_REST_PARAMETER = (1u << 12), /**< this function has rest parameter */
CBC_CODE_FLAG_HAS_TAGGED_LITERALS = (1u << 13), /**< this function has tagged template literal list */
} cbc_code_flags;
/**
+267 -227
View File
@@ -874,8 +874,15 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */
* Parse string.
*/
void
lexer_parse_string (parser_context_t *context_p) /**< context */
lexer_parse_string (parser_context_t *context_p, /**< context */
lexer_string_options_t opts) /**< options */
{
#if ENABLED (JERRY_ES2015)
const size_t raw_length_inc = (opts & LEXER_STRING_RAW) ? 1 : 0;
#else /* ENABLED (JERRY_ES2015) */
JERRY_UNUSED (opts);
#endif /* ENABLED (JERRY_ES2015) */
uint8_t str_end_character = context_p->source_p[0];
const uint8_t *source_p = context_p->source_p + 1;
const uint8_t *string_start_p = source_p;
@@ -918,6 +925,10 @@ lexer_parse_string (parser_context_t *context_p) /**< context */
continue;
}
#if ENABLED (JERRY_ES2015)
length += raw_length_inc;
#endif /* ENABLED (JERRY_ES2015) */
has_escape = true;
/* Newline is ignored. */
@@ -931,6 +942,9 @@ lexer_parse_string (parser_context_t *context_p) /**< context */
}
line++;
#if ENABLED (JERRY_ES2015)
length += raw_length_inc;
#endif /* ENABLED (JERRY_ES2015) */
column = 1;
continue;
}
@@ -938,6 +952,9 @@ lexer_parse_string (parser_context_t *context_p) /**< context */
{
source_p++;
line++;
#if ENABLED (JERRY_ES2015)
length += raw_length_inc;
#endif /* ENABLED (JERRY_ES2015) */
column = 1;
continue;
}
@@ -949,6 +966,13 @@ lexer_parse_string (parser_context_t *context_p) /**< context */
continue;
}
#if ENABLED (JERRY_ES2015)
if (opts & LEXER_STRING_RAW)
{
continue;
}
#endif /* ENABLED (JERRY_ES2015) */
if (*source_p == LIT_CHAR_0
&& source_p + 1 < source_end_p
&& (*(source_p + 1) < LIT_CHAR_0 || *(source_p + 1) > LIT_CHAR_9))
@@ -1086,16 +1110,17 @@ lexer_parse_string (parser_context_t *context_p) /**< context */
#if ENABLED (JERRY_ES2015)
else if (str_end_character == LIT_CHAR_GRAVE_ACCENT)
{
/* Newline (without backslash) is part of the string. */
/* Newline (without backslash) is part of the string.
Note: ECMAScript v6, 11.8.6.1 <CR> or <CR><LF> are both normalized to <LF> */
if (*source_p == LIT_CHAR_CR)
{
has_escape = true;
source_p++;
length++;
if (source_p < source_end_p
&& *source_p == LIT_CHAR_LF)
{
source_p++;
length++;
}
line++;
column = 1;
@@ -1658,7 +1683,7 @@ lexer_next_token (parser_context_t *context_p) /**< context */
case LIT_CHAR_GRAVE_ACCENT:
#endif /* ENABLED (JERRY_ES2015) */
{
lexer_parse_string (context_p);
lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
return;
}
@@ -1931,11 +1956,6 @@ lexer_process_char_literal (parser_context_t *context_p, /**< context */
context_p->literal_count++;
} /* lexer_process_char_literal */
/**
* Maximum local buffer size for identifiers which contains escape sequences.
*/
#define LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE 48
/**
* Convert an ident with escapes to a utf8 string.
*/
@@ -1974,6 +1994,238 @@ lexer_convert_ident_to_cesu8 (uint8_t *destination_p, /**< destination string */
while (destination_p < destination_end_p);
} /* lexer_convert_ident_to_cesu8 */
/**
* Convert literal to character sequence
*/
const uint8_t *
lexer_convert_literal_to_chars (parser_context_t *context_p, /**< context */
const lexer_lit_location_t *literal_p, /**< literal location */
uint8_t *local_byte_array_p, /**< local byte array to store chars */
lexer_string_options_t opts) /**< options */
{
JERRY_ASSERT (context_p->u.allocated_buffer_p == NULL);
if (!literal_p->has_escape)
{
return literal_p->char_p;
}
uint8_t *destination_start_p;
if (literal_p->length > LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE)
{
context_p->u.allocated_buffer_p = (uint8_t *) parser_malloc_local (context_p, literal_p->length);
context_p->allocated_buffer_size = literal_p->length;
destination_start_p = context_p->u.allocated_buffer_p;
}
else
{
destination_start_p = local_byte_array_p;
}
if (literal_p->type == LEXER_IDENT_LITERAL)
{
lexer_convert_ident_to_cesu8 (destination_start_p, literal_p->char_p, literal_p->length);
return destination_start_p;
}
const uint8_t *source_p = literal_p->char_p;
uint8_t *destination_p = destination_start_p;
uint8_t str_end_character = source_p[-1];
#if ENABLED (JERRY_ES2015)
if (str_end_character == LIT_CHAR_RIGHT_BRACE)
{
str_end_character = LIT_CHAR_GRAVE_ACCENT;
}
bool is_raw = (opts & LEXER_STRING_RAW) != 0;
#else /* !ENABLED (JERRY_ES2015) */
JERRY_UNUSED (opts);
bool is_raw = false;
#endif /* ENABLED (JERRY_ES2015) */
while (true)
{
if (*source_p == str_end_character)
{
break;
}
if (*source_p == LIT_CHAR_BACKSLASH && !is_raw)
{
uint8_t conv_character;
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
/* Newline is ignored. */
if (*source_p == LIT_CHAR_CR)
{
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p == LIT_CHAR_LF)
{
source_p++;
}
continue;
}
else if (*source_p == LIT_CHAR_LF)
{
source_p++;
continue;
}
else if (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p))
{
source_p += 3;
continue;
}
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_3)
{
lit_code_point_t octal_number = (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
{
octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
{
octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
}
}
destination_p += lit_code_point_to_cesu8_bytes (destination_p, octal_number);
continue;
}
if (*source_p >= LIT_CHAR_4 && *source_p <= LIT_CHAR_7)
{
uint32_t octal_number = (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
{
octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
}
*destination_p++ = (uint8_t) octal_number;
continue;
}
if (*source_p == LIT_CHAR_LOWERCASE_X || *source_p == LIT_CHAR_LOWERCASE_U)
{
source_p++;
destination_p += lit_code_point_to_cesu8_bytes (destination_p,
lexer_unchecked_hex_to_character (&source_p));
continue;
}
conv_character = *source_p;
switch (*source_p)
{
case LIT_CHAR_LOWERCASE_B:
{
conv_character = 0x08;
break;
}
case LIT_CHAR_LOWERCASE_T:
{
conv_character = 0x09;
break;
}
case LIT_CHAR_LOWERCASE_N:
{
conv_character = 0x0a;
break;
}
case LIT_CHAR_LOWERCASE_V:
{
conv_character = 0x0b;
break;
}
case LIT_CHAR_LOWERCASE_F:
{
conv_character = 0x0c;
break;
}
case LIT_CHAR_LOWERCASE_R:
{
conv_character = 0x0d;
break;
}
}
if (conv_character != *source_p)
{
*destination_p++ = conv_character;
source_p++;
continue;
}
}
#if ENABLED (JERRY_ES2015)
else if (str_end_character == LIT_CHAR_GRAVE_ACCENT)
{
if (source_p[0] == LIT_CHAR_DOLLAR_SIGN
&& source_p[1] == LIT_CHAR_LEFT_BRACE)
{
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
break;
}
if (*source_p == LIT_CHAR_CR)
{
*destination_p++ = LIT_CHAR_LF;
source_p++;
if (*source_p != str_end_character
&& *source_p == LIT_CHAR_LF)
{
source_p++;
}
continue;
}
}
#endif /* ENABLED (JERRY_ES2015) */
if (*source_p >= LEXER_UTF8_4BYTE_START)
{
/* Processing 4 byte unicode sequence (even if it is
* after a backslash). Always converted to two 3 byte
* long sequence. */
lit_four_byte_utf8_char_to_cesu8 (destination_p, source_p);
destination_p += 6;
source_p += 4;
continue;
}
*destination_p++ = *source_p++;
/* There is no need to check the source_end_p
* since the string is terminated by a quotation mark. */
while (IS_UTF8_INTERMEDIATE_OCTET (*source_p))
{
*destination_p++ = *source_p++;
}
}
JERRY_ASSERT (destination_p == destination_start_p + literal_p->length);
return destination_start_p;
} /* lexer_convert_literal_to_chars */
/**
* Construct a literal object from an identifier.
*/
@@ -1982,213 +2234,11 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */
const lexer_lit_location_t *literal_p, /**< literal location */
uint8_t literal_type) /**< final literal type */
{
uint8_t *destination_start_p;
const uint8_t *source_p;
uint8_t local_byte_array[LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE];
JERRY_ASSERT (literal_p->type == LEXER_IDENT_LITERAL
|| literal_p->type == LEXER_STRING_LITERAL);
JERRY_ASSERT (context_p->u.allocated_buffer_p == NULL);
destination_start_p = local_byte_array;
source_p = literal_p->char_p;
if (literal_p->has_escape)
{
uint8_t *destination_p;
if (literal_p->length > LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE)
{
destination_start_p = (uint8_t *) parser_malloc_local (context_p, literal_p->length);
context_p->u.allocated_buffer_p = destination_start_p;
context_p->allocated_buffer_size = literal_p->length;
}
destination_p = destination_start_p;
if (literal_p->type == LEXER_IDENT_LITERAL)
{
lexer_convert_ident_to_cesu8 (destination_start_p, source_p, literal_p->length);
}
else
{
uint8_t str_end_character = source_p[-1];
#if ENABLED (JERRY_ES2015)
if (str_end_character == LIT_CHAR_RIGHT_BRACE)
{
str_end_character = LIT_CHAR_GRAVE_ACCENT;
}
#endif /* ENABLED (JERRY_ES2015) */
while (true)
{
if (*source_p == str_end_character)
{
break;
}
if (*source_p == LIT_CHAR_BACKSLASH)
{
uint8_t conv_character;
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
/* Newline is ignored. */
if (*source_p == LIT_CHAR_CR)
{
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p == LIT_CHAR_LF)
{
source_p++;
}
continue;
}
else if (*source_p == LIT_CHAR_LF)
{
source_p++;
continue;
}
else if (*source_p == LEXER_NEWLINE_LS_PS_BYTE_1 && LEXER_NEWLINE_LS_PS_BYTE_23 (source_p))
{
source_p += 3;
continue;
}
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_3)
{
lit_code_point_t octal_number = (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
{
octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
{
octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
}
}
destination_p += lit_code_point_to_cesu8_bytes (destination_p, octal_number);
continue;
}
if (*source_p >= LIT_CHAR_4 && *source_p <= LIT_CHAR_7)
{
uint32_t octal_number = (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
if (*source_p >= LIT_CHAR_0 && *source_p <= LIT_CHAR_7)
{
octal_number = octal_number * 8 + (uint32_t) (*source_p - LIT_CHAR_0);
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
}
*destination_p++ = (uint8_t) octal_number;
continue;
}
if (*source_p == LIT_CHAR_LOWERCASE_X || *source_p == LIT_CHAR_LOWERCASE_U)
{
source_p++;
destination_p += lit_code_point_to_cesu8_bytes (destination_p,
lexer_unchecked_hex_to_character (&source_p));
continue;
}
conv_character = *source_p;
switch (*source_p)
{
case LIT_CHAR_LOWERCASE_B:
{
conv_character = 0x08;
break;
}
case LIT_CHAR_LOWERCASE_T:
{
conv_character = 0x09;
break;
}
case LIT_CHAR_LOWERCASE_N:
{
conv_character = 0x0a;
break;
}
case LIT_CHAR_LOWERCASE_V:
{
conv_character = 0x0b;
break;
}
case LIT_CHAR_LOWERCASE_F:
{
conv_character = 0x0c;
break;
}
case LIT_CHAR_LOWERCASE_R:
{
conv_character = 0x0d;
break;
}
}
if (conv_character != *source_p)
{
*destination_p++ = conv_character;
source_p++;
continue;
}
}
#if ENABLED (JERRY_ES2015)
else if (str_end_character == LIT_CHAR_GRAVE_ACCENT
&& source_p[0] == LIT_CHAR_DOLLAR_SIGN
&& source_p[1] == LIT_CHAR_LEFT_BRACE)
{
source_p++;
JERRY_ASSERT (source_p < context_p->source_end_p);
break;
}
#endif /* ENABLED (JERRY_ES2015) */
if (*source_p >= LEXER_UTF8_4BYTE_START)
{
/* Processing 4 byte unicode sequence (even if it is
* after a backslash). Always converted to two 3 byte
* long sequence. */
lit_four_byte_utf8_char_to_cesu8 (destination_p, source_p);
destination_p += 6;
source_p += 4;
continue;
}
*destination_p++ = *source_p++;
/* There is no need to check the source_end_p
* since the string is terminated by a quotation mark. */
while (IS_UTF8_INTERMEDIATE_OCTET (*source_p))
{
*destination_p++ = *source_p++;
}
}
JERRY_ASSERT (destination_p == destination_start_p + literal_p->length);
}
source_p = destination_start_p;
}
const uint8_t *source_p = lexer_convert_literal_to_chars (context_p,
literal_p,
local_byte_array,
LEXER_STRING_NO_OPTS);
lexer_process_char_literal (context_p,
source_p,
@@ -2196,20 +2246,10 @@ lexer_construct_literal_object (parser_context_t *context_p, /**< context */
literal_type,
literal_p->has_escape);
if (destination_start_p != local_byte_array)
{
JERRY_ASSERT (context_p->u.allocated_buffer_p == destination_start_p);
context_p->u.allocated_buffer_p = NULL;
parser_free_local (destination_start_p,
context_p->allocated_buffer_size);
}
parser_free_allocated_buffer (context_p);
JERRY_ASSERT (context_p->u.allocated_buffer_p == NULL);
} /* lexer_construct_literal_object */
#undef LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE
/**
* Construct a number object.
*
@@ -2784,7 +2824,7 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */
case LIT_CHAR_DOUBLE_QUOTE:
case LIT_CHAR_SINGLE_QUOTE:
{
lexer_parse_string (context_p);
lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
create_literal_object = true;
break;
}
+14
View File
@@ -214,6 +214,11 @@ typedef enum
#define LEXER_BINARY_LVALUE_OP_TOKEN_TO_OPCODE(token_type) \
((cbc_opcode_t) ((((token_type) - LEXER_ASSIGN_ADD) * 2) + CBC_ASSIGN_ADD))
/**
* Maximum local buffer size for identifiers which contains escape sequences.
*/
#define LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE 48
/**
* Lexer newline flags.
*/
@@ -234,6 +239,15 @@ typedef enum
LEXER_OBJ_IDENT_OBJECT_PATTERN = (1u << 3), /**< parse "get"/"set" as string literal in object pattern */
} lexer_obj_ident_opts_t;
/**
* Lexer string options.
*/
typedef enum
{
LEXER_STRING_NO_OPTS = (1u << 0), /**< no options */
LEXER_STRING_RAW = (1u << 1), /**< raw string ECMAScript v6, 11.8.6.1: TVR */
} lexer_string_options_t;
/**
* Lexer scan identifier parse options.
*/
+118 -41
View File
@@ -20,6 +20,7 @@
#include "ecma-helpers.h"
#include "lit-char-helpers.h"
#include "js-parser-tagged-template-literal.h"
/** \addtogroup parser Parser
* @{
@@ -1142,21 +1143,21 @@ parser_parse_template_literal (parser_context_t *context_p) /**< context */
{
if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS)
{
context_p->last_cbc_opcode = CBC_ADD_TWO_LITERALS;
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_TWO_LITERALS);
}
else if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
{
context_p->last_cbc_opcode = CBC_ADD_RIGHT_LITERAL;
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_RIGHT_LITERAL);
}
else
{
parser_emit_cbc (context_p, CBC_ADD);
parser_emit_cbc_ext (context_p, CBC_EXT_STRING_CONCAT);
}
}
context_p->source_p--;
context_p->column--;
lexer_parse_string (context_p);
lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
if (is_empty_head || context_p->token.lit_location.length > 0)
{
@@ -1166,20 +1167,21 @@ parser_parse_template_literal (parser_context_t *context_p) /**< context */
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
{
context_p->last_cbc_opcode = CBC_ADD_TWO_LITERALS;
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_TWO_LITERALS);
context_p->last_cbc.value = context_p->lit_object.index;
context_p->last_cbc.literal_type = context_p->token.lit_location.type;
context_p->last_cbc.literal_keyword_type = context_p->token.keyword_type;
}
else
{
parser_emit_cbc_literal_from_token (context_p, CBC_ADD_RIGHT_LITERAL);
parser_emit_cbc_ext_literal_from_token (context_p, CBC_EXT_STRING_CONCAT_RIGHT_LITERAL);
}
}
while (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT)
{
lexer_next_token (context_p);
parser_parse_expression (context_p, PARSE_EXPR);
if (context_p->token.type != LEXER_RIGHT_BRACE)
@@ -1189,16 +1191,16 @@ parser_parse_template_literal (parser_context_t *context_p) /**< context */
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
{
context_p->last_cbc_opcode = CBC_ADD_RIGHT_LITERAL;
context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_STRING_CONCAT_RIGHT_LITERAL);
}
else
{
parser_emit_cbc (context_p, CBC_ADD);
parser_emit_cbc_ext (context_p, CBC_EXT_STRING_CONCAT);
}
context_p->source_p--;
context_p->column--;
lexer_parse_string (context_p);
lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
if (context_p->token.lit_location.length > 0)
{
@@ -1206,12 +1208,75 @@ parser_parse_template_literal (parser_context_t *context_p) /**< context */
&context_p->token.lit_location,
context_p->token.lit_location.type);
parser_emit_cbc_literal_from_token (context_p, CBC_ADD_RIGHT_LITERAL);
parser_emit_cbc_ext_literal_from_token (context_p, CBC_EXT_STRING_CONCAT_RIGHT_LITERAL);
}
}
} /* parser_parse_template_literal */
/**
* Parse tagged template literal.
*/
static size_t
parser_parse_tagged_template_literal (parser_context_t *context_p) /**< context */
{
JERRY_ASSERT (context_p->token.type == LEXER_TEMPLATE_LITERAL);
uint32_t call_arguments = 0;
ecma_collection_t *collection_p;
if (context_p->tagged_template_literal_cp == JMEM_CP_NULL)
{
collection_p = ecma_new_collection ();
ECMA_SET_INTERNAL_VALUE_POINTER (context_p->tagged_template_literal_cp, collection_p);
}
else
{
collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, context_p->tagged_template_literal_cp);
if (collection_p->item_count > CBC_MAXIMUM_BYTE_VALUE)
{
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
}
}
return;
} /* parser_parse_template_literal */
const uint32_t tagged_id = collection_p->item_count;
uint32_t prop_idx = 0;
ecma_object_t *raw_strings_p;
ecma_object_t *template_obj_p = parser_new_tagged_template_literal (&raw_strings_p);
ecma_collection_push_back (collection_p, ecma_make_object_value (template_obj_p));
parser_tagged_template_literal_append_strings (context_p, template_obj_p, raw_strings_p, prop_idx++);
call_arguments++;
parser_emit_cbc_ext_call (context_p, CBC_EXT_GET_TAGGED_TEMPLATE_LITERAL, tagged_id);
while (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT)
{
JERRY_ASSERT (context_p->source_p[-1] == LIT_CHAR_LEFT_BRACE);
lexer_next_token (context_p);
if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE)
{
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
}
parser_parse_expression (context_p, PARSE_EXPR);
if (context_p->token.type != LEXER_RIGHT_BRACE)
{
parser_raise_error (context_p, PARSER_ERR_RIGHT_BRACE_EXPECTED);
}
context_p->source_p--;
context_p->column--;
lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
parser_tagged_template_literal_append_strings (context_p, template_obj_p, raw_strings_p, prop_idx++);
}
parser_tagged_template_literal_finalize (template_obj_p, raw_strings_p);
return call_arguments;
} /* parser_parse_tagged_template_literal */
/**
* Throws an error if the current expression is not an assignment expression.
@@ -1681,6 +1746,9 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */
continue;
}
#if ENABLED (JERRY_ES2015)
case LEXER_TEMPLATE_LITERAL:
#endif /* ENABLED (JERRY_ES2015) */
case LEXER_LEFT_PAREN:
{
size_t call_arguments = 0;
@@ -1724,45 +1792,54 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */
}
}
lexer_next_token (context_p);
#if ENABLED (JERRY_ES2015)
bool has_spread_element = false;
#endif /* ENABLED (JERRY_ES2015) */
if (context_p->token.type != LEXER_RIGHT_PAREN)
if (context_p->token.type == LEXER_TEMPLATE_LITERAL)
{
call_arguments = parser_parse_tagged_template_literal (context_p);
}
else
{
while (true)
{
if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE)
{
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
}
#if ENABLED (JERRY_ES2015)
if (context_p->token.type == LEXER_THREE_DOTS)
{
has_spread_element = true;
call_arguments++;
parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SPREAD_ELEMENT);
lexer_next_token (context_p);
}
#endif /* ENABLED (JERRY_ES2015) */
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
if (context_p->token.type != LEXER_COMMA)
{
break;
}
lexer_next_token (context_p);
}
lexer_next_token (context_p);
if (context_p->token.type != LEXER_RIGHT_PAREN)
{
parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
while (true)
{
if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE)
{
parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED);
}
#if ENABLED (JERRY_ES2015)
if (context_p->token.type == LEXER_THREE_DOTS)
{
has_spread_element = true;
call_arguments++;
parser_emit_cbc_ext (context_p, CBC_EXT_PUSH_SPREAD_ELEMENT);
lexer_next_token (context_p);
}
#endif /* ENABLED (JERRY_ES2015) */
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
if (context_p->token.type != LEXER_COMMA)
{
break;
}
lexer_next_token (context_p);
}
if (context_p->token.type != LEXER_RIGHT_PAREN)
{
parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED);
}
}
#if ENABLED (JERRY_ES2015)
}
#endif /* ENABLED (JERRY_ES2015) */
lexer_next_token (context_p);
+9 -1
View File
@@ -421,6 +421,7 @@ typedef struct parser_saved_context_t
uint16_t scope_stack_reg_top; /**< preserved top register of scope stack */
#if ENABLED (JERRY_ES2015)
uint16_t scope_stack_global_end; /**< end of global declarations of a function */
ecma_value_t tagged_template_literal_cp; /**< compessed pointer to the tagged template literal collection */
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
@@ -491,6 +492,7 @@ typedef struct
uint16_t scope_stack_reg_top; /**< current top register of scope stack */
#if ENABLED (JERRY_ES2015)
uint16_t scope_stack_global_end; /**< end of global declarations of a function */
ecma_value_t tagged_template_literal_cp; /**< compessed pointer to the tagged template literal collection */
#endif /* ENABLED (JERRY_ES2015) */
uint8_t stack_top_uint8; /**< top byte stored on the stack */
@@ -533,6 +535,7 @@ void *parser_malloc (parser_context_t *context_p, size_t size);
void parser_free (void *ptr, size_t size);
void *parser_malloc_local (parser_context_t *context_p, size_t size);
void parser_free_local (void *ptr, size_t size);
void parser_free_allocated_buffer (parser_context_t *context_p);
/* Parser byte stream. */
@@ -602,6 +605,8 @@ void parser_set_continues_to_current_position (parser_context_t *context_p, pars
parser_emit_cbc ((context_p), PARSER_TO_EXT_OPCODE (opcode))
#define parser_emit_cbc_ext_literal(context_p, opcode, literal_index) \
parser_emit_cbc_literal ((context_p), PARSER_TO_EXT_OPCODE (opcode), (literal_index))
#define parser_emit_cbc_ext_literal_from_token(context_p, opcode) \
parser_emit_cbc_literal_from_token ((context_p), PARSER_TO_EXT_OPCODE (opcode))
#define parser_emit_cbc_ext_call(context_p, opcode, call_arguments) \
parser_emit_cbc_call ((context_p), PARSER_TO_EXT_OPCODE (opcode), (call_arguments))
#define parser_emit_cbc_ext_call(context_p, opcode, call_arguments) \
@@ -631,10 +636,13 @@ bool lexer_check_arrow (parser_context_t *context_p);
bool lexer_check_arrow_param (parser_context_t *context_p);
bool lexer_check_yield_no_arg (parser_context_t *context_p);
#endif /* ENABLED (JERRY_ES2015) */
void lexer_parse_string (parser_context_t *context_p);
void lexer_parse_string (parser_context_t *context_p, lexer_string_options_t opts);
void lexer_expect_identifier (parser_context_t *context_p, uint8_t literal_type);
void lexer_scan_identifier (parser_context_t *context_p, uint32_t ident_opts);
void lexer_convert_ident_to_cesu8 (uint8_t *destination_p, const uint8_t *source_p, prop_length_t length);
const uint8_t *lexer_convert_literal_to_chars (parser_context_t *context_p, const lexer_lit_location_t *literal_p,
uint8_t *local_byte_array_p, lexer_string_options_t opts);
void lexer_expect_object_literal_id (parser_context_t *context_p, uint32_t ident_opts);
void lexer_construct_literal_object (parser_context_t *context_p, const lexer_lit_location_t *literal_p,
uint8_t literal_type);
+17 -2
View File
@@ -82,12 +82,27 @@ parser_malloc_local (parser_context_t *context_p, /**< context */
/**
* Free memory allocated by parser_malloc_local.
*/
void parser_free_local (void *ptr, /**< pointer to free */
size_t size) /**< size of the memory */
void
parser_free_local (void *ptr, /**< pointer to free */
size_t size) /**< size of the memory */
{
jmem_heap_free_block (ptr, size);
} /* parser_free_local */
/**
* Free the dynamically allocated buffer stored in the context
*/
void
parser_free_allocated_buffer (parser_context_t *context_p) /**< context */
{
if (context_p->u.allocated_buffer_p != NULL)
{
parser_free_local (context_p->u.allocated_buffer_p,
context_p->allocated_buffer_size);
context_p->u.allocated_buffer_p = NULL;
}
} /* parser_free_allocated_buffer */
/**********************************************************************/
/* Parser data management functions */
/**********************************************************************/
@@ -0,0 +1,153 @@
/* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "js-parser-tagged-template-literal.h"
#include "js-lexer.h"
#include "ecma-array-object.h"
#include "ecma-builtin-helpers.h"
#include "ecma-gc.h"
#include "ecma-helpers.h"
/* \addtogroup parser Parser
* @{
*
* \addtogroup jsparser JavaScript
* @{
*
* \addtogroup jsparser_tagged_template_literal Tagged template literal
* @{
*/
#if ENABLED (JERRY_ES2015)
/**
* Append the cooked and raw string to the corresponding array
*/
void
parser_tagged_template_literal_append_strings (parser_context_t *context_p, /**< parser context */
ecma_object_t *template_obj_p, /**< template object */
ecma_object_t *raw_strings_p, /**< raw strings object */
uint32_t prop_idx) /**< property index to set the values */
{
lexer_lit_location_t *lit_loc_p = &context_p->token.lit_location;
if (lit_loc_p->length == 0)
{
ecma_builtin_helper_def_prop_by_index (template_obj_p,
prop_idx,
ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY),
ECMA_PROPERTY_FIXED);
ecma_builtin_helper_def_prop_by_index (raw_strings_p,
prop_idx,
ecma_make_magic_string_value (LIT_MAGIC_STRING__EMPTY),
ECMA_PROPERTY_FIXED);
return;
}
uint8_t local_byte_array[LEXER_MAX_LITERAL_LOCAL_BUFFER_SIZE];
const uint8_t *source_p = lexer_convert_literal_to_chars (context_p,
&context_p->token.lit_location,
local_byte_array,
LEXER_STRING_NO_OPTS);
ecma_string_t *raw_str_p;
ecma_string_t *cooked_str_p = ecma_new_ecma_string_from_utf8 (source_p, lit_loc_p->length);
parser_free_allocated_buffer (context_p);
if (lit_loc_p->has_escape)
{
context_p->source_p = context_p->token.lit_location.char_p - 1;
lexer_parse_string (context_p, LEXER_STRING_RAW);
source_p = lexer_convert_literal_to_chars (context_p,
&context_p->token.lit_location,
local_byte_array,
LEXER_STRING_RAW);
raw_str_p = ecma_new_ecma_string_from_utf8 (source_p, lit_loc_p->length);
parser_free_allocated_buffer (context_p);
}
else
{
ecma_ref_ecma_string (cooked_str_p);
raw_str_p = cooked_str_p;
}
ecma_builtin_helper_def_prop_by_index (template_obj_p,
prop_idx,
ecma_make_string_value (cooked_str_p),
ECMA_PROPERTY_FIXED);
ecma_builtin_helper_def_prop_by_index (raw_strings_p,
prop_idx,
ecma_make_string_value (raw_str_p),
ECMA_PROPERTY_FIXED);
ecma_deref_ecma_string (cooked_str_p);
ecma_deref_ecma_string (raw_str_p);
} /* parser_tagged_template_literal_append_strings */
/**
* Create new tagged template literal object
*
* @return pointer to the allocated object
*/
ecma_object_t *
parser_new_tagged_template_literal (ecma_object_t **raw_strings_p) /**< [out] raw strings object */
{
ecma_object_t *template_obj_p = ecma_op_new_array_object (0);
*raw_strings_p = ecma_op_new_array_object (0);
ecma_builtin_helper_def_prop (template_obj_p,
ecma_get_magic_string (LIT_MAGIC_STRING_RAW),
ecma_make_object_value (*raw_strings_p),
ECMA_PROPERTY_FIXED);
ecma_deref_object (*raw_strings_p);
return template_obj_p;
} /* parser_new_tagged_template_literal */
/**
* Set integrity level of the given template array object to "frozen"
*/
static void
parser_tagged_template_literal_freeze_array (ecma_object_t *obj_p)
{
JERRY_ASSERT (ecma_get_object_type (obj_p) == ECMA_OBJECT_TYPE_ARRAY);
ecma_set_object_extensible (obj_p, false);
ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p;
uint8_t new_prop_value = (uint8_t) (ext_obj_p->u.array.u.length_prop & ~ECMA_PROPERTY_FLAG_WRITABLE);
ext_obj_p->u.array.u.length_prop = new_prop_value;
} /* parser_tagged_template_literal_freeze_array */
/**
* Finalize the tagged template object
*/
void
parser_tagged_template_literal_finalize (ecma_object_t *template_obj_p, /**< template object */
ecma_object_t *raw_strings_p) /**< raw strings object */
{
parser_tagged_template_literal_freeze_array (template_obj_p);
parser_tagged_template_literal_freeze_array (raw_strings_p);
ecma_deref_object (template_obj_p);
} /* parser_tagged_template_literal_finalize */
#endif /* ENABLED (JERRY_ES2015) */
/**
* @}
* @}
* @}
*/
@@ -0,0 +1,51 @@
/* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ECMA_TAGGED_TEMPLATE_LITERAL_H
#define ECMA_TAGGED_TEMPLATE_LITERAL_H
/* \addtogroup parser Parser
* @{
*
* \addtogroup jsparser JavaScript
* @{
*
* \addtogroup jsparser_tagged_template_literal Tagged template literal
* @{
*/
#include "common.h"
#include "ecma-globals.h"
#include "js-parser-internal.h"
#if ENABLED (JERRY_ES2015)
ecma_object_t *
parser_new_tagged_template_literal (ecma_object_t **raw_strings_p);
void
parser_tagged_template_literal_append_strings (parser_context_t *context_p, ecma_object_t *template_obj_p,
ecma_object_t *raw_strings_p, uint32_t prop_index);
void
parser_tagged_template_literal_finalize (ecma_object_t *template_obj_p, ecma_object_t *raw_strings_p);
#endif /* ENABLED (JERRY_ES2015) */
#endif /* ECMA_TAGGED_TEMPLATE_LITERAL_H */
/**
* @}
* @}
* @}
*/
+55 -5
View File
@@ -1163,6 +1163,13 @@ parser_post_processing (parser_context_t *context_p) /**< context */
total_size += context_p->argument_count * sizeof (ecma_value_t);
}
#if ENABLED (JERRY_ES2015)
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
total_size += sizeof (ecma_value_t);
}
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM)
if (JERRY_CONTEXT (resource_name) != ECMA_VALUE_UNDEFINED)
{
@@ -1290,6 +1297,11 @@ parser_post_processing (parser_context_t *context_p) /**< context */
{
compiled_code_p->status_flags |= CBC_CODE_FLAGS_REST_PARAMETER;
}
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
compiled_code_p->status_flags |= CBC_CODE_FLAG_HAS_TAGGED_LITERALS;
}
#endif /* ENABLED (JERRY_ES2015) */
literal_pool_p = ((ecma_value_t *) byte_code_p) - context_p->register_count;
@@ -1572,6 +1584,20 @@ parser_post_processing (parser_context_t *context_p) /**< context */
}
}
#if ENABLED (JERRY_ES2015)
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
ecma_value_t *tagged_base_p = (ecma_value_t *) (((uint8_t *) compiled_code_p) + total_size);
if (PARSER_NEEDS_MAPPED_ARGUMENTS (context_p->status_flags))
{
tagged_base_p -= context_p->argument_count;
}
tagged_base_p[-1] = (ecma_value_t) context_p->tagged_template_literal_cp;
}
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM)
if (JERRY_CONTEXT (resource_name) != ECMA_VALUE_UNDEFINED)
{
@@ -1582,6 +1608,13 @@ parser_post_processing (parser_context_t *context_p) /**< context */
resource_name_p -= context_p->argument_count;
}
#if ENABLED (JERRY_ES2015)
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
resource_name_p--;
}
#endif /* ENABLED (JERRY_ES2015) */
resource_name_p[-1] = JERRY_CONTEXT (resource_name);
}
#endif /* ENABLED (JERRY_LINE_INFO) || ENABLED (JERRY_ES2015_MODULE_SYSTEM) */
@@ -1852,6 +1885,7 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
#if ENABLED (JERRY_ES2015)
context.status_flags |= PARSER_GET_CLASS_PARSER_OPTS (parse_opts);
context.tagged_template_literal_cp = JMEM_CP_NULL;
#endif /* ENABLED (JERRY_ES2015) */
context.stack_depth = 0;
@@ -1890,6 +1924,7 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
context.scope_stack_reg_top = 0;
#if ENABLED (JERRY_ES2015)
context.scope_stack_global_end = 0;
context.tagged_template_literal_cp = JMEM_CP_NULL;
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
@@ -2082,11 +2117,7 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */
parser_free_jumps (context.last_statement);
}
if (context.u.allocated_buffer_p != NULL)
{
parser_free_local (context.u.allocated_buffer_p,
context.allocated_buffer_size);
}
parser_free_allocated_buffer (&context);
scanner_cleanup (&context);
@@ -2168,6 +2199,7 @@ parser_save_context (parser_context_t *context_p, /**< context */
saved_context_p->scope_stack_reg_top = context_p->scope_stack_reg_top;
#if ENABLED (JERRY_ES2015)
saved_context_p->scope_stack_global_end = context_p->scope_stack_global_end;
saved_context_p->tagged_template_literal_cp = context_p->tagged_template_literal_cp;
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
@@ -2195,6 +2227,7 @@ parser_save_context (parser_context_t *context_p, /**< context */
context_p->scope_stack_reg_top = 0;
#if ENABLED (JERRY_ES2015)
context_p->scope_stack_global_end = 0;
context_p->tagged_template_literal_cp = JMEM_CP_NULL;
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
@@ -2239,6 +2272,7 @@ parser_restore_context (parser_context_t *context_p, /**< context */
context_p->scope_stack_reg_top = saved_context_p->scope_stack_reg_top;
#if ENABLED (JERRY_ES2015)
context_p->scope_stack_global_end = saved_context_p->scope_stack_global_end;
context_p->tagged_template_literal_cp = saved_context_p->tagged_template_literal_cp;
#endif /* ENABLED (JERRY_ES2015) */
#ifndef JERRY_NDEBUG
@@ -2482,9 +2516,25 @@ parser_raise_error (parser_context_t *context_p, /**< context */
parser_free_jumps (saved_context_p->last_statement);
}
#if ENABLED (JERRY_ES2015)
if (saved_context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
ecma_collection_free (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
saved_context_p->tagged_template_literal_cp));
}
#endif /* ENABLED (JERRY_ES2015) */
saved_context_p = saved_context_p->prev_context_p;
}
#if ENABLED (JERRY_ES2015)
if (context_p->tagged_template_literal_cp != JMEM_CP_NULL)
{
ecma_collection_free (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t,
context_p->tagged_template_literal_cp));
}
#endif /* ENABLED (JERRY_ES2015) */
context_p->error = error;
PARSER_THROW (context_p->try_buffer);
/* Should never been reached. */
@@ -100,6 +100,7 @@ typedef enum
SCAN_STACK_COMPUTED_PROPERTY, /**< computed property name */
SCAN_STACK_COMPUTED_GENERATOR_FUNCTION, /**< computed property name */
SCAN_STACK_TEMPLATE_STRING, /**< template string */
SCAN_STACK_TAGGED_TEMPLATE_LITERAL, /**< tagged template literal */
SCAN_STACK_FOR_BLOCK_END, /**< end of "for" statement with let/const declaration */
SCAN_STACK_ARROW_ARGUMENTS, /**< might be arguments of an arrow function */
SCAN_STACK_ARROW_EXPRESSION, /**< expression body of an arrow function */
+13 -1
View File
@@ -613,6 +613,17 @@ scanner_scan_post_primary_expression (parser_context_t *context_p, /**< context
scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
return true;
}
#if ENABLED (JERRY_ES2015)
case LEXER_TEMPLATE_LITERAL:
{
if (JERRY_UNLIKELY (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT))
{
scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION;
parser_stack_push_uint8 (context_p, SCAN_STACK_TAGGED_TEMPLATE_LITERAL);
}
return true;
}
#endif /* ENABLED (JERRY_ES2015) */
case LEXER_LEFT_SQUARE:
{
parser_stack_push_uint8 (context_p, SCAN_STACK_PROPERTY_ACCESSOR);
@@ -1268,6 +1279,7 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context *
return SCAN_KEEP_TOKEN;
}
case SCAN_STACK_TEMPLATE_STRING:
case SCAN_STACK_TAGGED_TEMPLATE_LITERAL:
{
if (type != LEXER_RIGHT_BRACE)
{
@@ -1276,7 +1288,7 @@ scanner_scan_primary_expression_end (parser_context_t *context_p, /**< context *
context_p->source_p--;
context_p->column--;
lexer_parse_string (context_p);
lexer_parse_string (context_p, LEXER_STRING_NO_OPTS);
if (context_p->source_p[-1] != LIT_CHAR_GRAVE_ACCENT)
{