mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 03:57:43 +00:00
JSSpecCompiler: Provide an adequate command line interface
This commit is contained in:
parent
867ce0df52
commit
6ed069ea8d
10 changed files with 226 additions and 33 deletions
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/Function.h>
|
||||||
|
#include <AK/StringView.h>
|
||||||
|
|
||||||
|
#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<typename Func>
|
||||||
|
NonOwningCompilationStep(StringView name, Func&& func)
|
||||||
|
: CompilationStep(name)
|
||||||
|
, m_func(func)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(TranslationUnitRef translation_unit) override { m_func(translation_unit); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
AK::Function<void(TranslationUnitRef)> m_func;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -17,6 +17,8 @@ namespace JSSpecCompiler {
|
||||||
// `f(a, b, c, d)` as `f "function_call_operator" (a, (b, (c, d))))`.
|
// `f(a, b, c, d)` as `f "function_call_operator" (a, (b, (c, d))))`.
|
||||||
class FunctionCallCanonicalizationPass : public GenericASTPass {
|
class FunctionCallCanonicalizationPass : public GenericASTPass {
|
||||||
public:
|
public:
|
||||||
|
inline static constexpr StringView name = "function-call-canonicalization"sv;
|
||||||
|
|
||||||
using GenericASTPass::GenericASTPass;
|
using GenericASTPass::GenericASTPass;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -24,6 +24,8 @@ namespace JSSpecCompiler {
|
||||||
// ```
|
// ```
|
||||||
class IfBranchMergingPass : public GenericASTPass {
|
class IfBranchMergingPass : public GenericASTPass {
|
||||||
public:
|
public:
|
||||||
|
inline static constexpr StringView name = "if-branch-merging"sv;
|
||||||
|
|
||||||
using GenericASTPass::GenericASTPass;
|
using GenericASTPass::GenericASTPass;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -14,6 +14,8 @@ namespace JSSpecCompiler {
|
||||||
// UnresolvedReference nodes with either SlotName, Variable, or FunctionPointer nodes.
|
// UnresolvedReference nodes with either SlotName, Variable, or FunctionPointer nodes.
|
||||||
class ReferenceResolvingPass : public GenericASTPass {
|
class ReferenceResolvingPass : public GenericASTPass {
|
||||||
public:
|
public:
|
||||||
|
inline static constexpr StringView name = "reference-resolving"sv;
|
||||||
|
|
||||||
using GenericASTPass::GenericASTPass;
|
using GenericASTPass::GenericASTPass;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace JSSpecCompiler {
|
||||||
struct TranslationUnit {
|
struct TranslationUnit {
|
||||||
FunctionDefinitionRef adopt_function(NonnullRefPtr<FunctionDefinition>&& function);
|
FunctionDefinitionRef adopt_function(NonnullRefPtr<FunctionDefinition>&& function);
|
||||||
|
|
||||||
|
StringView filename;
|
||||||
Vector<NonnullRefPtr<FunctionDefinition>> function_definitions;
|
Vector<NonnullRefPtr<FunctionDefinition>> function_definitions;
|
||||||
HashMap<StringView, FunctionPointerRef> function_index;
|
HashMap<StringView, FunctionPointerRef> function_index;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Parser/CppASTConverter.h"
|
#include <LibCore/File.h>
|
||||||
|
|
||||||
#include "Function.h"
|
#include "Function.h"
|
||||||
|
#include "Parser/CppASTConverter.h"
|
||||||
#include "Parser/SpecParser.h"
|
#include "Parser/SpecParser.h"
|
||||||
|
|
||||||
namespace JSSpecCompiler {
|
namespace JSSpecCompiler {
|
||||||
|
@ -219,4 +221,32 @@ Tree CppASTConverter::as_possibly_empty_tree(Cpp::Statement const* statement)
|
||||||
return make_ref_counted<TreeList>(Vector<Tree> {});
|
return make_ref_counted<TreeList>(Vector<Tree> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<Cpp::FunctionDeclaration>(declaration.ptr());
|
||||||
|
translation_unit->adopt_function(CppASTConverter(cpp_function).convert());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/OwnPtr.h>
|
||||||
#include <LibCpp/AST.h>
|
#include <LibCpp/AST.h>
|
||||||
|
#include <LibCpp/Parser.h>
|
||||||
|
|
||||||
#include "Forward.h"
|
#include "CompilationPipeline.h"
|
||||||
|
|
||||||
namespace JSSpecCompiler {
|
namespace JSSpecCompiler {
|
||||||
|
|
||||||
|
@ -31,4 +33,16 @@ private:
|
||||||
RefPtr<Cpp::FunctionDeclaration> m_function;
|
RefPtr<Cpp::FunctionDeclaration> m_function;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CppParsingStep : public CompilationStep {
|
||||||
|
public:
|
||||||
|
CppParsingStep();
|
||||||
|
~CppParsingStep();
|
||||||
|
|
||||||
|
void run(TranslationUnitRef translation_unit) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
OwnPtr<Cpp::Parser> m_parser;
|
||||||
|
ByteBuffer m_input;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/NonnullOwnPtr.h>
|
#include <AK/NonnullOwnPtr.h>
|
||||||
|
#include <LibCore/File.h>
|
||||||
|
#include <LibXML/Parser/Parser.h>
|
||||||
|
|
||||||
|
#include "Function.h"
|
||||||
#include "Parser/Lexer.h"
|
#include "Parser/Lexer.h"
|
||||||
#include "Parser/SpecParser.h"
|
#include "Parser/SpecParser.h"
|
||||||
#include "Parser/TextParser.h"
|
#include "Parser/TextParser.h"
|
||||||
|
@ -173,4 +176,31 @@ ParseErrorOr<void> SpecFunction::parse_definition(XML::Node const* element)
|
||||||
return {};
|
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<FunctionDefinition>(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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/OwnPtr.h>
|
||||||
|
|
||||||
#include "AST/AST.h"
|
#include "AST/AST.h"
|
||||||
|
#include "CompilationPipeline.h"
|
||||||
#include "Forward.h"
|
#include "Forward.h"
|
||||||
#include "Parser/ParseError.h"
|
#include "Parser/ParseError.h"
|
||||||
#include "Parser/Token.h"
|
#include "Parser/Token.h"
|
||||||
|
@ -58,4 +61,16 @@ public:
|
||||||
Algorithm m_algorithm;
|
Algorithm m_algorithm;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SpecParsingStep : public CompilationStep {
|
||||||
|
public:
|
||||||
|
SpecParsingStep();
|
||||||
|
~SpecParsingStep();
|
||||||
|
|
||||||
|
void run(TranslationUnitRef translation_unit) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
OwnPtr<XML::Document> m_document;
|
||||||
|
ByteBuffer m_input;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,21 +5,96 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Format.h>
|
#include <AK/Format.h>
|
||||||
#include <LibCore/File.h>
|
#include <LibCore/ArgsParser.h>
|
||||||
#include <LibMain/Main.h>
|
#include <LibMain/Main.h>
|
||||||
#include <LibXML/Parser/Parser.h>
|
|
||||||
|
|
||||||
#include "Compiler/Passes/FunctionCallCanonicalizationPass.h"
|
#include "Compiler/Passes/FunctionCallCanonicalizationPass.h"
|
||||||
#include "Compiler/Passes/IfBranchMergingPass.h"
|
#include "Compiler/Passes/IfBranchMergingPass.h"
|
||||||
#include "Compiler/Passes/ReferenceResolvingPass.h"
|
#include "Compiler/Passes/ReferenceResolvingPass.h"
|
||||||
#include "Function.h"
|
#include "Function.h"
|
||||||
|
#include "Parser/CppASTConverter.h"
|
||||||
#include "Parser/SpecParser.h"
|
#include "Parser/SpecParser.h"
|
||||||
|
|
||||||
ErrorOr<int> serenity_main(Main::Arguments)
|
using namespace JSSpecCompiler;
|
||||||
|
|
||||||
|
class CompilationPipeline {
|
||||||
|
public:
|
||||||
|
template<typename T>
|
||||||
|
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<typename T>
|
||||||
|
void for_each_step_in(StringView pass_list, T&& func)
|
||||||
|
{
|
||||||
|
HashTable<StringView> 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<CompilationStep>&& step)
|
||||||
|
{
|
||||||
|
m_pipeline.append(move(step));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto const& pipeline() const { return m_pipeline; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector<OwnPtr<CompilationStep>> m_pipeline;
|
||||||
|
};
|
||||||
|
|
||||||
|
ErrorOr<int> 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<FunctionCallCanonicalizationPass>();
|
||||||
|
pipeline.add_compilation_pass<IfBranchMergingPass>();
|
||||||
|
pipeline.add_compilation_pass<ReferenceResolvingPass>();
|
||||||
|
|
||||||
TranslationUnit translation_unit;
|
TranslationUnit translation_unit;
|
||||||
|
translation_unit.filename = filename;
|
||||||
|
|
||||||
// Functions referenced in DifferenceISODate
|
// Functions referenced in DifferenceISODate
|
||||||
// TODO: This is here just for testing. In a long run, we need some place, which is not
|
// TODO: This is here just for testing. In a long run, we need some place, which is not
|
||||||
|
@ -33,33 +108,8 @@ ErrorOr<int> serenity_main(Main::Arguments)
|
||||||
functions.set("truncate"sv, make_ref_counted<FunctionPointer>("truncate"sv));
|
functions.set("truncate"sv, make_ref_counted<FunctionPointer>("truncate"sv));
|
||||||
functions.set("remainder"sv, make_ref_counted<FunctionPointer>("remainder"sv));
|
functions.set("remainder"sv, make_ref_counted<FunctionPointer>("remainder"sv));
|
||||||
|
|
||||||
auto input = TRY(TRY(Core::File::standard_input())->read_until_eof());
|
for (auto const& step : pipeline.pipeline())
|
||||||
XML::Parser parser { StringView(input.bytes()) };
|
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<FunctionDefinition>(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(&translation_unit).run();
|
|
||||||
IfBranchMergingPass(&translation_unit).run();
|
|
||||||
ReferenceResolvingPass(&translation_unit).run();
|
|
||||||
|
|
||||||
out("{}", function->m_ast);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue