diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CompilationPipeline.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CompilationPipeline.h new file mode 100644 index 0000000000..dd25914582 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CompilationPipeline.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023, Dan Klishch + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +#include "Forward.h" + +namespace JSSpecCompiler { + +class CompilationStep { +public: + CompilationStep(StringView name) + : m_name(name) + { + } + + virtual ~CompilationStep() = default; + virtual void run(TranslationUnitRef translation_unit) = 0; + + StringView name() const { return m_name; } + +private: + StringView m_name; +}; + +class NonOwningCompilationStep : public CompilationStep { +public: + template + NonOwningCompilationStep(StringView name, Func&& func) + : CompilationStep(name) + , m_func(func) + { + } + + void run(TranslationUnitRef translation_unit) override { m_func(translation_unit); } + +private: + AK::Function m_func; +}; + +} diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/FunctionCallCanonicalizationPass.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/FunctionCallCanonicalizationPass.h index 32e12f748e..8f1079d71c 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/FunctionCallCanonicalizationPass.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/FunctionCallCanonicalizationPass.h @@ -17,6 +17,8 @@ namespace JSSpecCompiler { // `f(a, b, c, d)` as `f "function_call_operator" (a, (b, (c, d))))`. class FunctionCallCanonicalizationPass : public GenericASTPass { public: + inline static constexpr StringView name = "function-call-canonicalization"sv; + using GenericASTPass::GenericASTPass; protected: diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/IfBranchMergingPass.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/IfBranchMergingPass.h index 69610b9301..d61c85eefe 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/IfBranchMergingPass.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/IfBranchMergingPass.h @@ -24,6 +24,8 @@ namespace JSSpecCompiler { // ``` class IfBranchMergingPass : public GenericASTPass { public: + inline static constexpr StringView name = "if-branch-merging"sv; + using GenericASTPass::GenericASTPass; protected: diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/ReferenceResolvingPass.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/ReferenceResolvingPass.h index 3ba5e93176..316c41da54 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/ReferenceResolvingPass.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/ReferenceResolvingPass.h @@ -14,6 +14,8 @@ namespace JSSpecCompiler { // UnresolvedReference nodes with either SlotName, Variable, or FunctionPointer nodes. class ReferenceResolvingPass : public GenericASTPass { public: + inline static constexpr StringView name = "reference-resolving"sv; + using GenericASTPass::GenericASTPass; protected: diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h index bd34554eb8..ea8472b12b 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h @@ -18,6 +18,7 @@ namespace JSSpecCompiler { struct TranslationUnit { FunctionDefinitionRef adopt_function(NonnullRefPtr&& function); + StringView filename; Vector> function_definitions; HashMap function_index; }; diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/CppASTConverter.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/CppASTConverter.cpp index b116a307ba..032e5ba0f8 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/CppASTConverter.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/CppASTConverter.cpp @@ -4,8 +4,10 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include "Parser/CppASTConverter.h" +#include + #include "Function.h" +#include "Parser/CppASTConverter.h" #include "Parser/SpecParser.h" namespace JSSpecCompiler { @@ -219,4 +221,32 @@ Tree CppASTConverter::as_possibly_empty_tree(Cpp::Statement const* statement) return make_ref_counted(Vector {}); } +CppParsingStep::CppParsingStep() + : CompilationStep("parser"sv) +{ +} + +CppParsingStep::~CppParsingStep() = default; + +void CppParsingStep::run(TranslationUnitRef translation_unit) +{ + auto filename = translation_unit->filename; + + auto file = Core::File::open_file_or_standard_stream(filename, Core::File::OpenMode::Read).release_value_but_fixme_should_propagate_errors(); + m_input = file->read_until_eof().release_value_but_fixme_should_propagate_errors(); + + Cpp::Preprocessor preprocessor { filename, m_input }; + m_parser = adopt_own_if_nonnull(new Cpp::Parser { preprocessor.process_and_lex(), filename }); + + auto cpp_translation_unit = m_parser->parse(); + VERIFY(m_parser->errors().is_empty()); + + for (auto const& declaration : cpp_translation_unit->declarations()) { + if (declaration->is_function()) { + auto const* cpp_function = AK::verify_cast(declaration.ptr()); + translation_unit->adopt_function(CppASTConverter(cpp_function).convert()); + } + } +} + } diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/CppASTConverter.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/CppASTConverter.h index b1e0be5aea..65aea91ef2 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/CppASTConverter.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/CppASTConverter.h @@ -6,9 +6,11 @@ #pragma once +#include #include +#include -#include "Forward.h" +#include "CompilationPipeline.h" namespace JSSpecCompiler { @@ -31,4 +33,16 @@ private: RefPtr m_function; }; +class CppParsingStep : public CompilationStep { +public: + CppParsingStep(); + ~CppParsingStep(); + + void run(TranslationUnitRef translation_unit) override; + +private: + OwnPtr m_parser; + ByteBuffer m_input; +}; + } diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecParser.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecParser.cpp index 9c888470ec..357e230537 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecParser.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecParser.cpp @@ -5,7 +5,10 @@ */ #include +#include +#include +#include "Function.h" #include "Parser/Lexer.h" #include "Parser/SpecParser.h" #include "Parser/TextParser.h" @@ -173,4 +176,31 @@ ParseErrorOr SpecFunction::parse_definition(XML::Node const* element) return {}; } +SpecParsingStep::SpecParsingStep() + : CompilationStep("parser"sv) +{ +} + +SpecParsingStep::~SpecParsingStep() = default; + +void SpecParsingStep::run(TranslationUnitRef translation_unit) +{ + auto filename = translation_unit->filename; + + auto file = Core::File::open_file_or_standard_stream(filename, Core::File::OpenMode::Read).release_value_but_fixme_should_propagate_errors(); + m_input = file->read_until_eof().release_value_but_fixme_should_propagate_errors(); + + XML::Parser parser { m_input }; + auto document = parser.parse().release_value_but_fixme_should_propagate_errors(); + m_document = AK::adopt_own_if_nonnull(new XML::Document(move(document))); + + auto spec_function = SpecFunction::create(&m_document->root()).release_value_but_fixme_should_propagate_errors(); + + auto* function = translation_unit->adopt_function( + make_ref_counted(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)); +} + } diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecParser.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecParser.h index 72ba05ddc4..4f00e10302 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecParser.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecParser.h @@ -6,7 +6,10 @@ #pragma once +#include + #include "AST/AST.h" +#include "CompilationPipeline.h" #include "Forward.h" #include "Parser/ParseError.h" #include "Parser/Token.h" @@ -58,4 +61,16 @@ public: Algorithm m_algorithm; }; +class SpecParsingStep : public CompilationStep { +public: + SpecParsingStep(); + ~SpecParsingStep(); + + void run(TranslationUnitRef translation_unit) override; + +private: + OwnPtr m_document; + ByteBuffer m_input; +}; + } diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp index 9e9432f2c2..4bf274143c 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp @@ -5,21 +5,96 @@ */ #include -#include +#include #include -#include #include "Compiler/Passes/FunctionCallCanonicalizationPass.h" #include "Compiler/Passes/IfBranchMergingPass.h" #include "Compiler/Passes/ReferenceResolvingPass.h" #include "Function.h" +#include "Parser/CppASTConverter.h" #include "Parser/SpecParser.h" -ErrorOr serenity_main(Main::Arguments) +using namespace JSSpecCompiler; + +class CompilationPipeline { +public: + template + void add_compilation_pass() + { + auto func = +[](TranslationUnitRef translation_unit) { + T { translation_unit }.run(); + }; + add_step(adopt_own_if_nonnull(new NonOwningCompilationStep(T::name, func))); + } + + template + void for_each_step_in(StringView pass_list, T&& func) + { + HashTable selected_steps; + for (auto pass : pass_list.split_view(',')) { + if (pass == "all") { + for (auto const& step : m_pipeline) + selected_steps.set(step->name()); + } else if (pass == "last") { + selected_steps.set(m_pipeline.last()->name()); + } else if (pass.starts_with('-')) { + VERIFY(selected_steps.remove(pass.substring_view(1))); + } else { + selected_steps.set(pass); + } + } + + for (auto& step : m_pipeline) + if (selected_steps.contains(step->name())) + func(step); + } + + void add_step(OwnPtr&& step) + { + m_pipeline.append(move(step)); + } + + auto const& pipeline() const { return m_pipeline; } + +private: + Vector> m_pipeline; +}; + +ErrorOr serenity_main(Main::Arguments arguments) { - using namespace JSSpecCompiler; + Core::ArgsParser args_parser; + + StringView filename; + args_parser.add_positional_argument(filename, "File to compile", "file"); + + constexpr StringView language_spec = "spec"sv; + constexpr StringView language_cpp = "c++"sv; + StringView language = language_spec; + args_parser.add_option(Core::ArgsParser::Option { + .argument_mode = Core::ArgsParser::OptionArgumentMode::Optional, + .help_string = "Specify the language of the input file.", + .short_name = 'x', + .value_name = "{c++|spec}", + .accept_value = [&](StringView value) { + language = value; + return language.is_one_of(language_spec, language_cpp); + }, + }); + + args_parser.parse(arguments); + + CompilationPipeline pipeline; + if (language == language_cpp) + pipeline.add_step(adopt_own_if_nonnull(new CppParsingStep())); + else + pipeline.add_step(adopt_own_if_nonnull(new SpecParsingStep())); + pipeline.add_compilation_pass(); + pipeline.add_compilation_pass(); + pipeline.add_compilation_pass(); TranslationUnit translation_unit; + translation_unit.filename = filename; // Functions referenced in DifferenceISODate // TODO: This is here just for testing. In a long run, we need some place, which is not @@ -33,33 +108,8 @@ ErrorOr serenity_main(Main::Arguments) 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()) }; + for (auto const& step : pipeline.pipeline()) + step->run(&translation_unit); - auto maybe_document = parser.parse(); - if (maybe_document.is_error()) { - outln("{}", maybe_document.error()); - return 1; - } - auto document = maybe_document.release_value(); - - auto maybe_function = JSSpecCompiler::SpecFunction::create(&document.root()); - if (maybe_function.is_error()) { - outln("{}", maybe_function.error()->to_string()); - return 1; - } - auto spec_function = maybe_function.value(); - - auto* function = translation_unit.adopt_function( - make_ref_counted(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(&translation_unit).run(); - IfBranchMergingPass(&translation_unit).run(); - ReferenceResolvingPass(&translation_unit).run(); - - out("{}", function->m_ast); return 0; }