mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 04:38:11 +00:00
LibJS: Hoist function declarations
This patch adds function declaration hoisting. The mechanism is similar to var hoisting. Hoisted function declarations are to be put before the hoisted var declarations, hence they have to be treated separately.
This commit is contained in:
parent
e162b59a5e
commit
2579d0bf55
6 changed files with 73 additions and 15 deletions
|
@ -37,6 +37,7 @@ public:
|
|||
enum Type {
|
||||
Var = 1,
|
||||
Let = 2,
|
||||
Function = 3,
|
||||
};
|
||||
|
||||
ScopePusher(Parser& parser, unsigned mask)
|
||||
|
@ -47,6 +48,8 @@ public:
|
|||
m_parser.m_parser_state.m_var_scopes.append(NonnullRefPtrVector<VariableDeclaration>());
|
||||
if (m_mask & Let)
|
||||
m_parser.m_parser_state.m_let_scopes.append(NonnullRefPtrVector<VariableDeclaration>());
|
||||
if (m_mask & Function)
|
||||
m_parser.m_parser_state.m_function_scopes.append(NonnullRefPtrVector<FunctionDeclaration>());
|
||||
}
|
||||
|
||||
~ScopePusher()
|
||||
|
@ -55,6 +58,8 @@ public:
|
|||
m_parser.m_parser_state.m_var_scopes.take_last();
|
||||
if (m_mask & Let)
|
||||
m_parser.m_parser_state.m_let_scopes.take_last();
|
||||
if (m_mask & Function)
|
||||
m_parser.m_parser_state.m_function_scopes.take_last();
|
||||
}
|
||||
|
||||
Parser& m_parser;
|
||||
|
@ -204,7 +209,7 @@ Associativity Parser::operator_associativity(TokenType type) const
|
|||
|
||||
NonnullRefPtr<Program> Parser::parse_program()
|
||||
{
|
||||
ScopePusher scope(*this, ScopePusher::Var | ScopePusher::Let);
|
||||
ScopePusher scope(*this, ScopePusher::Var | ScopePusher::Let | ScopePusher::Function);
|
||||
auto program = adopt(*new Program);
|
||||
|
||||
bool first = true;
|
||||
|
@ -228,6 +233,7 @@ NonnullRefPtr<Program> Parser::parse_program()
|
|||
if (m_parser_state.m_var_scopes.size() == 1) {
|
||||
program->add_variables(m_parser_state.m_var_scopes.last());
|
||||
program->add_variables(m_parser_state.m_let_scopes.last());
|
||||
program->add_functions(m_parser_state.m_function_scopes.last());
|
||||
} else {
|
||||
syntax_error("Unclosed scope");
|
||||
}
|
||||
|
@ -238,8 +244,11 @@ NonnullRefPtr<Statement> Parser::parse_statement()
|
|||
{
|
||||
auto statement = [this]() -> NonnullRefPtr<Statement> {
|
||||
switch (m_parser_state.m_current_token.type()) {
|
||||
case TokenType::Function:
|
||||
return parse_function_node<FunctionDeclaration>();
|
||||
case TokenType::Function: {
|
||||
auto declaration = parse_function_node<FunctionDeclaration>();
|
||||
m_parser_state.m_function_scopes.last().append(declaration);
|
||||
return declaration;
|
||||
}
|
||||
case TokenType::CurlyOpen:
|
||||
return parse_block_statement();
|
||||
case TokenType::Return:
|
||||
|
@ -590,8 +599,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
|
|||
syntax_error(
|
||||
"Expected '(' for object getter or setter property",
|
||||
m_parser_state.m_current_token.line_number(),
|
||||
m_parser_state.m_current_token.line_column()
|
||||
);
|
||||
m_parser_state.m_current_token.line_column());
|
||||
skip_to_next_property();
|
||||
continue;
|
||||
}
|
||||
|
@ -606,8 +614,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
|
|||
syntax_error(
|
||||
"Object getter property must have no arguments",
|
||||
m_parser_state.m_current_token.line_number(),
|
||||
m_parser_state.m_current_token.line_column()
|
||||
);
|
||||
m_parser_state.m_current_token.line_column());
|
||||
skip_to_next_property();
|
||||
continue;
|
||||
}
|
||||
|
@ -615,8 +622,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
|
|||
syntax_error(
|
||||
"Object setter property must have one argument",
|
||||
m_parser_state.m_current_token.line_number(),
|
||||
m_parser_state.m_current_token.line_column()
|
||||
);
|
||||
m_parser_state.m_current_token.line_column());
|
||||
skip_to_next_property();
|
||||
continue;
|
||||
}
|
||||
|
@ -1059,13 +1065,14 @@ NonnullRefPtr<BlockStatement> Parser::parse_block_statement()
|
|||
m_parser_state.m_strict_mode = initial_strict_mode_state;
|
||||
consume(TokenType::CurlyClose);
|
||||
block->add_variables(m_parser_state.m_let_scopes.last());
|
||||
block->add_functions(m_parser_state.m_function_scopes.last());
|
||||
return block;
|
||||
}
|
||||
|
||||
template<typename FunctionNodeType>
|
||||
NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(bool check_for_function_and_name)
|
||||
{
|
||||
ScopePusher scope(*this, ScopePusher::Var);
|
||||
ScopePusher scope(*this, ScopePusher::Var | ScopePusher::Function);
|
||||
|
||||
if (check_for_function_and_name)
|
||||
consume(TokenType::Function);
|
||||
|
@ -1109,6 +1116,7 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(bool check_for_funct
|
|||
|
||||
auto body = parse_block_statement();
|
||||
body->add_variables(m_parser_state.m_var_scopes.last());
|
||||
body->add_functions(m_parser_state.m_function_scopes.last());
|
||||
return create_ast_node<FunctionNodeType>(name, move(body), move(parameters), function_length, NonnullRefPtrVector<VariableDeclaration>());
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue