diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.cpp new file mode 100644 index 0000000000..18e559dbb8 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "AST/AST.h" + +namespace JSSpecCompiler { + +Vector BinaryOperation::subtrees() +{ + return { { &m_left }, { &m_right } }; +} + +Vector UnaryOperation::subtrees() +{ + return { { &m_operand } }; +} + +Vector IsOneOfOperation::subtrees() +{ + Vector result = { { &m_operand } }; + for (auto& child : m_compare_values) + result.append({ &child }); + return result; +} + +Vector ReturnExpression::subtrees() +{ + return { { &m_return_value } }; +} + +Vector AssertExpression::subtrees() +{ + return { { &m_condition } }; +} + +Vector IfBranch::subtrees() +{ + return { { &m_condition }, { &m_branch } }; +} + +Vector ElseIfBranch::subtrees() +{ + if (m_condition.has_value()) + return { { &m_condition.value() }, { &m_branch } }; + return { { &m_branch } }; +} + +Vector TreeList::subtrees() +{ + Vector result; + for (auto& expression : m_expressions) + result.append({ &expression }); + return result; +} + +Vector RecordDirectListInitialization::subtrees() +{ + Vector result { &m_type_reference }; + for (auto& argument : m_arguments) { + result.append({ &argument.name }); + result.append({ &argument.value }); + } + return result; +} + +Vector FunctionCall::subtrees() +{ + Vector result = { { &m_name } }; + for (auto& child : m_arguments) + result.append({ &child }); + return result; +} + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.h index c6bde40165..27841c64ac 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/AST.h @@ -20,6 +20,21 @@ RefPtr as(NullableTree const& tree) return dynamic_cast(tree.ptr()); } +class NodeSubtreePointer { +public: + NodeSubtreePointer(Tree* tree_ptr) + : m_tree_ptr(tree_ptr) + { + } + + Tree& get() { return *m_tree_ptr; } + + void replace_subtree(Tree tree) { *m_tree_ptr = move(tree); } + +private: + Tree* m_tree_ptr; +}; + // ===== Generic nodes ===== class Node : public RefCounted { public: @@ -27,6 +42,9 @@ public: void format_tree(StringBuilder& builder); + // For expressions, order must be the same as the evaluation order. + virtual Vector subtrees() { return {}; } + virtual bool is_type() { return false; } protected: @@ -128,6 +146,8 @@ public: { } + Vector subtrees() override; + BinaryOperator m_operation; Tree m_left; Tree m_right; @@ -144,6 +164,8 @@ public: { } + Vector subtrees() override; + UnaryOperator m_operation; Tree m_operand; @@ -159,6 +181,8 @@ public: { } + Vector subtrees() override; + Tree m_operand; Vector m_compare_values; @@ -186,6 +210,8 @@ public: { } + Vector subtrees() override; + Tree m_return_value; protected: @@ -199,6 +225,8 @@ public: { } + Vector subtrees() override; + Tree m_condition; protected: @@ -213,6 +241,8 @@ public: { } + Vector subtrees() override; + Tree m_condition; Tree m_branch; @@ -228,6 +258,8 @@ public: { } + Vector subtrees() override; + Optional m_condition; Tree m_branch; @@ -242,6 +274,8 @@ public: { } + Vector subtrees() override; + Vector m_expressions; protected: @@ -261,6 +295,8 @@ public: { } + Vector subtrees() override; + Tree m_type_reference; Vector m_arguments; @@ -276,6 +312,8 @@ public: { } + Vector subtrees() override; + Tree m_name; Vector m_arguments; diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt index 11a4b565d8..5c02c2559f 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt @@ -1,5 +1,7 @@ set(SOURCES + AST/AST.cpp AST/ASTPrinting.cpp + Compiler/GenericASTPass.cpp Parser/Lexer.cpp Parser/ParseError.cpp Parser/SpecParser.cpp diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/CompilerPass.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/CompilerPass.h new file mode 100644 index 0000000000..088f4393d3 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/CompilerPass.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +#include "Forward.h" + +namespace JSSpecCompiler { + +class CompilerPass { +public: + CompilerPass(FunctionRef function) + : m_function(function) + { + } + + virtual ~CompilerPass() = default; + + virtual void run() = 0; + +protected: + FunctionRef m_function; +}; + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/GenericASTPass.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/GenericASTPass.cpp new file mode 100644 index 0000000000..aae5168488 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/GenericASTPass.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Compiler/GenericASTPass.h" +#include "AST/AST.h" +#include "Function.h" + +namespace JSSpecCompiler { + +void RecursiveASTVisitor::run_in_subtree(Tree& tree) +{ + NodeSubtreePointer pointer { &tree }; + recurse(tree, pointer); +} + +void RecursiveASTVisitor::replace_current_node_with(Tree tree) +{ + m_current_subtree_pointer->replace_subtree(move(tree)); +} + +RecursionDecision RecursiveASTVisitor::recurse(Tree root, NodeSubtreePointer& pointer) +{ + RecursionDecision decision; + + m_current_subtree_pointer = &pointer; + decision = on_entry(root); + + if (decision == RecursionDecision::Recurse) { + for (auto& child : root->subtrees()) { + if (recurse(child.get(), child) == RecursionDecision::Break) + return RecursionDecision::Break; + } + } + + m_current_subtree_pointer = &pointer; + on_leave(root); + + return RecursionDecision::Continue; +} + +void GenericASTPass::run() +{ + run_in_subtree(m_function->m_ast); +} + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/GenericASTPass.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/GenericASTPass.h new file mode 100644 index 0000000000..6d82fc3e94 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/GenericASTPass.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +#include "Compiler/CompilerPass.h" + +namespace JSSpecCompiler { + +class RecursiveASTVisitor { +public: + virtual ~RecursiveASTVisitor() = default; + + void run_in_subtree(Tree& tree); + +protected: + virtual RecursionDecision on_entry(Tree) { return RecursionDecision::Recurse; } + virtual void on_leave(Tree) { } + + void replace_current_node_with(Tree tree); + +private: + RecursionDecision recurse(Tree root, NodeSubtreePointer& pointer); + + NodeSubtreePointer* m_current_subtree_pointer = nullptr; +}; + +class GenericASTPass + : public CompilerPass + , protected RecursiveASTVisitor { +public: + GenericASTPass(FunctionRef function) + : CompilerPass(function) + { + } + + void run() override; +}; + +}