diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h index 4eb7acd5897..0b49c0d76dd 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Function.h @@ -43,6 +43,7 @@ private: struct FunctionArgument { StringView name; + size_t optional_arguments_group; }; class FunctionDeclaration : public RefCounted { diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/Lexer.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/Lexer.cpp index d4ba9901ed8..0a14990f25e 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/Lexer.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/Lexer.cpp @@ -74,6 +74,8 @@ void tokenize_string(SpecificationParsingContext& ctx, XML::Node const* node, St { "("sv, TokenType::ParenOpen }, { "+"sv, TokenType::Plus }, { "?"sv, TokenType::QuestionMark }, + { "]"sv, TokenType::SquareBracketClose }, + { "["sv, TokenType::SquareBracketOpen }, }; LineTrackingLexer lexer(view, node->offset); diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.cpp index 638dbdb6222..845cad052ac 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/TextParser.cpp @@ -13,6 +13,8 @@ namespace JSSpecCompiler { void TextParser::save_error(Variant&& expected) { + if (expected.has() && expected.get() == TokenType::Invalid) + return; if (m_max_parsed_tokens > m_next_token_index) return; if (m_max_parsed_tokens < m_next_token_index) @@ -650,21 +652,45 @@ TextParseErrorOr> TextParser::parse_qualified_name() // :== '(' ( (, )*)? ')' TextParseErrorOr> TextParser::parse_function_arguments_in_declaration() { - Vector arguments; TRY(consume_token_with_type(TokenType::ParenOpen)); + + Vector arguments; + size_t optional_arguments_group = 0; + 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) + Token token = TRY(consume_token_with_one_of_types({ + TokenType::SquareBracketOpen, + arguments.is_empty() ? TokenType::Identifier : TokenType::Comma, + !optional_arguments_group ? TokenType::ParenClose : TokenType::Invalid, + optional_arguments_group ? TokenType::SquareBracketClose : TokenType::Invalid, + })); + + StringView identifier; + + 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; + } 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; } diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/Token.h b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/Token.h index 117ffb495db..67b4b4ebc34 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/Token.h +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Parser/Token.h @@ -48,6 +48,8 @@ constexpr i32 closing_bracket_precedence = 18; F(Plus, 6, Invalid, Plus, Invalid, "plus") \ F(QuestionMark, 3, ReturnIfAbrubt, Invalid, Invalid, "question mark") \ 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(Superscript, 4, Invalid, Power, Invalid, "subscript") \ F(UnaryMinus, 3, Minus, Invalid, Invalid, "unary minus") \ diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Tests/spec-optional-arguments.xml b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Tests/spec-optional-arguments.xml new file mode 100644 index 00000000000..f6ed3c5648f --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Tests/spec-optional-arguments.xml @@ -0,0 +1,12 @@ +]> + + +

1 TestOptionalArgumentsGroups1 ( [a, b[, c]] )

+
  1. Return unused.
+
+ + +

2 TestOptionalArgumentsGroups2 ( a, b[, c, d] )

+
  1. Return unused.
+
+
diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Tests/spec-optional-arguments.xml.expectation b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Tests/spec-optional-arguments.xml.expectation new file mode 100644 index 00000000000..b67d640eb61 --- /dev/null +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/Tests/spec-optional-arguments.xml.expectation @@ -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 + diff --git a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp index 950a239037f..9e86e6e2a2d 100644 --- a/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/main.cpp @@ -74,11 +74,17 @@ template<> struct AK::Formatter> : AK::Formatter { ErrorOr format(FormatBuilder& builder, Vector const& arguments) { + size_t previous_optional_group = 0; 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)); if (i + 1 != arguments.size()) TRY(builder.put_literal(", "sv)); } + TRY(builder.put_string(TRY(String::repeated(']', previous_optional_group)))); return {}; } }; diff --git a/Tests/JSSpecCompiler/test-runner.cpp b/Tests/JSSpecCompiler/test-runner.cpp index 43b0d7d22dc..e4fb58e7902 100644 --- a/Tests/JSSpecCompiler/test-runner.cpp +++ b/Tests/JSSpecCompiler/test-runner.cpp @@ -50,6 +50,7 @@ const Array regression_tests = { TestDescription { .sources = { "spec-no-new-line-after-dot.xml"sv, + "spec-optional-arguments.xml"sv, "spec-parsing.xml"sv, "spec-single-function-simple.xml"sv, },