diff --git a/starlark/parser.h b/starlark/parser.h index 0e2d5cd..177d410 100644 --- a/starlark/parser.h +++ b/starlark/parser.h @@ -347,6 +347,7 @@ class Parser { std::optional> parse_argument_list() { std::vector args; + bool seen_kw_arg = false; while (true) { auto maybe_token = next_token(); @@ -391,6 +392,7 @@ class Parser { } if (std::holds_alternative(*next)) { + seen_kw_arg = true; name = Identifier{.name = std::move(ident->name)}; auto value_token = next_token(); @@ -414,6 +416,11 @@ class Parser { reconsume(std::move(*next)); } + if (seen_kw_arg) { + std::cerr << "Positional argument may not follow keyword argument.\n"; + return std::nullopt; + } + auto value_expr = parse_expression(token); if (!value_expr) { std::cerr << "Failed to parse expression for argument value.\n"; diff --git a/starlark/parser_test.cc b/starlark/parser_test.cc index 1fa44da..9117f7f 100644 --- a/starlark/parser_test.cc +++ b/starlark/parser_test.cc @@ -65,7 +65,7 @@ int main() { }, }, { - R"(foo(bar = "baz", "qux"))", + R"(foo("qux", bar = "baz"))", starlark::Program{ .statements{ starlark::ExpressionStmt{ @@ -74,14 +74,14 @@ int main() { .target = std::make_shared( starlark::Identifier{"foo"}), .args{ - { - starlark::Identifier{"bar"}, - starlark::StringLiteral{"baz"}, - }, { std::nullopt, starlark::StringLiteral{"qux"}, }, + { + starlark::Identifier{"bar"}, + starlark::StringLiteral{"baz"}, + }, }, }, }, @@ -90,7 +90,7 @@ int main() { }, }, { - R"(foo(bar = baz, qux()))", + R"(foo(qux(), bar = baz))", starlark::Program{ .statements{ starlark::ExpressionStmt{ @@ -99,10 +99,6 @@ int main() { .target = std::make_shared( starlark::Identifier{"foo"}), .args{ - { - starlark::Identifier{"bar"}, - starlark::Identifier{"baz"}, - }, { std::nullopt, starlark::CallExpr{ @@ -111,6 +107,10 @@ int main() { .args{}, }, }, + { + starlark::Identifier{"bar"}, + starlark::Identifier{"baz"}, + }, }, }, }, @@ -331,7 +331,9 @@ int main() { static constexpr auto kExpectedParseFailures = std::to_array({ // CallExpr // Missing closing parenthesis. - R"(foo(bar = "baz", "qux")", + R"(foo("qux")", + // Positional argument after kw argument. + R"(foo(bar = baz, qux()))", // ListExpr // Tokenization error.