Implement nullish coalescing operator (#3997)

JerryScript-DCO-1.0-Signed-off-by: bence gabor kis kisbg@inf.u-szeged.hu
This commit is contained in:
kisbg
2020-07-17 15:42:51 +02:00
committed by GitHub
parent f60e16d9d5
commit dfd9d4497a
13 changed files with 323 additions and 72 deletions
+174 -20
View File
@@ -35,7 +35,7 @@
/**
* Maximum precedence for right-to-left binary operation evaluation.
*/
#define PARSER_RIGHT_TO_LEFT_ORDER_MAX_PRECEDENCE 6
#define PARSER_RIGHT_TO_LEFT_ORDER_MAX_PRECEDENCE 7
/**
* Precedence for ternary operation.
@@ -45,12 +45,17 @@
/**
* Precedence for exponentiation operation.
*/
#define PARSER_RIGHT_TO_LEFT_ORDER_EXPONENTIATION 15
#define PARSER_RIGHT_TO_LEFT_ORDER_EXPONENTIATION 16
/**
* Value of grouping level increase and decrease.
*/
#define PARSER_GROUPING_LEVEL_INCREASE 2
#define PARSER_GROUPING_LEVEL_INCREASE 4
/**
* Represents whether logical expression was emitted in the current group expression.
*/
#define PARSER_GROUPING_LOGICAL_FOUND (1 << 1)
/**
* Precedence of the binary tokens.
@@ -60,21 +65,56 @@
*/
static const uint8_t parser_binary_precedence_table[] =
{
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, /**< "=" */
3, /**< "+=" */
3, /**< "-=" */
3, /**< "*=" */
3, /**< "/=" */
3, /**< "=" */
3, /**< "<<=" */
3, /**< ">>=" */
3, /**< ">>>=" */
3, /**< "&=" */
3, /**< "|=" */
3, /**< "^=" */
#if ENABLED (JERRY_ESNEXT)
3,
3, /**< "**=" */
#endif /* ENABLED (JERRY_ESNEXT) */
4, 5, 6, 7, 8, 9, 10, 10, 10, 10,
11, 11, 11, 11, 11, 11, 12, 12, 12,
13, 13, 14, 14, 14,
4, /**< "?"*/
#if ENABLED (JERRY_ESNEXT)
15,
5, /**< "??" */
#endif /* ENABLED (JERRY_ESNEXT) */
6, /**< "||" */
7, /**< "&&" */
8, /**< "|" */
9, /**< "^" */
10, /**< "&" */
11, /**< "==" */
11, /**< "!=" */
11, /**< "===" */
11, /**< "!==" */
12, /**< "<" */
12, /**< ">" */
12, /**< "<=" */
12, /**< ">=" */
12, /**< in */
12, /**< instanceof */
13, /**< "<<" */
13, /**< ">>" */
13, /**< ">>>" */
14, /**< "+" */
14, /**< "-" */
15, /**< "*" */
15, /**< "/" */
15, /**< "%" */
#if ENABLED (JERRY_ESNEXT)
16, /**< "**" */
#endif /* ENABLED (JERRY_ESNEXT) */
};
#if ENABLED (JERRY_ESNEXT)
JERRY_STATIC_ASSERT (sizeof (parser_binary_precedence_table) == 38,
parser_binary_precedence_table_should_have_38_values_in_es2015);
JERRY_STATIC_ASSERT (sizeof (parser_binary_precedence_table) == 39,
parser_binary_precedence_table_should_have_39_values_in_es2015);
#else /* !ENABLED (JERRY_ESNEXT) */
JERRY_STATIC_ASSERT (sizeof (parser_binary_precedence_table) == 36,
parser_binary_precedence_table_should_have_36_values_in_es51);
@@ -2653,14 +2693,87 @@ parser_append_binary_single_assignment_token (parser_context_t *context_p, /**<
return assign_opcode;
} /* parser_append_binary_single_assignment_token */
/**
* Check for invalid logical operator and nullish chaining
*/
#if ENABLED (JERRY_ESNEXT)
static void
parser_check_invalid_logical_op (parser_context_t *context_p, /**< context */
size_t grouping_level) /**< grouping_level */
{
parser_stack_iterator_t iterator;
parser_stack_iterator_init (context_p, &iterator);
bool logical_found = (grouping_level & PARSER_GROUPING_LOGICAL_FOUND) != 0;
bool nullish_found = false;
while (true)
{
uint8_t token = parser_stack_iterator_read_uint8 (&iterator);
if (token == LEXER_EXPRESSION_START
|| token == LEXER_LEFT_PAREN
|| !LEXER_IS_BINARY_OP_TOKEN (token))
{
return;
}
parser_stack_iterator_skip (&iterator, sizeof (uint8_t));
if (token == LEXER_ASSIGN)
{
cbc_opcode_t opcode = (cbc_opcode_t) parser_stack_iterator_read_uint8 (&iterator);
if (cbc_flags[opcode] & CBC_HAS_LITERAL_ARG)
{
parser_stack_iterator_skip (&iterator, sizeof (uint16_t));
}
token = parser_stack_iterator_read_uint8 (&iterator);
if (token == LEXER_ASSIGN_GROUP_EXPR)
{
parser_stack_iterator_skip (&iterator, sizeof (uint8_t));
}
if (token == LEXER_ASSIGN_CONST)
{
parser_stack_iterator_skip (&iterator, sizeof (uint8_t));
}
}
else if (token == LEXER_LOGICAL_OR || token == LEXER_LOGICAL_AND)
{
if (nullish_found)
{
parser_raise_error (context_p, PARSER_ERR_INVALID_NULLISH_COALESCING);
}
parser_stack_iterator_skip (&iterator, sizeof (parser_branch_t));
logical_found = true;
}
else if (token == LEXER_NULLISH_COALESCING)
{
if (logical_found)
{
parser_raise_error (context_p, PARSER_ERR_INVALID_NULLISH_COALESCING);
}
parser_stack_iterator_skip (&iterator, sizeof (parser_branch_t));
nullish_found = true;
}
}
} /* parser_check_invalid_logical_op */
#endif /* ENABLED (JERRY_ESNEXT) */
/**
* Append a binary token.
*/
static void
parser_append_binary_token (parser_context_t *context_p) /**< context */
parser_append_binary_token (parser_context_t *context_p, /**< context */
size_t grouping_level) /**< grouping_level */
{
JERRY_ASSERT (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type));
#if !ENABLED (JERRY_ESNEXT)
JERRY_UNUSED (grouping_level);
#endif /* !ENABLED (JERRY_ESNEXT) */
parser_push_result (context_p);
if (context_p->token.type == LEXER_ASSIGN)
@@ -2715,7 +2828,25 @@ parser_append_binary_token (parser_context_t *context_p) /**< context */
parser_emit_cbc_forward_branch (context_p, opcode, &branch);
parser_stack_push (context_p, &branch, sizeof (parser_branch_t));
parser_stack_push_uint8 (context_p, context_p->token.type);
#if ENABLED (JERRY_ESNEXT)
parser_check_invalid_logical_op (context_p, grouping_level);
#endif /* ENABLED (JERRY_ESNEXT) */
return;
}
#if ENABLED (JERRY_ESNEXT)
else if (context_p->token.type == LEXER_NULLISH_COALESCING)
{
parser_branch_t branch;
uint16_t opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_BRANCH_IF_NULLISH);
parser_emit_cbc_forward_branch (context_p, opcode, &branch);
parser_stack_push (context_p, &branch, sizeof (parser_branch_t));
parser_stack_push_uint8 (context_p, context_p->token.type);
parser_check_invalid_logical_op (context_p, grouping_level);
return;
}
#endif /* ENABLED (JERRY_ESNEXT) */
parser_stack_push_uint8 (context_p, context_p->token.type);
} /* parser_append_binary_token */
@@ -2725,8 +2856,12 @@ parser_append_binary_token (parser_context_t *context_p) /**< context */
*/
static void
parser_process_binary_opcodes (parser_context_t *context_p, /**< context */
uint8_t min_prec_treshold) /**< minimal precedence of tokens */
uint8_t min_prec_treshold, /**< minimal precedence of tokens */
size_t *grouping_level_p) /**< grouping level */
{
#if !ENABLED (JERRY_ESNEXT)
JERRY_UNUSED (grouping_level_p);
#endif /* !ENABLED (JERRY_ESNEXT) */
while (true)
{
uint8_t token = context_p->stack_top_uint8;
@@ -2843,8 +2978,22 @@ parser_process_binary_opcodes (parser_context_t *context_p, /**< context */
parser_branch_t branch;
parser_stack_pop (context_p, &branch, sizeof (parser_branch_t));
parser_set_branch_to_current_position (context_p, &branch);
#if ENABLED (JERRY_ESNEXT)
JERRY_ASSERT (grouping_level_p != NULL);
*grouping_level_p |= PARSER_GROUPING_LOGICAL_FOUND;
#endif /* ENABLED (JERRY_ESNEXT) */
continue;
}
#if ENABLED (JERRY_ESNEXT)
else if (token == LEXER_NULLISH_COALESCING)
{
parser_branch_t branch;
parser_stack_pop (context_p, &branch, sizeof (parser_branch_t));
parser_set_branch_to_current_position (context_p, &branch);
continue;
}
#endif /* ENABLED (JERRY_ESNEXT) */
else
{
opcode = LEXER_BINARY_OP_TOKEN_TO_OPCODE (token);
@@ -3054,7 +3203,7 @@ parser_pattern_form_assignment (parser_context_t *context_p, /**< context */
parser_set_branch_to_current_position (context_p, &skip_init);
}
parser_process_binary_opcodes (context_p, 0);
parser_process_binary_opcodes (context_p, 0, NULL);
JERRY_ASSERT (context_p->stack_top_uint8 == LEXER_EXPRESSION_START);
parser_stack_pop_uint8 (context_p);
@@ -3433,7 +3582,7 @@ parser_process_ternary_expression (parser_context_t *context_p) /**< context */
* the result may come from the first branch. */
parser_flush_cbc (context_p);
parser_process_binary_opcodes (context_p, 0);
parser_process_binary_opcodes (context_p, 0, NULL);
} /* parser_process_ternary_expression */
/**
@@ -3477,6 +3626,12 @@ parser_process_group_expression (parser_context_t *context_p, /**< context */
parser_push_result (context_p);
parser_flush_cbc (context_p);
}
#if ENABLED (JERRY_ESNEXT)
else
{
*grouping_level_p &= (size_t) ~(PARSER_GROUPING_LOGICAL_FOUND);
}
#endif /* ENABLED (JERRY_ESNEXT) */
parser_stack_pop_uint8 (context_p);
lexer_next_token (context_p);
@@ -3555,7 +3710,7 @@ parser_parse_expression (parser_context_t *context_p, /**< context */
{
if (parser_parse_unary_expression (context_p, &grouping_level))
{
parser_process_binary_opcodes (context_p, 0);
parser_process_binary_opcodes (context_p, 0, &grouping_level);
break;
}
@@ -3592,9 +3747,8 @@ process_unary_expression:
#endif /* ENABLED (JERRY_ESNEXT) */
}
parser_process_binary_opcodes (context_p, min_prec_treshold);
parser_process_binary_opcodes (context_p, min_prec_treshold, &grouping_level);
}
if (context_p->token.type == LEXER_RIGHT_PAREN
&& (context_p->stack_top_uint8 == LEXER_LEFT_PAREN
|| context_p->stack_top_uint8 == LEXER_COMMA_SEP_LIST))
@@ -3622,7 +3776,7 @@ process_unary_expression:
}
else if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type))
{
parser_append_binary_token (context_p);
parser_append_binary_token (context_p, grouping_level);
lexer_next_token (context_p);
continue;
}