JSSpecCompiler: Add reference resolving pass

It replaces UnresolvedReference with Variable, FunctionPointer, or
SlotName nodes. Also, it gathers all variable names from their
declarations.
This commit is contained in:
Dan Klishch 2023-08-19 23:17:06 -04:00 committed by Andrew Kaster
parent 326bac19d9
commit 81519975c5
Notes: sideshowbarker 2024-07-17 22:01:16 +09:00
9 changed files with 116 additions and 4 deletions

View file

@ -40,6 +40,16 @@ private:
Variant<Tree*, NullableTree*> m_tree_ptr;
};
class VariableDeclaration : public RefCounted<VariableDeclaration> {
public:
VariableDeclaration(StringView name)
: m_name(name)
{
}
StringView m_name;
};
class Node : public RefCounted<Node> {
public:
virtual ~Node() = default;
@ -380,12 +390,12 @@ protected:
class Variable : public Expression {
public:
Variable(StringView variable_name)
: m_name(variable_name)
Variable(VariableDeclarationRef variable_declaration)
: m_variable_declaration(move(variable_declaration))
{
}
StringView m_name;
VariableDeclarationRef m_variable_declaration;
protected:
void dump_tree(StringBuilder& builder) override;

View file

@ -139,7 +139,7 @@ void SlotName::dump_tree(StringBuilder& builder)
void Variable::dump_tree(StringBuilder& builder)
{
dump_node(builder, "Var {}", m_name);
dump_node(builder, "Var {}", m_variable_declaration->m_name);
}
void FunctionPointer::dump_tree(StringBuilder& builder)

View file

@ -4,6 +4,7 @@ set(SOURCES
Compiler/FunctionCallCanonicalizationPass.cpp
Compiler/GenericASTPass.cpp
Compiler/IfBranchMergingPass.cpp
Compiler/ReferenceResolvingPass.cpp
Parser/Lexer.cpp
Parser/ParseError.cpp
Parser/SpecParser.cpp

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/HashMap.h>
#include "AST/AST.h"
#include "Compiler/ReferenceResolvingPass.h"
#include "Function.h"
namespace JSSpecCompiler {
RecursionDecision ReferenceResolvingPass::on_entry(Tree tree)
{
if (auto binary_operation = as<BinaryOperation>(tree); binary_operation) {
if (binary_operation->m_operation != BinaryOperator::Declaration)
return RecursionDecision::Recurse;
if (auto variable_name = as<UnresolvedReference>(binary_operation->m_left); variable_name) {
auto name = variable_name->m_name;
if (!m_function->m_local_variables.contains(name))
m_function->m_local_variables.set(name, make_ref_counted<VariableDeclaration>(name));
}
}
return RecursionDecision::Recurse;
}
void ReferenceResolvingPass::on_leave(Tree tree)
{
auto& functions = m_function->m_context->m_functions;
if (auto reference = as<UnresolvedReference>(tree); reference) {
auto name = reference->m_name;
if (name.starts_with("[["sv) && name.ends_with("]]"sv)) {
replace_current_node_with(make_ref_counted<SlotName>(name.substring_view(2, name.length() - 4)));
return;
}
if (auto it = m_function->m_local_variables.find(name); it != m_function->m_local_variables.end()) {
replace_current_node_with(make_ref_counted<Variable>(it->value));
return;
}
if (auto it = functions.find(name); it != functions.end()) {
replace_current_node_with(it->value);
return;
}
}
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "Compiler/GenericASTPass.h"
namespace JSSpecCompiler {
// ReferenceResolvingPass collects all variable names declared in the function and replaces
// UnresolvedReference nodes with either SlotName, Variable, or FunctionPointer nodes.
class ReferenceResolvingPass : public GenericASTPass {
public:
using GenericASTPass::GenericASTPass;
protected:
RecursionDecision on_entry(Tree tree) override;
void on_leave(Tree tree) override;
};
}

View file

@ -12,6 +12,8 @@ namespace JSSpecCompiler {
// AST/AST.h
class NodeSubtreePointer;
class VariableDeclaration;
using VariableDeclarationRef = NonnullRefPtr<VariableDeclaration>;
class Node;
using NullableTree = RefPtr<Node>;

View file

@ -13,6 +13,7 @@ Function::Function(ExecutionContext* context, StringView name, Tree ast)
: m_context(context)
, m_name(name)
, m_ast(move(ast))
, m_return_value(make_ref_counted<VariableDeclaration>("$return"sv))
{
}

View file

@ -27,6 +27,8 @@ public:
ExecutionContext* m_context;
StringView m_name;
Tree m_ast;
VariableDeclarationRef m_return_value;
HashMap<StringView, VariableDeclarationRef> m_local_variables;
};
}

View file

@ -11,6 +11,7 @@
#include "Compiler/FunctionCallCanonicalizationPass.h"
#include "Compiler/IfBranchMergingPass.h"
#include "Compiler/ReferenceResolvingPass.h"
#include "Function.h"
#include "Parser/SpecParser.h"
@ -20,6 +21,18 @@ ErrorOr<int> serenity_main(Main::Arguments)
ExecutionContext context;
// Functions referenced in DifferenceISODate
// TODO: This is here just for testing. In a long run, we need some place, which is not
// `serenity_main`, to store built-in functions.
auto& functions = context.m_functions;
functions.set("CompareISODate"sv, make_ref_counted<FunctionPointer>("CompareISODate"sv));
functions.set("CreateDateDurationRecord"sv, make_ref_counted<FunctionPointer>("CreateDateDurationRecord"sv));
functions.set("AddISODate"sv, make_ref_counted<FunctionPointer>("AddISODate"sv));
functions.set("ISODaysInMonth"sv, make_ref_counted<FunctionPointer>("ISODaysInMonth"sv));
functions.set("ISODateToEpochDays"sv, make_ref_counted<FunctionPointer>("ISODateToEpochDays"sv));
functions.set("truncate"sv, make_ref_counted<FunctionPointer>("truncate"sv));
functions.set("remainder"sv, make_ref_counted<FunctionPointer>("remainder"sv));
auto input = TRY(TRY(Core::File::standard_input())->read_until_eof());
XML::Parser parser { StringView(input.bytes()) };
@ -39,8 +52,12 @@ ErrorOr<int> serenity_main(Main::Arguments)
auto function = make_ref_counted<JSSpecCompiler::Function>(&context, spec_function.m_name, spec_function.m_algorithm.m_tree);
for (auto const& argument : spec_function.m_arguments)
function->m_local_variables.set(argument.name, make_ref_counted<VariableDeclaration>(argument.name));
FunctionCallCanonicalizationPass(function).run();
IfBranchMergingPass(function).run();
ReferenceResolvingPass(function).run();
out("{}", function->m_ast);
return 0;