diff options
author | Jack Karamanian <karamanian.jack@gmail.com> | 2020-03-30 08:26:09 -0500 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-03-30 15:41:36 +0200 |
commit | 098f1cd0ca94b8025c2e0e8caacd94d62d18f272 (patch) | |
tree | d221a4f30712c5b9a4246d11543888c6eab99b42 /Libraries/LibJS/Parser.cpp | |
parent | f90da71d2856f86e76b654f4ee2c66f09d2afdcf (diff) | |
download | serenity-098f1cd0ca94b8025c2e0e8caacd94d62d18f272.zip |
LibJS: Add support for arrow functions
Diffstat (limited to 'Libraries/LibJS/Parser.cpp')
-rw-r--r-- | Libraries/LibJS/Parser.cpp | 85 |
1 files changed, 84 insertions, 1 deletions
diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 94a69bdb37..59db60e5ee 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -223,6 +223,78 @@ NonnullRefPtr<Statement> Parser::parse_statement() return statement; } +RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expect_parens) +{ + save_state(); + + Vector<FlyString> parameters; + bool parse_failed = false; + while (true) { + if (match(TokenType::Comma)) { + consume(TokenType::Comma); + } else if (match(TokenType::Identifier)) { + auto token = consume(TokenType::Identifier); + parameters.append(token.value()); + } else if (match(TokenType::ParenClose)) { + if (expect_parens) { + consume(TokenType::ParenClose); + if (match(TokenType::Arrow)) { + consume(TokenType::Arrow); + } else { + parse_failed = true; + } + break; + } + parse_failed = true; + break; + } else if (match(TokenType::Arrow)) { + if (!expect_parens) { + consume(TokenType::Arrow); + break; + } + parse_failed = true; + break; + } else { + parse_failed = true; + break; + } + } + + if (parse_failed) { + load_state(); + return nullptr; + } + + auto function_body_result = [this]() -> RefPtr<BlockStatement> { + if (match(TokenType::CurlyOpen)) { + // Parse a function body with statements + return parse_block_statement(); + } + if (match_expression()) { + // Parse a function body which returns a single expression + + // FIXME: We synthesize a block with a return statement + // for arrow function bodies which are a single expression. + // Esprima generates a single "ArrowFunctionExpression" + // with a "body" property. + auto return_expression = parse_expression(0); + auto return_block = create_ast_node<BlockStatement>(); + return_block->append<ReturnStatement>(move(return_expression)); + return return_block; + } + // Invalid arrow function body + return nullptr; + }(); + + if (!function_body_result.is_null()) { + auto body = function_body_result.release_nonnull(); + return create_ast_node<FunctionExpression>("", move(body), move(parameters)); + } + + load_state(); + return nullptr; +} + NonnullRefPtr<Expression> Parser::parse_primary_expression() { if (match_unary_prefixed_expression()) @@ -231,12 +303,23 @@ NonnullRefPtr<Expression> Parser::parse_primary_expression() switch (m_parser_state.m_current_token.type()) { case TokenType::ParenOpen: { consume(TokenType::ParenOpen); + if (match(TokenType::ParenClose) || match(TokenType::Identifier)) { + auto arrow_function_result = try_parse_arrow_function_expression(true); + if (!arrow_function_result.is_null()) { + return arrow_function_result.release_nonnull(); + } + } auto expression = parse_expression(0); consume(TokenType::ParenClose); return expression; } - case TokenType::Identifier: + case TokenType::Identifier: { + auto arrow_function_result = try_parse_arrow_function_expression(false); + if (!arrow_function_result.is_null()) { + return arrow_function_result.release_nonnull(); + } return create_ast_node<Identifier>(consume().value()); + } case TokenType::NumericLiteral: return create_ast_node<NumericLiteral>(consume().double_value()); case TokenType::BoolLiteral: |