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).
This commit is contained in:
Dan Klishch 2024-03-11 15:51:14 -04:00 committed by Andrew Kaster
parent 70cfa60f56
commit 0e7c33b1be
Notes: sideshowbarker 2024-07-17 01:46:43 +09:00
12 changed files with 170 additions and 96 deletions

View file

@ -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)

View file

@ -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<NamedVariableDeclaration>(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<FunctionPointer>(function));
return;
}

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Enumerate.h>
#include <AK/Queue.h>
#include "AST/AST.h"
@ -430,9 +431,9 @@ void SSABuildingPass::rename_variables(Vertex u, Vertex from)
void SSABuildingPass::rename_variables()
{
HashMap<StringView, size_t> 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);

View file

@ -19,23 +19,21 @@ TranslationUnit::~TranslationUnit() = default;
void TranslationUnit::adopt_declaration(NonnullRefPtr<FunctionDeclaration>&& declaration)
{
declaration->m_translation_unit = this;
m_function_index.set(declaration->m_name, declaration.ptr());
if (auto decl_name = declaration->declaration(); decl_name.has<AbstractOperationDeclaration>())
m_abstract_operation_index.set(decl_name.get<AbstractOperationDeclaration>().name, declaration.ptr());
m_declarations_owner.append(move(declaration));
}
FunctionDefinitionRef TranslationUnit::adopt_function(NonnullRefPtr<FunctionDefinition>&& definition)
void TranslationUnit::adopt_function(NonnullRefPtr<FunctionDefinition>&& 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<FunctionArgument>&& 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<FunctionArgument>&& 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<FunctionArgument> FunctionDeclaration::arguments() const
{
return m_declaration.visit(
[&](AccessorDeclaration const&) {
return ReadonlySpan<FunctionArgument> {};
},
[&](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<NamedVariableDeclaration>("$return"sv))
{

View file

@ -22,9 +22,8 @@ public:
~TranslationUnit();
void adopt_declaration(NonnullRefPtr<FunctionDeclaration>&& declaration);
FunctionDefinitionRef adopt_function(NonnullRefPtr<FunctionDefinition>&& definition);
FunctionDeclarationRef find_declaration_by_name(StringView name) const;
void adopt_function(NonnullRefPtr<FunctionDefinition>&& 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<FunctionDefinitionRef> m_functions_to_compile;
Vector<NonnullRefPtr<FunctionDeclaration>> m_declarations_owner;
HashMap<StringView, FunctionDeclarationRef> m_function_index;
HashMap<FlyString, FunctionDeclarationRef> m_abstract_operation_index;
HashMap<StringView, EnumeratorRef> m_enumerator_nodes;
};
@ -46,20 +45,95 @@ struct FunctionArgument {
size_t optional_arguments_group;
};
class QualifiedName {
public:
QualifiedName() { }
QualifiedName(ReadonlySpan<StringView> 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<FlyString> 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<FlyString> const& components() const
{
return m_components;
}
FlyString last_component() const
{
return m_components.last();
}
ReadonlySpan<FlyString> 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<FlyString> m_components;
};
struct AbstractOperationDeclaration {
FlyString name;
Vector<FunctionArgument> arguments;
};
struct AccessorDeclaration {
QualifiedName name;
};
struct MethodDeclaration {
QualifiedName name;
Vector<FunctionArgument> arguments;
};
using Declaration = Variant<AbstractOperationDeclaration, AccessorDeclaration, MethodDeclaration>;
class FunctionDeclaration : public RefCounted<FunctionDeclaration> {
public:
FunctionDeclaration(StringView name, Vector<FunctionArgument>&& arguments);
FunctionDeclaration(Declaration&& declaration, Location location);
virtual ~FunctionDeclaration() = default;
TranslationUnitRef m_translation_unit = nullptr;
StringView m_name;
Vector<FunctionArgument> m_arguments;
Declaration const& declaration() const { return m_declaration; }
Location location() const { return m_location; }
String name() const;
ReadonlySpan<FunctionArgument> arguments() const;
private:
Declaration m_declaration;
Location m_location;
};
class FunctionDefinition : public FunctionDeclaration {
public:
FunctionDefinition(StringView name, Tree ast, Vector<FunctionArgument>&& arguments);
FunctionDefinition(Declaration&& declaration, Location location, Tree ast);
void reindex_ssa_variables();

View file

@ -28,7 +28,13 @@ NonnullRefPtr<FunctionDefinition> CppASTConverter::convert()
for (auto const& parameter : m_function->parameters())
arguments.append({ .name = parameter->full_name() });
return make_ref_counted<FunctionDefinition>(name, tree, move(arguments));
return make_ref_counted<FunctionDefinition>(
AbstractOperationDeclaration {
.name = MUST(FlyString::from_utf8(name)),
.arguments = move(arguments),
},
Location {},
tree);
}
template<>

View file

@ -24,7 +24,7 @@ NonnullOwnPtr<SpecificationClause> SpecificationClause::create(SpecificationPars
[&](AK::Empty const&) {
result = make<SpecificationClause>(move(specification_clause));
},
[&](OneOf<ClauseHeader::AbstractOperation, ClauseHeader::Accessor, ClauseHeader::Method> auto const&) {
[&](OneOf<AbstractOperationDeclaration, AccessorDeclaration, MethodDeclaration> auto const&) {
result = make<SpecificationFunction>(move(specification_clause));
});

View file

@ -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 <emu-clause>[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<AccessorDeclaration, MethodDeclaration> 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,
"<emu-clause> specifing function should have exactly one <emu-alg> 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<FunctionDefinition>(m_name, m_algorithm.tree(), move(m_arguments)));
translation_unit->adopt_function(make_ref_counted<FunctionDefinition>(m_declaration.release_value(), m_location, m_algorithm.tree()));
}
}

View file

@ -145,9 +145,8 @@ protected:
private:
StringView m_id;
String m_name;
Vector<FunctionArgument> m_arguments;
Optional<Declaration> m_declaration;
Location m_location;
Algorithm m_algorithm;
};

View file

@ -644,14 +644,14 @@ TextParseErrorOr<Tree> TextParser::parse_step_with_substeps(Tree substeps)
}
// <qualified_name> :== <word> (. <word>)*
TextParseErrorOr<Vector<StringView>> TextParser::parse_qualified_name()
TextParseErrorOr<QualifiedName> TextParser::parse_qualified_name()
{
Vector<StringView> 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<Vector<FunctionArgument>> TextParser::parse_function_arguments_
}
// <ao_declaration> :== <word> <function_arguments> $
TextParseErrorOr<ClauseHeader::AbstractOperation> TextParser::parse_abstract_operation_declaration()
TextParseErrorOr<AbstractOperationDeclaration> 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<ClauseHeader::AbstractOperation> TextParser::parse_abstract_ope
}
// <accessor_declaration> :== get <qualified_name> $
TextParseErrorOr<ClauseHeader::Accessor> TextParser::parse_accessor_declaration()
TextParseErrorOr<AccessorDeclaration> 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<ClauseHeader::Method> TextParser::parse_method_declaration()
TextParseErrorOr<MethodDeclaration> 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());

View file

@ -13,22 +13,8 @@
namespace JSSpecCompiler {
struct ClauseHeader {
struct AbstractOperation {
StringView name;
Vector<FunctionArgument> arguments;
};
struct Accessor {
Vector<StringView> qualified_name;
};
struct Method {
Vector<StringView> qualified_name;
Vector<FunctionArgument> arguments;
};
StringView section_number;
Variant<AK::Empty, AbstractOperation, Accessor, Method> header;
Variant<AK::Empty, AbstractOperationDeclaration, AccessorDeclaration, MethodDeclaration> header;
};
struct TextParseError { };
@ -102,11 +88,11 @@ private:
TextParseErrorOr<Tree> parse_if(Tree then_branch);
TextParseErrorOr<Tree> parse_else(Tree else_branch);
TextParseErrorOr<Vector<StringView>> parse_qualified_name();
TextParseErrorOr<QualifiedName> parse_qualified_name();
TextParseErrorOr<Vector<FunctionArgument>> parse_function_arguments_in_declaration();
TextParseErrorOr<ClauseHeader::AbstractOperation> parse_abstract_operation_declaration();
TextParseErrorOr<ClauseHeader::Method> parse_method_declaration();
TextParseErrorOr<ClauseHeader::Accessor> parse_accessor_declaration();
TextParseErrorOr<AbstractOperationDeclaration> parse_abstract_operation_declaration();
TextParseErrorOr<MethodDeclaration> parse_method_declaration();
TextParseErrorOr<AccessorDeclaration> parse_accessor_declaration();
SpecificationParsingContext& m_ctx;
Vector<Token> const& m_tokens;

View file

@ -71,8 +71,8 @@ private:
};
template<>
struct AK::Formatter<Vector<FunctionArgument>> : AK::Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, Vector<FunctionArgument> const& arguments)
struct AK::Formatter<ReadonlySpan<FunctionArgument>> : AK::Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, ReadonlySpan<FunctionArgument> const& arguments)
{
size_t previous_optional_group = 0;
for (size_t i = 0; i < arguments.size(); ++i) {
@ -139,17 +139,6 @@ ErrorOr<int> 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<FunctionDeclaration>("CompareISODate"sv, Vector<FunctionArgument> {}));
translation_unit.adopt_declaration(make_ref_counted<FunctionDeclaration>("CreateDateDurationRecord"sv, Vector<FunctionArgument> {}));
translation_unit.adopt_declaration(make_ref_counted<FunctionDeclaration>("AddISODate"sv, Vector<FunctionArgument> {}));
translation_unit.adopt_declaration(make_ref_counted<FunctionDeclaration>("ISODaysInMonth"sv, Vector<FunctionArgument> {}));
translation_unit.adopt_declaration(make_ref_counted<FunctionDeclaration>("ISODateToEpochDays"sv, Vector<FunctionArgument> {}));
translation_unit.adopt_declaration(make_ref_counted<FunctionDeclaration>("truncate"sv, Vector<FunctionArgument> {}));
translation_unit.adopt_declaration(make_ref_counted<FunctionDeclaration>("remainder"sv, Vector<FunctionArgument> {}));
for (auto const& step : pipeline.pipeline()) {
step.step->run(&translation_unit);
@ -161,14 +150,14 @@ ErrorOr<int> 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);
}
}