mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 20:15:17 +00:00
JSSpecCompiler: Parse accessor property headers
This commit is contained in:
parent
a9f3a14a13
commit
d1fc84c638
Notes:
sideshowbarker
2024-07-17 01:00:06 +09:00
Author: https://github.com/DanShaders Commit: https://github.com/SerenityOS/serenity/commit/d1fc84c638 Pull-request: https://github.com/SerenityOS/serenity/pull/23123 Reviewed-by: https://github.com/ADKaster ✅
4 changed files with 108 additions and 41 deletions
|
@ -239,7 +239,7 @@ NonnullOwnPtr<SpecificationClause> SpecificationClause::create(SpecificationPars
|
|||
[&](AK::Empty const&) {
|
||||
result = make<SpecificationClause>(move(specification_clause));
|
||||
},
|
||||
[&](ClauseHeader::FunctionDefinition const&) {
|
||||
[&](OneOf<ClauseHeader::AbstractOperation, ClauseHeader::Accessor> auto const&) {
|
||||
result = make<SpecFunction>(move(specification_clause));
|
||||
});
|
||||
|
||||
|
@ -346,18 +346,26 @@ bool SpecFunction::post_initialize(XML::Node const* element)
|
|||
m_id = maybe_id.value();
|
||||
}
|
||||
|
||||
auto maybe_abstract_operation_id = get_attribute_by_name(element, attribute_aoid);
|
||||
if (maybe_abstract_operation_id.has_value())
|
||||
m_name = maybe_abstract_operation_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()));
|
||||
|
||||
m_section_number = m_header.section_number;
|
||||
auto const& [function_name, arguments] = m_header.header.get<ClauseHeader::FunctionDefinition>();
|
||||
m_arguments = arguments;
|
||||
auto const& [function_name, arguments] = abstract_operation;
|
||||
m_arguments = arguments;
|
||||
|
||||
if (m_name != function_name) {
|
||||
ctx.diag().warn(ctx.location_from_xml_offset(element->offset),
|
||||
"function name in header and <emu-clause>[aoid] do not match");
|
||||
}
|
||||
if (m_name != function_name) {
|
||||
ctx.diag().warn(ctx.location_from_xml_offset(element->offset),
|
||||
"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;
|
||||
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Specification specification;
|
||||
specification.parse(ctx, element);
|
||||
auto specification = make<Specification>();
|
||||
specification->parse(ctx, element);
|
||||
return specification;
|
||||
}
|
||||
|
||||
|
@ -486,7 +494,7 @@ void SpecParsingStep::run(TranslationUnitRef translation_unit)
|
|||
return;
|
||||
}
|
||||
|
||||
auto specification = Specification::create(ctx, &root);
|
||||
specification.collect_into(translation_unit);
|
||||
m_specification = Specification::create(ctx, &root);
|
||||
m_specification->collect_into(translation_unit);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,9 +132,8 @@ protected:
|
|||
void do_collect(TranslationUnitRef translation_unit) override;
|
||||
|
||||
private:
|
||||
StringView m_section_number;
|
||||
StringView m_id;
|
||||
StringView m_name;
|
||||
String m_name;
|
||||
|
||||
Vector<FunctionArgument> m_arguments;
|
||||
Algorithm m_algorithm;
|
||||
|
@ -142,7 +141,7 @@ private:
|
|||
|
||||
class Specification {
|
||||
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);
|
||||
|
||||
|
@ -161,6 +160,8 @@ public:
|
|||
|
||||
private:
|
||||
OwnPtr<XML::Document> m_document;
|
||||
OwnPtr<Specification> m_specification;
|
||||
|
||||
ByteBuffer m_input;
|
||||
};
|
||||
|
||||
|
|
|
@ -599,6 +599,69 @@ TextParseErrorOr<Tree> TextParser::parse_step_with_substeps(Tree substeps)
|
|||
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()
|
||||
{
|
||||
ClauseHeader result;
|
||||
|
@ -606,27 +669,13 @@ TextParseErrorOr<ClauseHeader> TextParser::parse_clause_header()
|
|||
auto section_number_token = TRY(consume_token_with_type(TokenType::SectionNumber));
|
||||
result.section_number = section_number_token.data;
|
||||
|
||||
ClauseHeader::FunctionDefinition function_definition;
|
||||
|
||||
function_definition.name = TRY(consume_token_with_type(TokenType::Word)).data;
|
||||
|
||||
TRY(consume_token_with_type(TokenType::ParenOpen));
|
||||
while (true) {
|
||||
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;
|
||||
if (auto ao_declaration = parse_abstract_operation_declaration(); !ao_declaration.is_error()) {
|
||||
result.header = ao_declaration.release_value();
|
||||
} else if (auto accessor = parse_accessor_declaration(); !accessor.is_error()) {
|
||||
result.header = accessor.release_value();
|
||||
} else {
|
||||
return TextParseError {};
|
||||
}
|
||||
TRY(expect_eof());
|
||||
|
||||
result.header = function_definition;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -13,13 +13,17 @@
|
|||
namespace JSSpecCompiler {
|
||||
|
||||
struct ClauseHeader {
|
||||
struct FunctionDefinition {
|
||||
struct AbstractOperation {
|
||||
StringView name;
|
||||
Vector<FunctionArgument> arguments;
|
||||
};
|
||||
|
||||
struct Accessor {
|
||||
Vector<StringView> qualified_name;
|
||||
};
|
||||
|
||||
StringView section_number;
|
||||
Variant<AK::Empty, FunctionDefinition> header;
|
||||
Variant<AK::Empty, AbstractOperation, Accessor> header;
|
||||
};
|
||||
|
||||
struct TextParseError { };
|
||||
|
@ -86,6 +90,11 @@ private:
|
|||
TextParseErrorOr<Tree> parse_if(Tree then_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;
|
||||
Vector<Token> const& m_tokens;
|
||||
size_t m_next_token_index = 0;
|
||||
|
|
Loading…
Add table
Reference in a new issue