mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 16:47:36 +00:00
JSSpecCompiler: Parse optional arguments groups
This commit is contained in:
parent
3e6a07154b
commit
a35a751f9e
8 changed files with 72 additions and 11 deletions
|
@ -43,6 +43,7 @@ private:
|
||||||
|
|
||||||
struct FunctionArgument {
|
struct FunctionArgument {
|
||||||
StringView name;
|
StringView name;
|
||||||
|
size_t optional_arguments_group;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FunctionDeclaration : public RefCounted<FunctionDeclaration> {
|
class FunctionDeclaration : public RefCounted<FunctionDeclaration> {
|
||||||
|
|
|
@ -74,6 +74,8 @@ void tokenize_string(SpecificationParsingContext& ctx, XML::Node const* node, St
|
||||||
{ "("sv, TokenType::ParenOpen },
|
{ "("sv, TokenType::ParenOpen },
|
||||||
{ "+"sv, TokenType::Plus },
|
{ "+"sv, TokenType::Plus },
|
||||||
{ "?"sv, TokenType::QuestionMark },
|
{ "?"sv, TokenType::QuestionMark },
|
||||||
|
{ "]"sv, TokenType::SquareBracketClose },
|
||||||
|
{ "["sv, TokenType::SquareBracketOpen },
|
||||||
};
|
};
|
||||||
|
|
||||||
LineTrackingLexer lexer(view, node->offset);
|
LineTrackingLexer lexer(view, node->offset);
|
||||||
|
|
|
@ -13,6 +13,8 @@ namespace JSSpecCompiler {
|
||||||
|
|
||||||
void TextParser::save_error(Variant<TokenType, StringView, CustomMessage>&& expected)
|
void TextParser::save_error(Variant<TokenType, StringView, CustomMessage>&& expected)
|
||||||
{
|
{
|
||||||
|
if (expected.has<TokenType>() && expected.get<TokenType>() == TokenType::Invalid)
|
||||||
|
return;
|
||||||
if (m_max_parsed_tokens > m_next_token_index)
|
if (m_max_parsed_tokens > m_next_token_index)
|
||||||
return;
|
return;
|
||||||
if (m_max_parsed_tokens < m_next_token_index)
|
if (m_max_parsed_tokens < m_next_token_index)
|
||||||
|
@ -650,21 +652,45 @@ TextParseErrorOr<Vector<StringView>> TextParser::parse_qualified_name()
|
||||||
// <function_arguments> :== '(' (<word> (, <word>)*)? ')'
|
// <function_arguments> :== '(' (<word> (, <word>)*)? ')'
|
||||||
TextParseErrorOr<Vector<FunctionArgument>> TextParser::parse_function_arguments_in_declaration()
|
TextParseErrorOr<Vector<FunctionArgument>> TextParser::parse_function_arguments_in_declaration()
|
||||||
{
|
{
|
||||||
Vector<FunctionArgument> arguments;
|
|
||||||
TRY(consume_token_with_type(TokenType::ParenOpen));
|
TRY(consume_token_with_type(TokenType::ParenOpen));
|
||||||
|
|
||||||
|
Vector<FunctionArgument> arguments;
|
||||||
|
size_t optional_arguments_group = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (arguments.is_empty()) {
|
Token token = TRY(consume_token_with_one_of_types({
|
||||||
auto argument = TRY(consume_token_with_one_of_types({ TokenType::ParenClose, TokenType::Identifier }));
|
TokenType::SquareBracketOpen,
|
||||||
if (argument.type == TokenType::ParenClose)
|
arguments.is_empty() ? TokenType::Identifier : TokenType::Comma,
|
||||||
break;
|
!optional_arguments_group ? TokenType::ParenClose : TokenType::Invalid,
|
||||||
arguments.append({ argument.data });
|
optional_arguments_group ? TokenType::SquareBracketClose : TokenType::Invalid,
|
||||||
} else {
|
}));
|
||||||
arguments.append({ TRY(consume_token_with_type(TokenType::Identifier)).data });
|
|
||||||
}
|
StringView identifier;
|
||||||
auto next_token = TRY(consume_token_with_one_of_types({ TokenType::ParenClose, TokenType::Comma }));
|
|
||||||
if (next_token.type == TokenType::ParenClose)
|
if (token.type == TokenType::SquareBracketClose) {
|
||||||
|
VERIFY(optional_arguments_group != 0);
|
||||||
|
for (size_t i = 1; i < optional_arguments_group; ++i)
|
||||||
|
TRY(consume_token_with_type(TokenType::SquareBracketClose));
|
||||||
|
TRY(consume_token_with_type(TokenType::ParenClose));
|
||||||
break;
|
break;
|
||||||
|
} else if (token.type == TokenType::ParenClose) {
|
||||||
|
VERIFY(optional_arguments_group == 0);
|
||||||
|
break;
|
||||||
|
} else if (token.type == TokenType::SquareBracketOpen) {
|
||||||
|
++optional_arguments_group;
|
||||||
|
if (!arguments.is_empty())
|
||||||
|
TRY(consume_token_with_type(TokenType::Comma));
|
||||||
|
identifier = TRY(consume_token_with_type(TokenType::Identifier)).data;
|
||||||
|
} else if (token.type == TokenType::Comma) {
|
||||||
|
identifier = TRY(consume_token_with_type(TokenType::Identifier)).data;
|
||||||
|
} else {
|
||||||
|
VERIFY(token.type == TokenType::Identifier);
|
||||||
|
identifier = token.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments.append({ identifier, optional_arguments_group });
|
||||||
}
|
}
|
||||||
|
|
||||||
return arguments;
|
return arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,8 @@ constexpr i32 closing_bracket_precedence = 18;
|
||||||
F(Plus, 6, Invalid, Plus, Invalid, "plus") \
|
F(Plus, 6, Invalid, Plus, Invalid, "plus") \
|
||||||
F(QuestionMark, 3, ReturnIfAbrubt, Invalid, Invalid, "question mark") \
|
F(QuestionMark, 3, ReturnIfAbrubt, Invalid, Invalid, "question mark") \
|
||||||
F(SectionNumber, -1, Invalid, Invalid, Invalid, "section number") \
|
F(SectionNumber, -1, Invalid, Invalid, Invalid, "section number") \
|
||||||
|
F(SquareBracketClose, -1, Invalid, Invalid, Invalid, "']'") \
|
||||||
|
F(SquareBracketOpen, -1, Invalid, Invalid, Invalid, "'['") \
|
||||||
F(String, -1, Invalid, Invalid, Invalid, "string literal") \
|
F(String, -1, Invalid, Invalid, Invalid, "string literal") \
|
||||||
F(Superscript, 4, Invalid, Power, Invalid, "subscript") \
|
F(Superscript, 4, Invalid, Power, Invalid, "subscript") \
|
||||||
F(UnaryMinus, 3, Minus, Invalid, Invalid, "unary minus") \
|
F(UnaryMinus, 3, Minus, Invalid, Invalid, "unary minus") \
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE inline_dtd[<!ENTITY nbsp " ">]>
|
||||||
|
<specification>
|
||||||
|
<emu-clause id="1" aoid="TestOptionalArgumentsGroups1">
|
||||||
|
<h1><span class="secnum">1</span> TestOptionalArgumentsGroups1 ( [<var>a</var>, <var>b</var>[, <var>c</var>]] )</h1>
|
||||||
|
<emu-alg><ol><li>Return <emu-const>unused</emu-const>.</li></ol></emu-alg>
|
||||||
|
</emu-clause>
|
||||||
|
|
||||||
|
<emu-clause id="2" aoid="TestOptionalArgumentsGroups2">
|
||||||
|
<h1><span class="secnum">2</span> TestOptionalArgumentsGroups2 ( <var>a</var>, <var>b</var>[, <var>c</var>, <var>d</var>] )</h1>
|
||||||
|
<emu-alg><ol><li>Return <emu-const>unused</emu-const>.</li></ol></emu-alg>
|
||||||
|
</emu-clause>
|
||||||
|
</specification>
|
|
@ -0,0 +1,11 @@
|
||||||
|
===== AST after reference-resolving =====
|
||||||
|
TestOptionalArgumentsGroups1([a, b, [c]]):
|
||||||
|
TreeList
|
||||||
|
ReturnNode
|
||||||
|
Enumerator unused
|
||||||
|
|
||||||
|
TestOptionalArgumentsGroups2(a, b, [c, d]):
|
||||||
|
TreeList
|
||||||
|
ReturnNode
|
||||||
|
Enumerator unused
|
||||||
|
|
|
@ -74,11 +74,17 @@ template<>
|
||||||
struct AK::Formatter<Vector<FunctionArgument>> : AK::Formatter<StringView> {
|
struct AK::Formatter<Vector<FunctionArgument>> : AK::Formatter<StringView> {
|
||||||
ErrorOr<void> format(FormatBuilder& builder, Vector<FunctionArgument> const& arguments)
|
ErrorOr<void> format(FormatBuilder& builder, Vector<FunctionArgument> const& arguments)
|
||||||
{
|
{
|
||||||
|
size_t previous_optional_group = 0;
|
||||||
for (size_t i = 0; i < arguments.size(); ++i) {
|
for (size_t i = 0; i < arguments.size(); ++i) {
|
||||||
|
if (previous_optional_group != arguments[i].optional_arguments_group) {
|
||||||
|
previous_optional_group = arguments[i].optional_arguments_group;
|
||||||
|
TRY(builder.put_string("["sv));
|
||||||
|
}
|
||||||
TRY(builder.put_string(arguments[i].name));
|
TRY(builder.put_string(arguments[i].name));
|
||||||
if (i + 1 != arguments.size())
|
if (i + 1 != arguments.size())
|
||||||
TRY(builder.put_literal(", "sv));
|
TRY(builder.put_literal(", "sv));
|
||||||
}
|
}
|
||||||
|
TRY(builder.put_string(TRY(String::repeated(']', previous_optional_group))));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -50,6 +50,7 @@ const Array regression_tests = {
|
||||||
TestDescription {
|
TestDescription {
|
||||||
.sources = {
|
.sources = {
|
||||||
"spec-no-new-line-after-dot.xml"sv,
|
"spec-no-new-line-after-dot.xml"sv,
|
||||||
|
"spec-optional-arguments.xml"sv,
|
||||||
"spec-parsing.xml"sv,
|
"spec-parsing.xml"sv,
|
||||||
"spec-single-function-simple.xml"sv,
|
"spec-single-function-simple.xml"sv,
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue