/* 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_scanner Scanner * @{ */ /** * Scan mode types types. */ typedef enum { SCAN_MODE_PRIMARY_EXPRESSION, /**< scanning primary expression */ SCAN_MODE_PRIMARY_EXPRESSION_AFTER_NEW, /**< scanning primary expression after new */ SCAN_MODE_POST_PRIMARY_EXPRESSION, /**< scanning post primary expression */ SCAN_MODE_PRIMARY_EXPRESSION_END, /**< scanning prymary expression end */ SCAN_MODE_STATEMENT, /**< scanning statement */ SCAN_MODE_FUNCTION_ARGUMENTS, /**< scanning function arguments */ SCAN_MODE_PROPERTY_NAME, /**< scanning property name */ } scan_modes_t; /** * Scan stack mode types types. */ typedef enum { SCAN_STACK_HEAD, /**< head */ SCAN_STACK_PAREN_EXPRESSION, /**< parent expression group */ SCAN_STACK_PAREN_STATEMENT, /**< parent stetement group */ SCAN_STACK_COLON_EXPRESSION, /**< colon expression group */ SCAN_STACK_COLON_STATEMENT, /**< colon statement group*/ SCAN_STACK_SQUARE_BRACKETED_EXPRESSION, /**< square bracketed expression group */ SCAN_STACK_OBJECT_LITERAL, /**< object literal group */ SCAN_STACK_BLOCK_STATEMENT, /**< block statement group */ SCAN_STACK_BLOCK_EXPRESSION, /**< block expression group*/ SCAN_STACK_BLOCK_PROPERTY, /**< block property group */ } scan_stack_modes_t; /** * Scan primary expression. * * @return true for continue, false for break */ static bool parser_scan_primary_expression (parser_context_t *context_p, /**< context */ lexer_token_type_t type, /**< current token type */ scan_stack_modes_t stack_top, /**< current stack top */ scan_modes_t *mode) /**< scan mode */ { switch (type) { case LEXER_KEYW_NEW: { *mode = SCAN_MODE_PRIMARY_EXPRESSION_AFTER_NEW; break; } case LEXER_DIVIDE: case LEXER_ASSIGN_DIVIDE: { lexer_construct_regexp_object (context_p, true); *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; break; } case LEXER_KEYW_FUNCTION: { parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_EXPRESSION); *mode = SCAN_MODE_FUNCTION_ARGUMENTS; break; } case LEXER_LEFT_PAREN: { parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_EXPRESSION); *mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } case LEXER_LEFT_SQUARE: { parser_stack_push_uint8 (context_p, SCAN_STACK_SQUARE_BRACKETED_EXPRESSION); *mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } case LEXER_LEFT_BRACE: { parser_stack_push_uint8 (context_p, SCAN_STACK_OBJECT_LITERAL); *mode = SCAN_MODE_PROPERTY_NAME; return true; } case LEXER_LITERAL: case LEXER_KEYW_THIS: case LEXER_LIT_TRUE: case LEXER_LIT_FALSE: case LEXER_LIT_NULL: { *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; break; } case LEXER_RIGHT_SQUARE: { if (stack_top != SCAN_STACK_SQUARE_BRACKETED_EXPRESSION) { parser_raise_error (context_p, PARSER_ERR_PRIMARY_EXP_EXPECTED); } parser_stack_pop_uint8 (context_p); *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; break; } case LEXER_COMMA: { if (stack_top != SCAN_STACK_SQUARE_BRACKETED_EXPRESSION) { parser_raise_error (context_p, PARSER_ERR_PRIMARY_EXP_EXPECTED); } *mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } case LEXER_RIGHT_PAREN: { *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; if (stack_top == SCAN_STACK_PAREN_STATEMENT) { *mode = SCAN_MODE_STATEMENT; } else if (stack_top != SCAN_STACK_PAREN_EXPRESSION) { parser_raise_error (context_p, PARSER_ERR_PRIMARY_EXP_EXPECTED); } parser_stack_pop_uint8 (context_p); break; } case LEXER_SEMICOLON: { /* Needed by for (;;) statements. */ if (stack_top != SCAN_STACK_PAREN_STATEMENT) { parser_raise_error (context_p, PARSER_ERR_PRIMARY_EXP_EXPECTED); } *mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } default: { parser_raise_error (context_p, PARSER_ERR_PRIMARY_EXP_EXPECTED); } } return false; } /* parser_scan_primary_expression */ /** * Scan the tokens after the primary expression. * * @return true for break, false for fall through */ static bool parser_scan_post_primary_expression (parser_context_t *context_p, /**< context */ lexer_token_type_t type, /**< current token type */ scan_modes_t *mode) /**< scan mode */ { switch (type) { case LEXER_DOT: { lexer_scan_identifier (context_p, false); return true; } case LEXER_LEFT_PAREN: { parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_EXPRESSION); *mode = SCAN_MODE_PRIMARY_EXPRESSION; return true; } case LEXER_LEFT_SQUARE: { parser_stack_push_uint8 (context_p, SCAN_STACK_SQUARE_BRACKETED_EXPRESSION); *mode = SCAN_MODE_PRIMARY_EXPRESSION; return true; } case LEXER_INCREASE: case LEXER_DECREASE: { if (!context_p->token.was_newline) { *mode = SCAN_MODE_PRIMARY_EXPRESSION_END; return true; } /* FALLTHRU */ } default: { break; } } return false; } /* parser_scan_post_primary_expression */ /** * Scan the tokens after the primary expression. * * @return true for continue, false for break */ static bool parser_scan_primary_expression_end (parser_context_t *context_p, /**< context */ lexer_token_type_t type, /**< current token type */ scan_stack_modes_t stack_top, /**< current stack top */ lexer_token_type_t end_type, /**< terminator token type */ scan_modes_t *mode) /**< scan mode */ { switch (type) { case LEXER_QUESTION_MARK: { parser_stack_push_uint8 (context_p, SCAN_STACK_COLON_EXPRESSION); *mode = SCAN_MODE_PRIMARY_EXPRESSION; return false; } case LEXER_COMMA: { if (stack_top == SCAN_STACK_OBJECT_LITERAL) { *mode = SCAN_MODE_PROPERTY_NAME; return true; } *mode = SCAN_MODE_PRIMARY_EXPRESSION; return false; } case LEXER_COLON: { if (stack_top == SCAN_STACK_COLON_EXPRESSION || stack_top == SCAN_STACK_COLON_STATEMENT) { if (stack_top == SCAN_STACK_COLON_EXPRESSION) { *mode = SCAN_MODE_PRIMARY_EXPRESSION; } else { *mode = SCAN_MODE_STATEMENT; } parser_stack_pop_uint8 (context_p); return false; } /* FALLTHRU */ } default: { break; } } if (LEXER_IS_BINARY_OP_TOKEN (type) || (type == LEXER_SEMICOLON && stack_top == SCAN_STACK_PAREN_STATEMENT)) { *mode = SCAN_MODE_PRIMARY_EXPRESSION; return false; } if ((type == LEXER_RIGHT_SQUARE && stack_top == SCAN_STACK_SQUARE_BRACKETED_EXPRESSION) || (type == LEXER_RIGHT_PAREN && stack_top == SCAN_STACK_PAREN_EXPRESSION) || (type == LEXER_RIGHT_BRACE && stack_top == SCAN_STACK_OBJECT_LITERAL)) { parser_stack_pop_uint8 (context_p); *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; return false; } *mode = SCAN_MODE_STATEMENT; if (type == LEXER_RIGHT_PAREN && stack_top == SCAN_STACK_PAREN_STATEMENT) { parser_stack_pop_uint8 (context_p); return false; } /* Check whether we can enter to statement mode. */ if (stack_top != SCAN_STACK_BLOCK_STATEMENT && stack_top != SCAN_STACK_BLOCK_EXPRESSION && !(stack_top == SCAN_STACK_HEAD && end_type == LEXER_SCAN_SWITCH)) { parser_raise_error (context_p, PARSER_ERR_INVALID_EXPRESSION); } if (type == LEXER_RIGHT_BRACE || context_p->token.was_newline) { return true; } if (type != LEXER_SEMICOLON) { parser_raise_error (context_p, PARSER_ERR_INVALID_EXPRESSION); } return false; } /* parser_scan_primary_expression_end */ /** * Scan statements. * * @return true for continue, false for break */ static bool parser_scan_statement (parser_context_t *context_p, /**< context */ lexer_token_type_t type, /**< current token type */ scan_stack_modes_t stack_top, /**< current stack top */ scan_modes_t *mode) /**< scan mode */ { switch (type) { case LEXER_SEMICOLON: case LEXER_KEYW_ELSE: case LEXER_KEYW_DO: case LEXER_KEYW_TRY: case LEXER_KEYW_FINALLY: case LEXER_KEYW_DEBUGGER: { return false; } case LEXER_KEYW_IF: case LEXER_KEYW_WHILE: case LEXER_KEYW_WITH: case LEXER_KEYW_SWITCH: case LEXER_KEYW_CATCH: { lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_PAREN) { parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); } parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_STATEMENT); *mode = SCAN_MODE_PRIMARY_EXPRESSION; return false; } case LEXER_KEYW_FOR: { lexer_next_token (context_p); if (context_p->token.type != LEXER_LEFT_PAREN) { parser_raise_error (context_p, PARSER_ERR_LEFT_PAREN_EXPECTED); } lexer_next_token (context_p); parser_stack_push_uint8 (context_p, SCAN_STACK_PAREN_STATEMENT); *mode = SCAN_MODE_PRIMARY_EXPRESSION; if (context_p->token.type == LEXER_KEYW_VAR) { return false; } return true; } case LEXER_KEYW_VAR: case LEXER_KEYW_THROW: { *mode = SCAN_MODE_PRIMARY_EXPRESSION; return false; } case LEXER_KEYW_RETURN: { lexer_next_token (context_p); if (!context_p->token.was_newline && context_p->token.type != LEXER_SEMICOLON) { *mode = SCAN_MODE_PRIMARY_EXPRESSION; } return true; } case LEXER_KEYW_BREAK: case LEXER_KEYW_CONTINUE: { lexer_next_token (context_p); if (!context_p->token.was_newline && context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { return false; } return true; } case LEXER_KEYW_DEFAULT: { lexer_next_token (context_p); if (context_p->token.type != LEXER_COLON) { parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); } return false; } case LEXER_KEYW_CASE: { parser_stack_push_uint8 (context_p, SCAN_STACK_COLON_STATEMENT); *mode = SCAN_MODE_PRIMARY_EXPRESSION; return false; } case LEXER_RIGHT_BRACE: { if (stack_top == SCAN_STACK_BLOCK_STATEMENT || stack_top == SCAN_STACK_BLOCK_EXPRESSION || stack_top == SCAN_STACK_BLOCK_PROPERTY) { parser_stack_pop_uint8 (context_p); if (stack_top == SCAN_STACK_BLOCK_EXPRESSION) { *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; } else if (stack_top == SCAN_STACK_BLOCK_PROPERTY) { *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; lexer_next_token (context_p); if (context_p->token.type != LEXER_COMMA && context_p->token.type != LEXER_RIGHT_BRACE) { parser_raise_error (context_p, PARSER_ERR_OBJECT_ITEM_SEPARATOR_EXPECTED); } return true; } return false; } break; } case LEXER_LEFT_BRACE: { parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_STATEMENT); return false; } case LEXER_KEYW_FUNCTION: { parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_STATEMENT); *mode = SCAN_MODE_FUNCTION_ARGUMENTS; return false; } default: { break; } } *mode = SCAN_MODE_PRIMARY_EXPRESSION; if (type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { lexer_next_token (context_p); if (context_p->token.type == LEXER_COLON) { *mode = SCAN_MODE_STATEMENT; return false; } *mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; } return true; } /* parser_scan_statement */ /** * Pre-scan for token(s). */ void parser_scan_until (parser_context_t *context_p, /**< context */ lexer_range_t *range_p, /**< destination range */ lexer_token_type_t end_type) /**< terminator token type */ { scan_modes_t mode; lexer_token_type_t end_type_b = end_type; range_p->source_p = context_p->source_p; range_p->source_end_p = context_p->source_p; range_p->line = context_p->line; range_p->column = context_p->column; mode = SCAN_MODE_PRIMARY_EXPRESSION; if (end_type == LEXER_KEYW_CASE) { end_type = LEXER_SCAN_SWITCH; end_type_b = LEXER_SCAN_SWITCH; mode = SCAN_MODE_STATEMENT; } else { lexer_next_token (context_p); if (end_type == LEXER_KEYW_IN) { end_type_b = LEXER_SEMICOLON; if (context_p->token.type == LEXER_KEYW_VAR) { lexer_next_token (context_p); } } } parser_stack_push_uint8 (context_p, SCAN_STACK_HEAD); while (true) { lexer_token_type_t type = (lexer_token_type_t) context_p->token.type; scan_stack_modes_t stack_top = (scan_stack_modes_t) context_p->stack_top_uint8; if (type == LEXER_EOS) { parser_raise_error (context_p, PARSER_ERR_EXPRESSION_EXPECTED); } if (stack_top == SCAN_STACK_HEAD && (type == end_type || type == end_type_b)) { parser_stack_pop_uint8 (context_p); return; } switch (mode) { case SCAN_MODE_PRIMARY_EXPRESSION: { if (type == LEXER_ADD || type == LEXER_SUBTRACT || LEXER_IS_UNARY_OP_TOKEN (type)) { break; } /* FALLTHRU */ } case SCAN_MODE_PRIMARY_EXPRESSION_AFTER_NEW: { if (parser_scan_primary_expression (context_p, type, stack_top, &mode)) { continue; } break; } case SCAN_MODE_POST_PRIMARY_EXPRESSION: { if (parser_scan_post_primary_expression (context_p, type, &mode)) { break; } /* FALLTHRU */ } case SCAN_MODE_PRIMARY_EXPRESSION_END: { if (parser_scan_primary_expression_end (context_p, type, stack_top, end_type, &mode)) { continue; } break; } case SCAN_MODE_STATEMENT: { if (end_type == LEXER_SCAN_SWITCH && stack_top == SCAN_STACK_HEAD && (type == LEXER_KEYW_DEFAULT || type == LEXER_KEYW_CASE || type == LEXER_RIGHT_BRACE)) { parser_stack_pop_uint8 (context_p); return; } if (parser_scan_statement (context_p, type, stack_top, &mode)) { continue; } break; } case SCAN_MODE_FUNCTION_ARGUMENTS: { JERRY_ASSERT (stack_top == SCAN_STACK_BLOCK_STATEMENT || stack_top == SCAN_STACK_BLOCK_EXPRESSION || stack_top == SCAN_STACK_BLOCK_PROPERTY); if (context_p->token.type == LEXER_LITERAL && (context_p->token.lit_location.type == LEXER_IDENT_LITERAL || context_p->token.lit_location.type == LEXER_NUMBER_LITERAL)) { lexer_next_token (context_p); } if (context_p->token.type != LEXER_LEFT_PAREN) { parser_raise_error (context_p, PARSER_ERR_ARGUMENT_LIST_EXPECTED); } lexer_next_token (context_p); if (context_p->token.type != LEXER_RIGHT_PAREN) { while (true) { if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { parser_raise_error (context_p, PARSER_ERR_IDENTIFIER_EXPECTED); } lexer_next_token (context_p); 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 (context_p->token.type != LEXER_LEFT_BRACE) { parser_raise_error (context_p, PARSER_ERR_LEFT_BRACE_EXPECTED); } mode = SCAN_MODE_STATEMENT; break; } case SCAN_MODE_PROPERTY_NAME: { JERRY_ASSERT (stack_top == SCAN_STACK_OBJECT_LITERAL); lexer_scan_identifier (context_p, true); if (context_p->token.type == LEXER_RIGHT_BRACE) { parser_stack_pop_uint8 (context_p); mode = SCAN_MODE_POST_PRIMARY_EXPRESSION; break; } if (context_p->token.type == LEXER_PROPERTY_GETTER || context_p->token.type == LEXER_PROPERTY_SETTER) { parser_stack_push_uint8 (context_p, SCAN_STACK_BLOCK_PROPERTY); mode = SCAN_MODE_FUNCTION_ARGUMENTS; break; } lexer_next_token (context_p); if (context_p->token.type != LEXER_COLON) { parser_raise_error (context_p, PARSER_ERR_COLON_EXPECTED); } mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } } range_p->source_end_p = context_p->source_p; lexer_next_token (context_p); } } /* parser_scan_until */ /** * @} * @} * @} */ #endif /* JERRY_JS_PARSER */