/* * Copyright (C) Igor Sysoev * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ #include static njs_int_t njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type, njs_bool_t init_this); static void njs_parser_scope_end(njs_parser_t *parser); static njs_int_t njs_parser_check_error_state(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_primary_expression_test(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_regexp_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_template_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_template_literal_string(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_template_literal_expression( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_cover_parenthesized_expression( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_binding_identifier_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_cover_parenthesized_expression_after( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_cover_parenthesized_expression_end( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_array_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_array_element_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_array_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_array_spread_element(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_object_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_object_literal_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_property_definition_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_property_definition_list_after( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_property_definition(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_property_definition_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_computed_property_name_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_computed_property_async_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_computed_property_name_handler(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_bool_t async); static njs_int_t njs_parser_initializer(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_initializer_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_initializer_assign(njs_parser_t *parser, njs_token_type_t type); static njs_int_t njs_parser_member_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_bracket(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_new_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_super_property(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_import(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_new(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_new_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_new_args(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_parser_node_t *njs_parser_create_call(njs_parser_t *parser, njs_parser_node_t *node, uint8_t ctor); static njs_int_t njs_parser_call_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_call_expression_args(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_call_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_arguments(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_parenthesis_or_comma(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_argument_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_argument_list_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_optional_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_optional_chain(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_optional_chain_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_new_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_new_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_left_hand_side_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_left_hand_side_expression_after( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_left_hand_side_expression_node( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_left_hand_side_expression_optional( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_update_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_update_expression_post(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_update_expression_unary(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_unary_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_unary_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_unary_expression_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_await(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_await_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_exponentiation_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_exponentiation_expression_match( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_multiplicative_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_multiplicative_expression_match( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_additive_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_additive_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_shift_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_shift_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_relational_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_relational_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_equality_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_equality_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_bitwise_AND_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_bitwise_AND_expression_and(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_bitwise_XOR_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_bitwise_XOR_expression_xor(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_bitwise_OR_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_bitwise_OR_expression_or(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_logical_AND_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_logical_AND_expression_and(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_logical_OR_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_logical_OR_expression_or(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_coalesce_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_short_circuit_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_conditional_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_conditional_question_mark(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_conditional_colon(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_conditional_colon_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_assignment_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_match_arrow_expression(njs_parser_t *parser, njs_lexer_token_t *token); static njs_int_t njs_parser_assignment_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_assignment_operator(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_assignment_operator_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_expression_comma(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_statement_wo_node(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_hoistable_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_block_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_block_statement_open_brace(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_block_statement_close_brace(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_statement_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_statement_list_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_statement_list_item(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_lexical_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_variable_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_variable_declaration_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_variable_declaration_list_next( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_variable_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_binding_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_object_binding_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_array_binding_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_expression_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_expression_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_if_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_if_close_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_else_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_else_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_iteration_statement_do(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_iteration_statement_do_while(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_do_while_semicolon(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_iteration_statement_while(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_while_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_while_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_iteration_statement_for(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_expression_map_continue( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_expression_map_reparse( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_expression_continue_op(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_expression_continue_assign_comma( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_in_statement_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_iteration_statement_for_map(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_var_binding_or_var_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_token_type_t token_type); static njs_int_t njs_parser_for_var_in_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_var_in_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_var_in_of_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_in_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_in_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_expression_end(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_end(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_block(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_block_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_case(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_case_wo_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_case_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_bool_t with_default); static njs_int_t njs_parser_switch_case_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_case_block(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_case_after_wo_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_case_block_wo_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_continue_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_break_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_break_continue(njs_parser_t *parser, njs_lexer_token_t *token, njs_token_type_t type); static njs_int_t njs_parser_return_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_return_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_with_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_labelled_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_labelled_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_throw_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_throw_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_try_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_catch_or_finally(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_catch_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_catch_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_catch_statement_open_brace(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_catch_finally(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_debugger_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_declaration_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_parse(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_unique_formal_parameters(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_formal_parameters(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_formal_parameters_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_arrow_function(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_arrow_function_args_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_arrow_function_arrow(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_arrow_function_body_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_method_definition(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_get_set(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_get_set_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_get_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_set_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_lambda(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_lambda_args_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_lambda_body_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_export(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_export_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_import(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_export_sink(njs_parser_t *parser); static njs_parser_node_t *njs_parser_return_set(njs_parser_t *parser, njs_parser_node_t *expr); static njs_parser_node_t *njs_parser_variable_node(njs_parser_t *parser, uintptr_t atom_id, njs_variable_type_t type, njs_variable_t **retvar); static njs_parser_node_t *njs_parser_reference(njs_parser_t *parser, njs_lexer_token_t *token); static njs_parser_node_t *njs_parser_argument(njs_parser_t *parser, njs_parser_node_t *expr, njs_index_t index); static njs_int_t njs_parser_object_property(njs_parser_t *parser, njs_parser_node_t *parent, njs_parser_node_t *property, njs_parser_node_t *value, njs_bool_t proto_init); static njs_int_t njs_parser_property_accessor(njs_parser_t *parser, njs_parser_node_t *parent, njs_parser_node_t *property, njs_parser_node_t *value, njs_token_type_t accessor); static njs_int_t njs_parser_array_item(njs_parser_t *parser, njs_parser_node_t *array, njs_parser_node_t *value); static njs_int_t njs_parser_template_string(njs_parser_t *parser, njs_lexer_token_t *token); static njs_token_type_t njs_parser_escape_string_create(njs_parser_t *parser, njs_lexer_token_t *token, njs_value_t *value); static njs_int_t njs_parser_escape_string_calc_length(njs_parser_t *parser, njs_lexer_token_t *token, size_t *out_size, size_t *out_length); static void njs_parser_serialize_tree(njs_chb_t *chain, njs_parser_node_t *node, njs_int_t *ret, size_t indent); static njs_int_t njs_parser_serialize_node(njs_chb_t *chain, njs_parser_node_t *node); #define njs_parser_chain_top(parser) \ ((parser)->scope->top) #define njs_parser_chain_top_set(parser, node) \ (parser)->scope->top = node njs_inline njs_int_t njs_parser_not_supported(njs_parser_t *parser, njs_lexer_token_t *token) { if (token->type != NJS_TOKEN_END) { njs_parser_syntax_error(parser, "Token \"%V\" not supported " "in this version", &token->text); } else { njs_parser_syntax_error(parser, "Not supported in this version"); } return NJS_DONE; } njs_inline njs_int_t njs_parser_reject(njs_parser_t *parser) { njs_queue_link_t *link; njs_parser_stack_entry_t *entry; while (!njs_queue_is_empty(&parser->stack)) { entry = njs_queue_link_data(njs_queue_first(&parser->stack), njs_parser_stack_entry_t, link); link = njs_queue_first(&parser->stack); njs_queue_remove(link); if (!entry->optional) { njs_parser_next(parser, entry->state); parser->target = entry->node; return NJS_DECLINED; } } return njs_parser_failed(parser); } njs_int_t njs_parser_init(njs_vm_t *vm, njs_parser_t *parser, njs_parser_scope_t *scope, njs_str_t *file, u_char *start, u_char *end) { njs_lexer_t *lexer; njs_memzero(parser, sizeof(njs_parser_t)); parser->scope = scope; lexer = &parser->lexer0; parser->lexer = lexer; parser->use_lhs = 0; return njs_lexer_init(vm, lexer, file, start, end); } njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser) { njs_int_t ret; njs_lexer_token_t *token; parser->vm = vm; njs_set_invalid(&vm->exception); if (parser->scope == NULL) { ret = njs_parser_scope_begin(parser, parser->module ? NJS_SCOPE_FUNCTION : NJS_SCOPE_GLOBAL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } else { parser->scope->top = NULL; parser->node = NULL; parser->ret = NJS_OK; } njs_queue_init(&parser->stack); parser->target = NULL; njs_parser_next(parser, njs_parser_statement_list); ret = njs_parser_after(parser, njs_queue_first(&parser->stack), NULL, 0, njs_parser_check_error_state); if (ret != NJS_OK) { return ret; } do { token = njs_lexer_token(parser->lexer, 0); if (njs_slow_path(token == NULL)) { return NJS_ERROR; } parser->ret = parser->state(parser, token, njs_queue_first(&parser->stack)); } while (parser->ret != NJS_DONE && parser->ret != NJS_ERROR); if (parser->ret != NJS_DONE) { return NJS_ERROR; } if (njs_is_error(&vm->exception)) { return NJS_ERROR; } if (parser->node == NULL) { /* Empty string, just semicolons or variables declarations. */ parser->node = njs_parser_node_new(parser, 0); if (njs_slow_path(parser->node == NULL)) { return NJS_ERROR; } } if (parser->module) { ret = njs_parser_export_sink(parser); if (ret != NJS_OK) { return NJS_ERROR; } } else { parser->node->token_type = NJS_TOKEN_END; parser->node->token_line = parser->lexer->line; njs_parser_chain_top_set(parser, parser->node); } return NJS_OK; } static njs_int_t njs_parser_check_error_state(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_failed(parser); } njs_int_t njs_parser_failed_state(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_END) { njs_parser_syntax_error(parser, "Unexpected token \"%V\"", &token->text); } else { njs_parser_syntax_error(parser, "Unexpected end of input"); } return NJS_DONE; } static njs_int_t njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type, njs_bool_t init_this) { njs_variable_t *var; njs_parser_scope_t *scope, *parent; scope = njs_mp_zalloc(parser->vm->mem_pool, sizeof(njs_parser_scope_t)); if (njs_slow_path(scope == NULL)) { return NJS_ERROR; } scope->type = type; njs_rbtree_init(&scope->variables, njs_parser_scope_rbtree_compare); njs_rbtree_init(&scope->labels, njs_parser_scope_rbtree_compare); njs_rbtree_init(&scope->references, njs_parser_scope_rbtree_compare); parent = parser->scope; scope->parent = parent; parser->scope = scope; if (type == NJS_SCOPE_FUNCTION || type == NJS_SCOPE_GLOBAL) { if (init_this) { /* Add this as first variable. */ var = njs_variable_add(parser, scope, NJS_ATOM_STRING_this, NJS_VARIABLE_VAR); if (njs_slow_path(var == NULL)) { return NJS_ERROR; } var->index = njs_scope_index(type, 0, NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR); } } scope->items = 1; return NJS_OK; } static void njs_parser_scope_end(njs_parser_t *parser) { njs_parser_scope_t *scope, *parent; scope = parser->scope; parent = scope->parent; parser->scope = parent; } intptr_t njs_parser_scope_rbtree_compare(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2) { njs_variable_node_t *lex_node1, *lex_node2; lex_node1 = (njs_variable_node_t *) node1; lex_node2 = (njs_variable_node_t *) node2; if (lex_node1->key < lex_node2->key) { return -1; } if (lex_node1->key > lex_node2->key) { return 1; } return 0; } static njs_int_t njs_parser_generator_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_class_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_async_generator_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_generator_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_class_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_function_or_generator_handler(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_bool_t is_async) { njs_parser_node_t *node, *cur; cur = parser->node; if (token->type == NJS_TOKEN_MULTIPLICATION) { njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_generator_declaration); } else { if (is_async) { node = njs_parser_node_new(parser, NJS_TOKEN_ASYNC_FUNCTION_DECLARATION); } else { node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_DECLARATION); } if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_function_declaration); } return njs_parser_after(parser, current, cur, 1, njs_parser_statement_after); } static njs_int_t njs_parser_function_or_generator(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_FUNCTION) { return NJS_DECLINED; } return njs_parser_function_or_generator_handler(parser, token, current, 0); } static njs_int_t njs_parser_async_function_or_generator(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_ASYNC) { return NJS_DECLINED; } token = njs_lexer_peek_token(parser->lexer, token, 1); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_FUNCTION) { return NJS_DECLINED; } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_function_or_generator_handler(parser, token, current, 1); } njs_inline njs_int_t njs_parser_expect_semicolon(njs_parser_t *parser, njs_lexer_token_t *token) { if (token->type != NJS_TOKEN_SEMICOLON) { if (parser->strict_semicolon || (token->type != NJS_TOKEN_END && token->type != NJS_TOKEN_CLOSE_BRACE && parser->lexer->prev_type != NJS_TOKEN_LINE_END)) { return NJS_DECLINED; } return NJS_OK; } njs_lexer_consume_token(parser->lexer, 1); return NJS_OK; } static njs_int_t njs_parser_semicolon(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { return njs_parser_failed(parser); } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_close_bracked(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_BRACKET) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_close_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_expression_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->node = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 0, njs_parser_close_parenthesis); } static njs_int_t njs_parser_iteration_statement_for_end(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->node->token_line = (uint32_t) (uintptr_t) parser->target; parser->target = NULL; njs_parser_scope_end(parser); return njs_parser_stack_pop(parser); } /* * 12.2 Primary Expression. */ static njs_int_t njs_parser_primary_expression_test(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_lexer_token_t *next; njs_parser_node_t *node; switch (token->type) { /* IdentifierReference */ case NJS_TOKEN_THIS: case NJS_TOKEN_NULL: case NJS_TOKEN_NAME: case NJS_TOKEN_YIELD: case NJS_TOKEN_AWAIT: goto reference; case NJS_TOKEN_TRUE: node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->u.value = njs_value_true; node->token_line = token->line; parser->node = node; goto done; case NJS_TOKEN_FALSE: node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->u.value = njs_value_false; node->token_line = token->line; parser->node = node; goto done; case NJS_TOKEN_NUMBER: node = njs_parser_node_new(parser, NJS_TOKEN_NUMBER); if (node == NULL) { return NJS_ERROR; } njs_set_number(&node->u.value, token->number); if (njs_number_is_integer_index(token->number) && token->number < 0x80000000) { node->u.value.atom_id = njs_number_atom((uint32_t) token->number); } node->token_line = token->line; parser->node = node; goto done; case NJS_TOKEN_STRING: node = njs_parser_node_new(parser, NJS_TOKEN_STRING); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; ret = njs_parser_string_create(parser->vm, token, &node->u.value); if (ret != NJS_OK) { return NJS_ERROR; } parser->node = node; goto done; case NJS_TOKEN_ESCAPE_STRING: /* Internal optimization. This is not in the specification. */ node = njs_parser_node_new(parser, NJS_TOKEN_STRING); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; ret = njs_parser_escape_string_create(parser, token, &node->u.value); if (ret != NJS_TOKEN_STRING) { return NJS_ERROR; } parser->node = node; goto done; case NJS_TOKEN_UNTERMINATED_STRING: /* Internal optimization. This is not in the specification. */ njs_parser_syntax_error(parser, "Unterminated string \"%V\"", &token->text); return NJS_ERROR; /* ArrayLiteral */ case NJS_TOKEN_OPEN_BRACKET: node = njs_parser_node_new(parser, NJS_TOKEN_ARRAY); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; njs_parser_next(parser, njs_parser_array_literal); break; /* ObjectLiteral */ case NJS_TOKEN_OPEN_BRACE: node = njs_parser_node_new(parser, NJS_TOKEN_OBJECT); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; njs_parser_next(parser, njs_parser_object_literal); break; /* FunctionExpression */ case NJS_TOKEN_FUNCTION: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } /* GeneratorExpression */ if (token->type == NJS_TOKEN_MULTIPLICATION) { njs_parser_next(parser, njs_parser_generator_expression); } else { node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; njs_parser_next(parser, njs_parser_function_expression); } break; /* ClassExpression */ case NJS_TOKEN_CLASS: njs_parser_next(parser, njs_parser_class_expression); return NJS_OK; /* AsyncFunctionExpression */ case NJS_TOKEN_ASYNC: next = njs_lexer_peek_token(parser->lexer, token, 1); if (next == NULL) { return NJS_ERROR; } if (next->type != NJS_TOKEN_FUNCTION) { goto reference; } njs_lexer_consume_token(parser->lexer, 1); next = njs_lexer_peek_token(parser->lexer, next, 0); if (njs_slow_path(next == NULL)) { return NJS_ERROR; } /* GeneratorExpression */ if (next->type == NJS_TOKEN_MULTIPLICATION) { njs_parser_next(parser, njs_parser_async_generator_expression); } else { node = njs_parser_node_new(parser, NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION); if (node == NULL) { return NJS_ERROR; } node->token_line = next->line; parser->node = node; njs_parser_next(parser, njs_parser_function_expression); } break; /* RegularExpressionLiteral */ case NJS_TOKEN_DIVISION: case NJS_TOKEN_DIVISION_ASSIGNMENT: node = njs_parser_node_new(parser, NJS_TOKEN_REGEXP); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; ret = njs_parser_regexp_literal(parser, token, current); if (ret != NJS_OK) { return NJS_ERROR; } goto done; /* TemplateLiteral */ case NJS_TOKEN_GRAVE: node = njs_parser_node_new(parser, NJS_TOKEN_TEMPLATE_LITERAL); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; njs_parser_next(parser, njs_parser_template_literal); return NJS_OK; /* CoverParenthesizedExpressionAndArrowParameterList */ case NJS_TOKEN_OPEN_PARENTHESIS: njs_lexer_consume_token(parser->lexer, 1); /* TODO: By specification. */ (void) njs_parser_cover_parenthesized_expression; parser->node = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 0, njs_parser_close_parenthesis); default: if (njs_lexer_token_is_identifier_reference(token)) { goto reference; } return njs_parser_reject(parser); } njs_lexer_consume_token(parser->lexer, 1); return NJS_OK; reference: node = njs_parser_reference(parser, token); if (node == NULL) { return NJS_ERROR; } node->u.value.atom_id = token->atom_id; node->token_line = token->line; parser->node = node; done: njs_lexer_consume_token(parser->lexer, 1); return NJS_DONE; } static njs_int_t njs_parser_regexp_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { u_char *p; njs_str_t text; njs_int_t ret; njs_lexer_t *lexer; njs_value_t *value, retval; njs_regex_flags_t flags; njs_regexp_pattern_t *pattern; value = &parser->node->u.value; lexer = parser->lexer; if (token->type == NJS_TOKEN_DIVISION_ASSIGNMENT) { lexer->start--; } for (p = lexer->start; p < lexer->end; p++) { switch (*p) { case '\n': case '\r': goto failed; case '[': while (1) { if (++p >= lexer->end) { goto failed; } if (*p == ']') { break; } switch (*p) { case '\n': case '\r': goto failed; case '\\': if (++p >= lexer->end || *p == '\n' || *p == '\r') { goto failed; } break; } } break; case '\\': if (++p >= lexer->end || *p == '\n' || *p == '\r') { goto failed; } break; case '/': text.start = lexer->start; text.length = p - text.start; p++; lexer->start = p; flags = njs_regexp_flags(&p, lexer->end); if (njs_slow_path(flags < 0)) { njs_parser_syntax_error(parser, "Invalid RegExp flags \"%*s\"", p - lexer->start, lexer->start); return NJS_ERROR; } lexer->start = p; pattern = njs_regexp_pattern_create(parser->vm, text.start, text.length, flags); if (njs_slow_path(pattern == NULL)) { retval = njs_vm_exception(parser->vm); ret = njs_value_property(parser->vm, &retval, NJS_ATOM_STRING_message, &retval); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_string_get(parser->vm, &retval, &text); njs_parser_syntax_error(parser, "%V", &text); return NJS_ERROR; } value->data.u.data = pattern; return NJS_OK; } } failed: njs_parser_syntax_error(parser, "Unterminated RegExp \"%*s\"", p - (lexer->start - 1), lexer->start - 1); return NJS_ERROR; } static njs_int_t njs_parser_template_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_index_t index; njs_parser_node_t *node, *array, *template, *temp; temp = njs_parser_node_new(parser, 0); if (temp == NULL) { return NJS_ERROR; } array = njs_parser_node_new(parser, NJS_TOKEN_ARRAY); if (array == NULL) { return NJS_ERROR; } array->token_line = token->line; template = parser->node; index = njs_scope_temp_index(template->scope); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } if (template->token_type != NJS_TOKEN_TEMPLATE_LITERAL) { node = njs_parser_argument(parser, array, index); if (node == NULL) { return NJS_ERROR; } node->temporary = 1; template->right = node; temp->right = node; index = njs_scope_temp_index(template->scope); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } } else { template->left = array; temp->right = template; } temp->temporary = 1; temp->left = template; temp->index = index; parser->target = temp; token->text.start++; token->text.length = 0; njs_parser_next(parser, njs_parser_template_literal_string); return NJS_OK; } static njs_int_t njs_parser_template_literal_string(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret, ret_item; njs_parser_node_t *template; template = parser->target->left; ret = njs_parser_template_string(parser, token); if (ret == NJS_ERROR) { njs_parser_syntax_error(parser, "Unterminated template literal"); return NJS_DONE; } if (template->token_type != NJS_TOKEN_TEMPLATE_LITERAL) { ret_item = njs_parser_array_item(parser, template->right->left, parser->node); } else { ret_item = njs_parser_array_item(parser, template->left, parser->node); } if (ret_item != NJS_OK) { return NJS_ERROR; } if (ret == NJS_DONE) { parser->node = template; njs_parser_node_free(parser, parser->target); njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } parser->node = NULL; njs_parser_next(parser, njs_parser_expression); njs_lexer_consume_token(parser->lexer, 1); return njs_parser_after(parser, current, parser->target, 0, njs_parser_template_literal_expression); } static njs_int_t njs_parser_template_literal_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *template, *node, *parent; if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } if (token->type != NJS_TOKEN_CLOSE_BRACE) { njs_parser_syntax_error(parser, "Missing \"}\" in template expression"); return NJS_DONE; } parent = parser->target->right; template = parser->target->left; if (template->token_type != NJS_TOKEN_TEMPLATE_LITERAL) { node = njs_parser_argument(parser, parser->node, parser->target->index); if (node == NULL) { return NJS_ERROR; } parent->right = node; parent = node; parser->target->index = njs_scope_temp_index(node->scope); if (njs_slow_path(parser->target->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } } else { ret = njs_parser_array_item(parser, template->left, parser->node); if (ret != NJS_OK) { return NJS_ERROR; } } parser->target->right = parent; parser->node = NULL; njs_parser_next(parser, njs_parser_template_literal_string); token->text.length = 0; token->text.start += 1; return NJS_OK; } static njs_int_t njs_parser_cover_parenthesized_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { switch (token->type) { case NJS_TOKEN_CLOSE_PARENTHESIS: (void) njs_parser_stack_pop(parser); break; case NJS_TOKEN_ELLIPSIS: njs_parser_next(parser, njs_parser_binding_identifier_pattern); break; default: parser->node = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 0, njs_parser_cover_parenthesized_expression_after); } njs_lexer_consume_token(parser->lexer, 1); return NJS_OK; } static njs_int_t njs_parser_binding_identifier_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * BindingIdentifier ) * BindingPattern ) */ switch (token->type) { /* BindingIdentifier */ case NJS_TOKEN_NAME: njs_parser_next(parser, njs_parser_cover_parenthesized_expression_end); break; case NJS_TOKEN_YIELD: njs_parser_next(parser, njs_parser_cover_parenthesized_expression_end); break; case NJS_TOKEN_AWAIT: njs_parser_next(parser, njs_parser_cover_parenthesized_expression_end); break; /* BindingPattern */ case NJS_TOKEN_OPEN_BRACKET: njs_parser_next(parser, njs_parser_array_binding_pattern); njs_lexer_consume_token(parser->lexer, 1); return njs_parser_after(parser, current, NULL, 0, njs_parser_cover_parenthesized_expression_end); case NJS_TOKEN_OPEN_BRACE: njs_parser_next(parser, njs_parser_object_binding_pattern); njs_lexer_consume_token(parser->lexer, 1); return njs_parser_after(parser, current, NULL, 0, njs_parser_cover_parenthesized_expression_end); default: return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 1); return NJS_OK; } static njs_int_t njs_parser_cover_parenthesized_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * ) * ,) * , ... BindingIdentifier ) * , ... BindingPattern ) */ if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { goto shift_stack; } if (token->type != NJS_TOKEN_COMMA) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (njs_slow_path(token == NULL)) { return NJS_ERROR; } if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { goto shift_stack; } if(token->type != NJS_TOKEN_ELLIPSIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_binding_identifier_pattern); return NJS_OK; shift_stack: njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_cover_parenthesized_expression_end(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } /* * 12.2.5 Array Initializer. */ static njs_int_t njs_parser_array_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_array_element_list); return NJS_OK; } static njs_int_t njs_parser_array_element_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *array; array = parser->target; switch (token->type) { case NJS_TOKEN_CLOSE_BRACKET: njs_lexer_consume_token(parser->lexer, 1); parser->node = array; return njs_parser_stack_pop(parser); case NJS_TOKEN_COMMA: njs_lexer_consume_token(parser->lexer, 1); array->ctor = 1; array->u.length++; return NJS_OK; case NJS_TOKEN_ELLIPSIS: #if 0 njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, array, 0, njs_parser_array_spread_element); #else (void) njs_parser_array_spread_element; return njs_parser_failed(parser); #endif default: break; } njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, array, 0, njs_parser_array_after); } static njs_int_t njs_parser_array_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } ret = njs_parser_array_item(parser, parser->target, parser->node); if (ret != NJS_OK) { return NJS_ERROR; } switch (token->type) { case NJS_TOKEN_COMMA: njs_lexer_consume_token(parser->lexer, 1); /* Fall through. */ case NJS_TOKEN_CLOSE_BRACKET: njs_parser_next(parser, njs_parser_array_element_list); break; default: return njs_parser_failed(parser); } return NJS_OK; } static njs_int_t njs_parser_array_spread_element(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK || token->type != NJS_TOKEN_CLOSE_BRACKET) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 12.2.6 Object Initializer. */ static njs_int_t njs_parser_object_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; node = njs_parser_node_new(parser, 0); if (node == NULL) { return NJS_ERROR; } node->left = parser->node; parser->node = NULL; parser->target = node; njs_parser_next(parser, njs_parser_property_definition_list); return njs_parser_after(parser, current, node, 1, njs_parser_object_literal_after); } static njs_int_t njs_parser_object_literal_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type == NJS_TOKEN_COMMA) { njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 1); if (token == NULL) { return NJS_ERROR; } } if (token->type != NJS_TOKEN_CLOSE_BRACE) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->node = parser->target->left; njs_parser_node_free(parser, parser->target); parser->target = NULL; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_property_definition_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_property_definition); return njs_parser_after(parser, current, parser->target, 1, njs_parser_property_definition_list_after); } static njs_int_t njs_parser_property_definition_list_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_COMMA) { return njs_parser_stack_pop(parser); } njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_property_definition); return njs_parser_after(parser, current, parser->target, 1, njs_parser_property_definition_list_after); } njs_inline njs_parser_node_t * njs_parser_property_name_node(njs_parser_t *parser, njs_lexer_token_t *token) { njs_int_t ret; njs_parser_node_t *property; if (token->type == NJS_TOKEN_NUMBER) { property = njs_parser_node_new(parser, NJS_TOKEN_NUMBER); if (property != NULL) { njs_set_number(&property->u.value, token->number); } } else if (token->type == NJS_TOKEN_ESCAPE_STRING) { property = njs_parser_node_new(parser, NJS_TOKEN_STRING); if (property != NULL) { ret = njs_parser_escape_string_create(parser, token, &property->u.value); if (ret != NJS_TOKEN_STRING) { return NULL; } } } else { property = njs_parser_node_string(parser->vm, token, parser); } if (property != NULL) { property->token_line = token->line; } return property; } njs_inline njs_int_t njs_parser_property_name(njs_parser_t *parser, njs_queue_link_t *current, unsigned consume) { njs_lexer_token_t *token; njs_parser_node_t *property; if (consume > 1) { token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } property = njs_parser_property_name_node(parser, token); if (property == NULL) { return NJS_ERROR; } parser->target->right = property; parser->target->index = (token->type == NJS_TOKEN_NAME); } njs_lexer_consume_token(parser->lexer, consume); parser->node = NULL; njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, parser->target, 1, njs_parser_property_definition_after); } static njs_int_t njs_parser_property_definition_ident(njs_parser_t *parser, njs_lexer_token_t *token, njs_parser_node_t *temp) { temp->right = njs_parser_node_string(parser->vm, token, parser); if (temp->right == NULL) { return NJS_ERROR; } temp->right->index = NJS_TOKEN_OPEN_BRACKET; parser->node = njs_parser_reference(parser, token); if (parser->node == NULL) { return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } /* CoverInitializedName */ if (token->type == NJS_TOKEN_ASSIGNMENT) { return njs_parser_not_supported(parser, token); } njs_parser_next(parser, njs_parser_property_definition_after); return NJS_OK; } static njs_int_t njs_parser_property_definition(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_str_t *name; njs_token_type_t accessor; njs_lexer_token_t *next; njs_parser_node_t *temp, *property; temp = parser->target; switch (token->type) { case NJS_TOKEN_CLOSE_BRACE: return njs_parser_stack_pop(parser); /* PropertyName */ case NJS_TOKEN_STRING: case NJS_TOKEN_ESCAPE_STRING: case NJS_TOKEN_NUMBER: next = njs_lexer_peek_token(parser->lexer, token, 0); if (next == NULL) { return NJS_ERROR; } if (next->type == NJS_TOKEN_COLON) { return njs_parser_property_name(parser, current, 2); } else if (next->type == NJS_TOKEN_OPEN_PARENTHESIS) { goto method_definition; } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_failed(parser); /* ComputedPropertyName */ case NJS_TOKEN_OPEN_BRACKET: njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, temp, 1, njs_parser_computed_property_name_after); case NJS_TOKEN_ELLIPSIS: return njs_parser_not_supported(parser, token); case NJS_TOKEN_ASYNC: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_OPEN_BRACKET) { njs_lexer_consume_token(parser->lexer, 2); njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, temp, 1, njs_parser_computed_property_async_after); } if (token->type == NJS_TOKEN_COLON) { return njs_parser_property_name(parser, current, 2); } if (!njs_lexer_token_is_identifier_name(token)) { return njs_parser_failed(parser); } next = njs_lexer_peek_token(parser->lexer, token, 0); if (next == NULL) { return NJS_ERROR; } if (next->type == NJS_TOKEN_OPEN_PARENTHESIS) { goto method_definition; } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_failed(parser); default: if (!njs_lexer_token_is_identifier_name(token)) { return njs_parser_reject(parser); } next = njs_lexer_peek_token(parser->lexer, token, 0); if (next == NULL) { return NJS_ERROR; } /* PropertyName */ if (next->type == NJS_TOKEN_COLON) { return njs_parser_property_name(parser, current, 2); /* MethodDefinition */ } else if (next->type == NJS_TOKEN_OPEN_PARENTHESIS) { goto method_definition; } else if (njs_lexer_token_is_reserved(token)) { njs_lexer_consume_token(parser->lexer, 1); return njs_parser_failed(parser); } name = &token->text; if (name->length == 3 && (memcmp(name->start, "get", 3) == 0 || memcmp(name->start, "set", 3) == 0)) { accessor = (name->start[0] == 'g') ? NJS_TOKEN_PROPERTY_GETTER : NJS_TOKEN_PROPERTY_SETTER; temp->right = (njs_parser_node_t *) (uintptr_t) accessor; njs_parser_next(parser, njs_parser_get_set); return NJS_OK; } return njs_parser_property_definition_ident(parser, token, temp); } method_definition: property = njs_parser_property_name_node(parser, token); if (property == NULL) { return NJS_ERROR; } temp->right = property; temp->right->index = NJS_TOKEN_OPEN_BRACKET; njs_parser_next(parser, njs_parser_method_definition); return njs_parser_after(parser, current, temp, 1, njs_parser_property_definition_after); } static njs_int_t njs_parser_property_definition_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_str_t name; njs_bool_t proto_init; njs_parser_node_t *property, *temp; static const njs_str_t proto_string = njs_str("__proto__"); temp = parser->target; property = temp->right; proto_init = 0; if (property->index != NJS_TOKEN_OPEN_BRACKET && njs_is_string(&property->u.value)) { njs_string_get(parser->vm, &property->u.value, &name); if (njs_slow_path(njs_strstr_eq(&name, &proto_string))) { if (temp->token_type == NJS_TOKEN_PROTO_INIT) { njs_parser_syntax_error(parser, "Duplicate __proto__ fields are not allowed " "in object literals"); return NJS_ERROR; } temp->token_type = NJS_TOKEN_PROTO_INIT; proto_init = 1; } } if (property->index != 0) { property->index = 0; } ret = njs_parser_object_property(parser, temp->left, property, parser->node, proto_init); if (ret != NJS_OK) { return NJS_ERROR; } temp->right = NULL; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_computed_property_name_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_computed_property_name_handler(parser, token, current, 0); } static njs_int_t njs_parser_computed_property_async_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_computed_property_name_handler(parser, token, current, 1); } static njs_int_t njs_parser_computed_property_name_handler(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_bool_t async) { njs_token_type_t type; njs_parser_node_t *expr, *target; if (token->type != NJS_TOKEN_CLOSE_BRACKET) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } target = parser->target; /* * For further identification. * In njs_parser_property_definition_after() index will be reset to zero. */ parser->node->index = NJS_TOKEN_OPEN_BRACKET; target->right = parser->node; if (!async && token->type == NJS_TOKEN_COLON) { return njs_parser_property_name(parser, current, 1); /* MethodDefinition */ } else if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { type = (async) ? NJS_TOKEN_ASYNC_FUNCTION : NJS_TOKEN_FUNCTION; expr = njs_parser_node_new(parser, type); if (expr == NULL) { return NJS_ERROR; } expr->token_line = token->line; parser->node = expr; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_function_lambda); return njs_parser_after(parser, current, parser->target, 1, njs_parser_property_definition_after); } return njs_parser_failed(parser); } static njs_int_t njs_parser_initializer(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (token->type != NJS_TOKEN_ASSIGNMENT) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); node = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, node, 1, njs_parser_initializer_after); } static njs_int_t njs_parser_initializer_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *stmt; stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); if (stmt == NULL) { return NJS_ERROR; } stmt->left = NULL; stmt->right = parser->target; parser->target->right = parser->node; parser->node = stmt; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_initializer_assign(njs_parser_t *parser, njs_token_type_t type) { njs_parser_node_t *assign; assign = njs_parser_node_new(parser, type); if (assign == NULL) { return NJS_ERROR; } assign->u.operation = NJS_VMCODE_MOVE; assign->left = parser->node; /* assign->right in njs_parser_initializer_after. */ parser->node = assign; return NJS_OK; } static njs_int_t njs_parser_tagged_template_literal_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->scope->in_tagged_template--; return njs_parser_stack_pop(parser); } /* * 12.3 Left-Hand-Side Expressions. */ static njs_int_t njs_parser_property(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node, *prop_node; /* * [ Expression ] * . IdentifierName * TemplateLiteral */ switch (token->type) { case NJS_TOKEN_OPEN_BRACKET: node = njs_parser_node_new(parser, NJS_TOKEN_PROPERTY); if (node == NULL) { return NJS_ERROR; } node->u.operation = NJS_VMCODE_PROPERTY_GET; node->left = parser->node; node->token_line = token->line; parser->node = NULL; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, node, 1, njs_parser_member_expression_bracket); case NJS_TOKEN_DOT: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (njs_lexer_token_is_identifier_name(token)) { node = njs_parser_node_new(parser, NJS_TOKEN_PROPERTY); if (node == NULL) { return NJS_ERROR; } node->u.operation = NJS_VMCODE_PROPERTY_ATOM_GET; node->token_line = token->line; prop_node = njs_parser_node_string(parser->vm, token, parser); if (prop_node == NULL) { return NJS_ERROR; } prop_node->token_line = token->line; node->left = parser->node; node->right = prop_node; parser->node = node; njs_lexer_consume_token(parser->lexer, 2); return NJS_AGAIN; } njs_lexer_consume_token(parser->lexer, 1); return NJS_DECLINED; case NJS_TOKEN_GRAVE: node = njs_parser_create_call(parser, parser->node, 0); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; parser->scope->in_tagged_template++; njs_parser_next(parser, njs_parser_template_literal); return njs_parser_after(parser, current, node, 1, njs_parser_tagged_template_literal_after); default: return NJS_DONE; } return NJS_OK; } static njs_int_t njs_parser_member_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; /* * PrimaryExpression * MemberExpression [ Expression ] * MemberExpression . IdentifierName * MemberExpression TemplateLiteral * SuperProperty * MetaProperty * new MemberExpression Arguments */ switch (token->type) { /* SuperProperty */ case NJS_TOKEN_SUPER: /* TODO: By specification. */ (void) njs_parser_super_property; return njs_parser_not_supported(parser, token); /* MetaProperty */ case NJS_TOKEN_IMPORT: /* TODO: By specification. */ (void) njs_parser_member_expression_import; return njs_parser_not_supported(parser, token); case NJS_TOKEN_NEW: njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_member_expression_new); break; default: ret = njs_parser_primary_expression_test(parser, token, current); if (ret != NJS_OK) { if (ret == NJS_DONE) { njs_parser_next(parser, njs_parser_member_expression_next); return NJS_OK; } if (njs_is_error(&parser->vm->exception)) { return NJS_DONE; } return ret; } break; } return njs_parser_after(parser, current, NULL, 1, njs_parser_member_expression_next); } static njs_int_t njs_parser_member_expression_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; /* * [ Expression ] * . IdentifierName * TemplateLiteral */ ret = njs_parser_property(parser, token, current); switch (ret) { case NJS_AGAIN: return NJS_OK; case NJS_DONE: return njs_parser_stack_pop(parser); case NJS_DECLINED: return njs_parser_failed(parser); default: break; } return njs_parser_after(parser, current, NULL, 1, njs_parser_member_expression_next); } static njs_int_t njs_parser_member_expression_bracket(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_BRACKET) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_member_expression_new_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; /* * PrimaryExpression * SuperProperty * MetaProperty * Arguments */ switch (token->type) { /* SuperProperty */ case NJS_TOKEN_SUPER: (void) njs_parser_super_property; return njs_parser_not_supported(parser, token); /* MetaProperty */ case NJS_TOKEN_IMPORT: (void) njs_parser_member_expression_import; return njs_parser_not_supported(parser, token); default: ret = njs_parser_primary_expression_test(parser, token, current); if (ret != NJS_OK) { if (ret == NJS_DONE) { njs_parser_next(parser, njs_parser_member_expression_next); return NJS_OK; } return ret; } return njs_parser_after(parser, current, NULL, 1, njs_parser_member_expression_next); } } static njs_int_t njs_parser_super_property(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * [ Expression ] * . IdentifierName */ if (token->type == NJS_TOKEN_OPEN_BRACKET) { parser->node = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_close_bracked); } if (token->type != NJS_TOKEN_DOT) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (!njs_lexer_token_is_identifier_name(token)) { return njs_parser_failed(parser); } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_member_expression_import(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * import . meta */ if (token->type != NJS_TOKEN_DOT) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_META) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_member_expression_new(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * new MemberExpression Arguments * new . target */ if (token->type != NJS_TOKEN_DOT) { njs_parser_next(parser, njs_parser_member_expression_new_next); return njs_parser_after(parser, current, NULL, 1, njs_parser_member_expression_new_after); } /* njs_lexer_consume_token(parser->lexer, 1); */ token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_TARGET) { return njs_parser_failed(parser); } /* njs_lexer_consume_token(parser->lexer, 1); */ /* return njs_parser_stack_pop(parser); */ return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_member_expression_new_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *func; if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { parser->node = njs_parser_create_call(parser, parser->node, 1); if (parser->node == NULL) { return NJS_ERROR; } parser->node->token_line = token->line; return njs_parser_stack_pop(parser); } func = njs_parser_create_call(parser, parser->node, 1); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_arguments); return njs_parser_after(parser, current, func, 1, njs_parser_member_expression_new_args); } static njs_int_t njs_parser_member_expression_new_args(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_parser_node_t * njs_parser_create_call(njs_parser_t *parser, njs_parser_node_t *node, uint8_t ctor) { njs_parser_node_t *func; switch (node->token_type) { case NJS_TOKEN_NAME: func = node; func->token_type = NJS_TOKEN_FUNCTION_CALL; break; case NJS_TOKEN_PROPERTY: func = njs_parser_node_new(parser, NJS_TOKEN_METHOD_CALL); if (func == NULL) { return NULL; } func->left = node; break; default: /* * NJS_TOKEN_METHOD_CALL, * NJS_TOKEN_FUNCTION_CALL, * NJS_TOKEN_FUNCTION_EXPRESSION, * NJS_TOKEN_OPEN_PARENTHESIS, * NJS_TOKEN_EVAL. */ func = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_CALL); if (func == NULL) { return NULL; } func->left = node; break; } func->ctor = ctor; return func; } static njs_int_t njs_parser_call_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; /* * CoverCallExpressionAndAsyncArrowHead * SuperCall * ImportCall * CallExpression Arguments * CallExpression [ Expression ] * CallExpression . IdentifierName * CallExpression TemplateLiteral */ switch (token->type) { /* MemberExpression or SuperCall */ case NJS_TOKEN_SUPER: return njs_parser_not_supported(parser, token); case NJS_TOKEN_IMPORT: return njs_parser_not_supported(parser, token); default: break; } njs_parser_next(parser, njs_parser_member_expression); ret = njs_parser_after(parser, current, NULL, 1, njs_parser_call_expression_args); if (ret != NJS_OK) { return NJS_ERROR; } return njs_parser_after(parser, current, NULL, 1, njs_parser_call_expression_after); } static njs_int_t njs_parser_call_expression_args(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *func; if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } func = njs_parser_create_call(parser, parser->node, 0); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_arguments); return njs_parser_after(parser, current, func, 1, njs_parser_left_hand_side_expression_node); } static njs_int_t njs_parser_call_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *func; /* * Arguments * [ Expression ] * . IdentifierName * TemplateLiteral */ switch (token->type) { case NJS_TOKEN_OPEN_PARENTHESIS: func = njs_parser_create_call(parser, parser->node, 0); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_arguments); ret = njs_parser_after(parser, current, func, 1, njs_parser_left_hand_side_expression_node); if (ret != NJS_OK) { return NJS_ERROR; } break; default: ret = njs_parser_property(parser, token, current); switch (ret) { case NJS_AGAIN: return NJS_OK; case NJS_DONE: return njs_parser_stack_pop(parser); case NJS_DECLINED: return njs_parser_failed(parser); default: break; } break; } return njs_parser_after(parser, current, NULL, 1, njs_parser_call_expression_after); } static njs_int_t njs_parser_arguments(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * ) * ArgumentList ) * ArgumentList , ) */ if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } parser->scope->in_args++; njs_parser_next(parser, njs_parser_argument_list); return njs_parser_after(parser, current, NULL, 1, njs_parser_parenthesis_or_comma); } static njs_int_t njs_parser_parenthesis_or_comma(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->scope->in_args--; if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } if (token->type != NJS_TOKEN_COMMA) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (njs_slow_path(token == NULL)) { return NJS_ERROR; } if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_argument_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * AssignmentExpression * ... AssignmentExpression * ArgumentList , AssignmentExpression * ArgumentList , ... AssignmentExpression */ #if 0 /* TODO. */ if (token->type == NJS_TOKEN_ELLIPSIS) { njs_lexer_consume_token(parser->lexer, 1); } #endif njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, parser->node, 1, njs_parser_argument_list_after); } static njs_int_t njs_parser_argument_list_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; node = njs_parser_node_new(parser, NJS_TOKEN_ARGUMENT); if (node == NULL) { return NJS_ERROR; } node->index = njs_scope_temp_index(node->scope); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } node->token_line = token->line; node->left = parser->node; parser->node->dest = node; parser->target->right = node; parser->node = node; if (token->type == NJS_TOKEN_COMMA) { njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_stack_pop(parser); } return njs_parser_argument_list(parser, token, current); } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_optional_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CONDITIONAL) { return njs_parser_stack_pop(parser); } token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_DOT) { return njs_parser_stack_pop(parser); } njs_parser_next(parser, njs_parser_optional_chain); return njs_parser_after(parser, current, NULL, 1, njs_parser_optional_expression_after); } static njs_int_t njs_parser_optional_chain(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *func; /* * ? . Arguments * ? . [ Expression ] * ? . IdentifierName * ? . TemplateLiteral * OptionalChain Arguments * OptionalChain [ Expression ] * OptionalChain . IdentifierName * OptionalChain TemplateLiteral */ if (token->type != NJS_TOKEN_CONDITIONAL) { return njs_parser_failed(parser); } token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_DOT) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } switch (token->type) { case NJS_TOKEN_OPEN_PARENTHESIS: func = njs_parser_create_call(parser, parser->node, 0); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; njs_lexer_consume_token(parser->lexer, 2); njs_parser_next(parser, njs_parser_arguments); ret = njs_parser_after(parser, current, func, 1, njs_parser_left_hand_side_expression_node); if (ret != NJS_OK) { return NJS_ERROR; } break; default: ret = njs_parser_property(parser, token, current); switch (ret) { case NJS_DONE: case NJS_DECLINED: return njs_parser_failed(parser); default: break; } break; } return njs_parser_after(parser, current, NULL, 1, njs_parser_optional_chain_after); } static njs_int_t njs_parser_optional_chain_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *func; /* * OptionalChain Arguments * OptionalChain [ Expression ] * OptionalChain . IdentifierName * OptionalChain TemplateLiteral */ switch (token->type) { case NJS_TOKEN_OPEN_PARENTHESIS: func = njs_parser_create_call(parser, parser->node, 0); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_arguments); ret = njs_parser_after(parser, current, func, 1, njs_parser_left_hand_side_expression_node); if (ret != NJS_OK) { return NJS_ERROR; } break; default: ret = njs_parser_property(parser, token, current); switch (ret) { case NJS_AGAIN: return NJS_OK; case NJS_DONE: return njs_parser_stack_pop(parser); case NJS_DECLINED: return njs_parser_failed(parser); default: break; } break; } return njs_parser_after(parser, current, NULL, 1, njs_parser_optional_chain_after); } static njs_int_t njs_parser_new_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_NEW) { parser->node = NULL; njs_parser_next(parser, njs_parser_member_expression_new); return NJS_OK; } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_after(parser, current, NULL, 1, njs_parser_new_expression_after); } static njs_int_t njs_parser_new_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *func; if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { njs_parser_next(parser, njs_parser_member_expression_new_after); return NJS_OK; } func = njs_parser_create_call(parser, parser->node, 1); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_left_hand_side_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * NewExpression = new MemberExpression * CallExpression = MemberExpression Arguments * OptionalExpression = MemberExpression OptionalChain */ switch (token->type) { /* NewExpression or MemberExpression */ case NJS_TOKEN_NEW: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_NEW) { njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_new_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_left_hand_side_expression_after); } break; /* CallExpression or MemberExpression */ case NJS_TOKEN_SUPER: case NJS_TOKEN_IMPORT: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { njs_parser_next(parser, njs_parser_call_expression); return NJS_OK; } break; default: break; } njs_parser_next(parser, njs_parser_member_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_left_hand_side_expression_after); } static njs_int_t njs_parser_left_hand_side_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *func; switch (token->type) { /* CallExpression */ case NJS_TOKEN_OPEN_PARENTHESIS: func = njs_parser_create_call(parser, parser->node, 0); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_arguments); ret = njs_parser_after(parser, current, func, 1, njs_parser_left_hand_side_expression_node); if (ret != NJS_OK) { return NJS_ERROR; } return njs_parser_after(parser, current, NULL, 1, njs_parser_left_hand_side_expression_optional); /* OptionalExpression */ case NJS_TOKEN_CONDITIONAL: njs_parser_next(parser, njs_parser_optional_expression_after); break; default: return njs_parser_stack_pop(parser); } return NJS_OK; } static njs_int_t njs_parser_left_hand_side_expression_node(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_left_hand_side_expression_optional(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* OptionalExpression */ if (token->type == NJS_TOKEN_CONDITIONAL) { njs_parser_next(parser, njs_parser_optional_expression_after); return NJS_OK; } njs_parser_next(parser, njs_parser_optional_chain_after); return NJS_OK; } static njs_int_t njs_parser_expression_node(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_token_type_t type, njs_vmcode_t operation, njs_parser_state_func_t after) { njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } if (token->type != type) { return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); return njs_parser_after(parser, current, node, 1, after); } /* * 12.4 Update Expressions. */ static njs_int_t njs_parser_update_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_parser_node_t *node; switch (token->type) { case NJS_TOKEN_INCREMENT: operation = NJS_VMCODE_INCREMENT; break; case NJS_TOKEN_DECREMENT: operation = NJS_VMCODE_DECREMENT; break; default: njs_parser_next(parser, njs_parser_left_hand_side_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_update_expression_post); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_left_hand_side_expression); return njs_parser_after(parser, current, node, 1, njs_parser_update_expression_unary); } static njs_int_t njs_parser_update_expression_post(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_token_type_t type; njs_parser_node_t *node; /* [no LineTerminator here] */ switch (token->type) { case NJS_TOKEN_INCREMENT: type = NJS_TOKEN_POST_INCREMENT; operation = NJS_VMCODE_POST_INCREMENT; break; case NJS_TOKEN_DECREMENT: type = NJS_TOKEN_POST_DECREMENT; operation = NJS_VMCODE_POST_DECREMENT; break; default: return njs_parser_stack_pop(parser); } if (parser->lexer->prev_type == NJS_TOKEN_LINE_END) { return njs_parser_stack_pop(parser); } if (!njs_parser_is_lvalue(parser->node)) { njs_lexer_consume_token(parser->lexer, 1); njs_parser_ref_error(parser, "Invalid left-hand side in postfix operation"); return NJS_DONE; } node = njs_parser_node_new(parser, type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; parser->node = node; njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_update_expression_unary(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (!njs_parser_is_lvalue(parser->node)) { njs_parser_ref_error(parser, "Invalid left-hand side in prefix operation"); return NJS_DONE; } parser->target->left = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 12.5 Unary Operators. */ static njs_int_t njs_parser_unary_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_token_type_t type; njs_parser_node_t *node; switch (token->type) { case NJS_TOKEN_DELETE: type = NJS_TOKEN_DELETE; operation = NJS_VMCODE_DELETE; break; case NJS_TOKEN_VOID: type = NJS_TOKEN_VOID; operation = NJS_VMCODE_VOID; break; case NJS_TOKEN_TYPEOF: type = NJS_TOKEN_TYPEOF; operation = NJS_VMCODE_TYPEOF; break; case NJS_TOKEN_ADDITION: type = NJS_TOKEN_UNARY_PLUS; operation = NJS_VMCODE_UNARY_PLUS; break; case NJS_TOKEN_SUBTRACTION: type = NJS_TOKEN_UNARY_NEGATION; operation = NJS_VMCODE_UNARY_NEGATION; break; case NJS_TOKEN_BITWISE_NOT: type = NJS_TOKEN_BITWISE_NOT; operation = NJS_VMCODE_BITWISE_NOT; break; case NJS_TOKEN_LOGICAL_NOT: type = NJS_TOKEN_LOGICAL_NOT; operation = NJS_VMCODE_LOGICAL_NOT; break; /* AwaitExpression */ case NJS_TOKEN_AWAIT: njs_parser_next(parser, njs_parser_await); return NJS_OK; default: njs_parser_next(parser, njs_parser_update_expression); return njs_parser_after(parser, current, parser->target, 1, njs_parser_unary_expression_after); } node = njs_parser_node_new(parser, type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; parser->target = node; njs_lexer_consume_token(parser->lexer, 1); return njs_parser_after(parser, current, node, 1, njs_parser_unary_expression_next); } static njs_int_t njs_parser_unary_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->target == NULL && token->type == NJS_TOKEN_EXPONENTIATION) { return njs_parser_exponentiation_expression_match(parser, token, current); } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_unary_expression_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { double num; njs_token_type_t type; njs_parser_node_t *node; type = parser->target->token_type; node = parser->node; if (token->type == NJS_TOKEN_EXPONENTIATION) { njs_parser_syntax_error(parser, "Either left-hand side or entire " "exponentiation must be parenthesized"); return NJS_DONE; } if (node->token_type == NJS_TOKEN_NUMBER) { if (type == NJS_TOKEN_UNARY_PLUS) { /* Skip the unary plus of number. */ return njs_parser_stack_pop(parser); } if (type == NJS_TOKEN_UNARY_NEGATION) { /* Optimization of common negative number. */ num = -njs_number(&node->u.value); njs_set_number(&node->u.value, num); return njs_parser_stack_pop(parser); } } if (type == NJS_TOKEN_DELETE) { switch (node->token_type) { case NJS_TOKEN_PROPERTY: node->token_type = NJS_TOKEN_PROPERTY_DELETE; node->u.operation = NJS_VMCODE_PROPERTY_DELETE; return njs_parser_stack_pop(parser); case NJS_TOKEN_NAME: njs_parser_syntax_error(parser, "Delete of an unqualified identifier"); return NJS_DONE; default: break; } } if (type == NJS_TOKEN_TYPEOF && node->token_type == NJS_TOKEN_NAME) { node->u.reference.type = NJS_TYPEOF; } parser->target->left = parser->node; parser->target->left->dest = parser->target; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_await(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (!njs_function_scope(parser->scope)->async) { njs_parser_syntax_error(parser, "await is only valid in async functions"); return NJS_ERROR; } if (parser->scope->in_args > 0) { njs_parser_syntax_error(parser, "await in arguments not supported"); return NJS_ERROR; } if (parser->scope->in_tagged_template > 0) { njs_parser_syntax_error(parser, "await in tagged template not supported"); return NJS_ERROR; } node = njs_parser_node_new(parser, NJS_TOKEN_AWAIT); if (njs_slow_path(node == NULL)) { return NJS_ERROR; } node->token_line = token->line; njs_lexer_consume_token(parser->lexer, 1); parser->node = NULL; njs_parser_next(parser, njs_parser_unary_expression); return njs_parser_after(parser, current, node, 0, njs_parser_await_after); } static njs_int_t njs_parser_await_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 12.6 Exponentiation Operator. */ static njs_int_t njs_parser_exponentiation_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target = NULL; if (parser->use_lhs == 0) { njs_parser_next(parser, njs_parser_unary_expression); /* For UpdateExpression, see njs_parser_unary_expression_after. */ return NJS_OK; } else { parser->use_lhs = 0; return njs_parser_update_expression_post(parser, token, current); } } static njs_int_t njs_parser_exponentiation_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; return njs_parser_stack_pop(parser); } if (token->type != NJS_TOKEN_EXPONENTIATION) { return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = NJS_VMCODE_EXPONENTIATION; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_exponentiation_expression); return njs_parser_after(parser, current, node, 1, njs_parser_exponentiation_expression_match); } /* * 12.7 Multiplicative Operators. */ static njs_int_t njs_parser_multiplicative_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_exponentiation_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_multiplicative_expression_match); } static njs_int_t njs_parser_multiplicative_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } switch (token->type) { case NJS_TOKEN_MULTIPLICATION: operation = NJS_VMCODE_MULTIPLICATION; break; case NJS_TOKEN_DIVISION: operation = NJS_VMCODE_DIVISION; break; case NJS_TOKEN_REMAINDER: operation = NJS_VMCODE_REMAINDER; break; default: return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_exponentiation_expression); return njs_parser_after(parser, current, node, 1, njs_parser_multiplicative_expression_match); } /* * 12.8 Additive Operators. */ static njs_int_t njs_parser_additive_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_multiplicative_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_additive_expression_match); } static njs_int_t njs_parser_additive_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } switch (token->type) { case NJS_TOKEN_ADDITION: operation = NJS_VMCODE_ADDITION; break; case NJS_TOKEN_SUBTRACTION: operation = NJS_VMCODE_SUBTRACTION; break; default: return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_multiplicative_expression); return njs_parser_after(parser, current, node, 1, njs_parser_additive_expression_match); } /* * 12.9 Bitwise Shift Operators */ static njs_int_t njs_parser_shift_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_additive_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_shift_expression_match); } static njs_int_t njs_parser_shift_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } switch (token->type) { case NJS_TOKEN_RIGHT_SHIFT: operation = NJS_VMCODE_RIGHT_SHIFT; break; case NJS_TOKEN_LEFT_SHIFT: operation = NJS_VMCODE_LEFT_SHIFT; break; case NJS_TOKEN_UNSIGNED_RIGHT_SHIFT: operation = NJS_VMCODE_UNSIGNED_RIGHT_SHIFT; break; default: return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_additive_expression); return njs_parser_after(parser, current, node, 1, njs_parser_shift_expression_match); } /* * 12.10 Relational Operators. */ static njs_int_t njs_parser_relational_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_shift_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_relational_expression_match); } static njs_int_t njs_parser_relational_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } switch (token->type) { case NJS_TOKEN_LESS: operation = NJS_VMCODE_LESS; break; case NJS_TOKEN_GREATER: operation = NJS_VMCODE_GREATER; break; case NJS_TOKEN_LESS_OR_EQUAL: operation = NJS_VMCODE_LESS_OR_EQUAL; break; case NJS_TOKEN_GREATER_OR_EQUAL: operation = NJS_VMCODE_GREATER_OR_EQUAL; break; case NJS_TOKEN_INSTANCEOF: operation = NJS_VMCODE_INSTANCE_OF; break; case NJS_TOKEN_IN: if (njs_lexer_in_fail_get(parser->lexer)) { njs_parser_syntax_error(parser, "Invalid left-hand side in for-loop"); return NJS_ERROR; } operation = NJS_VMCODE_PROPERTY_IN; break; default: return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_shift_expression); return njs_parser_after(parser, current, node, 1, njs_parser_relational_expression_match); } /* * 12.11 Equality Operators. */ static njs_int_t njs_parser_equality_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_relational_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_equality_expression_match); } static njs_int_t njs_parser_equality_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } switch (token->type) { case NJS_TOKEN_EQUAL: operation = NJS_VMCODE_EQUAL; break; case NJS_TOKEN_NOT_EQUAL: operation = NJS_VMCODE_NOT_EQUAL; break; case NJS_TOKEN_STRICT_EQUAL: operation = NJS_VMCODE_STRICT_EQUAL; break; case NJS_TOKEN_STRICT_NOT_EQUAL: operation = NJS_VMCODE_STRICT_NOT_EQUAL; break; default: return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_relational_expression); return njs_parser_after(parser, current, node, 1, njs_parser_equality_expression_match); } /* * 12.12 Binary Bitwise Operators. */ static njs_int_t njs_parser_bitwise_AND_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_equality_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_bitwise_AND_expression_and); } static njs_int_t njs_parser_bitwise_AND_expression_and(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_equality_expression); return njs_parser_expression_node(parser, token, current, NJS_TOKEN_BITWISE_AND, NJS_VMCODE_BITWISE_AND, njs_parser_bitwise_AND_expression_and); } static njs_int_t njs_parser_bitwise_XOR_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_bitwise_AND_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_bitwise_XOR_expression_xor); } static njs_int_t njs_parser_bitwise_XOR_expression_xor(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_bitwise_AND_expression); return njs_parser_expression_node(parser, token, current, NJS_TOKEN_BITWISE_XOR, NJS_VMCODE_BITWISE_XOR, njs_parser_bitwise_XOR_expression_xor); } static njs_int_t njs_parser_bitwise_OR_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_bitwise_XOR_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_bitwise_OR_expression_or); } static njs_int_t njs_parser_bitwise_OR_expression_or(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_bitwise_XOR_expression); return njs_parser_expression_node(parser, token, current, NJS_TOKEN_BITWISE_OR, NJS_VMCODE_BITWISE_OR, njs_parser_bitwise_OR_expression_or); } /* * 12.13 Binary Logical Operators. */ static njs_int_t njs_parser_logical_AND_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_bitwise_OR_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_logical_AND_expression_and); } static njs_int_t njs_parser_logical_AND_expression_and(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_bitwise_OR_expression); return njs_parser_expression_node(parser, token, current, NJS_TOKEN_LOGICAL_AND, NJS_VMCODE_TEST_IF_FALSE, njs_parser_logical_AND_expression_and); } static njs_int_t njs_parser_logical_OR_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_logical_AND_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_logical_OR_expression_or); } static njs_int_t njs_parser_logical_OR_expression_or(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_logical_AND_expression); return njs_parser_expression_node(parser, token, current, NJS_TOKEN_LOGICAL_OR, NJS_VMCODE_TEST_IF_TRUE, njs_parser_logical_OR_expression_or); } static njs_int_t njs_parser_coalesce_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_token_type_t type; njs_parser_node_t *node; node = parser->node; if (parser->target != NULL) { parser->target->right = node; parser->target->right->dest = parser->target; parser->node = parser->target; } if (token->type != NJS_TOKEN_COALESCE) { return njs_parser_stack_pop(parser); } type = node->token_type; if (parser->lexer->prev_type != NJS_TOKEN_CLOSE_PARENTHESIS && (type == NJS_TOKEN_LOGICAL_OR || type == NJS_TOKEN_LOGICAL_AND)) { return njs_parser_failed(parser); } node = njs_parser_node_new(parser, NJS_TOKEN_COALESCE); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = NJS_VMCODE_COALESCE; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_bitwise_OR_expression); return njs_parser_after(parser, current, node, 1, njs_parser_coalesce_expression); } static njs_int_t njs_parser_short_circuit_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_logical_OR_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_coalesce_expression); } /* * 12.14 Conditional Operator ( ? : ). */ static njs_int_t njs_parser_conditional_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_short_circuit_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_conditional_question_mark); } static njs_int_t njs_parser_conditional_question_mark(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node, *cond; if (token->type != NJS_TOKEN_CONDITIONAL) { return njs_parser_stack_pop(parser); } cond = njs_parser_node_new(parser, NJS_TOKEN_CONDITIONAL); if (cond == NULL) { return NJS_ERROR; } cond->token_line = token->line; cond->left = parser->node; node = njs_parser_node_new(parser, NJS_TOKEN_BRANCHING); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; cond->right = node; njs_lexer_consume_token(parser->lexer, 1); if (njs_lexer_in_stack_push(parser->lexer) != NJS_OK) { return NJS_ERROR; } njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, cond, 1, njs_parser_conditional_colon); } static njs_int_t njs_parser_conditional_colon(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (token->type != NJS_TOKEN_COLON) { return njs_parser_failed(parser); } njs_lexer_in_stack_pop(parser->lexer); njs_lexer_consume_token(parser->lexer, 1); node = parser->target->right; node->left = parser->node; node->left->dest = parser->target; njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, parser->target, 1, njs_parser_conditional_colon_after); } static njs_int_t njs_parser_conditional_colon_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; node = parser->target->right; node->right = parser->node; node->right->dest = parser->target; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 12.15 Assignment Operators. */ static njs_int_t njs_parser_assignment_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; if (!parser->use_lhs) { ret = njs_parser_match_arrow_expression(parser, token); if (ret == NJS_OK) { njs_parser_next(parser, njs_parser_arrow_function); return NJS_OK; } else if (ret == NJS_ERROR) { return NJS_ERROR; } } njs_parser_next(parser, njs_parser_conditional_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_assignment_expression_after); } /* * TODO: this function is a crutch. * See NJS_TOKEN_OPEN_PARENTHESIS in njs_parser_primary_expression_test. * and look CoverParenthesizedExpressionAndArrowParameterList in spec. */ static njs_int_t njs_parser_match_arrow_expression(njs_parser_t *parser, njs_lexer_token_t *token) { njs_bool_t rest_parameters; if (token->type == NJS_TOKEN_ASYNC) { token = njs_lexer_peek_token(parser->lexer, token, 1); if (token == NULL) { return NJS_ERROR; } } if (token->type != NJS_TOKEN_OPEN_PARENTHESIS && !njs_lexer_token_is_binding_identifier(token)) { return NJS_DECLINED; } if (njs_lexer_token_is_binding_identifier(token)) { goto arrow; } token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } rest_parameters = 0; while (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { if (rest_parameters) { return NJS_DECLINED; } if (token->type == NJS_TOKEN_ELLIPSIS) { rest_parameters = 1; token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } } if (!njs_lexer_token_is_binding_identifier(token)) { return NJS_DECLINED; } token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_COMMA) { token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } } } arrow: token = njs_lexer_peek_token(parser->lexer, token, 1); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_LINE_END) { return NJS_DECLINED; } if (token->type != NJS_TOKEN_ARROW) { return NJS_DECLINED; } return NJS_OK; } static njs_int_t njs_parser_assignment_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_assignment_operator(parser, token, current); } static njs_int_t njs_parser_assignment_operator(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_token_type_t type; njs_parser_node_t *node; switch (token->type) { case NJS_TOKEN_ASSIGNMENT: njs_thread_log_debug("JS: ="); operation = NJS_VMCODE_MOVE; break; case NJS_TOKEN_MULTIPLICATION_ASSIGNMENT: njs_thread_log_debug("JS: *="); operation = NJS_VMCODE_MULTIPLICATION; break; case NJS_TOKEN_DIVISION_ASSIGNMENT: njs_thread_log_debug("JS: /="); operation = NJS_VMCODE_DIVISION; break; case NJS_TOKEN_REMAINDER_ASSIGNMENT: njs_thread_log_debug("JS: %="); operation = NJS_VMCODE_REMAINDER; break; case NJS_TOKEN_ADDITION_ASSIGNMENT: njs_thread_log_debug("JS: +="); operation = NJS_VMCODE_ADDITION; break; case NJS_TOKEN_SUBTRACTION_ASSIGNMENT: njs_thread_log_debug("JS: -="); operation = NJS_VMCODE_SUBTRACTION; break; case NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT: njs_thread_log_debug("JS: <<="); operation = NJS_VMCODE_LEFT_SHIFT; break; case NJS_TOKEN_RIGHT_SHIFT_ASSIGNMENT: njs_thread_log_debug("JS: >>="); operation = NJS_VMCODE_RIGHT_SHIFT; break; case NJS_TOKEN_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: njs_thread_log_debug("JS: >>>="); operation = NJS_VMCODE_UNSIGNED_RIGHT_SHIFT; break; case NJS_TOKEN_BITWISE_AND_ASSIGNMENT: njs_thread_log_debug("JS: &="); operation = NJS_VMCODE_BITWISE_AND; break; case NJS_TOKEN_BITWISE_XOR_ASSIGNMENT: njs_thread_log_debug("JS: ^="); operation = NJS_VMCODE_BITWISE_XOR; break; case NJS_TOKEN_BITWISE_OR_ASSIGNMENT: njs_thread_log_debug("JS: |="); operation = NJS_VMCODE_BITWISE_OR; break; case NJS_TOKEN_EXPONENTIATION_ASSIGNMENT: njs_thread_log_debug("JS: **="); operation = NJS_VMCODE_EXPONENTIATION; break; default: return njs_parser_stack_pop(parser); } if (!njs_parser_is_lvalue(parser->node)) { type = parser->node->token_type; if (njs_parser_restricted_identifier(type)) { njs_parser_syntax_error(parser, "Identifier \"%s\" " "is forbidden as left-hand in assignment", (type == NJS_TOKEN_EVAL) ? "eval" : "arguments"); } else { njs_parser_ref_error(parser, "Invalid left-hand side in assignment"); } return NJS_DONE; } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, node, 1, njs_parser_assignment_operator_after); } static njs_int_t njs_parser_assignment_operator_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 12.16 Comma Operator ( , ). */ static njs_int_t njs_parser_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_expression_comma); } static njs_int_t njs_parser_expression_comma(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_expression_node(parser, token, current, NJS_TOKEN_COMMA, 0, njs_parser_expression_comma); } /* * 13 Statements and Declarations. */ static njs_int_t njs_parser_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_queue_link_t *lnk; njs_parser_stack_entry_t *entry; if (token->type == NJS_TOKEN_END) { lnk = njs_queue_next(njs_queue_first(&parser->stack)); if (lnk == njs_queue_head(&parser->stack)) { return njs_parser_reject(parser); } entry = njs_queue_link_data(lnk, njs_parser_stack_entry_t, link); if (entry->state == njs_parser_check_error_state) { return NJS_DONE; } return njs_parser_reject(parser); } switch (token->type) { case NJS_TOKEN_SEMICOLON: njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); case NJS_TOKEN_EXPORT: parser->line = token->line; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_export); return njs_parser_after(parser, current, parser->node, 1, njs_parser_statement_after); case NJS_TOKEN_IMPORT: parser->line = token->line; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_import); return njs_parser_after(parser, current, parser->node, 1, njs_parser_statement_after); default: break; } ret = njs_parser_statement_wo_node(parser, token, current); if (ret != NJS_OK) { return ret; } return njs_parser_after(parser, current, parser->node, 1, njs_parser_statement_after); } static njs_int_t njs_parser_statement_wo_node(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { switch (token->type) { case NJS_TOKEN_OPEN_BRACE: njs_parser_next(parser, njs_parser_block_statement); break; case NJS_TOKEN_VAR: njs_lexer_consume_token(parser->lexer, 1); return njs_parser_variable_statement(parser, token, current); case NJS_TOKEN_SEMICOLON: njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); case NJS_TOKEN_IF: njs_parser_next(parser, njs_parser_if_statement); break; /* BreakableStatement */ case NJS_TOKEN_DO: njs_parser_next(parser, njs_parser_iteration_statement_do); break; case NJS_TOKEN_WHILE: njs_parser_next(parser, njs_parser_iteration_statement_while); break; case NJS_TOKEN_FOR: njs_parser_next(parser, njs_parser_iteration_statement_for); break; case NJS_TOKEN_SWITCH: njs_parser_next(parser, njs_parser_switch_statement); break; case NJS_TOKEN_CONTINUE: njs_parser_next(parser, njs_parser_continue_statement); break; case NJS_TOKEN_BREAK: njs_parser_next(parser, njs_parser_break_statement); break; case NJS_TOKEN_RETURN: njs_parser_next(parser, njs_parser_return_statement); break; case NJS_TOKEN_WITH: njs_parser_next(parser, njs_parser_with_statement); break; case NJS_TOKEN_THROW: njs_parser_next(parser, njs_parser_throw_statement); break; case NJS_TOKEN_TRY: njs_parser_next(parser, njs_parser_try_statement); break; case NJS_TOKEN_DEBUGGER: njs_parser_next(parser, njs_parser_debugger_statement); break; case NJS_TOKEN_END: return njs_parser_failed(parser); default: if (njs_lexer_token_is_identifier_reference(token)) { token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_COLON) { njs_parser_next(parser, njs_parser_labelled_statement); return NJS_OK; } } njs_parser_next(parser, njs_parser_expression_statement); return NJS_OK; } parser->line = token->line; njs_lexer_consume_token(parser->lexer, 1); return NJS_OK; } static njs_int_t njs_parser_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *stmt, *last, *new_node, **child; njs_parser_node_t *node, *top; child = &parser->target; last = *child; new_node = parser->node; if (new_node != NULL) { if (new_node->hoist) { child = &njs_parser_chain_top(parser); while (*child != NULL) { node = *child; if (node->hoist) { break; } child = &node->left; } last = *child; } stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); if (njs_slow_path(stmt == NULL)) { return NJS_ERROR; } stmt->hoist = new_node->hoist; stmt->left = last; stmt->right = new_node; *child = stmt; top = (child != &parser->target) ? njs_parser_chain_top(parser) : stmt; parser->node = top; njs_parser_chain_top_set(parser, top); } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; ret = njs_parser_hoistable_declaration(parser, token, current); if (ret == NJS_OK) { return NJS_OK; } switch (token->type) { case NJS_TOKEN_CLASS: njs_parser_next(parser, njs_parser_class_declaration); return NJS_OK; case NJS_TOKEN_LET: case NJS_TOKEN_CONST: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } switch (token->type) { case NJS_TOKEN_OPEN_BRACE: case NJS_TOKEN_OPEN_BRACKET: njs_parser_next(parser, njs_parser_lexical_declaration); break; default: if (njs_lexer_token_is_reserved(token)) { njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_failed_state); return NJS_OK; } if (njs_lexer_token_is_binding_identifier(token)) { njs_parser_next(parser, njs_parser_lexical_declaration); break; } return NJS_DECLINED; } break; default: return NJS_DECLINED; } return njs_parser_after(parser, current, parser->node, 1, njs_parser_statement_after); } static njs_int_t njs_parser_hoistable_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; ret = njs_parser_function_or_generator(parser, token, current); if (ret == NJS_OK) { return NJS_OK; } ret = njs_parser_async_function_or_generator(parser, token, current); if (ret == NJS_OK) { return NJS_OK; } return NJS_DECLINED; } /* * 13.2 Block. */ static njs_int_t njs_parser_block_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { void *target; njs_int_t ret; ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0); if (ret != NJS_OK) { return NJS_ERROR; } target = (void *) (uintptr_t) parser->line; parser->node = NULL; if (token->type == NJS_TOKEN_CLOSE_BRACE) { parser->target = target; njs_parser_next(parser, njs_parser_block_statement_close_brace); return NJS_OK; } njs_parser_next(parser, njs_parser_statement_list); return njs_parser_after(parser, current, target, 0, njs_parser_block_statement_close_brace); } static njs_int_t njs_parser_block_statement_open_brace(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_OPEN_BRACE) { return njs_parser_failed(parser); } parser->line = token->line; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } return njs_parser_block_statement(parser, token, current); } static njs_int_t njs_parser_block_statement_close_brace(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } if (token->type != NJS_TOKEN_CLOSE_BRACE) { return njs_parser_failed(parser); } node = njs_parser_node_new(parser, NJS_TOKEN_BLOCK); if (node == NULL) { return NJS_ERROR; } node->token_line = (uint32_t) (uintptr_t) parser->target; node->left = parser->node; node->right = NULL; parser->target = NULL; parser->node = node; njs_parser_scope_end(parser); njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_statement_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_statement_list_item); return njs_parser_after(parser, current, NULL, 1, njs_parser_statement_list_next); } static njs_int_t njs_parser_statement_list_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { if (token->type != NJS_TOKEN_CLOSE_BRACE) { parser->node = parser->target; (void) njs_parser_stack_pop(parser); return parser->ret; } return njs_parser_failed(parser); } if (token->type == NJS_TOKEN_CLOSE_BRACE) { return njs_parser_stack_pop(parser); } njs_parser_next(parser, njs_parser_statement_list_item); return njs_parser_after(parser, current, parser->node, 0, njs_parser_statement_list_next); } static njs_int_t njs_parser_statement_list_item(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; ret = njs_parser_declaration(parser, token, current); if (ret == NJS_OK) { return NJS_OK; } njs_parser_next(parser, njs_parser_statement); return NJS_OK; } /* * 13.3.1 Let and Const Declarations */ static njs_int_t njs_parser_lexical_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->var_type = (token->type == NJS_TOKEN_LET) ? NJS_VARIABLE_LET : NJS_VARIABLE_CONST; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_variable_declaration_list); return njs_parser_after(parser, current, NULL, 1, njs_parser_semicolon); } /* * 13.3.2 Variable Statement */ static njs_int_t njs_parser_variable_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->var_type = NJS_VARIABLE_VAR; njs_parser_next(parser, njs_parser_variable_declaration_list); return njs_parser_after(parser, current, NULL, 1, njs_parser_semicolon); } static njs_int_t njs_parser_variable_declaration_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_variable_declaration); return njs_parser_after(parser, current, NULL, 1, njs_parser_variable_declaration_list_next); } static njs_int_t njs_parser_variable_declaration_list_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (parser->target != NULL) { parser->node->left = parser->target; } if (token->type != NJS_TOKEN_COMMA) { return njs_parser_stack_pop(parser); } njs_lexer_consume_token(parser->lexer, 1); node = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_variable_declaration); return njs_parser_after(parser, current, node, 1, njs_parser_variable_declaration_list_next); } static njs_int_t njs_parser_variable_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_variable_t *var; njs_token_type_t type; njs_parser_node_t *name; ret = njs_parser_binding_pattern(parser, token, current); if (ret == NJS_OK) { return njs_parser_after(parser, current, NULL, 1, njs_parser_initializer); } if (!njs_lexer_token_is_binding_identifier(token)) { return njs_parser_failed(parser); } if (njs_parser_restricted_identifier(token->type)) { njs_parser_syntax_error(parser, "Identifier \"%V\" is forbidden in" " var declaration", &token->text); return NJS_DONE; } name = njs_parser_variable_node(parser, token->atom_id, parser->var_type, &var); if (name == NULL) { return NJS_ERROR; } if (var->self) { var->type = parser->var_type; var->self = 0; } name->token_line = token->line; parser->node = name; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } switch (parser->var_type) { case NJS_VARIABLE_LET: type = NJS_TOKEN_LET; break; case NJS_VARIABLE_CONST: type = NJS_TOKEN_CONST; break; default: type = NJS_TOKEN_VAR; break; } ret = njs_parser_initializer_assign(parser, type); if (ret != NJS_OK) { return ret; } parser->node->token_line = token->line; if (token->type == NJS_TOKEN_ASSIGNMENT) { njs_parser_next(parser, njs_parser_initializer); return NJS_OK; } parser->target = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_initializer_after); return NJS_OK; } /* * 13.3.3 Destructuring Binding Patterns. */ static njs_int_t njs_parser_binding_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type == NJS_TOKEN_OPEN_BRACE) { njs_parser_next(parser, njs_parser_object_binding_pattern); } else if (token->type == NJS_TOKEN_OPEN_BRACKET) { njs_parser_next(parser, njs_parser_array_binding_pattern); } else { return NJS_DECLINED; } njs_lexer_consume_token(parser->lexer, 1); return NJS_OK; } static njs_int_t njs_parser_object_binding_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_array_binding_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } /* * 13.5 Expression Statement. */ static njs_int_t njs_parser_expression_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_token_type_t type; njs_lexer_token_t *next; switch (token->type) { case NJS_TOKEN_FUNCTION: njs_parser_syntax_error(parser, "Functions can only be declared " "at top level or inside a block"); return NJS_DONE; case NJS_TOKEN_CLASS: njs_parser_syntax_error(parser, "Class can only be declared " "at top level or inside a block"); return NJS_DONE; case NJS_TOKEN_OPEN_BRACE: return njs_parser_reject(parser); case NJS_TOKEN_ASYNC: next = njs_lexer_peek_token(parser->lexer, token, 1); if (next == NULL) { return NJS_ERROR; } if (next->type == NJS_TOKEN_FUNCTION) { return njs_parser_not_supported(parser, token); } break; case NJS_TOKEN_CONST: case NJS_TOKEN_LET: type = token->type; token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_NAME) { njs_parser_syntax_error(parser, "%s declaration cannot appear " "in a single-statement context", (type == NJS_TOKEN_CONST ? "const" : "let" )); return NJS_DONE; } if (token->type == NJS_TOKEN_OPEN_BRACKET) { return njs_parser_failed(parser); } break; default: break; } parser->node = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_expression_statement_after); } static njs_int_t njs_parser_expression_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { return njs_parser_failed(parser); } return njs_parser_stack_pop(parser); } /* * 13.6 if Statement. */ static njs_int_t njs_parser_if_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *node; if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); node = njs_parser_node_new(parser, NJS_TOKEN_IF); if (node == NULL) { return NJS_ERROR; } node->token_line = parser->line; parser->node = NULL; njs_parser_next(parser, njs_parser_expression); ret = njs_parser_after(parser, current, node, 1, njs_parser_if_close_parenthesis); if (ret != NJS_OK) { return ret; } ret = njs_parser_after(parser, current, NULL, 1, njs_parser_statement_wo_node); if (ret != NJS_OK) { return ret; } return njs_parser_after(parser, current, node, 1, njs_parser_else_statement); } static njs_int_t njs_parser_if_close_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->left = parser->node; parser->node = NULL; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_else_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; parser->target->right = parser->node; parser->node = NULL; if (token->type == NJS_TOKEN_ELSE) { node = njs_parser_node_new(parser, NJS_TOKEN_BRANCHING); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->left = parser->target->right; parser->target->right = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, parser->target, 1, njs_parser_else_statement_after); } parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_else_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->right->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_iteration_statement_do(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; node = njs_parser_node_new(parser, NJS_TOKEN_DO); if (node == NULL) { return NJS_ERROR; } node->token_line = parser->line; parser->node = NULL; njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, node, 1, njs_parser_iteration_statement_do_while); } static njs_int_t njs_parser_iteration_statement_do_while(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_WHILE) { return njs_parser_failed(parser); } parser->target->left = parser->node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_expression_parenthesis); return njs_parser_after(parser, current, parser->target, 1, njs_parser_do_while_semicolon); } static njs_int_t njs_parser_do_while_semicolon(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->strict_semicolon) { return njs_parser_failed(parser); } parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_iteration_statement_while(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; node = njs_parser_node_new(parser, NJS_TOKEN_WHILE); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; njs_parser_next(parser, njs_parser_expression_parenthesis); return njs_parser_after(parser, current, node, 1, njs_parser_while_statement); } static njs_int_t njs_parser_while_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->right = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, parser->target, 1, njs_parser_while_after); } static njs_int_t njs_parser_while_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->left = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_iteration_statement_for(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { njs_lexer_consume_token(parser->lexer, 1); ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_parser_next(parser, njs_parser_iteration_statement_for_map); return njs_parser_after(parser, current, (void *) (uintptr_t) parser->line, 1, njs_parser_iteration_statement_for_end); } if (token->type == NJS_TOKEN_AWAIT) { return njs_parser_not_supported(parser, token); } return njs_parser_failed(parser); } static njs_int_t njs_parser_for_expression_map_reparse(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK && parser->node != NULL) { return njs_parser_failed(parser); } if (parser->node == NULL) { njs_lexer_in_fail_set(parser->lexer, 1); njs_parser_next(parser, njs_parser_expression); return NJS_OK; } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_for_expression_map_continue(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t operation; njs_str_t *text; njs_parser_node_t *node; if (token->type != NJS_TOKEN_IN) { njs_lexer_in_fail_set(parser->lexer, 1); /* Continue parsing of expr1 in "for (expr1;[expr2];[expr3])". */ njs_parser_next(parser, njs_parser_expression_continue_op); /* * Here we pass not a node, but a token, this is important. * This is necessary for correct error output. */ text = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_str_t)); if (text == NULL) { return NJS_ERROR; } *text = token->text; return njs_parser_after(parser, current, text, 1, njs_parser_for_var_in_of_expression); } else { /* for-in */ if (parser->node->token_type != NJS_TOKEN_NAME && parser->node->token_type != NJS_TOKEN_PROPERTY) { text = (njs_str_t *) parser->target; njs_parser_ref_error(parser, "Invalid left-hand side \"%V\" " "in for-in statement", text); njs_mp_free(parser->vm->mem_pool, text); return NJS_DONE; } operation = NJS_VMCODE_PROPERTY_IN; node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, node, 0, njs_parser_for_in_statement_statement); } } static njs_int_t njs_parser_after_expr(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_comma_expression_comma(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } if (token->type != NJS_TOKEN_COMMA) { return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, NJS_TOKEN_COMMA); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = 0; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, node, 1, njs_parser_after_expr); } static njs_int_t njs_parser_expression_continue_op(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type == NJS_TOKEN_CONDITIONAL) { njs_parser_next(parser, njs_parser_conditional_question_mark); return njs_parser_after(parser, current, NULL, 0, njs_parser_expression_continue_assign_comma); } else { parser->target = NULL; parser->use_lhs = 1; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_comma_expression_comma); } } static njs_int_t njs_parser_expression_continue_assign_comma(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } njs_parser_next(parser, njs_parser_assignment_expression_after); return njs_parser_after(parser, current, NULL, 1, njs_parser_expression_comma); } static njs_int_t njs_parser_iteration_statement_for_map(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_str_t *text; njs_token_type_t token_type; /* * "var" ";" ? ";" ? ")" * * "var" "in" ")" * "var" "of" ")" * "in" ")" * "of" ")" * ? ";" ? ";" ? ")" * ? ";" ? ")" * "in" ")" * "of" ")" */ parser->node = NULL; switch (token->type) { case NJS_TOKEN_SEMICOLON: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_SEMICOLON) { njs_lexer_consume_token(parser->lexer, 1); parser->target = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_for_expression); } parser->node = NULL; parser->target = NULL; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_for_expression); return NJS_OK; case NJS_TOKEN_VAR: case NJS_TOKEN_LET: case NJS_TOKEN_CONST: token_type = token->type; token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 1); ret = njs_parser_for_var_binding_or_var_list(parser, token, current, token_type); if (ret != NJS_OK) { if (ret == NJS_DONE) { return NJS_OK; } return ret; } goto expression_after; case NJS_TOKEN_AWAIT: njs_parser_next(parser, njs_parser_expression); goto expression_after; default: ret = njs_parser_match_arrow_expression(parser, token); if (ret == NJS_OK) { parser->target = NULL; njs_parser_next(parser, njs_parser_expression); goto expression_after; } else if (ret == NJS_ERROR) { return NJS_ERROR; } parser->target = NULL; njs_parser_next(parser, njs_parser_left_hand_side_expression); /* * Here we pass not a node, but a token, this is important. * This is necessary for correct error output. */ text = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_str_t)); if (text == NULL) { return NJS_ERROR; } *text = token->text; ret = njs_parser_after(parser, current, text, 0, njs_parser_for_expression_map_reparse); if (ret != NJS_OK) { return NJS_ERROR; } return njs_parser_after(parser, current, text, 1, njs_parser_for_expression_map_continue); } expression_after: /* * Here we pass not a node, but a token, this is important. * This is necessary for correct error output. */ text = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_str_t)); if (text == NULL) { return NJS_ERROR; } *text = token->text; return njs_parser_after(parser, current, text, 1, njs_parser_for_var_in_of_expression); } static njs_int_t njs_parser_for_var_binding_or_var_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_token_type_t token_type) { njs_int_t ret; njs_lexer_token_t *next; njs_parser_node_t *node, *var, *node_type, *statement; njs_variable_type_t type; switch (token_type) { case NJS_TOKEN_LET: type = NJS_VARIABLE_LET; break; case NJS_TOKEN_CONST: type = NJS_VARIABLE_CONST; break; default: type = NJS_VARIABLE_VAR; break; } switch (token->type) { /* BindingPattern */ case NJS_TOKEN_OPEN_BRACE: njs_parser_next(parser, njs_parser_object_binding_pattern); return NJS_DONE; case NJS_TOKEN_OPEN_BRACKET: njs_parser_next(parser, njs_parser_array_binding_pattern); return NJS_DONE; default: if (njs_lexer_token_is_binding_identifier(token)) { if (njs_parser_restricted_identifier(token->type)) { njs_parser_syntax_error(parser, "Identifier \"%V\" is forbidden" " in var declaration", &token->text); return NJS_DONE; } next = njs_lexer_peek_token(parser->lexer, token, 0); if (next == NULL) { return NJS_ERROR; } if (next->type != NJS_TOKEN_IN) { parser->var_type = type; njs_lexer_in_fail_set(parser->lexer, 1); njs_parser_next(parser, njs_parser_variable_declaration_list); return NJS_OK; } statement = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); if (njs_slow_path(statement == NULL)) { return NJS_ERROR; } node_type = njs_parser_node_new(parser, token_type); if (njs_slow_path(node_type == NULL)) { return NJS_ERROR; } var = njs_parser_variable_node(parser, token->atom_id, type, NULL); if (var == NULL) { return NJS_ERROR; } node_type->token_line = token->line; var->token_line = token->line; statement->right = node_type; node_type->left = var; parser->node = NULL; node = njs_parser_node_new(parser, NJS_TOKEN_IN); if (node == NULL) { return NJS_ERROR; } node->token_line = next->line; node->left = statement; njs_parser_next(parser, njs_parser_expression); ret = njs_parser_after(parser, current, node, 1, njs_parser_for_var_in_statement); if (ret != NJS_OK) { return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 2); return NJS_DONE; } else { return njs_parser_failed(parser); } } } static njs_int_t njs_parser_for_var_in_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->right = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, parser->target, 1, njs_parser_for_var_in_statement_after); } static njs_int_t njs_parser_for_var_in_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *foreach; foreach = njs_parser_node_new(parser, NJS_TOKEN_FOR_IN); if (foreach == NULL) { return NJS_ERROR; } foreach->left = parser->target; foreach->right = parser->node; parser->node = foreach; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_for_var_in_of_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_str_t *text; njs_parser_node_t *node; /* * ";" ? ";" ? ")" * "in" ")" * "of" ")" */ if (token->type != NJS_TOKEN_SEMICOLON && token->type != NJS_TOKEN_CLOSE_PARENTHESIS && parser->node != NULL && parser->node->token_type == NJS_TOKEN_IN) { node = parser->node->left; if (node->token_type != NJS_TOKEN_NAME && node->token_type != NJS_TOKEN_PROPERTY) { text = (njs_str_t *) parser->target; njs_parser_ref_error(parser, "Invalid left-hand side \"%V\" " "in for-in statement", text); njs_mp_free(parser->vm->mem_pool, text); return NJS_DONE; } njs_parser_next(parser, njs_parser_for_in_statement); return NJS_OK; } if (parser->target != NULL) { text = (njs_str_t *) parser->target; njs_mp_free(parser->vm->mem_pool, text); } switch (token->type) { case NJS_TOKEN_SEMICOLON: njs_lexer_in_fail_set(parser->lexer, 0); token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } node = parser->node; parser->node = NULL; if (token->type != NJS_TOKEN_SEMICOLON) { njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, node, 1, njs_parser_for_expression); } parser->target = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_for_expression); return NJS_OK; case NJS_TOKEN_OF: return njs_parser_not_supported(parser, token); default: return njs_parser_failed(parser); } } static njs_int_t njs_parser_for_in_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node, *forin; if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); node = parser->node; if (node->right != NULL && node->right->token_type == NJS_TOKEN_VAR) { return NJS_ERROR; } forin = njs_parser_node_new(parser, NJS_TOKEN_FOR_IN); if (forin == NULL) { return NJS_ERROR; } forin->left = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, forin, 1, njs_parser_for_in_statement_after); } static njs_int_t njs_parser_for_in_statement_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *forin; if (parser->ret != NJS_OK || token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->right = parser->node; forin = njs_parser_node_new(parser, NJS_TOKEN_FOR_IN); if (forin == NULL) { return NJS_ERROR; } forin->left = parser->target; parser->node = NULL; njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, forin, 1, njs_parser_for_in_statement_after); } static njs_int_t njs_parser_for_in_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_for_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *body, *cond, *for_node; if (token->type != NJS_TOKEN_SEMICOLON) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); for_node = njs_parser_node_new(parser, NJS_TOKEN_FOR); if (for_node == NULL) { return NJS_ERROR; } cond = njs_parser_node_new(parser, 0); if (cond == NULL) { return NJS_ERROR; } body = njs_parser_node_new(parser, 0); if (body == NULL) { return NJS_ERROR; } for_node->left = parser->target; for_node->right = cond; cond->left = parser->node; cond->right = body; parser->node = NULL; token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { parser->target = for_node; njs_parser_next(parser, njs_parser_for_expression_end); return NJS_OK; } njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, for_node, 1, njs_parser_for_expression_end); } static njs_int_t njs_parser_for_expression_end(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *body; if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); body = parser->target->right->right; body->right = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, parser->target, 1, njs_parser_for_end); } static njs_int_t njs_parser_for_end(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *body; body = parser->target->right->right; body->left = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 13.8 continue Statement. */ static njs_int_t njs_parser_continue_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_break_continue(parser, token, NJS_TOKEN_CONTINUE); } /* * 13.9 break Statement. */ static njs_int_t njs_parser_break_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_break_continue(parser, token, NJS_TOKEN_BREAK); } static njs_int_t njs_parser_break_continue(njs_parser_t *parser, njs_lexer_token_t *token, njs_token_type_t type) { njs_int_t ret; parser->node = njs_parser_node_new(parser, type); if (parser->node == NULL) { return NJS_ERROR; } parser->node->token_line = parser->line; switch (token->type) { case NJS_TOKEN_SEMICOLON: break; case NJS_TOKEN_LINE_END: return njs_parser_failed(parser); default: if (njs_lexer_token_is_label_identifier(token)) { if (parser->lexer->prev_type == NJS_TOKEN_LINE_END) { return njs_parser_stack_pop(parser); } if (njs_label_find(parser->vm, parser->scope, token->atom_id) == NULL) { njs_parser_syntax_error(parser, "Undefined label \"%V\"", &token->text); return NJS_DONE; } ret = njs_name_copy(parser->vm, &parser->node->name, &token->text); if (ret != NJS_OK) { return NJS_ERROR; } break; } if (parser->strict_semicolon || (token->type != NJS_TOKEN_END && token->type != NJS_TOKEN_CLOSE_BRACE && parser->lexer->prev_type != NJS_TOKEN_LINE_END)) { return njs_parser_failed(parser); } return njs_parser_stack_pop(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } /* * 13.10 return Statement. */ static njs_int_t njs_parser_return_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; njs_parser_scope_t *scope; for (scope = parser->scope; scope != NULL; scope = scope->parent) { if (scope->type == NJS_SCOPE_FUNCTION) { break; } if (scope->parent == NULL) { njs_parser_syntax_error(parser, "Illegal return statement"); return NJS_ERROR; } } node = njs_parser_node_new(parser, NJS_TOKEN_RETURN); if (njs_slow_path(node == NULL)) { return NJS_ERROR; } node->token_line = parser->line; switch (token->type) { case NJS_TOKEN_SEMICOLON: njs_lexer_consume_token(parser->lexer, 1); break; case NJS_TOKEN_LINE_END: return njs_parser_failed(parser); default: if (!parser->strict_semicolon && parser->lexer->prev_type == NJS_TOKEN_LINE_END) { break; } parser->node = NULL; if (token->type != NJS_TOKEN_CLOSE_BRACE) { njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, node, 0, njs_parser_return_statement_after); } } parser->node = node; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_return_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { njs_parser_syntax_error(parser, "Unexpected token \"%V\"", &token->text); return NJS_DONE; } if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { return njs_parser_failed(parser); } parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 13.11 with Statement */ static njs_int_t njs_parser_with_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } /* * 13.12 switch Statement */ static njs_int_t njs_parser_switch_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *swtch; swtch = njs_parser_node_new(parser, NJS_TOKEN_SWITCH); if (swtch == NULL) { return NJS_ERROR; } swtch->token_line = parser->line; njs_parser_next(parser, njs_parser_expression_parenthesis); ret = njs_parser_after(parser, current, swtch, 1, njs_parser_switch_block); if (ret != NJS_OK) { return ret; } return njs_parser_after(parser, current, swtch, 1, njs_parser_switch_statement_after); } static njs_int_t njs_parser_switch_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_switch_block(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; if (token->type != NJS_TOKEN_OPEN_BRACE) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->left = parser->node; ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0); if (ret != NJS_OK) { return NJS_ERROR; } njs_parser_next(parser, njs_parser_switch_case); return njs_parser_after(parser, current, NULL, 1, njs_parser_switch_block_after); } static njs_int_t njs_parser_switch_block_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_scope_end(parser); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_switch_case(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_switch_case_def(parser, token, current, 1); } static njs_int_t njs_parser_switch_case_wo_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_switch_case_def(parser, token, current, 0); } static njs_int_t njs_parser_switch_case_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_bool_t with_default) { njs_parser_node_t *node, *branch; node = njs_parser_node_new(parser, 0); if (node == NULL) { return NJS_ERROR; } parser->node = NULL; switch (token->type) { case NJS_TOKEN_CASE: branch = njs_parser_node_new(parser, 0); if (branch == NULL) { return NJS_ERROR; } branch->token_line = token->line; branch->right = node; njs_parser_next(parser, njs_parser_expression); njs_lexer_consume_token(parser->lexer, 1); if (parser->target->token_type == NJS_TOKEN_SWITCH) { parser->target->right = branch; } else { parser->target->left = branch; } if (with_default) { return njs_parser_after(parser, current, branch, 1, njs_parser_switch_case_after); } else { return njs_parser_after(parser, current, branch, 1, njs_parser_switch_case_after_wo_def); } case NJS_TOKEN_DEFAULT: if (!with_default) { njs_parser_syntax_error(parser, "More than one default clause " "in switch statement"); return NJS_DONE; } if (parser->target->token_type == NJS_TOKEN_SWITCH) { parser->target->right = node; } else { parser->target->left = node; } node->token_line = token->line; node->token_type = NJS_TOKEN_DEFAULT; parser->target = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_switch_case_after_wo_def); break; case NJS_TOKEN_CLOSE_BRACE: njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); default: return njs_parser_failed(parser); } return NJS_OK; } static njs_int_t njs_parser_switch_case_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_COLON) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->right->left = parser->node; parser->node = NULL; token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } switch (token->type) { case NJS_TOKEN_CASE: case NJS_TOKEN_DEFAULT: case NJS_TOKEN_CLOSE_BRACE: njs_parser_next(parser, njs_parser_switch_case_block); return NJS_OK; default: njs_parser_next(parser, njs_parser_statement_list); break; } return njs_parser_after(parser, current, parser->target, 1, njs_parser_switch_case_block); } static njs_int_t njs_parser_switch_case_block(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->right->right = parser->node; if (parser->ret != NJS_OK && parser->target->scope != parser->scope) { return njs_parser_failed(parser); } njs_parser_next(parser, njs_parser_switch_case); return NJS_OK; } static njs_int_t njs_parser_switch_case_after_wo_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_COLON) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); if (parser->target->right != NULL) { parser->target->right->left = parser->node; } parser->node = NULL; token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } switch (token->type) { case NJS_TOKEN_CASE: case NJS_TOKEN_DEFAULT: case NJS_TOKEN_CLOSE_BRACE: njs_parser_next(parser, njs_parser_switch_case_block_wo_def); return NJS_OK; default: njs_parser_next(parser, njs_parser_statement_list); break; } return njs_parser_after(parser, current, parser->target, 1, njs_parser_switch_case_block_wo_def); } static njs_int_t njs_parser_switch_case_block_wo_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->target->right != NULL) { parser->target->right->right = parser->node; } else { parser->target->right = parser->node; } if (parser->ret != NJS_OK && parser->target->scope != parser->scope) { return njs_parser_failed(parser); } njs_parser_next(parser, njs_parser_switch_case_wo_def); return NJS_OK; } /* * 13.13 Labelled Statements */ static njs_int_t njs_parser_labelled_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { uintptr_t atom_id; njs_variable_t *label; atom_id = token->atom_id; label = njs_label_find(parser->vm, parser->scope, atom_id); if (label != NULL) { njs_parser_syntax_error(parser, "Label \"%V\" " "has already been declared", &token->text); return NJS_DONE; } label = njs_label_add(parser->vm, parser->scope, atom_id); if (label == NULL) { return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 2); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } parser->node = NULL; if (token->type == NJS_TOKEN_FUNCTION) { njs_syntax_error(parser->vm, "In strict mode code, functions can only " "be declared at top level or inside a block."); return NJS_DONE; } else { njs_parser_next(parser, njs_parser_statement_wo_node); } return njs_parser_after(parser, current, (void *) atom_id, 1, njs_parser_labelled_statement_after); } static njs_int_t njs_parser_labelled_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_str_t str; uintptr_t atom_id; njs_value_t entry; njs_parser_node_t *node; node = parser->node; if (node == NULL) { node = njs_parser_node_new(parser, NJS_TOKEN_BLOCK); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; } atom_id = (uint32_t) (uintptr_t) parser->target; njs_atom_to_value(parser->vm, &entry, atom_id); njs_string_get(parser->vm, &entry, &str); ret = njs_name_copy(parser->vm, &parser->node->name, &str); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_label_remove(parser->vm, parser->scope, atom_id); if (ret != NJS_OK) { return NJS_ERROR; } return njs_parser_stack_pop(parser); } /* * 13.14 throw Statement */ static njs_int_t njs_parser_throw_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; node = njs_parser_node_new(parser, NJS_TOKEN_THROW); if (node == NULL) { return NJS_ERROR; } node->token_line = parser->line; if (parser->lexer->prev_type == NJS_TOKEN_LINE_END) { njs_parser_syntax_error(parser, "Illegal newline after throw"); return NJS_DONE; } parser->node = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, node, 1, njs_parser_throw_statement_after); } static njs_int_t njs_parser_throw_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { parser->node = parser->target; return njs_parser_reject(parser); } if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { return njs_parser_failed(parser); } parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 13.15 try Statement. */ static njs_int_t njs_parser_try_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *try; try = njs_parser_node_new(parser, NJS_TOKEN_TRY); if (try == NULL) { return NJS_ERROR; } try->token_line = parser->line; parser->node = NULL; njs_parser_next(parser, njs_parser_block_statement_open_brace); return njs_parser_after(parser, current, try, 1, njs_parser_catch_or_finally); } static njs_int_t njs_parser_catch_or_finally(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *catch, *node, *try; try = parser->target; try->left = parser->node; if (token->type == NJS_TOKEN_FINALLY) { node = njs_parser_node_new(parser, NJS_TOKEN_FINALLY); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; if (try->right != NULL) { node->left = try->right; } try->right = node; parser->node = NULL; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_block_statement_open_brace); return njs_parser_after(parser, current, try, 0, njs_parser_catch_finally); } else if (token->type != NJS_TOKEN_CATCH) { njs_parser_syntax_error(parser, "Missing catch or finally after try"); return NJS_DONE; } catch = njs_parser_node_new(parser, NJS_TOKEN_CATCH); if (catch == NULL) { return NJS_ERROR; } catch->token_line = token->line; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0); if (ret != NJS_OK) { return NJS_ERROR; } if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { parser->node = NULL; njs_parser_next(parser, njs_parser_block_statement_open_brace); try->right = catch; /* TODO: it is necessary to change the generator. */ return njs_parser_not_supported(parser, token); /* * return njs_parser_after(parser, current, parser->target, 0, * njs_parser_catch_after); */ } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } try->right = catch; if (njs_lexer_token_is_binding_identifier(token)) { node = njs_parser_variable_node(parser, token->atom_id, NJS_VARIABLE_CATCH, NULL); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; catch->left = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_catch_parenthesis); return NJS_OK; } if (token->type != NJS_TOKEN_OPEN_BRACE) { return njs_parser_failed(parser); } /* * njs_parser_next(parser, njs_parser_block_statement_open_brace); * * return njs_parser_after(parser, current, try, 1, * njs_parser_catch_parenthesis); */ return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_catch_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; parser->target->right->right = parser->node; if (token->type == NJS_TOKEN_FINALLY) { node = njs_parser_node_new(parser, NJS_TOKEN_FINALLY); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; if (parser->target->right != NULL) { node->left = parser->target->right; } parser->target->right = node; parser->node = NULL; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_block_statement_open_brace); return njs_parser_after(parser, current, parser->target, 1, njs_parser_catch_finally); } parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_catch_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->right->right = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_catch_statement_open_brace); return njs_parser_after(parser, current, parser->target, 1, njs_parser_catch_after); } static njs_int_t njs_parser_catch_statement_open_brace(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { void *target; if (token->type != NJS_TOKEN_OPEN_BRACE) { return njs_parser_failed(parser); } parser->line = token->line; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } target = (void *) (uintptr_t) parser->line; parser->node = NULL; if (token->type == NJS_TOKEN_CLOSE_BRACE) { parser->target = target; njs_parser_next(parser, njs_parser_block_statement_close_brace); return NJS_OK; } njs_parser_next(parser, njs_parser_statement_list); return njs_parser_after(parser, current, target, 0, njs_parser_block_statement_close_brace); } static njs_int_t njs_parser_catch_finally(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } parser->target->right->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_debugger_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->node = njs_parser_node_new(parser, NJS_TOKEN_DEBUGGER); if (parser->node == NULL) { return NJS_ERROR; } parser->node->token_line = parser->line; if (token->type != NJS_TOKEN_SEMICOLON && token->type != NJS_TOKEN_END) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } /* * 14.1 Function Definitions. */ static njs_int_t njs_parser_function_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; uintptr_t atom_id; njs_bool_t async; njs_variable_t *var; njs_parser_node_t *node; if (!njs_lexer_token_is_binding_identifier(token)) { return njs_parser_failed(parser); } if (njs_parser_restricted_identifier(token->type)) { njs_parser_syntax_error(parser, "Identifier \"%V\" is forbidden" " in function declaration", &token->text); return NJS_DONE; } node = parser->node; atom_id = token->atom_id; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); var = njs_variable_function_add(parser, parser->scope, atom_id, NJS_VARIABLE_FUNCTION); if (var == NULL) { return NJS_ERROR; } node->u.value.data.u.lambda = njs_variable_lambda(var); node->left = (njs_parser_node_t *) atom_id; parser->node = node; ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1); if (ret != NJS_OK) { return NJS_ERROR; } async = (node->token_type == NJS_TOKEN_ASYNC_FUNCTION_DECLARATION); parser->scope->async = async; njs_parser_next(parser, njs_parser_function_parse); return njs_parser_after(parser, current, node, 1, njs_parser_function_declaration_after); } static njs_int_t njs_parser_function_declaration_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; uintptr_t atom_id; atom_id = (uint32_t) (uintptr_t) parser->node->left; parser->node->left = NULL; njs_value_null_set(&parser->node->u.value); ret = njs_parser_variable_reference(parser, parser->scope, parser->node, atom_id, NJS_DECLARATION); if (ret != NJS_OK) { return NJS_ERROR; } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_function_parse(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_formal_parameters); return njs_parser_after(parser, current, parser->target, 1, njs_parser_function_lambda_args_after); } static njs_int_t njs_parser_function_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; uintptr_t atom_id; njs_bool_t async; njs_variable_t *var; njs_function_lambda_t *lambda; ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1); if (ret != NJS_OK) { return NJS_ERROR; } async = (parser->node->token_type == NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION); parser->scope->async = async; var = NULL; if (njs_lexer_token_is_binding_identifier(token)) { atom_id = token->atom_id; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } } else { atom_id = NJS_ATOM_STRING_empty; } if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->node->left = njs_parser_node_new(parser, NJS_TOKEN_NAME); if (parser->node->left == NULL) { return NJS_ERROR; } var = njs_variable_scope_add(parser, parser->scope, parser->scope, atom_id, NJS_VARIABLE_FUNCTION, 1); if (var == NULL) { return NJS_ERROR; } var->self = 1; ret = njs_parser_variable_reference(parser, parser->scope, parser->node->left, atom_id, NJS_DECLARATION); if (ret != NJS_OK) { return NJS_ERROR; } lambda = njs_function_lambda_alloc(parser->vm, !async); if (lambda == NULL) { return NJS_ERROR; } parser->node->u.value.data.u.lambda = lambda; njs_parser_next(parser, njs_parser_function_parse); return njs_parser_after(parser, current, var, 1, njs_parser_function_expression_after); } static njs_int_t njs_parser_function_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_variable_t *var; var = (njs_variable_t *) parser->target; if (var->self) { var->init = 1; var->type = NJS_VARIABLE_CONST; } var->index = njs_scope_index(var->scope->type, var->scope->items, NJS_LEVEL_LOCAL, var->type); var->scope->items++; if (var->self) { parser->node->u.value.data.u.lambda->self = var->index; } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_unique_formal_parameters(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->node = NULL; njs_parser_next(parser, njs_parser_formal_parameters); return NJS_OK; } static njs_int_t njs_parser_formal_parameters(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_variable_t *arg; njs_rbtree_node_t *rb_node; njs_variable_node_t var_node; njs_function_lambda_t *lambda; lambda = parser->target->u.value.data.u.lambda; switch (token->type) { /* BindingRestElement */ case NJS_TOKEN_ELLIPSIS: if (lambda->rest_parameters != 0) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (!njs_lexer_token_is_binding_identifier(token)) { return njs_parser_failed(parser); } lambda->rest_parameters = 1; return NJS_OK; /* BindingElement */ case NJS_TOKEN_OPEN_BRACE: return njs_parser_not_supported(parser, token); case NJS_TOKEN_OPEN_BRACKET: return njs_parser_not_supported(parser, token); default: /* SingleNameBinding */ if (njs_lexer_token_is_binding_identifier(token)) { var_node.key = token->atom_id; rb_node = njs_rbtree_find(&parser->scope->variables, &var_node.node); if (rb_node != NULL) { arg = ((njs_variable_node_t *) rb_node)->variable; if (!arg->self) { njs_parser_syntax_error(parser, "Duplicate parameter names"); return NJS_DONE; } arg->self = 0; } else { arg = njs_variable_add(parser, parser->scope, token->atom_id, NJS_VARIABLE_VAR); } if (arg == NULL) { return NJS_ERROR; } arg->argument = 1; lambda->nargs++; /* Crutch for temporary storage. */ parser->node = (njs_parser_node_t *) arg; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_formal_parameters_after); break; } return njs_parser_stack_pop(parser); } return NJS_OK; } static njs_int_t njs_parser_formal_parameters_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_function_lambda_t *lambda; if (token->type != NJS_TOKEN_COMMA) { return njs_parser_stack_pop(parser); } lambda = parser->target->u.value.data.u.lambda; if (lambda->rest_parameters) { njs_parser_syntax_error(parser, "Rest parameter must be " "last formal parameter"); return NJS_DONE; } njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_formal_parameters); return NJS_OK; } /* * 14.2 Arrow Function Definitions * * TODO: implement according to specification. */ static njs_int_t njs_parser_arrow_function(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_bool_t async; njs_variable_t *arg, *var; njs_parser_node_t *node, *name; njs_function_lambda_t *lambda; if (token->type == NJS_TOKEN_ASYNC) { njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } async = 1; node = njs_parser_node_new(parser, NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION); } else { async = 0; node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION); } if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 0); if (ret != NJS_OK) { return NJS_ERROR; } parser->scope->async = async; name = njs_parser_node_new(parser, NJS_TOKEN_NAME); if (name == NULL) { return NJS_ERROR; } node->left = name; var = njs_variable_scope_add(parser, parser->scope, parser->scope, NJS_ATOM_STRING_empty, NJS_VARIABLE_FUNCTION, 1); if (var == NULL) { return NJS_ERROR; } ret = njs_parser_variable_reference(parser, parser->scope, node->left, NJS_ATOM_STRING_empty, NJS_DECLARATION); if (ret != NJS_OK) { return NJS_ERROR; } node->left->u.reference.variable = var; lambda = njs_function_lambda_alloc(parser->vm, 0); if (lambda == NULL) { return NJS_ERROR; } node->u.value.data.u.lambda = lambda; parser->scope->arrow_function = 1; if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { njs_lexer_consume_token(parser->lexer, 1); parser->node = NULL; parser->target = node; njs_parser_next(parser, njs_parser_formal_parameters); return njs_parser_after(parser, current, node, 1, njs_parser_arrow_function_args_after); } else if (njs_lexer_token_is_binding_identifier(token)) { arg = njs_variable_add(parser, parser->scope, token->atom_id, NJS_VARIABLE_VAR); if (arg == NULL) { return NJS_ERROR; } arg->argument = 1; var->index = njs_scope_index(parser->scope->type, parser->scope->items, NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR); parser->scope->items++; lambda->self = var->index; lambda->nargs++; njs_lexer_consume_token(parser->lexer, 1); parser->target = node; njs_parser_next(parser, njs_parser_arrow_function_arrow); } else { return njs_parser_failed(parser); } return NJS_OK; } static njs_int_t njs_parser_arrow_function_args_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_variable_t *var, **vv; if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); vv = &parser->target->left->u.reference.variable; var = *vv; *vv = NULL; var->index = njs_scope_index(var->scope->type, var->scope->items, NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR); var->scope->items++; parser->target->u.value.data.u.lambda->self = var->index; njs_parser_next(parser, njs_parser_arrow_function_arrow); return NJS_OK; } static njs_int_t njs_parser_arrow_function_arrow(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_ARROW) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_OPEN_BRACE) { njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_CLOSE_BRACE) { parser->node = NULL; njs_parser_next(parser, njs_parser_function_lambda_body_after); return NJS_OK; } parser->node = NULL; njs_parser_next(parser, njs_parser_statement_list); return njs_parser_after(parser, current, parser->target, 1, njs_parser_function_lambda_body_after); } parser->node = NULL; njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, parser->target, 1, njs_parser_arrow_function_body_after); } static njs_int_t njs_parser_arrow_function_body_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *body; body = njs_parser_return_set(parser, parser->node); if (body == NULL) { return NJS_ERROR; } parser->target->right = body; parser->node = parser->target; njs_parser_scope_end(parser); return njs_parser_stack_pop(parser); } /* * 14.3 Method Definitions. */ static njs_int_t njs_parser_method_definition(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_token_type_t type; njs_lexer_token_t *next; njs_parser_node_t *expr; type = NJS_TOKEN_FUNCTION; if (token->type == NJS_TOKEN_ASYNC) { njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } type = NJS_TOKEN_ASYNC_FUNCTION; } switch (token->type) { /* PropertyName */ case NJS_TOKEN_STRING: case NJS_TOKEN_ESCAPE_STRING: case NJS_TOKEN_NUMBER: break; default: if (njs_lexer_token_is_identifier_name(token)) { break; } return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); next = njs_lexer_token(parser->lexer, 0); if (next == NULL) { return NJS_ERROR; } if (next->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } expr = njs_parser_node_new(parser, type); if (expr == NULL) { return NJS_ERROR; } expr->token_line = next->line; parser->node = expr; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_function_lambda); return NJS_OK; } static njs_int_t njs_parser_get_set(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_token_type_t accessor; njs_lexer_token_t *name; njs_parser_node_t *property, *expression, *temp; temp = parser->target; accessor = (njs_token_type_t) (uintptr_t) temp->right; name = token; token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } switch (token->type) { /* IdentifierReference */ case NJS_TOKEN_NAME: case NJS_TOKEN_STRING: case NJS_TOKEN_ESCAPE_STRING: case NJS_TOKEN_NUMBER: break; case NJS_TOKEN_OPEN_BRACKET: njs_lexer_consume_token(parser->lexer, 2); njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, temp, 1, njs_parser_get_set_after); default: if (njs_lexer_token_is_identifier_name(token)) { break; } return njs_parser_property_definition_ident(parser, name, temp); } property = njs_parser_property_name_node(parser, token); if (property == NULL) { return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 2); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION); if (expression == NULL) { return NJS_ERROR; } expression->token_line = token->line; temp->right = property; parser->node = expression; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_function_lambda); if (accessor == NJS_TOKEN_PROPERTY_GETTER) { return njs_parser_after(parser, current, temp, 1, njs_parser_get_after); } return njs_parser_after(parser, current, temp, 1, njs_parser_set_after); } static njs_int_t njs_parser_get_set_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_token_type_t accessor; njs_parser_node_t *expression, *temp; if (token->type != NJS_TOKEN_CLOSE_BRACKET) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION); if (expression == NULL) { return NJS_ERROR; } expression->token_line = token->line; temp = parser->target; accessor = (njs_token_type_t) (uintptr_t) temp->right; temp->right = parser->node; parser->node = expression; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_function_lambda); if (accessor == NJS_TOKEN_PROPERTY_GETTER) { return njs_parser_after(parser, current, temp, 1, njs_parser_get_after); } return njs_parser_after(parser, current, temp, 1, njs_parser_set_after); } static njs_int_t njs_parser_get_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *expression, *temp; njs_function_lambda_t *lambda; temp = parser->target; expression = parser->node; lambda = expression->u.value.data.u.lambda; if (lambda->nargs != 0) { njs_parser_syntax_error(parser, "Getter must not have any formal parameters"); return NJS_DONE; } ret = njs_parser_property_accessor(parser, temp->left, temp->right, expression, NJS_TOKEN_PROPERTY_GETTER); if (ret != NJS_OK) { return NJS_ERROR; } parser->node = temp->left; parser->target = NULL; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_set_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *expression, *temp; njs_function_lambda_t *lambda; temp = parser->target; expression = parser->node; lambda = expression->u.value.data.u.lambda; if (lambda->nargs != 1) { njs_parser_syntax_error(parser, "Setter must have exactly one formal parameter"); return NJS_DONE; } ret = njs_parser_property_accessor(parser, temp->left, temp->right, expression, NJS_TOKEN_PROPERTY_SETTER); if (ret != NJS_OK) { return NJS_ERROR; } parser->node = temp->left; parser->target = NULL; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_function_lambda(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *expr; njs_function_lambda_t *lambda; lambda = njs_function_lambda_alloc(parser->vm, 0); if (lambda == NULL) { return NJS_ERROR; } expr = parser->node; expr->u.value.data.u.lambda = lambda; ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1); if (ret != NJS_OK) { return NJS_ERROR; } parser->scope->async = (parser->node->token_type == NJS_TOKEN_ASYNC_FUNCTION); parser->node = NULL; parser->target = expr; njs_parser_next(parser, njs_parser_unique_formal_parameters); return njs_parser_after(parser, current, expr, 1, njs_parser_function_lambda_args_after); } static njs_int_t njs_parser_function_lambda_args_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_OPEN_BRACE) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_CLOSE_BRACE) { parser->node = NULL; njs_parser_next(parser, njs_parser_function_lambda_body_after); return NJS_OK; } parser->node = NULL; njs_parser_next(parser, njs_parser_statement_list); return njs_parser_after(parser, current, parser->target, 1, njs_parser_function_lambda_body_after); } static njs_int_t njs_parser_function_lambda_body_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *body, *last, *parent; if (token->type != NJS_TOKEN_CLOSE_BRACE) { return njs_parser_failed(parser); } parent = parser->target; last = NULL; body = njs_parser_chain_top(parser); if (body != NULL) { /* Take the last function body statement. */ last = body->right; if (last == NULL) { /* * The last statement is terminated by semicolon. * Take the last statement itself. */ last = body->left; } } if (last == NULL || last->token_type != NJS_TOKEN_RETURN) { /* * There is no function body or the last function body * body statement is not "return" statement. */ body = njs_parser_return_set(parser, NULL); if (body == NULL) { return NJS_ERROR; } body->right->token_line = token->line; } parent->right = body; parser->node = parent; njs_parser_scope_end(parser); njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_export(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; njs_lexer_token_t *peek; static const njs_str_t as_string = njs_str("as"); if (!parser->module) { njs_parser_syntax_error(parser, "Illegal export statement"); return NJS_DONE; } if (token->type != NJS_TOKEN_DEFAULT) { if (token->type != NJS_TOKEN_OPEN_BRACE) { njs_parser_syntax_error(parser, "Non-default export is not supported"); return NJS_DONE; } /* * 'export {' * supported only: export {identifier as default}; */ njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (njs_slow_path(token == NULL)) { return NJS_ERROR; } if (token->type != NJS_TOKEN_NAME) { njs_parser_syntax_error(parser, "Identifier expected"); return NJS_DONE; } peek = njs_lexer_peek_token(parser->lexer, token, 0); if (njs_slow_path(peek == NULL)) { return NJS_ERROR; } if (peek->type != NJS_TOKEN_NAME || !njs_strstr_eq(&peek->text, &as_string)) { njs_parser_syntax_error(parser, "'as' expected"); return NJS_DONE; } peek = njs_lexer_peek_token(parser->lexer, peek, 0); if (njs_slow_path(peek == NULL)) { return NJS_ERROR; } if (peek->type != NJS_TOKEN_DEFAULT) { njs_parser_syntax_error(parser, "Non-default export is not supported"); return NJS_DONE; } peek = njs_lexer_peek_token(parser->lexer, peek, 0); if (njs_slow_path(peek == NULL)) { return NJS_ERROR; } if (peek->type != NJS_TOKEN_CLOSE_BRACE) { njs_parser_syntax_error(parser, "Close brace is expected"); return NJS_DONE; } node = njs_parser_node_new(parser, NJS_TOKEN_EXPORT); if (node == NULL) { return NJS_ERROR; } node->token_line = parser->line; node->right = njs_parser_reference(parser, token); if (node->right == NULL) { return NJS_ERROR; } parser->node = node; njs_lexer_consume_token(parser->lexer, 4); return njs_parser_stack_pop(parser); } njs_lexer_consume_token(parser->lexer, 1); node = njs_parser_node_new(parser, NJS_TOKEN_EXPORT); if (node == NULL) { return NJS_ERROR; } node->token_line = parser->line; parser->node = node; njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, node, 1, njs_parser_export_after); } static njs_int_t njs_parser_export_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { return njs_parser_failed(parser); } parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_mod_t * njs_parser_module(njs_parser_t *parser, njs_str_t *name) { njs_vm_t *vm; njs_mod_t *module; vm = parser->vm; if (name->length == 0) { njs_parser_ref_error(parser, "Cannot load module \"%V\"", name); return NULL; } module = njs_module_find(vm, name, 1); if (module != NULL) { goto done; } if (vm->module_loader == NULL) { njs_parser_ref_error(parser, "Module loader callback is not provided"); return NULL; } module = vm->module_loader(vm, vm->module_loader_opaque, name); if (module == NULL) { if (!njs_is_valid(&vm->exception)) { njs_parser_ref_error(parser, "Cannot load module \"%V\"", name); } return NULL; } done: if (module->index == 0) { module->index = vm->shared->module_items++; } return module; } static njs_int_t njs_parser_import(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_variable_t *var; njs_parser_node_t *name, *import; if (parser->scope->parent != NULL) { njs_parser_syntax_error(parser, "Illegal import statement"); return NJS_DONE; } if (token->type == NJS_TOKEN_MULTIPLICATION || token->type == NJS_TOKEN_OPEN_BRACE || token->type == NJS_TOKEN_STRING) { njs_parser_syntax_error(parser, "Non-default import is not supported"); return NJS_DONE; } if (token->type != NJS_TOKEN_NAME) { return njs_parser_failed(parser); } name = njs_parser_variable_node(parser, token->atom_id, NJS_VARIABLE_LET, &var); if (name == NULL) { return NJS_ERROR; } var->init = 1; name->token_line = token->line; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_FROM) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_STRING) { return njs_parser_failed(parser); } import = njs_parser_node_new(parser, NJS_TOKEN_IMPORT); if (import == NULL) { return NJS_ERROR; } import->hoist = 1; import->token_line = parser->line; import->left = name; import->u.module = njs_parser_module(parser, &token->text); if (njs_slow_path(import->u.module == NULL)) { return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { return njs_parser_failed(parser); } parser->node = import; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_export_sink(njs_parser_t *parser) { njs_uint_t n; njs_parser_node_t *node, *prev; n = 0; for (node = njs_parser_chain_top(parser); node != NULL; node = node->left) { if (node->right != NULL && node->right->token_type == NJS_TOKEN_EXPORT) { n++; } } if (n != 1) { njs_parser_syntax_error(parser, (n == 0) ? "export statement is required" : "Identifier \"default\" has already been declared"); return NJS_ERROR; } node = njs_parser_chain_top(parser); if (node->right && node->right->token_type == NJS_TOKEN_EXPORT) { return NJS_OK; } prev = njs_parser_chain_top(parser); while (prev->left != NULL) { node = prev->left; if (node->right != NULL && node->right->token_type == NJS_TOKEN_EXPORT) { prev->left = node->left; break; } prev = prev->left; } node->left = njs_parser_chain_top(parser); njs_parser_chain_top_set(parser, node); return NJS_OK; } static njs_parser_node_t * njs_parser_return_set(njs_parser_t *parser, njs_parser_node_t *expr) { njs_parser_node_t *stmt, *node; node = njs_parser_node_new(parser, NJS_TOKEN_RETURN); if (njs_slow_path(node == NULL)) { return NULL; } if (expr != NULL) { node->token_line = expr->token_line; } node->right = expr; stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); if (njs_slow_path(stmt == NULL)) { return NULL; } stmt->left = njs_parser_chain_top(parser); stmt->right = node; njs_parser_chain_top_set(parser, stmt); return stmt; } static njs_parser_node_t * njs_parser_variable_node(njs_parser_t *parser, uintptr_t atom_id, njs_variable_type_t type, njs_variable_t **retvar) { njs_int_t ret; njs_variable_t *var; njs_parser_node_t *node; var = njs_variable_add(parser, parser->scope, atom_id, type); if (njs_slow_path(var == NULL)) { return NULL; } if (retvar != NULL) { *retvar = var; } node = njs_parser_node_new(parser, NJS_TOKEN_NAME); if (njs_slow_path(node == NULL)) { return NULL; } ret = njs_parser_variable_reference(parser, parser->scope, node, atom_id, NJS_DECLARATION); if (njs_slow_path(ret != NJS_OK)) { return NULL; } return node; } static njs_parser_node_t * njs_parser_reference(njs_parser_t *parser, njs_lexer_token_t *token) { njs_int_t ret; njs_index_t index; njs_variable_t *var; njs_parser_node_t *node; njs_parser_scope_t *scope; node = njs_parser_node_new(parser, token->type); if (njs_slow_path(node == NULL)) { return NULL; } switch (token->type) { case NJS_TOKEN_NULL: njs_thread_log_debug("JS: null"); break; case NJS_TOKEN_THIS: njs_thread_log_debug("JS: this"); scope = njs_function_scope(parser->scope); if (njs_slow_path(scope == NULL)) { njs_parser_syntax_error(parser, "function or global scope not found"); return NULL; } if (parser->vm->options.module) { token->atom_id = NJS_ATOM_STRING_undefined; } else if (!scope->arrow_function) { index = njs_scope_index(scope->type, 0, NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR); var = njs_variable_scope_add(parser, scope, scope, token->atom_id, NJS_VARIABLE_VAR, index); if (njs_slow_path(var == NULL)) { return NULL; } } node->token_type = NJS_TOKEN_THIS; node->token_line = token->line; ret = njs_parser_variable_reference(parser, parser->scope, node, token->atom_id, NJS_REFERENCE); if (njs_slow_path(ret != NJS_OK)) { return NULL; } break; case NJS_TOKEN_ARGUMENTS: njs_thread_log_debug("JS: arguments"); scope = njs_function_scope(parser->scope); while (scope->arrow_function) { scope = njs_function_scope(scope->parent); } if (scope->parent == NULL) { njs_parser_syntax_error(parser, "\"%V\" object in global scope", &token->text); return NULL; } node->token_line = token->line; ret = njs_parser_variable_reference(parser, parser->scope, node, token->atom_id, NJS_REFERENCE); if (njs_slow_path(ret != NJS_OK)) { return NULL; } var = njs_variable_add(parser, scope, token->atom_id, NJS_VARIABLE_VAR); if (njs_slow_path(var == NULL)) { return NULL; } var->arguments_object = 1; break; default: if (token->type == NJS_TOKEN_EVAL || njs_lexer_token_is_identifier_reference(token)) { njs_thread_log_debug("JS: %V", name); if (token->type != NJS_TOKEN_EVAL) { node->token_type = NJS_TOKEN_NAME; } node->token_line = token->line; ret = njs_parser_variable_reference(parser, parser->scope, node, token->atom_id, NJS_REFERENCE); if (njs_slow_path(ret != NJS_OK)) { return NULL; } break; } (void) njs_parser_unexpected_token(parser->vm, parser, &token->text, token->type); return NULL; } return node; } static njs_parser_node_t * njs_parser_argument(njs_parser_t *parser, njs_parser_node_t *expr, njs_index_t index) { njs_parser_node_t *node; node = njs_parser_node_new(parser, NJS_TOKEN_ARGUMENT); if (njs_slow_path(node == NULL)) { return NULL; } node->token_line = expr->token_line; node->index = index; node->left = expr; expr->dest = node; return node; } static njs_int_t njs_parser_object_property(njs_parser_t *parser, njs_parser_node_t *parent, njs_parser_node_t *property, njs_parser_node_t *value, njs_bool_t proto_init) { njs_token_type_t type; njs_parser_node_t *stmt, *assign, *object, *propref; object = njs_parser_node_new(parser, NJS_TOKEN_OBJECT_VALUE); if (njs_slow_path(object == NULL)) { return NJS_TOKEN_ERROR; } object->token_line = value->token_line; object->u.object = parent; type = proto_init ? NJS_TOKEN_PROTO_INIT : NJS_TOKEN_PROPERTY_INIT; propref = njs_parser_node_new(parser, type); if (njs_slow_path(propref == NULL)) { return NJS_ERROR; } propref->token_line = value->token_line; propref->left = object; propref->right = property; assign = njs_parser_node_new(parser, NJS_TOKEN_ASSIGNMENT); if (njs_slow_path(assign == NULL)) { return NJS_ERROR; } assign->token_line = value->token_line; assign->u.operation = NJS_VMCODE_MOVE; assign->left = propref; assign->right = value; stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); if (njs_slow_path(stmt == NULL)) { return NJS_ERROR; } stmt->right = assign; stmt->left = parent->left; parent->left = stmt; return NJS_OK; } static njs_int_t njs_parser_property_accessor(njs_parser_t *parser, njs_parser_node_t *parent, njs_parser_node_t *property, njs_parser_node_t *value, njs_token_type_t accessor) { njs_parser_node_t *node, *stmt, *object, *propref; object = njs_parser_node_new(parser, NJS_TOKEN_OBJECT_VALUE); if (njs_slow_path(object == NULL)) { return NJS_TOKEN_ERROR; } object->token_line = value->token_line; object->u.object = parent; propref = njs_parser_node_new(parser, 0); if (njs_slow_path(propref == NULL)) { return NJS_ERROR; } propref->left = object; propref->right = property; node = njs_parser_node_new(parser, accessor); if (njs_slow_path(node == NULL)) { return NJS_ERROR; } node->token_line = value->token_line; node->left = propref; node->right = value; stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); if (njs_slow_path(stmt == NULL)) { return NJS_ERROR; } stmt->right = node; stmt->left = parent->left; parent->left = stmt; return NJS_OK; } static njs_int_t njs_parser_array_item(njs_parser_t *parser, njs_parser_node_t *array, njs_parser_node_t *value) { njs_int_t ret; njs_parser_node_t *number; number = njs_parser_node_new(parser, NJS_TOKEN_NUMBER); if (njs_slow_path(number == NULL)) { return NJS_ERROR; } njs_set_number(&number->u.value, array->u.length); number->token_line = value->token_line; ret = njs_parser_object_property(parser, array, number, value, 0); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } array->ctor = 0; array->u.length++; return NJS_OK; } static njs_int_t njs_parser_template_string(njs_parser_t *parser, njs_lexer_token_t *token) { u_char *p, c; njs_int_t ret; njs_str_t *text; njs_bool_t escape; njs_lexer_t *lexer; njs_parser_node_t *node; lexer = parser->lexer; text = &token->text; escape = 0; p = text->start; if (p == NULL) { return NJS_ERROR; } while (p < lexer->end) { c = *p++; switch (c) { case '\\': if (p == lexer->end) { return NJS_ERROR; } p++; escape = 1; continue; case '`': text->length = p - text->start - 1; goto done; case '$': if (p < lexer->end && *p == '{') { p++; text->length = p - text->start - 2; ret = njs_lexer_in_stack_push(lexer); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } goto done; } break; case '\n': parser->lexer->line++; break; } } return NJS_ERROR; done: node = njs_parser_node_new(parser, NJS_TOKEN_STRING); if (njs_slow_path(node == NULL)) { return NJS_ERROR; } node->token_line = token->line; if (escape) { ret = njs_parser_escape_string_create(parser, token, &node->u.value); if (njs_slow_path(ret != NJS_TOKEN_STRING)) { return NJS_ERROR; } } else { ret = njs_parser_string_create(parser->vm, token, &node->u.value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } lexer->start = p; parser->node = node; return c == '`' ? NJS_DONE : NJS_OK; } njs_int_t njs_parser_string_create(njs_vm_t *vm, njs_lexer_token_t *token, njs_value_t *value) { size_t length; njs_int_t ret; njs_str_t dst; length = njs_decode_utf8_length(&token->text, &dst.length); dst.start = njs_string_alloc(vm, value, dst.length, length); if (njs_slow_path(dst.start == NULL)) { return NJS_ERROR; } njs_decode_utf8(&dst, &token->text); if (length > NJS_STRING_MAP_STRIDE && dst.length != length) { njs_string_utf8_offset_map_init(value->string.data->start, dst.length); } ret = njs_atom_atomize_key(vm, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_TOKEN_ERROR; } return NJS_OK; } static njs_token_type_t njs_parser_escape_string_create(njs_parser_t *parser, njs_lexer_token_t *token, njs_value_t *value) { u_char c, *start, *dst; size_t size, length, hex_length; uint64_t cp, cp_pair; njs_int_t ret; njs_str_t *string; const u_char *src, *end, *hex_end; njs_unicode_decode_t ctx; ret = njs_parser_escape_string_calc_length(parser, token, &size, &length); if (njs_slow_path(ret != NJS_OK)) { return NJS_TOKEN_ILLEGAL; } start = njs_string_alloc(parser->vm, value, size, length); if (njs_slow_path(start == NULL)) { return NJS_TOKEN_ERROR; } dst = start; cp_pair = 0; string = &token->text; src = string->start; end = src + string->length; while (src < end) { c = *src++; if (c == '\\') { /* * Testing "src == end" is not required here * since this has been already tested by lexer. */ c = *src++; switch (c) { case 'u': /* * A character after "u" can be safely tested here * because there is always a closing quote at the * end of string: ...\u". */ if (*src != '{') { hex_length = 4; goto hex_length; } src++; hex_length = 0; hex_end = end; goto hex; case 'x': hex_length = 2; goto hex_length; case '0': c = '\0'; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': if (parser->node != NULL) { switch (parser->node->token_type) { case NJS_TOKEN_METHOD_CALL: case NJS_TOKEN_FUNCTION_CALL: case NJS_TOKEN_FUNCTION_EXPRESSION: case NJS_TOKEN_EVAL: goto next_char; default: break; } } njs_parser_syntax_error(parser, "Octal escape sequences can't be used " "in untagged template literals " "or in strict mode code"); return NJS_TOKEN_ILLEGAL; case '8': case '9': if (parser->node != NULL) { switch (parser->node->token_type) { case NJS_TOKEN_METHOD_CALL: case NJS_TOKEN_FUNCTION_CALL: case NJS_TOKEN_FUNCTION_EXPRESSION: case NJS_TOKEN_EVAL: goto next_char; default: break; } } njs_parser_syntax_error(parser, "The escapes \\8 and \\9 can't be used " "in untagged template literals " "or in strict mode code"); return NJS_TOKEN_ILLEGAL; next_char: break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case '\r': /* * A character after "\r" can be safely tested here * because there is always a closing quote at the * end of string: ...\\r". */ if (*src == '\n') { src++; } continue; case '\n': continue; default: if (c >= 0x80) { goto utf8_copy; } break; } } if (c < 0x80) { *dst++ = c; continue; } utf8_copy: src--; njs_utf8_decode_init(&ctx); cp = njs_utf8_decode(&ctx, &src, end); if (cp > NJS_UNICODE_MAX_CODEPOINT) { cp = NJS_UNICODE_REPLACEMENT; } dst = njs_utf8_encode(dst, cp); continue; hex_length: hex_end = src + hex_length; hex: cp = njs_number_hex_parse(&src, hex_end, 0); /* Skip '}' character. */ if (hex_length == 0) { src++; } if (cp_pair != 0) { if (njs_fast_path(njs_surrogate_trailing(cp))) { cp = njs_surrogate_pair(cp_pair, cp); } else if (njs_slow_path(njs_surrogate_leading(cp))) { cp = NJS_UNICODE_REPLACEMENT; dst = njs_utf8_encode(dst, (uint32_t) cp); } else { dst = njs_utf8_encode(dst, NJS_UNICODE_REPLACEMENT); } cp_pair = 0; } else if (njs_surrogate_any(cp)) { if (cp <= 0xdbff && src[0] == '\\' && src[1] == 'u') { cp_pair = cp; continue; } cp = NJS_UNICODE_REPLACEMENT; } dst = njs_utf8_encode(dst, (uint32_t) cp); if (njs_slow_path(dst == NULL)) { njs_parser_syntax_error(parser, "Invalid Unicode code point \"%V\"", &token->text); return NJS_TOKEN_ILLEGAL; } } if (length > NJS_STRING_MAP_STRIDE && length != size) { njs_string_utf8_offset_map_init(start, size); } ret = njs_atom_atomize_key(parser->vm, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_TOKEN_ERROR; } return NJS_TOKEN_STRING; } static njs_int_t njs_parser_escape_string_calc_length(njs_parser_t *parser, njs_lexer_token_t *token, size_t *out_size, size_t *out_length) { size_t size, length, hex_length; uint64_t cp, cp_pair; njs_str_t *string; const u_char *ptr, *src, *end, *hex_end; njs_unicode_decode_t ctx; size = 0; length = 0; cp_pair = 0; string = &token->text; src = string->start; end = src + string->length; while (src < end) { if (*src == '\\') { src++; switch (*src) { case 'u': src++; if (*src != '{') { hex_length = 4; goto hex_length; } src++; hex_length = 0; hex_end = end; goto hex; case 'x': src++; hex_length = 2; goto hex_length; case '\r': src++; if (*src == '\n') { src++; } continue; case '\n': src++; continue; default: break; } } if (*src >= 0x80) { njs_utf8_decode_init(&ctx); cp = njs_utf8_decode(&ctx, &src, end); if (cp > NJS_UNICODE_MAX_CODEPOINT) { cp = NJS_UNICODE_REPLACEMENT; } size += njs_utf8_size(cp); length++; continue; } src++; size++; length++; continue; hex_length: hex_end = src + hex_length; if (njs_slow_path(hex_end > end)) { goto invalid; } hex: ptr = src; cp = njs_number_hex_parse(&src, hex_end, 0); if (hex_length != 0) { if (src != hex_end) { goto invalid; } } else { if (src == ptr || (src - ptr) > 6) { goto invalid; } if (src == end || *src++ != '}') { goto invalid; } } if (cp_pair != 0) { if (njs_fast_path(njs_surrogate_trailing(cp))) { cp = njs_surrogate_pair(cp_pair, cp); } else if (njs_slow_path(njs_surrogate_leading(cp))) { cp = NJS_UNICODE_REPLACEMENT; size += njs_utf8_size(cp); length++; } else { size += njs_utf8_size(NJS_UNICODE_REPLACEMENT); length++; } cp_pair = 0; } else if (njs_surrogate_any(cp)) { if (cp <= 0xdbff && src[0] == '\\' && src[1] == 'u') { cp_pair = cp; continue; } cp = NJS_UNICODE_REPLACEMENT; } size += njs_utf8_size(cp); length++; } *out_size = size; *out_length = length; return NJS_OK; invalid: njs_parser_syntax_error(parser, "Invalid Unicode code point \"%V\"", &token->text); return NJS_ERROR; } njs_bool_t njs_parser_has_side_effect(njs_parser_node_t *node) { njs_bool_t side_effect; if (node == NULL) { return 0; } if (node->token_type >= NJS_TOKEN_ASSIGNMENT && node->token_type <= NJS_TOKEN_LAST_ASSIGNMENT) { return 1; } if (node->token_type == NJS_TOKEN_FUNCTION_CALL || node->token_type == NJS_TOKEN_METHOD_CALL) { return 1; } side_effect = njs_parser_has_side_effect(node->left); if (njs_fast_path(!side_effect)) { return njs_parser_has_side_effect(node->right); } return side_effect; } njs_int_t njs_parser_variable_reference(njs_parser_t *parser, njs_parser_scope_t *scope, njs_parser_node_t *node, uintptr_t atom_id, njs_reference_type_t type) { njs_rbtree_node_t *rb_node; njs_variable_reference_t *vr; njs_parser_rbtree_node_t parse_node, *rb_parse_node; vr = &node->u.reference; vr->atom_id = atom_id; vr->type = type; parse_node.key = atom_id; rb_node = njs_rbtree_find(&scope->references, &parse_node.node); if (rb_node != NULL) { return NJS_OK; } rb_parse_node = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_parser_rbtree_node_t)); if (njs_slow_path(rb_parse_node == NULL)) { return NJS_ERROR; } rb_parse_node->key = atom_id; rb_parse_node->index = NJS_INDEX_NONE; njs_rbtree_insert(&scope->references, &rb_parse_node->node); return NJS_OK; } njs_token_type_t njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser, njs_str_t *name, njs_token_type_t type) { if (type != NJS_TOKEN_END) { njs_parser_syntax_error(parser, "Unexpected token \"%V\"", name); } else { njs_parser_syntax_error(parser, "Unexpected end of input"); } return NJS_DONE; } static void njs_parser_error(njs_vm_t *vm, njs_object_type_t type, njs_str_t *file, uint32_t line, const char *fmt, va_list args) { size_t width; u_char msg[NJS_MAX_ERROR_STR]; u_char *p, *end; njs_int_t ret; njs_value_t value, error; if (njs_slow_path(vm->top_frame == NULL)) { njs_vm_runtime_init(vm); } p = msg; end = msg + NJS_MAX_ERROR_STR; p = njs_vsprintf(p, end, fmt, args); width = njs_length(" in ") + file->length + NJS_INT_T_LEN; if (p > end - width) { p = end - width; } if (file->length != 0 && !vm->options.quiet) { p = njs_sprintf(p, end, " in %V:%uD", file, line); } else { p = njs_sprintf(p, end, " in %uD", line); } njs_error_new(vm, &error, njs_vm_proto(vm, type), msg, p - msg); njs_set_number(&value, line); njs_value_property_set(vm, &error, NJS_ATOM_STRING_lineNumber, &value); if (file->length != 0) { ret = njs_string_create(vm, &value, file->start, file->length); if (ret == NJS_OK) { njs_value_property_set(vm, &error, NJS_ATOM_STRING_fileName, &value); } } njs_vm_throw(vm, &error); } void njs_parser_lexer_error(njs_parser_t *parser, njs_object_type_t type, const char *fmt, ...) { va_list args; if (njs_is_error(&parser->vm->exception)) { return; } va_start(args, fmt); njs_parser_error(parser->vm, type, &parser->lexer->file, parser->lexer->line, fmt, args); va_end(args); } void njs_parser_node_error(njs_vm_t *vm, njs_object_type_t type, njs_parser_node_t *node, njs_str_t *file, const char *fmt, ...) { va_list args; va_start(args, fmt); njs_parser_error(vm, type, file, node->token_line, fmt, args); va_end(args); } njs_int_t njs_parser_traverse(njs_vm_t *vm, njs_parser_node_t *root, void *ctx, njs_parser_traverse_cb_t cb) { njs_int_t ret; njs_arr_t *stack; njs_parser_node_t *node, **ref; if (root == NULL) { return NJS_OK; } stack = njs_arr_create(vm->mem_pool, 8, sizeof(njs_parser_node_t *)); if (njs_slow_path(stack == NULL)) { return NJS_ERROR; } ref = njs_arr_add(stack); if (njs_slow_path(ref == NULL)) { goto failed; } *ref = root; while (1) { if (njs_arr_is_empty(stack)) { break; } ref = njs_arr_remove_last(stack); node = *ref; ret = cb(vm, node, ctx); if (njs_slow_path(ret != NJS_OK)) { goto failed; } if (node->left != NULL) { ref = njs_arr_add(stack); if (njs_slow_path(ref == NULL)) { goto failed; } *ref = node->left; } if (node->right != NULL) { ref = njs_arr_add(stack); if (njs_slow_path(ref == NULL)) { goto failed; } *ref = node->right; } } njs_arr_destroy(stack); return NJS_OK; failed: njs_arr_destroy(stack); return NJS_ERROR; } njs_int_t njs_parser_serialize_ast(njs_parser_node_t *node, njs_chb_t *chain) { njs_int_t ret; ret = NJS_OK; njs_parser_serialize_tree(chain, node, &ret, 0); njs_chb_append_literal(chain, "\n"); return ret; } njs_inline void njs_parser_serialize_indent(njs_chb_t *chain, size_t indent) { size_t i; for (i = 0; i < indent; i++) { njs_chb_append_literal(chain, " "); } } static void njs_parser_serialize_tree(njs_chb_t *chain, njs_parser_node_t *node, njs_int_t *ret, size_t indent) { njs_str_t str; njs_chb_append_literal(chain, "{\"name\": \""); *ret |= njs_parser_serialize_node(chain, node); njs_chb_append_literal(chain, "\",\n"); njs_parser_serialize_indent(chain, indent); njs_chb_sprintf(chain, 32, " \"line\": %d", node->token_line); switch (node->token_type) { case NJS_TOKEN_NUMBER: case NJS_TOKEN_STRING: case NJS_TOKEN_NAME: case NJS_TOKEN_FUNCTION_CALL: njs_chb_append_literal(chain, ",\n"); njs_parser_serialize_indent(chain, indent); njs_chb_sprintf(chain, 32, " \"index\": \"%p\"", node->index); switch (node->token_type) { case NJS_TOKEN_NUMBER: case NJS_TOKEN_STRING: njs_chb_append_literal(chain, ",\n"); njs_parser_serialize_indent(chain, indent); if (node->token_type == NJS_TOKEN_NUMBER) { njs_chb_sprintf(chain, 32, " \"value\": %f", njs_number(&node->u.value)); } else { njs_string_get_unsafe(&node->u.value, &str); njs_chb_append_literal(chain, " \"value\": \""); njs_chb_append_str(chain, &str); njs_chb_append_literal(chain, "\""); } break; default: break; } break; default: break; } if (node->left != NULL) { njs_chb_append_literal(chain, ",\n"); njs_parser_serialize_indent(chain, indent); njs_chb_append_literal(chain, " \"left\": "); njs_parser_serialize_tree(chain, node->left, ret, indent + 1); } if (node->right != NULL) { njs_chb_append_literal(chain, ",\n"); njs_parser_serialize_indent(chain, indent); njs_chb_append_literal(chain, " \"right\": "); njs_parser_serialize_tree(chain, node->right, ret, indent + 1); } njs_chb_append_literal(chain, "}"); } static njs_int_t njs_parser_serialize_node(njs_chb_t *chain, njs_parser_node_t *node) { const char *name; #define njs_token_serialize(token) \ case token: \ name = &njs_stringify(token)[njs_length("NJS_TOKEN_")]; \ njs_chb_append(chain, name, njs_strlen(name)); \ break switch (node->token_type) { njs_token_serialize(NJS_TOKEN_END); /* FIXME: NJS_TOKEN_ILLEGAL should not be present in AST */ njs_token_serialize(NJS_TOKEN_ILLEGAL); njs_token_serialize(NJS_TOKEN_COMMA); njs_token_serialize(NJS_TOKEN_CONDITIONAL); njs_token_serialize(NJS_TOKEN_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_ADDITION_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_SUBTRACTION_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_MULTIPLICATION_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_EXPONENTIATION_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_DIVISION_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_REMAINDER_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_RIGHT_SHIFT_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_BITWISE_OR_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_BITWISE_XOR_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_BITWISE_AND_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_EQUAL); njs_token_serialize(NJS_TOKEN_NOT_EQUAL); njs_token_serialize(NJS_TOKEN_STRICT_EQUAL); njs_token_serialize(NJS_TOKEN_STRICT_NOT_EQUAL); njs_token_serialize(NJS_TOKEN_ADDITION); njs_token_serialize(NJS_TOKEN_UNARY_PLUS); njs_token_serialize(NJS_TOKEN_INCREMENT); njs_token_serialize(NJS_TOKEN_POST_INCREMENT); njs_token_serialize(NJS_TOKEN_SUBTRACTION); njs_token_serialize(NJS_TOKEN_UNARY_NEGATION); njs_token_serialize(NJS_TOKEN_DECREMENT); njs_token_serialize(NJS_TOKEN_POST_DECREMENT); njs_token_serialize(NJS_TOKEN_MULTIPLICATION); njs_token_serialize(NJS_TOKEN_EXPONENTIATION); njs_token_serialize(NJS_TOKEN_DIVISION); njs_token_serialize(NJS_TOKEN_REMAINDER); njs_token_serialize(NJS_TOKEN_LESS); njs_token_serialize(NJS_TOKEN_LESS_OR_EQUAL); njs_token_serialize(NJS_TOKEN_LEFT_SHIFT); njs_token_serialize(NJS_TOKEN_GREATER); njs_token_serialize(NJS_TOKEN_GREATER_OR_EQUAL); njs_token_serialize(NJS_TOKEN_RIGHT_SHIFT); njs_token_serialize(NJS_TOKEN_UNSIGNED_RIGHT_SHIFT); njs_token_serialize(NJS_TOKEN_BITWISE_OR); njs_token_serialize(NJS_TOKEN_LOGICAL_OR); njs_token_serialize(NJS_TOKEN_BITWISE_XOR); njs_token_serialize(NJS_TOKEN_BITWISE_AND); njs_token_serialize(NJS_TOKEN_LOGICAL_AND); njs_token_serialize(NJS_TOKEN_BITWISE_NOT); njs_token_serialize(NJS_TOKEN_LOGICAL_NOT); njs_token_serialize(NJS_TOKEN_COALESCE); njs_token_serialize(NJS_TOKEN_IN); njs_token_serialize(NJS_TOKEN_OF); njs_token_serialize(NJS_TOKEN_INSTANCEOF); njs_token_serialize(NJS_TOKEN_TYPEOF); njs_token_serialize(NJS_TOKEN_VOID); njs_token_serialize(NJS_TOKEN_NEW); njs_token_serialize(NJS_TOKEN_DELETE); njs_token_serialize(NJS_TOKEN_YIELD); njs_token_serialize(NJS_TOKEN_NULL); njs_token_serialize(NJS_TOKEN_NUMBER); njs_token_serialize(NJS_TOKEN_TRUE); njs_token_serialize(NJS_TOKEN_FALSE); njs_token_serialize(NJS_TOKEN_STRING); njs_token_serialize(NJS_TOKEN_TEMPLATE_LITERAL); njs_token_serialize(NJS_TOKEN_NAME); njs_token_serialize(NJS_TOKEN_OBJECT); njs_token_serialize(NJS_TOKEN_OBJECT_VALUE); njs_token_serialize(NJS_TOKEN_ARRAY); njs_token_serialize(NJS_TOKEN_REGEXP); njs_token_serialize(NJS_TOKEN_PROPERTY); njs_token_serialize(NJS_TOKEN_PROPERTY_INIT); njs_token_serialize(NJS_TOKEN_PROPERTY_DELETE); njs_token_serialize(NJS_TOKEN_PROPERTY_GETTER); njs_token_serialize(NJS_TOKEN_PROPERTY_SETTER); njs_token_serialize(NJS_TOKEN_PROTO_INIT); njs_token_serialize(NJS_TOKEN_FUNCTION); njs_token_serialize(NJS_TOKEN_ASYNC_FUNCTION); njs_token_serialize(NJS_TOKEN_FUNCTION_DECLARATION); njs_token_serialize(NJS_TOKEN_ASYNC_FUNCTION_DECLARATION); njs_token_serialize(NJS_TOKEN_FUNCTION_EXPRESSION); njs_token_serialize(NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION); njs_token_serialize(NJS_TOKEN_FUNCTION_CALL); njs_token_serialize(NJS_TOKEN_METHOD_CALL); njs_token_serialize(NJS_TOKEN_ARGUMENT); njs_token_serialize(NJS_TOKEN_RETURN); njs_token_serialize(NJS_TOKEN_STATEMENT); njs_token_serialize(NJS_TOKEN_BLOCK); njs_token_serialize(NJS_TOKEN_VAR); njs_token_serialize(NJS_TOKEN_LET); njs_token_serialize(NJS_TOKEN_CONST); njs_token_serialize(NJS_TOKEN_IF); njs_token_serialize(NJS_TOKEN_ELSE); njs_token_serialize(NJS_TOKEN_BRANCHING); njs_token_serialize(NJS_TOKEN_WHILE); njs_token_serialize(NJS_TOKEN_DO); njs_token_serialize(NJS_TOKEN_FOR); njs_token_serialize(NJS_TOKEN_FOR_IN); njs_token_serialize(NJS_TOKEN_BREAK); njs_token_serialize(NJS_TOKEN_CONTINUE); njs_token_serialize(NJS_TOKEN_SWITCH); njs_token_serialize(NJS_TOKEN_CASE); njs_token_serialize(NJS_TOKEN_DEFAULT); njs_token_serialize(NJS_TOKEN_WITH); njs_token_serialize(NJS_TOKEN_TRY); njs_token_serialize(NJS_TOKEN_CATCH); njs_token_serialize(NJS_TOKEN_FINALLY); njs_token_serialize(NJS_TOKEN_THROW); njs_token_serialize(NJS_TOKEN_THIS); njs_token_serialize(NJS_TOKEN_ARGUMENTS); njs_token_serialize(NJS_TOKEN_EVAL); njs_token_serialize(NJS_TOKEN_IMPORT); njs_token_serialize(NJS_TOKEN_EXPORT); njs_token_serialize(NJS_TOKEN_DEBUGGER); #if 0 njs_token_serialize(NJS_TOKEN_TARGET); njs_token_serialize(NJS_TOKEN_META); njs_token_serialize(NJS_TOKEN_ASYNC); njs_token_serialize(NJS_TOKEN_AWAIT); njs_token_serialize(NJS_TOKEN_ENUM); njs_token_serialize(NJS_TOKEN_CLASS); njs_token_serialize(NJS_TOKEN_EXTENDS); njs_token_serialize(NJS_TOKEN_IMPLEMENTS); njs_token_serialize(NJS_TOKEN_INTERFACE); njs_token_serialize(NJS_TOKEN_PACKAGE); njs_token_serialize(NJS_TOKEN_PRIVATE); njs_token_serialize(NJS_TOKEN_PROTECTED); njs_token_serialize(NJS_TOKEN_PUBLIC); njs_token_serialize(NJS_TOKEN_STATIC); njs_token_serialize(NJS_TOKEN_SUPER); #endif default: njs_chb_sprintf(chain, 32, "#UNDEF(%d)", (int) node->token_type); return NJS_DECLINED; } return NJS_OK; }