From 0e7c33b1be3a99b1b254e28cd5e0dedc17ba8532 Mon Sep 17 00:00:00 2001 From: Dan Klishch Date: Mon, 11 Mar 2024 15:51:14 -0400 Subject: [PATCH] JSSpecCompiler: Let FunctionDeclaration know its name and category This is achieved by moving ClauseHeader::{AbstractOperation,Accessor, Method} to Function.h itself and storing them in FunctionDeclaration. This commit also introduces QualifiedName class that is used to store function's name split by '.' (just like it appear in the spec). --- .../JSSpecCompiler/AST/ASTPrinting.cpp | 2 +- .../Passes/ReferenceResolvingPass.cpp | 4 +- .../Compiler/Passes/SSABuildingPass.cpp | 7 +- .../JSSpecCompiler/Function.cpp | 51 +++++++--- .../CodeGenerators/JSSpecCompiler/Function.h | 92 +++++++++++++++++-- .../JSSpecCompiler/Parser/CppASTConverter.cpp | 8 +- .../Parser/SpecificationClause.cpp | 2 +- .../Parser/SpecificationFunction.cpp | 28 +++--- .../Parser/SpecificationParsing.h | 5 +- .../JSSpecCompiler/Parser/TextParser.cpp | 24 ++--- .../JSSpecCompiler/Parser/TextParser.h | 24 +---- .../CodeGenerators/JSSpecCompiler/main.cpp | 19 +--- 12 files changed, 170 insertions(+), 96 deletions(-) diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/ASTPrinting.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/ASTPrinting.cpp index 6bdd67f467e..00e214770c3 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/ASTPrinting.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/AST/ASTPrinting.cpp @@ -185,7 +185,7 @@ void Enumerator::dump_tree(StringBuilder& builder) void FunctionPointer::dump_tree(StringBuilder& builder) { - dump_node(builder, "Func \"{}\"", m_declaration->m_name); + dump_node(builder, "Func \"{}\"", m_declaration->name()); } void List::dump_tree(StringBuilder& builder) diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/ReferenceResolvingPass.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/ReferenceResolvingPass.cpp index 231b49b8bed..faf2f756e03 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/ReferenceResolvingPass.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/ReferenceResolvingPass.cpp @@ -14,7 +14,7 @@ namespace JSSpecCompiler { void ReferenceResolvingPass::process_function() { - for (auto argument : m_function->m_arguments) + for (auto argument : m_function->arguments()) m_function->m_local_variables.set(argument.name, make_ref_counted(argument.name)); GenericASTPass::process_function(); } @@ -51,7 +51,7 @@ void ReferenceResolvingPass::on_leave(Tree tree) return; } - if (auto function = m_translation_unit->find_declaration_by_name(name)) { + if (auto function = m_translation_unit->find_abstract_operation_by_name(name)) { replace_current_node_with(make_ref_counted(function)); return; } diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/SSABuildingPass.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/SSABuildingPass.cpp index 84a31af2594..48a55d24d34 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/SSABuildingPass.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Compiler/Passes/SSABuildingPass.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include "AST/AST.h" @@ -430,9 +431,9 @@ void SSABuildingPass::rename_variables(Vertex u, Vertex from) void SSABuildingPass::rename_variables() { HashMap argument_index_by_name; - for (size_t i = 0; i < m_function->m_arguments.size(); ++i) - argument_index_by_name.set(m_function->m_arguments[i].name, i); - m_function->m_ssa_arguments.resize(m_function->m_arguments.size()); + for (auto [i, argument] : enumerate(m_function->arguments())) + argument_index_by_name.set(argument.name, i); + m_function->m_ssa_arguments.resize(m_function->arguments().size()); for (auto const& [name, var_decl] : m_function->m_local_variables) { make_new_ssa_variable_for(var_decl); diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.cpp index 23b89e0b280..5917a8bb483 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.cpp @@ -19,23 +19,21 @@ TranslationUnit::~TranslationUnit() = default; void TranslationUnit::adopt_declaration(NonnullRefPtr&& declaration) { - declaration->m_translation_unit = this; - m_function_index.set(declaration->m_name, declaration.ptr()); + if (auto decl_name = declaration->declaration(); decl_name.has()) + m_abstract_operation_index.set(decl_name.get().name, declaration.ptr()); m_declarations_owner.append(move(declaration)); } -FunctionDefinitionRef TranslationUnit::adopt_function(NonnullRefPtr&& definition) +void TranslationUnit::adopt_function(NonnullRefPtr&& definition) { - FunctionDefinitionRef result = definition.ptr(); - m_functions_to_compile.append(result); + m_functions_to_compile.append(definition); adopt_declaration(definition); - return result; } -FunctionDeclarationRef TranslationUnit::find_declaration_by_name(StringView name) const +FunctionDeclarationRef TranslationUnit::find_abstract_operation_by_name(StringView name) const { - auto it = m_function_index.find(name); - if (it == m_function_index.end()) + auto it = m_abstract_operation_index.find(name); + if (it == m_abstract_operation_index.end()) return nullptr; return it->value; } @@ -50,14 +48,39 @@ EnumeratorRef TranslationUnit::get_node_for_enumerator_value(StringView value) return enumerator; } -FunctionDeclaration::FunctionDeclaration(StringView name, Vector&& arguments) - : m_name(name) - , m_arguments(arguments) +FunctionDeclaration::FunctionDeclaration(Declaration&& declaration, Location location) + : m_declaration(move(declaration)) + , m_location(location) { } -FunctionDefinition::FunctionDefinition(StringView name, Tree ast, Vector&& arguments) - : FunctionDeclaration(name, move(arguments)) +String FunctionDeclaration::name() const +{ + return m_declaration.visit( + [&](AbstractOperationDeclaration const& abstract_operation) { + return abstract_operation.name.to_string(); + }, + [&](MethodDeclaration const& method) { + return MUST(String::formatted("%{}%", method.name.to_string())); + }, + [&](AccessorDeclaration const& accessor) { + return MUST(String::formatted("%get {}%", accessor.name.to_string())); + }); +} + +ReadonlySpan FunctionDeclaration::arguments() const +{ + return m_declaration.visit( + [&](AccessorDeclaration const&) { + return ReadonlySpan {}; + }, + [&](auto const& declaration) { + return declaration.arguments.span(); + }); +} + +FunctionDefinition::FunctionDefinition(Declaration&& declaration, Location location, Tree ast) + : FunctionDeclaration(move(declaration), location) , m_ast(move(ast)) , m_named_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 0b49c0d76dd..147c573c4a3 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h @@ -22,9 +22,8 @@ public: ~TranslationUnit(); void adopt_declaration(NonnullRefPtr&& declaration); - FunctionDefinitionRef adopt_function(NonnullRefPtr&& definition); - - FunctionDeclarationRef find_declaration_by_name(StringView name) const; + void adopt_function(NonnullRefPtr&& definition); + FunctionDeclarationRef find_abstract_operation_by_name(StringView name) const; StringView filename() const { return m_filename; } DiagnosticEngine& diag() { return m_diagnostic_engine; } @@ -37,7 +36,7 @@ private: DiagnosticEngine m_diagnostic_engine; Vector m_functions_to_compile; Vector> m_declarations_owner; - HashMap m_function_index; + HashMap m_abstract_operation_index; HashMap m_enumerator_nodes; }; @@ -46,20 +45,95 @@ struct FunctionArgument { size_t optional_arguments_group; }; +class QualifiedName { +public: + QualifiedName() { } + + QualifiedName(ReadonlySpan parsed_name) + { + m_components.ensure_capacity(parsed_name.size()); + for (auto component : parsed_name) + m_components.unchecked_append(MUST(FlyString::from_utf8(component))); + } + + QualifiedName(ReadonlySpan parsed_name) + { + m_components.ensure_capacity(parsed_name.size()); + for (auto component : parsed_name) + m_components.unchecked_append(component); + } + + String to_string() const + { + return MUST(String::join("."sv, m_components)); + } + + Vector const& components() const + { + return m_components; + } + + FlyString last_component() const + { + return m_components.last(); + } + + ReadonlySpan without_last_component() const + { + return components().span().slice(0, components().size() - 1); + } + + QualifiedName slice(size_t start, size_t length) const + { + return { m_components.span().slice(start, length) }; + } + + QualifiedName with_appended(FlyString component) const + { + auto new_components = m_components; + new_components.append(component); + return { new_components }; + } + +private: + Vector m_components; +}; + +struct AbstractOperationDeclaration { + FlyString name; + Vector arguments; +}; + +struct AccessorDeclaration { + QualifiedName name; +}; + +struct MethodDeclaration { + QualifiedName name; + Vector arguments; +}; + +using Declaration = Variant; + class FunctionDeclaration : public RefCounted { public: - FunctionDeclaration(StringView name, Vector&& arguments); + FunctionDeclaration(Declaration&& declaration, Location location); virtual ~FunctionDeclaration() = default; - TranslationUnitRef m_translation_unit = nullptr; - StringView m_name; - Vector m_arguments; + Declaration const& declaration() const { return m_declaration; } + Location location() const { return m_location; } + String name() const; + ReadonlySpan arguments() const; + +private: + Declaration m_declaration; + Location m_location; }; class FunctionDefinition : public FunctionDeclaration { public: - FunctionDefinition(StringView name, Tree ast, Vector&& arguments); + FunctionDefinition(Declaration&& declaration, Location location, Tree ast); void reindex_ssa_variables(); diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/CppASTConverter.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/CppASTConverter.cpp index 63d59b247db..ad88947ec14 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/CppASTConverter.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/CppASTConverter.cpp @@ -28,7 +28,13 @@ NonnullRefPtr CppASTConverter::convert() for (auto const& parameter : m_function->parameters()) arguments.append({ .name = parameter->full_name() }); - return make_ref_counted(name, tree, move(arguments)); + return make_ref_counted( + AbstractOperationDeclaration { + .name = MUST(FlyString::from_utf8(name)), + .arguments = move(arguments), + }, + Location {}, + tree); } template<> diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecificationClause.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecificationClause.cpp index f9b3a927369..bcf7d87e3e7 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecificationClause.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecificationClause.cpp @@ -24,7 +24,7 @@ NonnullOwnPtr SpecificationClause::create(SpecificationPars [&](AK::Empty const&) { result = make(move(specification_clause)); }, - [&](OneOf auto const&) { + [&](OneOf auto const&) { result = make(move(specification_clause)); }); diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecificationFunction.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecificationFunction.cpp index 26d6a51f4b8..33a29c91ebb 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecificationFunction.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecificationFunction.cpp @@ -15,35 +15,29 @@ bool SpecificationFunction::post_initialize(XML::Node const* element) VERIFY(element->as_element().name == tag_emu_clause); auto& ctx = context(); + m_location = ctx.location_from_xml_offset(element->offset); auto maybe_id = get_attribute_by_name(element, attribute_id); if (!maybe_id.has_value()) { - ctx.diag().error(ctx.location_from_xml_offset(element->offset), + ctx.diag().error(m_location, "no id attribute"); } else { m_id = maybe_id.value(); } m_header.header.visit( - [&](ClauseHeader::AbstractOperation const& abstract_operation) { - auto maybe_abstract_operation_id = get_attribute_by_name(element, attribute_aoid); - if (maybe_abstract_operation_id.has_value()) - m_name = MUST(String::from_utf8(maybe_abstract_operation_id.value())); + [&](AbstractOperationDeclaration const& abstract_operation) { + m_declaration = abstract_operation; - auto const& [function_name, arguments] = abstract_operation; - m_arguments = arguments; + auto abstract_operation_id = get_attribute_by_name(element, attribute_aoid).value(); - if (m_name != function_name) { - ctx.diag().warn(ctx.location_from_xml_offset(element->offset), + if (abstract_operation.name != abstract_operation_id) { + ctx.diag().warn(m_location, "function name in header and [aoid] do not match"); } }, - [&](ClauseHeader::Accessor const& accessor) { - m_name = MUST(String::formatted("%get {}%", MUST(String::join("."sv, accessor.qualified_name)))); - }, - [&](ClauseHeader::Method const& method) { - m_name = MUST(String::formatted("%{}%", MUST(String::join("."sv, method.qualified_name)))); - m_arguments = method.arguments; + [&](OneOf auto const& declaration) { + m_declaration = declaration; }, [&](auto const&) { VERIFY_NOT_REACHED(); @@ -70,7 +64,7 @@ bool SpecificationFunction::post_initialize(XML::Node const* element) } if (algorithm_nodes.size() != 1) { - ctx.diag().error(ctx.location_from_xml_offset(element->offset), + ctx.diag().error(m_location, " specifing function should have exactly one child"sv); return false; } @@ -86,7 +80,7 @@ bool SpecificationFunction::post_initialize(XML::Node const* element) void SpecificationFunction::do_collect(TranslationUnitRef translation_unit) { - translation_unit->adopt_function(make_ref_counted(m_name, m_algorithm.tree(), move(m_arguments))); + translation_unit->adopt_function(make_ref_counted(m_declaration.release_value(), m_location, m_algorithm.tree())); } } diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecificationParsing.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecificationParsing.h index 3bd8c0036d5..87e8226db11 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecificationParsing.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/SpecificationParsing.h @@ -145,9 +145,8 @@ protected: private: StringView m_id; - String m_name; - - Vector m_arguments; + Optional m_declaration; + Location m_location; Algorithm m_algorithm; }; diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.cpp index 9eee3541cfe..19a391d1769 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.cpp @@ -644,14 +644,14 @@ TextParseErrorOr TextParser::parse_step_with_substeps(Tree substeps) } // :== (. )* -TextParseErrorOr> TextParser::parse_qualified_name() +TextParseErrorOr TextParser::parse_qualified_name() { Vector qualified_name; qualified_name.append(TRY(consume_token_with_type(TokenType::Word)).data); while (true) { auto token_or_error = consume_token_with_type(TokenType::MemberAccess); if (token_or_error.is_error()) - return qualified_name; + return QualifiedName { qualified_name }; qualified_name.append(TRY(consume_token_with_type(TokenType::Word)).data); } } @@ -702,12 +702,14 @@ TextParseErrorOr> TextParser::parse_function_arguments_ } // :== $ -TextParseErrorOr TextParser::parse_abstract_operation_declaration() +TextParseErrorOr TextParser::parse_abstract_operation_declaration() { auto rollback = rollback_point(); - ClauseHeader::AbstractOperation function_definition; - function_definition.name = TRY(consume_token_with_type(TokenType::Word)).data; + auto name = TRY(consume_token_with_type(TokenType::Word)).data; + + AbstractOperationDeclaration function_definition; + function_definition.name = MUST(FlyString::from_utf8(name)); function_definition.arguments = TRY(parse_function_arguments_in_declaration()); TRY(expect_eof()); @@ -716,25 +718,25 @@ TextParseErrorOr TextParser::parse_abstract_ope } // :== get $ -TextParseErrorOr TextParser::parse_accessor_declaration() +TextParseErrorOr TextParser::parse_accessor_declaration() { auto rollback = rollback_point(); TRY(consume_word("get"sv)); - ClauseHeader::Accessor accessor; - accessor.qualified_name = TRY(parse_qualified_name()); + AccessorDeclaration accessor; + accessor.name = TRY(parse_qualified_name()); TRY(expect_eof()); rollback.disarm(); return accessor; } -TextParseErrorOr TextParser::parse_method_declaration() +TextParseErrorOr TextParser::parse_method_declaration() { auto rollback = rollback_point(); - ClauseHeader::Method method; - method.qualified_name = TRY(parse_qualified_name()); + MethodDeclaration method; + method.name = TRY(parse_qualified_name()); method.arguments = TRY(parse_function_arguments_in_declaration()); TRY(expect_eof()); diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.h index 530192b274f..8267499ed05 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.h @@ -13,22 +13,8 @@ namespace JSSpecCompiler { struct ClauseHeader { - struct AbstractOperation { - StringView name; - Vector arguments; - }; - - struct Accessor { - Vector qualified_name; - }; - - struct Method { - Vector qualified_name; - Vector arguments; - }; - StringView section_number; - Variant header; + Variant header; }; struct TextParseError { }; @@ -102,11 +88,11 @@ private: TextParseErrorOr parse_if(Tree then_branch); TextParseErrorOr parse_else(Tree else_branch); - TextParseErrorOr> parse_qualified_name(); + TextParseErrorOr parse_qualified_name(); TextParseErrorOr> parse_function_arguments_in_declaration(); - TextParseErrorOr parse_abstract_operation_declaration(); - TextParseErrorOr parse_method_declaration(); - TextParseErrorOr parse_accessor_declaration(); + TextParseErrorOr parse_abstract_operation_declaration(); + TextParseErrorOr parse_method_declaration(); + TextParseErrorOr parse_accessor_declaration(); SpecificationParsingContext& m_ctx; Vector const& m_tokens; diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp index 87b2e85a967..1e247568634 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp @@ -71,8 +71,8 @@ private: }; template<> -struct AK::Formatter> : AK::Formatter { - ErrorOr format(FormatBuilder& builder, Vector const& arguments) +struct AK::Formatter> : AK::Formatter { + ErrorOr format(FormatBuilder& builder, ReadonlySpan const& arguments) { size_t previous_optional_group = 0; for (size_t i = 0; i < arguments.size(); ++i) { @@ -139,17 +139,6 @@ ErrorOr serenity_main(Main::Arguments arguments) TranslationUnit translation_unit(filename); - // 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. - translation_unit.adopt_declaration(make_ref_counted("CompareISODate"sv, Vector {})); - translation_unit.adopt_declaration(make_ref_counted("CreateDateDurationRecord"sv, Vector {})); - translation_unit.adopt_declaration(make_ref_counted("AddISODate"sv, Vector {})); - translation_unit.adopt_declaration(make_ref_counted("ISODaysInMonth"sv, Vector {})); - translation_unit.adopt_declaration(make_ref_counted("ISODateToEpochDays"sv, Vector {})); - translation_unit.adopt_declaration(make_ref_counted("truncate"sv, Vector {})); - translation_unit.adopt_declaration(make_ref_counted("remainder"sv, Vector {})); - for (auto const& step : pipeline.pipeline()) { step.step->run(&translation_unit); @@ -161,14 +150,14 @@ ErrorOr serenity_main(Main::Arguments arguments) if (step.dump_ast) { outln(stderr, "===== AST after {} =====", step.step->name()); for (auto const& function : translation_unit.functions_to_compile()) { - outln(stderr, "{}({}):", function->m_name, function->m_arguments); + outln(stderr, "{}({}):", function->name(), function->arguments()); outln(stderr, "{}", function->m_ast); } } if (step.dump_cfg && translation_unit.functions_to_compile().size() && translation_unit.functions_to_compile()[0]->m_cfg != nullptr) { outln(stderr, "===== CFG after {} =====", step.step->name()); for (auto const& function : translation_unit.functions_to_compile()) { - outln(stderr, "{}({}):", function->m_name, function->m_arguments); + outln(stderr, "{}({}):", function->name(), function->arguments()); outln(stderr, "{}", *function->m_cfg); } }