/* 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-internal.h" #if JERRY_JS_PARSER /** \addtogroup parser Parser * @{ * * \addtogroup jsparser JavaScript * @{ * * \addtogroup jsparser_expr Expression parser * @{ */ /** * Precedence of the binary tokens. * * See also: * lexer_token_type_t */ static const uint8_t parser_binary_precedence_table[36] = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 13, 13, 14, 14, 14 }; /** * Generate byte code for operators with lvalue. */ static inline void parser_push_result (parser_context_t *context_p) /**< context */ { if (CBC_NO_RESULT_COMPOUND_ASSIGMENT (context_p->last_cbc_opcode)) { context_p->last_cbc_opcode = (uint16_t) PARSER_TO_BINARY_OPERATION_WITH_RESULT (context_p->last_cbc_opcode); parser_flush_cbc (context_p); } else if (CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) { JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, context_p->last_cbc_opcode + 1)); if (context_p->last_cbc_opcode == CBC_POST_INCR || context_p->last_cbc_opcode == CBC_POST_DECR) { if (context_p->stack_depth >= context_p->stack_limit) { /* Stack limit is increased for CBC_POST_INCR_PUSH_RESULT * and CBC_POST_DECR_PUSH_RESULT opcodes. Needed by vm.c. */ JERRY_ASSERT (context_p->stack_depth == context_p->stack_limit); context_p->stack_limit++; if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) { parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); } } } context_p->last_cbc_opcode++; parser_flush_cbc (context_p); } } /* parser_push_result */ /** * Generate byte code for operators with lvalue. */ static void parser_emit_unary_lvalue_opcode (parser_context_t *context_p, /**< context */ cbc_opcode_t opcode) /**< opcode */ { if (PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { if (context_p->status_flags & PARSER_IS_STRICT) { if (context_p->last_cbc.literal_object_type != LEXER_LITERAL_OBJECT_ANY) { parser_error_t error; if (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_EVAL) { error = PARSER_ERR_EVAL_CANNOT_ASSIGNED; } else { JERRY_ASSERT (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_ARGUMENTS); error = PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED; } parser_raise_error (context_p, error); } if (opcode == CBC_DELETE_PUSH_RESULT) { parser_raise_error (context_p, PARSER_ERR_DELETE_IDENT_NOT_ALLOWED); } } if (opcode == CBC_DELETE_PUSH_RESULT) { context_p->status_flags |= PARSER_LEXICAL_ENV_NEEDED; if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_DELETE_IDENT_PUSH_RESULT; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_LITERAL; parser_emit_cbc_literal (context_p, CBC_DELETE_IDENT_PUSH_RESULT, context_p->last_cbc.value); } else { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; parser_emit_cbc_literal (context_p, CBC_DELETE_IDENT_PUSH_RESULT, context_p->last_cbc.third_literal_index); } return; } JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_LITERAL, opcode + CBC_UNARY_LVALUE_WITH_IDENT)); if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_UNARY_LVALUE_WITH_IDENT); } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_LITERAL; parser_emit_cbc_literal (context_p, (uint16_t) (opcode + CBC_UNARY_LVALUE_WITH_IDENT), context_p->last_cbc.value); } else { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; parser_emit_cbc_literal (context_p, (uint16_t) (opcode + CBC_UNARY_LVALUE_WITH_IDENT), context_p->last_cbc.third_literal_index); } } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP, opcode)); context_p->last_cbc_opcode = (uint16_t) opcode; } else { switch (context_p->last_cbc_opcode) { case CBC_PUSH_PROP_LITERAL: { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL, CBC_PUSH_LITERAL)); context_p->last_cbc_opcode = CBC_PUSH_LITERAL; break; } case CBC_PUSH_PROP_LITERAL_LITERAL: { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL_LITERAL, CBC_PUSH_TWO_LITERALS)); context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; break; } case CBC_PUSH_PROP_THIS_LITERAL: { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_THIS_LITERAL, CBC_PUSH_THIS_LITERAL)); context_p->last_cbc_opcode = CBC_PUSH_THIS_LITERAL; break; } default: { /* Invalid LeftHandSide expression. */ parser_emit_cbc_ext (context_p, (opcode == CBC_DELETE_PUSH_RESULT) ? CBC_EXT_PUSH_UNDEFINED_BASE : CBC_EXT_THROW_REFERENCE_ERROR); break; } } parser_emit_cbc (context_p, opcode); } } /* parser_emit_unary_lvalue_opcode */ /** * Parse array literal. */ static void parser_parse_array_literal (parser_context_t *context_p) /**< context */ { uint32_t pushed_items = 0; JERRY_ASSERT (context_p->token.type == LEXER_LEFT_SQUARE); parser_emit_cbc (context_p, CBC_CREATE_ARRAY); lexer_next_token (context_p); while (true) { if (context_p->token.type == LEXER_RIGHT_SQUARE) { if (pushed_items > 0) { parser_emit_cbc_call (context_p, CBC_ARRAY_APPEND, pushed_items); } return; } pushed_items++; if (context_p->token.type == LEXER_COMMA) { parser_emit_cbc (context_p, CBC_PUSH_ELISION); lexer_next_token (context_p); } else { parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); if (context_p->token.type == LEXER_COMMA) { lexer_next_token (context_p); } else if (context_p->token.type != LEXER_RIGHT_SQUARE) { parser_raise_error (context_p, PARSER_ERR_ARRAY_ITEM_SEPARATOR_EXPECTED); } } if (pushed_items >= 64) { parser_emit_cbc_call (context_p, CBC_ARRAY_APPEND, pushed_items); pushed_items = 0; } } } /* parser_parse_array_literal */ /** * Object literal item types. */ typedef enum { PARSER_OBJECT_PROPERTY_START, /**< marks the start of the property list */ PARSER_OBJECT_PROPERTY_VALUE, /**< value property */ PARSER_OBJECT_PROPERTY_GETTER, /**< getter property */ PARSER_OBJECT_PROPERTY_SETTER, /**< setter property */ PARSER_OBJECT_PROPERTY_BOTH_ACCESSORS, /**< both getter and setter properties are set */ } parser_object_literal_item_types_t; /** * Parse object literal. */ static void parser_append_object_literal_item (parser_context_t *context_p, /**< context */ uint16_t item_index, /**< index of the item name */ parser_object_literal_item_types_t item_type) /**< type of the item */ { parser_stack_iterator_t iterator; uint8_t *current_item_type_p; iterator.current_p = context_p->stack.first_p; iterator.current_position = context_p->stack.last_position; while (true) { current_item_type_p = iterator.current_p->bytes + iterator.current_position - 1; if (*current_item_type_p == PARSER_OBJECT_PROPERTY_START) { parser_stack_push_uint16 (context_p, item_index); parser_stack_push_uint8 (context_p, (uint8_t) item_type); return; } iterator.current_position--; if (iterator.current_position == 0) { iterator.current_p = iterator.current_p->next_p; iterator.current_position = PARSER_STACK_PAGE_SIZE; } uint32_t current_item_index = iterator.current_p->bytes[iterator.current_position - 1]; iterator.current_position--; if (iterator.current_position == 0) { iterator.current_p = iterator.current_p->next_p; iterator.current_position = PARSER_STACK_PAGE_SIZE; } current_item_index |= ((uint32_t) iterator.current_p->bytes[iterator.current_position - 1]) << 8; iterator.current_position--; if (iterator.current_position == 0) { iterator.current_p = iterator.current_p->next_p; iterator.current_position = PARSER_STACK_PAGE_SIZE; } if (current_item_index == item_index) { if (item_type == PARSER_OBJECT_PROPERTY_VALUE && *current_item_type_p == PARSER_OBJECT_PROPERTY_VALUE && !(context_p->status_flags & PARSER_IS_STRICT)) { return; } if (item_type == PARSER_OBJECT_PROPERTY_GETTER && *current_item_type_p == PARSER_OBJECT_PROPERTY_SETTER) { break; } if (item_type == PARSER_OBJECT_PROPERTY_SETTER && *current_item_type_p == PARSER_OBJECT_PROPERTY_GETTER) { break; } parser_raise_error (context_p, PARSER_ERR_OBJECT_PROPERTY_REDEFINED); } } uint8_t *last_page_p = context_p->stack.first_p->bytes; *current_item_type_p = PARSER_OBJECT_PROPERTY_BOTH_ACCESSORS; if (current_item_type_p == (last_page_p + context_p->stack.last_position - 1)) { context_p->stack_top_uint8 = PARSER_OBJECT_PROPERTY_BOTH_ACCESSORS; } } /* parser_append_object_literal_item */ /** * Parse object literal. */ static void parser_parse_object_literal (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (context_p->token.type == LEXER_LEFT_BRACE); parser_emit_cbc (context_p, CBC_CREATE_OBJECT); parser_stack_push_uint8 (context_p, PARSER_OBJECT_PROPERTY_START); while (true) { lexer_expect_object_literal_id (context_p, false); if (context_p->token.type == LEXER_RIGHT_BRACE) { break; } if (context_p->token.type == LEXER_PROPERTY_GETTER || context_p->token.type == LEXER_PROPERTY_SETTER) { uint32_t status_flags; cbc_ext_opcode_t opcode; uint16_t literal_index, function_literal_index; parser_object_literal_item_types_t item_type; if (context_p->token.type == LEXER_PROPERTY_GETTER) { status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_PROPERTY_GETTER; opcode = CBC_EXT_SET_GETTER; item_type = PARSER_OBJECT_PROPERTY_GETTER; } else { status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE | PARSER_IS_PROPERTY_SETTER; opcode = CBC_EXT_SET_SETTER; item_type = PARSER_OBJECT_PROPERTY_SETTER; } if (context_p->status_flags & PARSER_INSIDE_WITH) { status_flags |= PARSER_RESOLVE_THIS_FOR_CALLS; } lexer_expect_object_literal_id (context_p, true); literal_index = context_p->lit_object.index; parser_append_object_literal_item (context_p, literal_index, item_type); parser_flush_cbc (context_p); function_literal_index = lexer_construct_function_object (context_p, status_flags); parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, literal_index); JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (opcode); context_p->last_cbc.value = function_literal_index; lexer_next_token (context_p); } else { uint16_t literal_index = context_p->lit_object.index; parser_append_object_literal_item (context_p, literal_index, PARSER_OBJECT_PROPERTY_VALUE); lexer_next_token (context_p); if (context_p->token.type != LEXER_COLON) { parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); } lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); parser_emit_cbc_literal (context_p, CBC_SET_PROPERTY, literal_index); } if (context_p->token.type == LEXER_RIGHT_BRACE) { break; } else if (context_p->token.type != LEXER_COMMA) { parser_raise_error (context_p, PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED); } } while (context_p->stack_top_uint8 != PARSER_OBJECT_PROPERTY_START) { parser_stack_pop (context_p, NULL, 3); } parser_stack_pop_uint8 (context_p); } /* parser_parse_object_literal */ /** * Parse and record unary operators, and parse the primary literal. */ static void parser_parse_unary_expression (parser_context_t *context_p, /**< context */ size_t *grouping_level_p) /**< grouping level */ { int new_was_seen = 0; /* Collect unary operators. */ while (true) { /* Convert plus and minus binary operators to unary operators. */ if (context_p->token.type == LEXER_ADD) { context_p->token.type = LEXER_PLUS; } else if (context_p->token.type == LEXER_SUBTRACT) { context_p->token.type = LEXER_NEGATE; } /* Bracketed expressions are primary expressions. At this * point their left paren is pushed onto the stack and * they are processed when their closing paren is reached. */ if (context_p->token.type == LEXER_LEFT_PAREN) { (*grouping_level_p)++; new_was_seen = 0; } else if (context_p->token.type == LEXER_KEYW_NEW) { /* After 'new' unary operators are not allowed. */ new_was_seen = 1; } else if (new_was_seen || !LEXER_IS_UNARY_OP_TOKEN (context_p->token.type)) { break; } parser_stack_push_uint8 (context_p, context_p->token.type); lexer_next_token (context_p); } /* Parse primary expression. */ switch (context_p->token.type) { case LEXER_LITERAL: { cbc_opcode_t opcode = CBC_PUSH_LITERAL; if (context_p->token.lit_location.type == LEXER_IDENT_LITERAL || context_p->token.lit_location.type == LEXER_STRING_LITERAL) { lexer_construct_literal_object (context_p, &context_p->token.lit_location, context_p->token.lit_location.type); } else if (context_p->token.lit_location.type == LEXER_NUMBER_LITERAL) { bool is_negative_number = false; while (context_p->stack_top_uint8 == LEXER_PLUS || context_p->stack_top_uint8 == LEXER_NEGATE) { if (context_p->stack_top_uint8 == LEXER_NEGATE) { is_negative_number = !is_negative_number; } parser_stack_pop_uint8 (context_p); } if (lexer_construct_number_object (context_p, true, is_negative_number)) { JERRY_ASSERT (context_p->lit_object.index <= CBC_PUSH_NUMBER_BYTE_RANGE_END); if (context_p->lit_object.index == 0) { parser_emit_cbc (context_p, CBC_PUSH_NUMBER_0); break; } parser_emit_cbc_push_number (context_p, is_negative_number); break; } } if (context_p->lit_object.type != LEXER_LITERAL_OBJECT_EVAL) { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_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_object_type = context_p->lit_object.type; break; } if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS; context_p->last_cbc.third_literal_index = context_p->lit_object.index; context_p->last_cbc.literal_type = context_p->token.lit_location.type; context_p->last_cbc.literal_object_type = context_p->lit_object.type; break; } if (context_p->last_cbc_opcode == CBC_PUSH_THIS) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; opcode = CBC_PUSH_THIS_LITERAL; } } parser_emit_cbc_literal_from_token (context_p, opcode); break; } case LEXER_KEYW_FUNCTION: { int literals = 0; uint16_t literal1 = 0; uint16_t literal2 = 0; uint16_t function_literal_index; if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { literals = 1; literal1 = context_p->last_cbc.literal_index; context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { literals = 2; literal1 = context_p->last_cbc.literal_index; literal2 = context_p->last_cbc.value; context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } else { parser_flush_cbc (context_p); } uint32_t status_flags = PARSER_IS_FUNCTION | PARSER_IS_FUNC_EXPRESSION | PARSER_IS_CLOSURE; if (context_p->status_flags & PARSER_INSIDE_WITH) { status_flags |= PARSER_RESOLVE_THIS_FOR_CALLS; } function_literal_index = lexer_construct_function_object (context_p, status_flags); JERRY_ASSERT (context_p->last_cbc_opcode == PARSER_CBC_UNAVAILABLE); if (literals == 1) { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; context_p->last_cbc.literal_index = literal1; context_p->last_cbc.value = function_literal_index; } else if (literals == 2) { context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS; context_p->last_cbc.literal_index = literal1; context_p->last_cbc.value = literal2; context_p->last_cbc.third_literal_index = function_literal_index; } else { parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, function_literal_index); } context_p->last_cbc.literal_type = LEXER_FUNCTION_LITERAL; context_p->last_cbc.literal_object_type = LEXER_LITERAL_OBJECT_ANY; break; } case LEXER_LEFT_BRACE: { parser_parse_object_literal (context_p); break; } case LEXER_LEFT_SQUARE: { parser_parse_array_literal (context_p); break; } case LEXER_DIVIDE: case LEXER_ASSIGN_DIVIDE: { lexer_construct_regexp_object (context_p, false); if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; context_p->last_cbc.value = (uint16_t) (context_p->literal_count - 1); } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_THREE_LITERALS; context_p->last_cbc.third_literal_index = (uint16_t) (context_p->literal_count - 1); } else { parser_emit_cbc_literal (context_p, CBC_PUSH_LITERAL, (uint16_t) (context_p->literal_count - 1)); } context_p->last_cbc.literal_type = LEXER_REGEXP_LITERAL; context_p->last_cbc.literal_object_type = LEXER_LITERAL_OBJECT_ANY; break; } case LEXER_KEYW_THIS: { parser_emit_cbc (context_p, CBC_PUSH_THIS); break; } case LEXER_LIT_TRUE: { parser_emit_cbc (context_p, CBC_PUSH_TRUE); break; } case LEXER_LIT_FALSE: { parser_emit_cbc (context_p, CBC_PUSH_FALSE); break; } case LEXER_LIT_NULL: { parser_emit_cbc (context_p, CBC_PUSH_NULL); break; } default: { parser_raise_error (context_p, PARSER_ERR_PRIMARY_EXP_EXPECTED); break; } } lexer_next_token (context_p); } /* parser_parse_unary_expression */ /** * Parse the postfix part of unary operators, and * generate byte code for the whole expression. */ static void parser_process_unary_expression (parser_context_t *context_p) /**< context */ { /* Parse postfix part of a primary expression. */ while (true) { /* Since break would only break the switch, we use * continue to continue this loop. Without continue, * the code abandons the loop. */ switch (context_p->token.type) { case LEXER_DOT: { parser_push_result (context_p); lexer_expect_identifier (context_p, LEXER_STRING_LITERAL); JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_STRING_LITERAL); if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { JERRY_ASSERT (CBC_ARGS_EQ (CBC_PUSH_PROP_LITERAL_LITERAL, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL; context_p->last_cbc.value = context_p->lit_object.index; } else if (context_p->last_cbc_opcode == CBC_PUSH_THIS) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_PROP_THIS_LITERAL); } else { parser_emit_cbc_literal_from_token (context_p, CBC_PUSH_PROP_LITERAL); } lexer_next_token (context_p); continue; } case LEXER_LEFT_SQUARE: { parser_push_result (context_p); lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR); if (context_p->token.type != LEXER_RIGHT_SQUARE) { parser_raise_error (context_p, PARSER_ERR_RIGHT_SQUARE_EXPECTED); } lexer_next_token (context_p); if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL; } else if (context_p->last_cbc_opcode == CBC_PUSH_THIS_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_PROP_THIS_LITERAL; } else { parser_emit_cbc (context_p, CBC_PUSH_PROP); } continue; } case LEXER_LEFT_PAREN: { size_t call_arguments = 0; uint16_t opcode = CBC_CALL; bool is_eval = false; parser_push_result (context_p); if (context_p->stack_top_uint8 == LEXER_KEYW_NEW) { parser_stack_pop_uint8 (context_p); opcode = CBC_NEW; } else { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL && context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_EVAL) { JERRY_ASSERT (context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL); context_p->status_flags |= PARSER_ARGUMENTS_NEEDED | PARSER_LEXICAL_ENV_NEEDED | PARSER_NO_REG_STORE; is_eval = true; } if (context_p->last_cbc_opcode == CBC_PUSH_PROP) { context_p->last_cbc_opcode = CBC_PUSH_PROP_REFERENCE; opcode = CBC_CALL_PROP; } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_REFERENCE; opcode = CBC_CALL_PROP; } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE; opcode = CBC_CALL_PROP; } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_THIS_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_PROP_THIS_LITERAL_REFERENCE; opcode = CBC_CALL_PROP; } else if ((context_p->status_flags & (PARSER_INSIDE_WITH | PARSER_RESOLVE_THIS_FOR_CALLS)) && PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { opcode = CBC_CALL_PROP; if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_IDENT_REFERENCE; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_LITERAL; parser_emit_cbc_literal (context_p, CBC_PUSH_IDENT_REFERENCE, context_p->last_cbc.value); } else { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; parser_emit_cbc_literal (context_p, CBC_PUSH_IDENT_REFERENCE, context_p->last_cbc.third_literal_index); } } } lexer_next_token (context_p); if (context_p->token.type != LEXER_RIGHT_PAREN) { while (true) { if (++call_arguments > CBC_MAXIMUM_BYTE_VALUE) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIMIT_REACHED); } 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); } } lexer_next_token (context_p); if (is_eval) { parser_emit_cbc (context_p, CBC_EVAL); } if (call_arguments == 0) { if (opcode == CBC_CALL) { parser_emit_cbc (context_p, CBC_CALL0); continue; } if (opcode == CBC_CALL_PROP) { parser_emit_cbc (context_p, CBC_CALL0_PROP); continue; } if (opcode == CBC_NEW) { parser_emit_cbc (context_p, CBC_NEW0); continue; } } if (call_arguments == 1) { if (opcode == CBC_CALL) { parser_emit_cbc (context_p, CBC_CALL1); continue; } if (opcode == CBC_CALL_PROP) { parser_emit_cbc (context_p, CBC_CALL1_PROP); continue; } if (opcode == CBC_NEW) { parser_emit_cbc (context_p, CBC_NEW1); continue; } } if (call_arguments == 2) { if (opcode == CBC_CALL) { parser_emit_cbc (context_p, CBC_CALL2); continue; } if (opcode == CBC_CALL_PROP) { parser_flush_cbc (context_p); /* Manually adjusting stack usage. */ JERRY_ASSERT (context_p->stack_depth > 0); context_p->stack_depth--; parser_emit_cbc (context_p, CBC_CALL2_PROP); continue; } } parser_emit_cbc_call (context_p, opcode, call_arguments); continue; } default: { if (context_p->stack_top_uint8 == LEXER_KEYW_NEW) { parser_push_result (context_p); parser_emit_cbc (context_p, CBC_NEW0); parser_stack_pop_uint8 (context_p); continue; } if (!context_p->token.was_newline && (context_p->token.type == LEXER_INCREASE || context_p->token.type == LEXER_DECREASE)) { cbc_opcode_t opcode = (context_p->token.type == LEXER_INCREASE) ? CBC_POST_INCR : CBC_POST_DECR; parser_push_result (context_p); parser_emit_unary_lvalue_opcode (context_p, opcode); lexer_next_token (context_p); } break; } } break; } /* Generate byte code for the unary operators. */ while (true) { uint8_t token = context_p->stack_top_uint8; if (!LEXER_IS_UNARY_OP_TOKEN (token)) { break; } parser_push_result (context_p); parser_stack_pop_uint8 (context_p); if (LEXER_IS_UNARY_LVALUE_OP_TOKEN (token)) { if (token == LEXER_KEYW_DELETE) { token = CBC_DELETE_PUSH_RESULT; } else { token = (uint8_t) (LEXER_UNARY_LVALUE_OP_TOKEN_TO_OPCODE (token)); } parser_emit_unary_lvalue_opcode (context_p, (cbc_opcode_t) token); } else { token = (uint8_t) (LEXER_UNARY_OP_TOKEN_TO_OPCODE (token)); if (token == CBC_TYPEOF) { if (PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_TYPEOF_IDENT; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_LITERAL; parser_emit_cbc_literal (context_p, CBC_TYPEOF_IDENT, context_p->last_cbc.value); } else { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; parser_emit_cbc_literal (context_p, CBC_TYPEOF_IDENT, context_p->last_cbc.third_literal_index); } } else { parser_emit_cbc (context_p, token); } } else { if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { /* It is not worth to combine with push multiple literals * since the byte code size will not decrease. */ JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, token + 1)); context_p->last_cbc_opcode = (uint16_t) (token + 1); } else { parser_emit_cbc (context_p, token); } } } } } /* parser_process_unary_expression */ /** * Append a binary token. */ static void parser_append_binary_token (parser_context_t *context_p) /**< context */ { JERRY_ASSERT (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type)); parser_push_result (context_p); if (context_p->token.type == LEXER_ASSIGN) { /* Unlike other tokens, the whole byte code is saved for binary * assignment, since it has multiple forms depending on the * previous instruction. */ if (PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_LITERAL, CBC_ASSIGN_SET_IDENT)); if ((context_p->status_flags & PARSER_IS_STRICT) && context_p->last_cbc.literal_object_type != LEXER_LITERAL_OBJECT_ANY) { parser_error_t error; if (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_EVAL) { error = PARSER_ERR_EVAL_CANNOT_ASSIGNED; } else { JERRY_ASSERT (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_ARGUMENTS); error = PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED; } parser_raise_error (context_p, error); } if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index); context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { parser_stack_push_uint16 (context_p, context_p->last_cbc.value); context_p->last_cbc_opcode = CBC_PUSH_LITERAL; } else { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); parser_stack_push_uint16 (context_p, context_p->last_cbc.third_literal_index); context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; } parser_stack_push_uint8 (context_p, CBC_ASSIGN_SET_IDENT); } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP, CBC_ASSIGN)); parser_stack_push_uint8 (context_p, CBC_ASSIGN); context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL) { if (context_p->last_cbc.literal_type != LEXER_IDENT_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL, CBC_ASSIGN_PROP_LITERAL)); parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index); parser_stack_push_uint8 (context_p, CBC_ASSIGN_PROP_LITERAL); context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } else { context_p->last_cbc_opcode = CBC_PUSH_LITERAL; parser_stack_push_uint8 (context_p, CBC_ASSIGN); } } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL_LITERAL, CBC_PUSH_TWO_LITERALS)); context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; parser_stack_push_uint8 (context_p, CBC_ASSIGN); } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_THIS_LITERAL) { if (context_p->last_cbc.literal_type != LEXER_IDENT_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_THIS_LITERAL, CBC_ASSIGN_PROP_THIS_LITERAL)); parser_stack_push_uint16 (context_p, context_p->last_cbc.literal_index); parser_stack_push_uint8 (context_p, CBC_ASSIGN_PROP_THIS_LITERAL); context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; } else { context_p->last_cbc_opcode = CBC_PUSH_THIS_LITERAL; parser_stack_push_uint8 (context_p, CBC_ASSIGN); } } else { /* Invalid LeftHandSide expression. */ parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR); parser_stack_push_uint8 (context_p, CBC_ASSIGN); } } else if (LEXER_IS_BINARY_LVALUE_TOKEN (context_p->token.type)) { if (PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { if ((context_p->status_flags & PARSER_IS_STRICT) && context_p->last_cbc.literal_object_type != LEXER_LITERAL_OBJECT_ANY) { parser_error_t error; if (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_EVAL) { error = PARSER_ERR_EVAL_CANNOT_ASSIGNED; } else { JERRY_ASSERT (context_p->last_cbc.literal_object_type == LEXER_LITERAL_OBJECT_ARGUMENTS); error = PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED; } parser_raise_error (context_p, error); } if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { context_p->last_cbc_opcode = CBC_PUSH_IDENT_REFERENCE; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { context_p->last_cbc_opcode = CBC_PUSH_LITERAL; parser_emit_cbc_literal (context_p, CBC_PUSH_IDENT_REFERENCE, context_p->last_cbc.value); } else { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_THREE_LITERALS); context_p->last_cbc_opcode = CBC_PUSH_TWO_LITERALS; parser_emit_cbc_literal (context_p, CBC_PUSH_IDENT_REFERENCE, context_p->last_cbc.third_literal_index); } } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP, CBC_PUSH_PROP_REFERENCE)); context_p->last_cbc_opcode = CBC_PUSH_PROP_REFERENCE; } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL, CBC_PUSH_PROP_LITERAL_REFERENCE)); context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_REFERENCE; } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_LITERAL_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_LITERAL_LITERAL, CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE)); context_p->last_cbc_opcode = CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE; } else if (context_p->last_cbc_opcode == CBC_PUSH_PROP_THIS_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (CBC_PUSH_PROP_THIS_LITERAL, CBC_PUSH_PROP_THIS_LITERAL_REFERENCE)); context_p->last_cbc_opcode = CBC_PUSH_PROP_THIS_LITERAL_REFERENCE; } else { /* Invalid LeftHandSide expression. */ parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR); parser_emit_cbc (context_p, CBC_PUSH_PROP_REFERENCE); } } else if (context_p->token.type == LEXER_LOGICAL_OR || context_p->token.type == LEXER_LOGICAL_AND) { parser_branch_t branch; uint16_t opcode = CBC_BRANCH_IF_LOGICAL_TRUE; if (context_p->token.type == LEXER_LOGICAL_AND) { opcode = CBC_BRANCH_IF_LOGICAL_FALSE; } 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_append_binary_token */ /** * Emit opcode for binary computations. */ static void parser_process_binary_opcodes (parser_context_t *context_p, /**< context */ uint8_t min_prec_treshold) /**< minimal precedence of tokens */ { while (true) { uint8_t token = context_p->stack_top_uint8; cbc_opcode_t opcode; /* For left-to-right operators (all binary operators except assignment * and logical operators), the byte code is flushed if the precedence * of the next operator is less or equal than the current operator. For * assignment and logical operators, we add 1 to the min precendence to * force right-to-left evaluation order. */ if (!LEXER_IS_BINARY_OP_TOKEN (token) || parser_binary_precedence_table[token - LEXER_FIRST_BINARY_OP] < min_prec_treshold) { return; } parser_push_result (context_p); parser_stack_pop_uint8 (context_p); if (token == LEXER_ASSIGN) { opcode = (cbc_opcode_t) context_p->stack_top_uint8; parser_stack_pop_uint8 (context_p); if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { if (opcode == CBC_ASSIGN_SET_IDENT) { JERRY_ASSERT (CBC_ARGS_EQ (CBC_ASSIGN_LITERAL_SET_IDENT, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); context_p->last_cbc.value = parser_stack_pop_uint16 (context_p); context_p->last_cbc_opcode = CBC_ASSIGN_LITERAL_SET_IDENT; continue; } } if (cbc_flags[opcode] & CBC_HAS_LITERAL_ARG) { uint16_t index = parser_stack_pop_uint16 (context_p); parser_emit_cbc_literal (context_p, opcode, index); if (opcode == CBC_ASSIGN_PROP_THIS_LITERAL && (context_p->stack_depth >= context_p->stack_limit)) { /* Stack limit is increased for VM_OC_ASSIGN_PROP_THIS. Needed by vm.c. */ JERRY_ASSERT (context_p->stack_depth == context_p->stack_limit); context_p->stack_limit++; if (context_p->stack_limit > PARSER_MAXIMUM_STACK_LIMIT) { parser_raise_error (context_p, PARSER_ERR_STACK_LIMIT_REACHED); } } continue; } } else if (LEXER_IS_BINARY_LVALUE_TOKEN (token)) { opcode = LEXER_BINARY_LVALUE_OP_TOKEN_TO_OPCODE (token); if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { JERRY_ASSERT (CBC_ARGS_EQ (opcode + CBC_BINARY_LVALUE_WITH_LITERAL, CBC_HAS_LITERAL_ARG)); context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_LVALUE_WITH_LITERAL); continue; } } else if (token == LEXER_LOGICAL_OR || token == LEXER_LOGICAL_AND) { parser_branch_t branch; parser_stack_pop (context_p, &branch, sizeof (parser_branch_t)); parser_set_branch_to_current_position (context_p, &branch); continue; } else { opcode = LEXER_BINARY_OP_TOKEN_TO_OPCODE (token); if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) { JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, opcode + CBC_BINARY_WITH_LITERAL)); context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_LITERAL); continue; } else if (context_p->last_cbc_opcode == CBC_PUSH_TWO_LITERALS) { JERRY_ASSERT (CBC_ARGS_EQ (opcode + CBC_BINARY_WITH_TWO_LITERALS, CBC_HAS_LITERAL_ARG | CBC_HAS_LITERAL_ARG2)); context_p->last_cbc_opcode = (uint16_t) (opcode + CBC_BINARY_WITH_TWO_LITERALS); continue; } } parser_emit_cbc (context_p, opcode); } } /* parser_process_binary_opcodes */ /** * Parse expression. */ void parser_parse_expression (parser_context_t *context_p, /**< context */ int options) /**< option flags */ { size_t grouping_level = 0; parser_stack_push_uint8 (context_p, LEXER_EXPRESSION_START); while (true) { if (options & PARSE_EXPR_HAS_LITERAL) { JERRY_ASSERT (context_p->last_cbc_opcode == CBC_PUSH_LITERAL); /* True only for the first expression. */ options &= ~PARSE_EXPR_HAS_LITERAL; } else { parser_parse_unary_expression (context_p, &grouping_level); } while (true) { parser_process_unary_expression (context_p); /* The engine flush binary opcodes above this precedence. */ uint8_t min_prec_treshold = CBC_MAXIMUM_BYTE_VALUE; if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type)) { min_prec_treshold = parser_binary_precedence_table[context_p->token.type - LEXER_FIRST_BINARY_OP]; if (LEXER_IS_BINARY_LVALUE_TOKEN (context_p->token.type) || context_p->token.type == LEXER_LOGICAL_OR || context_p->token.type == LEXER_LOGICAL_AND) { /* Right-to-left evaluation order. */ min_prec_treshold++; } } else { min_prec_treshold = 0; } parser_process_binary_opcodes (context_p, min_prec_treshold); if (context_p->token.type == LEXER_RIGHT_PAREN) { if (context_p->stack_top_uint8 == LEXER_LEFT_PAREN || context_p->stack_top_uint8 == LEXER_COMMA_SEP_LIST) { JERRY_ASSERT (grouping_level > 0); grouping_level--; if (context_p->stack_top_uint8 == LEXER_COMMA_SEP_LIST) { parser_push_result (context_p); parser_flush_cbc (context_p); } parser_stack_pop_uint8 (context_p); lexer_next_token (context_p); continue; } } else if (context_p->token.type == LEXER_QUESTION_MARK) { cbc_opcode_t opcode = CBC_BRANCH_IF_FALSE_FORWARD; parser_branch_t cond_branch; parser_branch_t uncond_branch; parser_push_result (context_p); if (context_p->last_cbc_opcode == CBC_LOGICAL_NOT) { context_p->last_cbc_opcode = PARSER_CBC_UNAVAILABLE; opcode = CBC_BRANCH_IF_TRUE_FORWARD; } parser_emit_cbc_forward_branch (context_p, opcode, &cond_branch); lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); parser_emit_cbc_forward_branch (context_p, CBC_JUMP_FORWARD, &uncond_branch); parser_set_branch_to_current_position (context_p, &cond_branch); /* Although byte code is constructed for two branches, * only one of them will be executed. To reflect this * the stack is manually adjusted. */ JERRY_ASSERT (context_p->stack_depth > 0); context_p->stack_depth--; if (context_p->token.type != LEXER_COLON) { parser_raise_error (context_p, PARSER_ERR_COLON_FOR_CONDITIONAL_EXPECTED); } lexer_next_token (context_p); parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); parser_set_branch_to_current_position (context_p, &uncond_branch); /* Last opcode rewrite is not allowed because * the result may come from the first branch. */ parser_flush_cbc (context_p); continue; } break; } if (context_p->token.type == LEXER_COMMA) { if (!(options & PARSE_EXPR_NO_COMMA) || grouping_level > 0) { if (!CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) { parser_emit_cbc (context_p, CBC_POP); } if (context_p->stack_top_uint8 == LEXER_LEFT_PAREN) { parser_mem_page_t *page_p = context_p->stack.first_p; JERRY_ASSERT (page_p != NULL); page_p->bytes[context_p->stack.last_position - 1] = LEXER_COMMA_SEP_LIST; context_p->stack_top_uint8 = LEXER_COMMA_SEP_LIST; } lexer_next_token (context_p); continue; } } else if (LEXER_IS_BINARY_OP_TOKEN (context_p->token.type)) { parser_append_binary_token (context_p); lexer_next_token (context_p); continue; } break; } if (grouping_level != 0) { parser_raise_error (context_p, PARSER_ERR_RIGHT_PAREN_EXPECTED); } JERRY_ASSERT (context_p->stack_top_uint8 == LEXER_EXPRESSION_START); parser_stack_pop_uint8 (context_p); if (options & PARSE_EXPR_STATEMENT) { if (!CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) { parser_emit_cbc (context_p, CBC_POP); } } else if (options & PARSE_EXPR_BLOCK) { if (CBC_NO_RESULT_COMPOUND_ASSIGMENT (context_p->last_cbc_opcode)) { context_p->last_cbc_opcode = PARSER_TO_BINARY_OPERATION_WITH_BLOCK (context_p->last_cbc_opcode); parser_flush_cbc (context_p); } else if (CBC_NO_RESULT_BLOCK (context_p->last_cbc_opcode)) { JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, context_p->last_cbc_opcode + 2)); PARSER_PLUS_EQUAL_U16 (context_p->last_cbc_opcode, 2); parser_flush_cbc (context_p); } else { if (CBC_NO_RESULT_OPERATION (context_p->last_cbc_opcode)) { JERRY_ASSERT (CBC_SAME_ARGS (context_p->last_cbc_opcode, context_p->last_cbc_opcode + 1)); context_p->last_cbc_opcode++; } parser_emit_cbc (context_p, CBC_POP_BLOCK); } } else { parser_push_result (context_p); } } /* parser_parse_expression */ /** * @} * @} * @} */ #endif /* JERRY_JS_PARSER */