From a9d707f8a96e7e9255e8f7c8ecee51237a7edc60 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Wed, 1 May 2024 13:34:29 +0200 Subject: [PATCH] feat: add support for index expression, loop, while and for loops (#43) Part of #29. --------- Signed-off-by: Sasha Pourcelot --- README.md | 2 +- rust-grammar-dpdfa/grammar.rs | 33 +++ rust-grammar-dpdfa/src/generated.rs | 243 ++++++++++++++++++--- tests/ui/fail/expr-tuple.stderr | 6 +- tests/ui/fail/invalid_fn_calls.stderr | 6 +- tests/ui/fail/js_concat.stderr | 2 +- tests/ui/fail/loops.rs | 24 ++ tests/ui/fail/loops.stderr | 17 ++ tests/ui/fail/python_power_operator.stderr | 2 +- tests/ui/pass/expr-index.rs | 14 ++ tests/ui/pass/field_access.rs | 4 + tests/ui/pass/loops.rs | 26 +++ 12 files changed, 335 insertions(+), 44 deletions(-) create mode 100644 tests/ui/fail/loops.rs create mode 100644 tests/ui/fail/loops.stderr create mode 100644 tests/ui/pass/expr-index.rs create mode 100644 tests/ui/pass/loops.rs diff --git a/README.md b/README.md index b99d3c8..afd07f7 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ macro_rules! js_concat { This emits the following error [^error-message]: ```none -error: Potentially invalid expansion. Expected `::`, `break`, `if`, `return`, a `(`, a `[` or 3 others. +error: Potentially invalid expansion. Expected `::`, `break`, `for`, `if`, `loop`, `return` or 6 others. --> tests/ui/fail/js_concat.rs:5:16 | 5 | $left ++ $right diff --git a/rust-grammar-dpdfa/grammar.rs b/rust-grammar-dpdfa/grammar.rs index 0d79117..2a8a28d 100644 --- a/rust-grammar-dpdfa/grammar.rs +++ b/rust-grammar-dpdfa/grammar.rs @@ -182,6 +182,12 @@ fn expr_after_atom() { } else if peek(LParen) { expr_call(); expr_after_atom(); + } else if peek(LBracket) { + // Index expression: + // https://doc.rust-lang.org/reference/expressions/array-expr.html#array-and-slice-indexing-expressions + bump(LBracket); + expr(); + bump(RBracket); } else if peek(Dot) { expr_dot_expr(); expr_after_atom(); @@ -203,6 +209,12 @@ fn expr_atom() { expr_array(); } else if peek(LBrace) { block(); + } else if peek(Loop) { + expr_loop(); + } else if peek(While) { + expr_while(); + } else if peek(For) { + expr_for(); } else { error(); } @@ -492,3 +504,24 @@ fn expr_tuple_() { } } } + +fn expr_loop() { + bump(Loop); + block(); +} + +fn expr_while() { + bump(While); + // TODO: we must not allow struct expressions here + expr(); + block(); +} + +fn expr_for() { + bump(For); + pat(); + bump(In); + // TODO: we must not allow struct expressions here + expr(); + block(); +} diff --git a/rust-grammar-dpdfa/src/generated.rs b/rust-grammar-dpdfa/src/generated.rs index 05c5720..7860118 100644 --- a/rust-grammar-dpdfa/src/generated.rs +++ b/rust-grammar-dpdfa/src/generated.rs @@ -1056,7 +1056,7 @@ fn expr_3(input: &mut RustParser) -> Result, fn expr(input: &mut RustParser) -> Result, Option> { call_now![input, expr_3] } -fn expr_after_atom_23( +fn expr_after_atom_28( input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, Plus] @@ -1072,7 +1072,7 @@ fn expr_after_atom_23( { call_now![input, expr_after_atom_2] } else { - call_now![input, expr_after_atom_22] + call_now![input, expr_after_atom_27] } } fn expr_after_atom_0( @@ -1094,7 +1094,7 @@ fn expr_after_atom_2( ) -> Result, Option> { call_now![input, expr_after_atom_0, expr_after_atom_1] } -fn expr_after_atom_22( +fn expr_after_atom_27( input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, EqualsEquals] @@ -1106,7 +1106,7 @@ fn expr_after_atom_22( { call_now![input, expr_after_atom_5] } else { - call_now![input, expr_after_atom_21] + call_now![input, expr_after_atom_26] } } fn expr_after_atom_3( @@ -1128,13 +1128,13 @@ fn expr_after_atom_5( ) -> Result, Option> { call_now![input, expr_after_atom_3, expr_after_atom_4] } -fn expr_after_atom_21( +fn expr_after_atom_26( input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, OrOr] || cond![input, peek, AndAnd] { call_now![input, expr_after_atom_8] } else { - call_now![input, expr_after_atom_20] + call_now![input, expr_after_atom_25] } } fn expr_after_atom_6( @@ -1156,13 +1156,13 @@ fn expr_after_atom_8( ) -> Result, Option> { call_now![input, expr_after_atom_6, expr_after_atom_7] } -fn expr_after_atom_20( +fn expr_after_atom_25( input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, DotDot] || cond![input, peek, DotDotEquals] { call_now![input, expr_after_atom_11] } else { - call_now![input, expr_after_atom_19] + call_now![input, expr_after_atom_24] } } fn expr_after_atom_9( @@ -1184,13 +1184,13 @@ fn expr_after_atom_11( ) -> Result, Option> { call_now![input, expr_after_atom_9, expr_after_atom_10] } -fn expr_after_atom_19( +fn expr_after_atom_24( input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, LParen] { call_now![input, expr_after_atom_14] } else { - call_now![input, expr_after_atom_18] + call_now![input, expr_after_atom_23] } } fn expr_after_atom_12( @@ -1208,47 +1208,89 @@ fn expr_after_atom_14( ) -> Result, Option> { call_now![input, expr_after_atom_12, expr_after_atom_13] } +fn expr_after_atom_23( + input: &mut RustParser, +) -> Result, Option> { + if cond![input, peek, LBracket] { + call_now![input, expr_after_atom_18] + } else { + call_now![input, expr_after_atom_22] + } +} +fn expr_after_atom_15( + input: &mut RustParser, +) -> Result, Option> { + if bump![input, RBracket] { + call_then![input,] + } else { + error![input] + } +} +fn expr_after_atom_16( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr] +} +fn expr_after_atom_17( + input: &mut RustParser, +) -> Result, Option> { + if bump![input, LBracket] { + call_then![input,] + } else { + error![input] + } +} fn expr_after_atom_18( input: &mut RustParser, +) -> Result, Option> { + call_now![ + input, + expr_after_atom_15, + expr_after_atom_16, + expr_after_atom_17 + ] +} +fn expr_after_atom_22( + input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, Dot] { - call_now![input, expr_after_atom_17] + call_now![input, expr_after_atom_21] } else { call_now![input,] } } -fn expr_after_atom_15( +fn expr_after_atom_19( input: &mut RustParser, ) -> Result, Option> { call_now![input, expr_after_atom] } -fn expr_after_atom_16( +fn expr_after_atom_20( input: &mut RustParser, ) -> Result, Option> { call_now![input, expr_dot_expr] } -fn expr_after_atom_17( +fn expr_after_atom_21( input: &mut RustParser, ) -> Result, Option> { - call_now![input, expr_after_atom_15, expr_after_atom_16] + call_now![input, expr_after_atom_19, expr_after_atom_20] } -fn expr_after_atom_24( +fn expr_after_atom_29( input: &mut RustParser, ) -> Result, Option> { - call_now![input, expr_after_atom_23] + call_now![input, expr_after_atom_28] } fn expr_after_atom( input: &mut RustParser, ) -> Result, Option> { - call_now![input, expr_after_atom_24] + call_now![input, expr_after_atom_29] } -fn expr_atom_22( +fn expr_atom_31( input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, Return] || cond![input, peek, Break] { call_now![input, expr_atom_1] } else { - call_now![input, expr_atom_21] + call_now![input, expr_atom_30] } } fn expr_atom_0(input: &mut RustParser) -> Result, Option> { @@ -1257,7 +1299,7 @@ fn expr_atom_0(input: &mut RustParser) -> Result(input: &mut RustParser) -> Result, Option> { call_now![input, expr_atom_0] } -fn expr_atom_21( +fn expr_atom_30( input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, Ident] @@ -1266,7 +1308,7 @@ fn expr_atom_21( { call_now![input, expr_atom_3] } else { - call_now![input, expr_atom_20] + call_now![input, expr_atom_29] } } fn expr_atom_2(input: &mut RustParser) -> Result, Option> { @@ -1275,13 +1317,13 @@ fn expr_atom_2(input: &mut RustParser) -> Result(input: &mut RustParser) -> Result, Option> { call_now![input, expr_atom_2] } -fn expr_atom_20( +fn expr_atom_29( input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, FragmentExpr] || cond![input, peek, Literal] { call_now![input, expr_atom_5] } else { - call_now![input, expr_atom_19] + call_now![input, expr_atom_28] } } fn expr_atom_4(input: &mut RustParser) -> Result, Option> { @@ -1294,13 +1336,13 @@ fn expr_atom_4(input: &mut RustParser) -> Result(input: &mut RustParser) -> Result, Option> { call_now![input, expr_atom_4] } -fn expr_atom_19( +fn expr_atom_28( input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, If] { call_now![input, expr_atom_7] } else { - call_now![input, expr_atom_18] + call_now![input, expr_atom_27] } } fn expr_atom_6(input: &mut RustParser) -> Result, Option> { @@ -1309,13 +1351,13 @@ fn expr_atom_6(input: &mut RustParser) -> Result(input: &mut RustParser) -> Result, Option> { call_now![input, expr_atom_6] } -fn expr_atom_18( +fn expr_atom_27( input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, LParen] { call_now![input, expr_atom_9] } else { - call_now![input, expr_atom_17] + call_now![input, expr_atom_26] } } fn expr_atom_8(input: &mut RustParser) -> Result, Option> { @@ -1324,13 +1366,13 @@ fn expr_atom_8(input: &mut RustParser) -> Result(input: &mut RustParser) -> Result, Option> { call_now![input, expr_atom_8] } -fn expr_atom_17( +fn expr_atom_26( input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, LBracket] { call_now![input, expr_atom_11] } else { - call_now![input, expr_atom_16] + call_now![input, expr_atom_25] } } fn expr_atom_10( @@ -1343,13 +1385,13 @@ fn expr_atom_11( ) -> Result, Option> { call_now![input, expr_atom_10] } -fn expr_atom_16( +fn expr_atom_25( input: &mut RustParser, ) -> Result, Option> { if cond![input, peek, LBrace] { call_now![input, expr_atom_13] } else { - call_now![input, expr_atom_15] + call_now![input, expr_atom_24] } } fn expr_atom_12( @@ -1362,10 +1404,19 @@ fn expr_atom_13( ) -> Result, Option> { call_now![input, expr_atom_12] } +fn expr_atom_24( + input: &mut RustParser, +) -> Result, Option> { + if cond![input, peek, Loop] { + call_now![input, expr_atom_15] + } else { + call_now![input, expr_atom_23] + } +} fn expr_atom_14( input: &mut RustParser, ) -> Result, Option> { - error![input] + call_now![input, expr_loop] } fn expr_atom_15( input: &mut RustParser, @@ -1375,10 +1426,58 @@ fn expr_atom_15( fn expr_atom_23( input: &mut RustParser, ) -> Result, Option> { - call_now![input, expr_atom_22] + if cond![input, peek, While] { + call_now![input, expr_atom_17] + } else { + call_now![input, expr_atom_22] + } +} +fn expr_atom_16( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_while] +} +fn expr_atom_17( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_atom_16] +} +fn expr_atom_22( + input: &mut RustParser, +) -> Result, Option> { + if cond![input, peek, For] { + call_now![input, expr_atom_19] + } else { + call_now![input, expr_atom_21] + } +} +fn expr_atom_18( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_for] +} +fn expr_atom_19( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_atom_18] +} +fn expr_atom_20( + input: &mut RustParser, +) -> Result, Option> { + error![input] +} +fn expr_atom_21( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_atom_20] +} +fn expr_atom_32( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_atom_31] } fn expr_atom(input: &mut RustParser) -> Result, Option> { - call_now![input, expr_atom_23] + call_now![input, expr_atom_32] } fn expr_return_or_break_2( input: &mut RustParser, @@ -2825,3 +2924,77 @@ fn expr_tuple__11( fn expr_tuple_(input: &mut RustParser) -> Result, Option> { call_now![input, expr_tuple__11] } +fn expr_loop_0(input: &mut RustParser) -> Result, Option> { + call_now![input, block] +} +fn expr_loop_1(input: &mut RustParser) -> Result, Option> { + if bump![input, Loop] { + call_then![input,] + } else { + error![input] + } +} +fn expr_loop_2(input: &mut RustParser) -> Result, Option> { + call_now![input, expr_loop_0, expr_loop_1] +} +fn expr_loop(input: &mut RustParser) -> Result, Option> { + call_now![input, expr_loop_2] +} +fn expr_while_0( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, block] +} +fn expr_while_1( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr] +} +fn expr_while_2( + input: &mut RustParser, +) -> Result, Option> { + if bump![input, While] { + call_then![input,] + } else { + error![input] + } +} +fn expr_while_3( + input: &mut RustParser, +) -> Result, Option> { + call_now![input, expr_while_0, expr_while_1, expr_while_2] +} +fn expr_while(input: &mut RustParser) -> Result, Option> { + call_now![input, expr_while_3] +} +fn expr_for_0(input: &mut RustParser) -> Result, Option> { + call_now![input, block] +} +fn expr_for_1(input: &mut RustParser) -> Result, Option> { + call_now![input, expr] +} +fn expr_for_2(input: &mut RustParser) -> Result, Option> { + if bump![input, In] { + call_then![input,] + } else { + error![input] + } +} +fn expr_for_3(input: &mut RustParser) -> Result, Option> { + call_now![input, pat] +} +fn expr_for_4(input: &mut RustParser) -> Result, Option> { + if bump![input, For] { + call_then![input,] + } else { + error![input] + } +} +fn expr_for_5(input: &mut RustParser) -> Result, Option> { + call_now![ + input, expr_for_0, expr_for_1, expr_for_2, expr_for_3, expr_for_4 + ] +} +fn expr_for(input: &mut RustParser) -> Result, Option> { + call_now![input, expr_for_5] +} diff --git a/tests/ui/fail/expr-tuple.stderr b/tests/ui/fail/expr-tuple.stderr index 306bbe3..069bb06 100644 --- a/tests/ui/fail/expr-tuple.stderr +++ b/tests/ui/fail/expr-tuple.stderr @@ -1,16 +1,16 @@ -error: Potentially invalid expansion. Expected `::`, `break`, `if`, `return`, a `(`, a `)` or 4 others. +error: Potentially invalid expansion. Expected `::`, `break`, `for`, `if`, `loop`, `return` or 7 others. --> tests/ui/fail/expr-tuple.rs:6:10 | 6 | (,) | ^ -error: Potentially invalid expansion. Expected `::`, `break`, `if`, `return`, a `(`, a `)` or 4 others. +error: Potentially invalid expansion. Expected `::`, `break`, `for`, `if`, `loop`, `return` or 7 others. --> tests/ui/fail/expr-tuple.rs:13:13 | 13 | (42,, 42) | ^ -error: Potentially invalid expansion. Expected `!=`, `%`, `&&`, `&`, `*`, `+` or 20 others. +error: Potentially invalid expansion. Expected `!=`, `%`, `&&`, `&`, `*`, `+` or 21 others. --> tests/ui/fail/expr-tuple.rs:20:12 | 20 | (a b) diff --git a/tests/ui/fail/invalid_fn_calls.stderr b/tests/ui/fail/invalid_fn_calls.stderr index 6743a35..ac4924c 100644 --- a/tests/ui/fail/invalid_fn_calls.stderr +++ b/tests/ui/fail/invalid_fn_calls.stderr @@ -1,16 +1,16 @@ -error: Potentially invalid expansion. Expected `::`, `break`, `if`, `return`, a `(`, a `)` or 4 others. +error: Potentially invalid expansion. Expected `::`, `break`, `for`, `if`, `loop`, `return` or 7 others. --> tests/ui/fail/invalid_fn_calls.rs:6:18 | 6 | $fn_name(,) | ^ -error: Potentially invalid expansion. Expected `::`, `break`, `if`, `return`, a `(`, a `)` or 4 others. +error: Potentially invalid expansion. Expected `::`, `break`, `for`, `if`, `loop`, `return` or 7 others. --> tests/ui/fail/invalid_fn_calls.rs:14:18 | 14 | $fn_name(,,) | ^ -error: Potentially invalid expansion. Expected `!=`, `%`, `&&`, `&`, `*`, `+` or 18 others. +error: Potentially invalid expansion. Expected `!=`, `%`, `&&`, `&`, `*`, `+` or 19 others. --> tests/ui/fail/invalid_fn_calls.rs:22:25 | 22 | $fn_name($arg1 $arg2) diff --git a/tests/ui/fail/js_concat.stderr b/tests/ui/fail/js_concat.stderr index 544da16..952b7ba 100644 --- a/tests/ui/fail/js_concat.stderr +++ b/tests/ui/fail/js_concat.stderr @@ -1,4 +1,4 @@ -error: Potentially invalid expansion. Expected `::`, `break`, `if`, `return`, a `(`, a `[` or 3 others. +error: Potentially invalid expansion. Expected `::`, `break`, `for`, `if`, `loop`, `return` or 6 others. --> tests/ui/fail/js_concat.rs:5:16 | 5 | $left ++ $right diff --git a/tests/ui/fail/loops.rs b/tests/ui/fail/loops.rs new file mode 100644 index 0000000..3498013 --- /dev/null +++ b/tests/ui/fail/loops.rs @@ -0,0 +1,24 @@ +#![allow(unused)] + +#[expandable::expr] +macro_rules! a { + () => { + loop loop {} + }; +} + +#[expandable::expr] +macro_rules! b { + () => { + while < 42 {} + }; +} + +#[expandable::expr] +macro_rules! c { + () => { + for ! in in {} + }; +} + +fn main() {} diff --git a/tests/ui/fail/loops.stderr b/tests/ui/fail/loops.stderr new file mode 100644 index 0000000..504810b --- /dev/null +++ b/tests/ui/fail/loops.stderr @@ -0,0 +1,17 @@ +error: Potentially invalid expansion. Expected a `{`. + --> tests/ui/fail/loops.rs:6:14 + | +6 | loop loop {} + | ^^^^ + +error: Potentially invalid expansion. Expected `::`, `break`, `for`, `if`, `loop`, `return` or 6 others. + --> tests/ui/fail/loops.rs:13:15 + | +13 | while < 42 {} + | ^ + +error: Potentially invalid expansion. Expected an identifier. + --> tests/ui/fail/loops.rs:20:13 + | +20 | for ! in in {} + | ^ diff --git a/tests/ui/fail/python_power_operator.stderr b/tests/ui/fail/python_power_operator.stderr index 47e6478..5e5f541 100644 --- a/tests/ui/fail/python_power_operator.stderr +++ b/tests/ui/fail/python_power_operator.stderr @@ -1,4 +1,4 @@ -error: Potentially invalid expansion. Expected `::`, `break`, `if`, `return`, a `(`, a `[` or 3 others. +error: Potentially invalid expansion. Expected `::`, `break`, `for`, `if`, `loop`, `return` or 6 others. --> tests/ui/fail/python_power_operator.rs:5:14 | 5 | $e * *$e diff --git a/tests/ui/pass/expr-index.rs b/tests/ui/pass/expr-index.rs new file mode 100644 index 0000000..e34255d --- /dev/null +++ b/tests/ui/pass/expr-index.rs @@ -0,0 +1,14 @@ +#![allow(unused)] + +#[expandable::expr] +macro_rules! a { + () => { + a[b] + }; + + ($a:expr, $b:expr) => { + $a[$b] + }; +} + +fn main() {} diff --git a/tests/ui/pass/field_access.rs b/tests/ui/pass/field_access.rs index 759c27c..37c91a2 100644 --- a/tests/ui/pass/field_access.rs +++ b/tests/ui/pass/field_access.rs @@ -12,6 +12,10 @@ macro_rules! field { () => { "foo".0 + 1 }; + + () => { + a.b.0.1.c.2.3 + }; } fn main() {} diff --git a/tests/ui/pass/loops.rs b/tests/ui/pass/loops.rs new file mode 100644 index 0000000..9ea2b3c --- /dev/null +++ b/tests/ui/pass/loops.rs @@ -0,0 +1,26 @@ +#![allow(unused)] + +#[expandable::expr] +macro_rules! all_loops { + () => { + loop {} + }; + + () => { + loop { + break 42; + } + }; + + () => { + while a > 0 {} + }; + + () => { + for i in 0..42 { + printf("%d\n", i); + } + }; +} + +fn main() {}