mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 13:37:45 +00:00
JSSpecCompiler: Parse accessor property headers
This commit is contained in:
parent
a9f3a14a13
commit
d1fc84c638
4 changed files with 108 additions and 41 deletions
|
@ -239,7 +239,7 @@ NonnullOwnPtr<SpecificationClause> SpecificationClause::create(SpecificationPars
|
||||||
[&](AK::Empty const&) {
|
[&](AK::Empty const&) {
|
||||||
result = make<SpecificationClause>(move(specification_clause));
|
result = make<SpecificationClause>(move(specification_clause));
|
||||||
},
|
},
|
||||||
[&](ClauseHeader::FunctionDefinition const&) {
|
[&](OneOf<ClauseHeader::AbstractOperation, ClauseHeader::Accessor> auto const&) {
|
||||||
result = make<SpecFunction>(move(specification_clause));
|
result = make<SpecFunction>(move(specification_clause));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -346,18 +346,26 @@ bool SpecFunction::post_initialize(XML::Node const* element)
|
||||||
m_id = maybe_id.value();
|
m_id = maybe_id.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto maybe_abstract_operation_id = get_attribute_by_name(element, attribute_aoid);
|
m_header.header.visit(
|
||||||
if (maybe_abstract_operation_id.has_value())
|
[&](ClauseHeader::AbstractOperation const& abstract_operation) {
|
||||||
m_name = maybe_abstract_operation_id.value();
|
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()));
|
||||||
|
|
||||||
m_section_number = m_header.section_number;
|
auto const& [function_name, arguments] = abstract_operation;
|
||||||
auto const& [function_name, arguments] = m_header.header.get<ClauseHeader::FunctionDefinition>();
|
m_arguments = arguments;
|
||||||
m_arguments = arguments;
|
|
||||||
|
|
||||||
if (m_name != function_name) {
|
if (m_name != function_name) {
|
||||||
ctx.diag().warn(ctx.location_from_xml_offset(element->offset),
|
ctx.diag().warn(ctx.location_from_xml_offset(element->offset),
|
||||||
"function name in header and <emu-clause>[aoid] do not match");
|
"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))));
|
||||||
|
},
|
||||||
|
[&](auto const&) {
|
||||||
|
VERIFY_NOT_REACHED();
|
||||||
|
});
|
||||||
|
|
||||||
Vector<XML::Node const*> algorithm_nodes;
|
Vector<XML::Node const*> algorithm_nodes;
|
||||||
|
|
||||||
|
@ -399,12 +407,12 @@ void SpecFunction::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_name, m_algorithm.tree(), move(m_arguments)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Specification Specification::create(SpecificationParsingContext& ctx, XML::Node const* element)
|
NonnullOwnPtr<Specification> Specification::create(SpecificationParsingContext& ctx, XML::Node const* element)
|
||||||
{
|
{
|
||||||
VERIFY(element->as_element().name == tag_specification);
|
VERIFY(element->as_element().name == tag_specification);
|
||||||
|
|
||||||
Specification specification;
|
auto specification = make<Specification>();
|
||||||
specification.parse(ctx, element);
|
specification->parse(ctx, element);
|
||||||
return specification;
|
return specification;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,7 +494,7 @@ void SpecParsingStep::run(TranslationUnitRef translation_unit)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto specification = Specification::create(ctx, &root);
|
m_specification = Specification::create(ctx, &root);
|
||||||
specification.collect_into(translation_unit);
|
m_specification->collect_into(translation_unit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,9 +132,8 @@ protected:
|
||||||
void do_collect(TranslationUnitRef translation_unit) override;
|
void do_collect(TranslationUnitRef translation_unit) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StringView m_section_number;
|
|
||||||
StringView m_id;
|
StringView m_id;
|
||||||
StringView m_name;
|
String m_name;
|
||||||
|
|
||||||
Vector<FunctionArgument> m_arguments;
|
Vector<FunctionArgument> m_arguments;
|
||||||
Algorithm m_algorithm;
|
Algorithm m_algorithm;
|
||||||
|
@ -142,7 +141,7 @@ private:
|
||||||
|
|
||||||
class Specification {
|
class Specification {
|
||||||
public:
|
public:
|
||||||
static Specification create(SpecificationParsingContext& ctx, XML::Node const* element);
|
static NonnullOwnPtr<Specification> create(SpecificationParsingContext& ctx, XML::Node const* element);
|
||||||
|
|
||||||
void collect_into(TranslationUnitRef translation_unit);
|
void collect_into(TranslationUnitRef translation_unit);
|
||||||
|
|
||||||
|
@ -161,6 +160,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
OwnPtr<XML::Document> m_document;
|
OwnPtr<XML::Document> m_document;
|
||||||
|
OwnPtr<Specification> m_specification;
|
||||||
|
|
||||||
ByteBuffer m_input;
|
ByteBuffer m_input;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -599,6 +599,69 @@ TextParseErrorOr<Tree> TextParser::parse_step_with_substeps(Tree substeps)
|
||||||
return TextParseError {};
|
return TextParseError {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// <qualified_name> :== <word> (. <word>)*
|
||||||
|
TextParseErrorOr<Vector<StringView>> 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;
|
||||||
|
qualified_name.append(TRY(consume_token_with_type(TokenType::Word)).data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <function_arguments> :== '(' (<word> (, <word>)*)? ')'
|
||||||
|
TextParseErrorOr<Vector<FunctionArgument>> TextParser::parse_function_arguments_in_declaration()
|
||||||
|
{
|
||||||
|
Vector<FunctionArgument> arguments;
|
||||||
|
TRY(consume_token_with_type(TokenType::ParenOpen));
|
||||||
|
while (true) {
|
||||||
|
if (arguments.is_empty()) {
|
||||||
|
auto argument = TRY(consume_token_with_one_of_types({ TokenType::ParenClose, TokenType::Identifier }));
|
||||||
|
if (argument.type == TokenType::ParenClose)
|
||||||
|
break;
|
||||||
|
arguments.append({ argument.data });
|
||||||
|
} else {
|
||||||
|
arguments.append({ TRY(consume_token_with_type(TokenType::Identifier)).data });
|
||||||
|
}
|
||||||
|
auto next_token = TRY(consume_token_with_one_of_types({ TokenType::ParenClose, TokenType::Comma }));
|
||||||
|
if (next_token.type == TokenType::ParenClose)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
// <ao_declaration> :== <word> <function_arguments> $
|
||||||
|
TextParseErrorOr<ClauseHeader::AbstractOperation> TextParser::parse_abstract_operation_declaration()
|
||||||
|
{
|
||||||
|
auto rollback = rollback_point();
|
||||||
|
|
||||||
|
ClauseHeader::AbstractOperation function_definition;
|
||||||
|
function_definition.name = TRY(consume_token_with_type(TokenType::Word)).data;
|
||||||
|
function_definition.arguments = TRY(parse_function_arguments_in_declaration());
|
||||||
|
TRY(expect_eof());
|
||||||
|
|
||||||
|
rollback.disarm();
|
||||||
|
return function_definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
// <accessor_declaration> :== get <qualified_name> $
|
||||||
|
TextParseErrorOr<ClauseHeader::Accessor> TextParser::parse_accessor_declaration()
|
||||||
|
{
|
||||||
|
auto rollback = rollback_point();
|
||||||
|
|
||||||
|
TRY(consume_word("get"sv));
|
||||||
|
ClauseHeader::Accessor accessor;
|
||||||
|
accessor.qualified_name = TRY(parse_qualified_name());
|
||||||
|
TRY(expect_eof());
|
||||||
|
|
||||||
|
rollback.disarm();
|
||||||
|
return accessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// <clause_header> :== <section_number> <ao_declaration> | <accessor_declaration>
|
||||||
TextParseErrorOr<ClauseHeader> TextParser::parse_clause_header()
|
TextParseErrorOr<ClauseHeader> TextParser::parse_clause_header()
|
||||||
{
|
{
|
||||||
ClauseHeader result;
|
ClauseHeader result;
|
||||||
|
@ -606,27 +669,13 @@ TextParseErrorOr<ClauseHeader> TextParser::parse_clause_header()
|
||||||
auto section_number_token = TRY(consume_token_with_type(TokenType::SectionNumber));
|
auto section_number_token = TRY(consume_token_with_type(TokenType::SectionNumber));
|
||||||
result.section_number = section_number_token.data;
|
result.section_number = section_number_token.data;
|
||||||
|
|
||||||
ClauseHeader::FunctionDefinition function_definition;
|
if (auto ao_declaration = parse_abstract_operation_declaration(); !ao_declaration.is_error()) {
|
||||||
|
result.header = ao_declaration.release_value();
|
||||||
function_definition.name = TRY(consume_token_with_type(TokenType::Word)).data;
|
} else if (auto accessor = parse_accessor_declaration(); !accessor.is_error()) {
|
||||||
|
result.header = accessor.release_value();
|
||||||
TRY(consume_token_with_type(TokenType::ParenOpen));
|
} else {
|
||||||
while (true) {
|
return TextParseError {};
|
||||||
if (function_definition.arguments.is_empty()) {
|
|
||||||
auto argument = TRY(consume_token_with_one_of_types({ TokenType::ParenClose, TokenType::Identifier }));
|
|
||||||
if (argument.type == TokenType::ParenClose)
|
|
||||||
break;
|
|
||||||
function_definition.arguments.append({ argument.data });
|
|
||||||
} else {
|
|
||||||
function_definition.arguments.append({ TRY(consume_token_with_type(TokenType::Identifier)).data });
|
|
||||||
}
|
|
||||||
auto next_token = TRY(consume_token_with_one_of_types({ TokenType::ParenClose, TokenType::Comma }));
|
|
||||||
if (next_token.type == TokenType::ParenClose)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
TRY(expect_eof());
|
|
||||||
|
|
||||||
result.header = function_definition;
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,17 @@
|
||||||
namespace JSSpecCompiler {
|
namespace JSSpecCompiler {
|
||||||
|
|
||||||
struct ClauseHeader {
|
struct ClauseHeader {
|
||||||
struct FunctionDefinition {
|
struct AbstractOperation {
|
||||||
StringView name;
|
StringView name;
|
||||||
Vector<FunctionArgument> arguments;
|
Vector<FunctionArgument> arguments;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Accessor {
|
||||||
|
Vector<StringView> qualified_name;
|
||||||
|
};
|
||||||
|
|
||||||
StringView section_number;
|
StringView section_number;
|
||||||
Variant<AK::Empty, FunctionDefinition> header;
|
Variant<AK::Empty, AbstractOperation, Accessor> header;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TextParseError { };
|
struct TextParseError { };
|
||||||
|
@ -86,6 +90,11 @@ private:
|
||||||
TextParseErrorOr<Tree> parse_if(Tree then_branch);
|
TextParseErrorOr<Tree> parse_if(Tree then_branch);
|
||||||
TextParseErrorOr<Tree> parse_else(Tree else_branch);
|
TextParseErrorOr<Tree> parse_else(Tree else_branch);
|
||||||
|
|
||||||
|
TextParseErrorOr<Vector<StringView>> parse_qualified_name();
|
||||||
|
TextParseErrorOr<Vector<FunctionArgument>> parse_function_arguments_in_declaration();
|
||||||
|
TextParseErrorOr<ClauseHeader::AbstractOperation> parse_abstract_operation_declaration();
|
||||||
|
TextParseErrorOr<ClauseHeader::Accessor> parse_accessor_declaration();
|
||||||
|
|
||||||
SpecificationParsingContext& m_ctx;
|
SpecificationParsingContext& m_ctx;
|
||||||
Vector<Token> const& m_tokens;
|
Vector<Token> const& m_tokens;
|
||||||
size_t m_next_token_index = 0;
|
size_t m_next_token_index = 0;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue