From 81519975c578ef9a37891db56f057c9a0342919f Mon Sep 17 00:00:00 2001 From: Dan Klishch Date: Sat, 19 Aug 2023 23:17:06 -0400 Subject: [PATCH] JSSpecCompiler: Add reference resolving pass It replaces UnresolvedReference with Variable, FunctionPointer, or SlotName nodes. Also, it gathers all variable names from their declarations. --- .../CodeGenerators/JSSpecCompiler/AST/AST.h | 16 ++++-- .../JSSpecCompiler/AST/ASTPrinting.cpp | 2 +- .../JSSpecCompiler/CMakeLists.txt | 1 + .../Compiler/ReferenceResolvingPass.cpp | 54 +++++++++++++++++++ .../Compiler/ReferenceResolvingPass.h | 25 +++++++++ .../CodeGenerators/JSSpecCompiler/Forward.h | 2 + .../JSSpecCompiler/Function.cpp | 1 + .../CodeGenerators/JSSpecCompiler/Function.h | 2 + .../CodeGenerators/JSSpecCompiler/main.cpp | 17 ++++++ 9 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/ReferenceResolvingPass.cpp create mode 100644 Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/ReferenceResolvingPass.h diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.h index 4a0ed558c41..a4111326b6c 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.h @@ -40,6 +40,16 @@ private: Variant m_tree_ptr; }; +class VariableDeclaration : public RefCounted { +public: + VariableDeclaration(StringView name) + : m_name(name) + { + } + + StringView m_name; +}; + class Node : public RefCounted { 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; diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/ASTPrinting.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/ASTPrinting.cpp index fe42a7c294b..bf324563c6a 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/ASTPrinting.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/ASTPrinting.cpp @@ -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) diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt index 5073b9d7d7e..6c823e33bff 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt @@ -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 diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/ReferenceResolvingPass.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/ReferenceResolvingPass.cpp new file mode 100644 index 00000000000..7f6954b99e8 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/ReferenceResolvingPass.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include "AST/AST.h" +#include "Compiler/ReferenceResolvingPass.h" +#include "Function.h" + +namespace JSSpecCompiler { + +RecursionDecision ReferenceResolvingPass::on_entry(Tree tree) +{ + if (auto binary_operation = as(tree); binary_operation) { + if (binary_operation->m_operation != BinaryOperator::Declaration) + return RecursionDecision::Recurse; + + if (auto variable_name = as(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(name)); + } + } + return RecursionDecision::Recurse; +} + +void ReferenceResolvingPass::on_leave(Tree tree) +{ + auto& functions = m_function->m_context->m_functions; + + if (auto reference = as(tree); reference) { + auto name = reference->m_name; + + if (name.starts_with("[["sv) && name.ends_with("]]"sv)) { + replace_current_node_with(make_ref_counted(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(it->value)); + return; + } + + if (auto it = functions.find(name); it != functions.end()) { + replace_current_node_with(it->value); + return; + } + } +} + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/ReferenceResolvingPass.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/ReferenceResolvingPass.h new file mode 100644 index 00000000000..3ba5e931763 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/ReferenceResolvingPass.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023, Dan Klishch + * + * 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; +}; + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Forward.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Forward.h index 56df2fa3a3f..b56b6699e18 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Forward.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Forward.h @@ -12,6 +12,8 @@ namespace JSSpecCompiler { // AST/AST.h class NodeSubtreePointer; +class VariableDeclaration; +using VariableDeclarationRef = NonnullRefPtr; class Node; using NullableTree = RefPtr; diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.cpp index 5694eebf30c..aed1882c2a3 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.cpp @@ -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("$return"sv)) { } diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h index 470dfbbecc1..85ee1cdd83b 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h @@ -27,6 +27,8 @@ public: ExecutionContext* m_context; StringView m_name; Tree m_ast; + VariableDeclarationRef m_return_value; + HashMap m_local_variables; }; } diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp index c6e83f5b675..d057bc9e15f 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp @@ -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 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("CompareISODate"sv)); + functions.set("CreateDateDurationRecord"sv, make_ref_counted("CreateDateDurationRecord"sv)); + functions.set("AddISODate"sv, make_ref_counted("AddISODate"sv)); + functions.set("ISODaysInMonth"sv, make_ref_counted("ISODaysInMonth"sv)); + functions.set("ISODateToEpochDays"sv, make_ref_counted("ISODateToEpochDays"sv)); + functions.set("truncate"sv, make_ref_counted("truncate"sv)); + functions.set("remainder"sv, make_ref_counted("remainder"sv)); + auto input = TRY(TRY(Core::File::standard_input())->read_until_eof()); XML::Parser parser { StringView(input.bytes()) }; @@ -39,8 +52,12 @@ ErrorOr serenity_main(Main::Arguments) auto function = make_ref_counted(&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(argument.name)); + FunctionCallCanonicalizationPass(function).run(); IfBranchMergingPass(function).run(); + ReferenceResolvingPass(function).run(); out("{}", function->m_ast); return 0;