Browse Source

LibJS: Parse FunctionExpressions

FunctionExpression is mostly like FunctionDeclaration, except the name
is optional. Share the parsing logic in parse_function_node<NodeType>.

This allows us to do nice things like:

    document.addEventListener("DOMContentLoaded", function() {
        alert("Hello friends!");
    });
Andreas Kling 5 years ago
parent
commit
07679e347c
3 changed files with 21 additions and 5 deletions
  1. 4 0
      Libraries/LibJS/AST.h
  2. 14 4
      Libraries/LibJS/Parser.cpp
  3. 3 1
      Libraries/LibJS/Parser.h

+ 4 - 0
Libraries/LibJS/AST.h

@@ -154,6 +154,8 @@ class FunctionDeclaration final
     : public Statement
     , public FunctionNode {
 public:
+    static bool must_have_name() { return true; }
+
     FunctionDeclaration(String name, NonnullRefPtr<ScopeNode> body, Vector<String> parameters = {})
         : FunctionNode(move(name), move(body), move(parameters))
     {
@@ -169,6 +171,8 @@ private:
 class FunctionExpression final : public Expression
     , public FunctionNode {
 public:
+    static bool must_have_name() { return false; }
+
     FunctionExpression(String name, NonnullRefPtr<ScopeNode> body, Vector<String> parameters = {})
         : FunctionNode(move(name), move(body), move(parameters))
     {

+ 14 - 4
Libraries/LibJS/Parser.cpp

@@ -187,7 +187,7 @@ NonnullRefPtr<Statement> Parser::parse_statement()
 
     switch (m_current_token.type()) {
     case TokenType::Function:
-        return parse_function_declaration();
+        return parse_function_node<FunctionDeclaration>();
     case TokenType::CurlyOpen:
         return parse_block_statement();
     case TokenType::Return:
@@ -231,6 +231,8 @@ NonnullRefPtr<Expression> Parser::parse_primary_expression()
         return create_ast_node<UndefinedLiteral>();
     case TokenType::CurlyOpen:
         return parse_object_expression();
+    case TokenType::Function:
+        return parse_function_node<FunctionExpression>();
     default:
         m_has_errors = true;
         expected("primary expression (missing switch case)");
@@ -416,10 +418,17 @@ NonnullRefPtr<BlockStatement> Parser::parse_block_statement()
     return block;
 }
 
-NonnullRefPtr<FunctionDeclaration> Parser::parse_function_declaration()
+template<typename FunctionNodeType>
+NonnullRefPtr<FunctionNodeType> Parser::parse_function_node()
 {
     consume(TokenType::Function);
-    auto name = consume(TokenType::Identifier).value();
+    String name;
+    if (FunctionNodeType::must_have_name()) {
+        name = consume(TokenType::Identifier).value();
+    } else {
+        if (match(TokenType::Identifier))
+            name = consume(TokenType::Identifier).value();
+    }
     consume(TokenType::ParenOpen);
     Vector<String> parameters;
     while (match(TokenType::Identifier)) {
@@ -432,7 +441,7 @@ NonnullRefPtr<FunctionDeclaration> Parser::parse_function_declaration()
     }
     consume(TokenType::ParenClose);
     auto body = parse_block_statement();
-    return create_ast_node<FunctionDeclaration>(name, move(body), move(parameters));
+    return create_ast_node<FunctionNodeType>(name, move(body), move(parameters));
 }
 
 NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration()
@@ -526,6 +535,7 @@ bool Parser::match_expression() const
         || type == TokenType::CurlyOpen
         || type == TokenType::BracketOpen
         || type == TokenType::ParenOpen
+        || type == TokenType::Function
         || match_unary_prefixed_expression();
 }
 

+ 3 - 1
Libraries/LibJS/Parser.h

@@ -43,10 +43,12 @@ public:
 
     NonnullRefPtr<Program> parse_program();
 
+    template<typename FunctionNodeType>
+    NonnullRefPtr<FunctionNodeType> parse_function_node();
+
     NonnullRefPtr<Statement> parse_statement();
     NonnullRefPtr<BlockStatement> parse_block_statement();
     NonnullRefPtr<ReturnStatement> parse_return_statement();
-    NonnullRefPtr<FunctionDeclaration> parse_function_declaration();
     NonnullRefPtr<VariableDeclaration> parse_variable_declaration();
     NonnullRefPtr<ForStatement> parse_for_statement();