JSSpecCompiler: Properly parse function calls with zero arguments

We cannot handle them normally since we need text between parenthesis to
be a valid expression. As a workaround, we now push an artificial value
to stack to act as an argument (it'll be later removed during function
call canonicalization).
This commit is contained in:
Dan Klishch 2024-01-16 00:21:59 -05:00 committed by Andrew Kaster
parent 14ee25b8ba
commit 33b36476d9
4 changed files with 50 additions and 1 deletions

View file

@ -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<ErrorNode>();
inline Tree const zero_argument_function_call = make_ref_counted<WellKnownNode>(WellKnownNode::ZeroArgumentFunctionCall);
class ControlFlowFunctionReturn : public ControlFlowOperator {
public:

View file

@ -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");

View file

@ -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<FunctionCall>(binary_operation->m_left, move(arguments)));
}
}

View file

@ -228,6 +228,15 @@ ParseErrorOr<Tree> 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<ClauseHeader> 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;