diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.h index 993faa3d33..31cd3c0c18 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.h @@ -130,7 +130,27 @@ protected: void dump_tree(StringBuilder& builder) override; }; +class WellKnownNode : public Expression { +public: + enum Type { + ZeroArgumentFunctionCall, + // Update WellKnownNode::dump_tree after adding an entry here + }; + + WellKnownNode(Type type) + : m_type(type) + { + } + +protected: + void dump_tree(StringBuilder& builder) override; + +private: + Type m_type; +}; + inline Tree const error_tree = make_ref_counted(); +inline Tree const zero_argument_function_call = make_ref_counted(WellKnownNode::ZeroArgumentFunctionCall); class ControlFlowFunctionReturn : public ControlFlowOperator { public: diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/ASTPrinting.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/ASTPrinting.cpp index 10b0ba11a7..771b4b1cad 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/ASTPrinting.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/ASTPrinting.cpp @@ -35,6 +35,14 @@ void ErrorNode::dump_tree(StringBuilder& builder) dump_node(builder, "Error \"{}\"", m_error); } +void WellKnownNode::dump_tree(StringBuilder& builder) +{ + static constexpr StringView type_to_name[] = { + "ZeroArgumentFunctionCall"sv, + }; + dump_node(builder, "WellKnownNode {}", type_to_name[m_type]); +} + void ControlFlowFunctionReturn::dump_tree(StringBuilder& builder) { dump_node(builder, "ControlFlowFunctionReturn"); diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/FunctionCallCanonicalizationPass.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/FunctionCallCanonicalizationPass.cpp index 7b7c5c7f5d..85413bce2e 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/FunctionCallCanonicalizationPass.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/FunctionCallCanonicalizationPass.cpp @@ -25,6 +25,11 @@ RecursionDecision FunctionCallCanonicalizationPass::on_entry(Tree tree) } arguments.append(current_tree); + if (arguments[0] == zero_argument_function_call) { + VERIFY(arguments.size() == 1); + arguments.clear(); + } + replace_current_node_with(make_ref_counted(binary_operation->m_left, move(arguments))); } } diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.cpp index a084d3cf53..5ace444ea1 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.cpp @@ -228,6 +228,15 @@ ParseErrorOr TextParser::parse_expression() if (last_element_type == ExpressionType) stack.append(Token { TokenType::FunctionCall, ""sv, m_node }); stack.append(token); + + if (m_next_token_index + 1 < m_tokens.size() + && m_tokens[m_next_token_index + 1].type == TokenType::ParenClose) { + // This is a call to function which does not take parameters. We cannot handle it + // normally since we need text between parenthesis to be a valid expression. As a + // workaround, we push an artificial tree to stack to act as an argument (it'll be + // removed later during function call canonicalization). + stack.append(zero_argument_function_call); + } } else if (token.is_pre_merged_binary_operator()) { THROW_PARSE_ERROR_IF(last_element_type != ExpressionType); stack.append(token); @@ -492,7 +501,14 @@ ParseErrorOr TextParser::parse_clause_header() TRY(consume_token_with_type(TokenType::ParenOpen)); while (true) { - function_definition.arguments.append({ TRY(consume_token_with_type(TokenType::Identifier))->data }); + if (function_definition.arguments.is_empty()) { + auto argument = TRY(consume_token_with_one_of_types({ TokenType::ParenClose, TokenType::Identifier })); + if (argument->type == TokenType::ParenClose) + break; + function_definition.arguments.append({ argument->data }); + } else { + function_definition.arguments.append({ TRY(consume_token_with_type(TokenType::Identifier))->data }); + } auto next_token = TRY(consume_token_with_one_of_types({ TokenType::ParenClose, TokenType::Comma })); if (next_token->type == TokenType::ParenClose) break;