From 4fb556e6db7f4d2a01bbba1bddf75f90b7eb346e Mon Sep 17 00:00:00 2001 From: Jonathan Giddy Date: Fri, 29 Mar 2024 08:08:52 +0000 Subject: [PATCH] Use typle for tuple functions This generates almost identical code to the existing macros while being more readable. The only semantic difference is for `Choice`, where the final option does not rewind the input. --- Cargo.lock | 24 ++++-- Cargo.toml | 1 + src/pratt.rs | 205 +++++++++++++++++++++++++---------------------- src/primitive.rs | 180 ++++++++++------------------------------- 4 files changed, 170 insertions(+), 240 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 70dd1b41..eba75d5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,6 +215,7 @@ dependencies = [ "sn", "spin", "stacker", + "typle", "unicode-ident", "vergen", "winnow", @@ -1662,9 +1663,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -1695,9 +1696,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1994,9 +1995,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.32" +version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", @@ -2102,6 +2103,17 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "typle" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2ded4bafe4b4ea72fe8e6a685a0fed63e5bc4fc1f287787d9c027d037a85e3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ucd-trie" version = "0.1.6" diff --git a/Cargo.toml b/Cargo.toml index bfd79f23..fe7cfc7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,6 +79,7 @@ spin = { version = "0.9", features = ["once"], default-features = false, optiona lexical = { version = "6.1.1", default-features = false, features = ["parse-integers", "parse-floats", "format"], optional = true } either = { version = "1.8.1", optional = true } serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } +typle = "0.9.6" unicode-ident = "1.0.10" [dev-dependencies] diff --git a/src/pratt.rs b/src/pratt.rs index c6951b82..9dac4115 100644 --- a/src/pratt.rs +++ b/src/pratt.rs @@ -89,6 +89,8 @@ //! ); //! ``` +use typle::typle; + use super::*; trait Operator<'a, I, O, E> @@ -404,117 +406,124 @@ pub struct Pratt { pub(crate) ops: Ops, } -macro_rules! impl_pratt_for_tuple { - () => {}; - ($head:ident $($X:ident)*) => { - impl_pratt_for_tuple!($($X)*); - impl_pratt_for_tuple!(~ $head $($X)*); - }; - (~ $($X:ident)+) => { - #[allow(unused_variables, non_snake_case)] - impl<'a, Atom, $($X),*> Pratt { - #[inline] - fn pratt_go(&self, inp: &mut InputRef<'a, '_, I, E>, min_power: u32) -> PResult - where - I: Input<'a>, - E: ParserExtra<'a, I>, - Atom: Parser<'a, I, O, E>, - $($X: Operator<'a, I, O, E>),* - { - let pre_expr = inp.save(); - let mut lhs = 'choice: { - let ($($X,)*) = &self.ops; - - // Prefix unary operators - $( - if $X::IS_PREFIX { - match $X.op_parser().go::(inp) { - Ok(op) => { - match recursive::recurse(|| self.pratt_go::(inp, $X.associativity().left_power())) { - Ok(rhs) => break 'choice M::combine(op, rhs, |op, rhs| { - $X.fold_prefix(op, rhs, &mut MapExtra::new(pre_expr.offset(), inp)) - }), - Err(()) => inp.rewind(pre_expr), - } - }, +#[typle(Tuple for 1..=26)] +impl<'a, Atom, T: Tuple> Pratt { + #[inline] + fn pratt_go( + &self, + inp: &mut InputRef<'a, '_, I, E>, + min_power: u32, + ) -> PResult + where + I: Input<'a>, + E: ParserExtra<'a, I>, + Atom: Parser<'a, I, O, E>, + T<_>: Operator<'a, I, O, E>, + { + let pre_expr = inp.save(); + let mut lhs = 'choice: { + // Prefix unary operators + for typle_index!(i) in 0..T::LEN { + if T::<{ i }>::IS_PREFIX { + let t = &self.ops[[i]]; + match t.op_parser().go::(inp) { + Ok(op) => { + match recursive::recurse(|| { + self.pratt_go::(inp, t.associativity().left_power()) + }) { + Ok(rhs) => { + break 'choice M::combine(op, rhs, |op, rhs| { + t.fold_prefix( + op, + rhs, + &mut MapExtra::new(pre_expr.offset(), inp), + ) + }) + } Err(()) => inp.rewind(pre_expr), } } - )* - - self.atom.go::(inp)? - }; - - loop { - let ($($X,)*) = &self.ops; - - let pre_op = inp.save(); - - // Postfix unary operators - $( - let assoc = $X.associativity(); - if $X::IS_POSTFIX && assoc.right_power() >= min_power { - match $X.op_parser().go::(inp) { - Ok(op) => { - lhs = M::combine(lhs, op, |lhs, op| { - $X.fold_postfix(lhs, op, &mut MapExtra::new(pre_expr.offset(), inp)) - }); - continue - }, - Err(()) => inp.rewind(pre_op), - } + Err(()) => inp.rewind(pre_expr), + } + } + } + + self.atom.go::(inp)? + }; + + 'start: loop { + let pre_op = inp.save(); + + // Postfix unary operators + for typle_index!(i) in 0..T::LEN { + let t = &self.ops[[i]]; + let assoc = t.associativity(); + if T::<{ i }>::IS_POSTFIX && assoc.right_power() >= min_power { + match t.op_parser().go::(inp) { + Ok(op) => { + lhs = M::combine(lhs, op, |lhs, op| { + t.fold_postfix(lhs, op, &mut MapExtra::new(pre_expr.offset(), inp)) + }); + continue 'start; } - )* - - // Infix binary operators - $( - let assoc = $X.associativity(); - if $X::IS_INFIX && assoc.left_power() >= min_power { - match $X.op_parser().go::(inp) { - Ok(op) => match recursive::recurse(|| self.pratt_go::(inp, assoc.right_power())) { - Ok(rhs) => { - lhs = M::combine( - M::combine(lhs, rhs, |lhs, rhs| (lhs, rhs)), + Err(()) => inp.rewind(pre_op), + } + } + } + + // Infix binary operators + for typle_index!(i) in 0..T::LEN { + let t = &self.ops[[i]]; + let assoc = t.associativity(); + if T::<{ i }>::IS_INFIX && assoc.left_power() >= min_power { + match t.op_parser().go::(inp) { + Ok(op) => match recursive::recurse(|| { + self.pratt_go::(inp, assoc.right_power()) + }) { + Ok(rhs) => { + lhs = M::combine( + M::combine(lhs, rhs, |lhs, rhs| (lhs, rhs)), + op, + |(lhs, rhs), op| { + t.fold_infix( + lhs, op, - |(lhs, rhs), op| { - $X.fold_infix(lhs, op, rhs, &mut MapExtra::new(pre_expr.offset(), inp)) - }, - ); - continue + rhs, + &mut MapExtra::new(pre_expr.offset(), inp), + ) }, - Err(()) => inp.rewind(pre_op), - }, - Err(()) => inp.rewind(pre_op), + ); + continue 'start; } - } - )* - - inp.rewind(pre_op); - break; + Err(()) => inp.rewind(pre_op), + }, + Err(()) => inp.rewind(pre_op), + } } - - Ok(lhs) } - } - #[allow(unused_variables, non_snake_case)] - impl<'a, I, O, E, Atom, $($X),*> ParserSealed<'a, I, O, E> for Pratt - where - I: Input<'a>, - E: ParserExtra<'a, I>, - Atom: Parser<'a, I, O, E>, - $($X: Operator<'a, I, O, E>),* - { - fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { - self.pratt_go::(inp, 0) - } - - go_extra!(O); + inp.rewind(pre_op); + break; } - }; + + Ok(lhs) + } } -impl_pratt_for_tuple!(A_ B_ C_ D_ E_ F_ G_ H_ I_ J_ K_ L_ M_ N_ O_ P_ Q_ R_ S_ T_ U_ V_ W_ X_ Y_ Z_); +#[typle(Tuple for 1..=26)] +impl<'a, I, O, E, Atom, T: Tuple> ParserSealed<'a, I, O, E> for Pratt +where + I: Input<'a>, + E: ParserExtra<'a, I>, + Atom: Parser<'a, I, O, E>, + T<_>: Operator<'a, I, O, E>, +{ + fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { + self.pratt_go::(inp, 0) + } + + go_extra!(O); +} #[cfg(test)] mod tests { diff --git a/src/primitive.rs b/src/primitive.rs index 889c778f..24474fa7 100644 --- a/src/primitive.rs +++ b/src/primitive.rs @@ -15,6 +15,8 @@ //! - [`none_of`]: parses any input that does not appear in a sequence of inputs //! - [`end`]: parses the end of input (i.e: if there any more inputs, this parse fails) +use typle::typle; + use super::*; /// See [`end`]. @@ -867,64 +869,32 @@ pub const fn choice(parsers: T) -> Choice { Choice { parsers } } -macro_rules! impl_choice_for_tuple { - () => {}; - ($head:ident $($X:ident)*) => { - impl_choice_for_tuple!($($X)*); - impl_choice_for_tuple!(~ $head $($X)*); - }; - (~ $Head:ident $($X:ident)+) => { - #[allow(unused_variables, non_snake_case)] - impl<'a, I, E, $Head, $($X),*, O> ParserSealed<'a, I, O, E> for Choice<($Head, $($X,)*)> - where - I: Input<'a>, - E: ParserExtra<'a, I>, - $Head: Parser<'a, I, O, E>, - $($X: Parser<'a, I, O, E>),* - { - #[inline] - fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { - let before = inp.save(); - - let Choice { parsers: ($Head, $($X,)*), .. } = self; - - match $Head.go::(inp) { +#[typle(Tuple for 1..=26)] +impl<'a, I, E, T, O> ParserSealed<'a, I, O, E> for Choice +where + I: Input<'a>, + E: ParserExtra<'a, I>, + T: Tuple, + T<_>: Parser<'a, I, O, E>, +{ + #[inline] + fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { + if typle_const!(T::LEN > 1) { + let before = inp.save(); + + for typle_index!(i) in 0..T::LEN - 1 { + match self.parsers[[i]].go::(inp) { Ok(out) => return Ok(out), Err(()) => inp.rewind(before), } - - $( - match $X.go::(inp) { - Ok(out) => return Ok(out), - Err(()) => inp.rewind(before), - } - )* - - Err(()) } - - go_extra!(O); } - }; - (~ $Head:ident) => { - impl<'a, I, E, $Head, O> ParserSealed<'a, I, O, E> for Choice<($Head,)> - where - I: Input<'a>, - E: ParserExtra<'a, I>, - $Head: Parser<'a, I, O, E>, - { - #[inline] - fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { - self.parsers.0.go::(inp) - } + self.parsers[[T::LEN - 1]].go::(inp) + } - go_extra!(O); - } - }; + go_extra!(O); } -impl_choice_for_tuple!(A_ B_ C_ D_ E_ F_ G_ H_ I_ J_ K_ L_ M_ N_ O_ P_ Q_ R_ S_ T_ U_ V_ W_ X_ Y_ Z_); - impl<'a, 'b, A, I, O, E> ParserSealed<'a, I, O, E> for Choice<&'b [A]> where A: Parser<'a, I, O, E>, @@ -1020,90 +990,28 @@ where go_extra!([O; N]); } -macro_rules! flatten_map { - // map a single element into a 1-tuple - (<$M:ident> $head:ident) => { - $M::map( - $head, - |$head| ($head,), - ) - }; - // combine two elements into a 2-tuple - (<$M:ident> $head1:ident $head2:ident) => { - $M::combine( - $head1, - $head2, - |$head1, $head2| ($head1, $head2), - ) - }; - // combine and flatten n-tuples from recursion - (<$M:ident> $head:ident $($X:ident)+) => { - $M::combine( - $head, - flatten_map!( - <$M> - $($X)+ - ), - |$head, ($($X),+)| ($head, $($X),+), - ) - }; -} - -macro_rules! impl_group_for_tuple { - () => {}; - ($head:ident $ohead:ident $($X:ident $O:ident)*) => { - impl_group_for_tuple!($($X $O)*); - impl_group_for_tuple!(~ $head $ohead $($X $O)*); - }; - (~ $($X:ident $O:ident)*) => { - #[allow(unused_variables, non_snake_case)] - impl<'a, I, E, $($X),*, $($O),*> ParserSealed<'a, I, ($($O,)*), E> for Group<($($X,)*)> - where - I: Input<'a>, - E: ParserExtra<'a, I>, - $($X: Parser<'a, I, $O, E>),* - { - #[inline] - fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { - let Group { parsers: ($($X,)*) } = self; - - $( - let $X = $X.go::(inp)?; - )* - - Ok(flatten_map!( $($X)*)) - } - - go_extra!(($($O,)*)); - } - }; -} - -impl_group_for_tuple! { - A_ OA - B_ OB - C_ OC - D_ OD - E_ OE - F_ OF - G_ OG - H_ OH - I_ OI - J_ OJ - K_ OK - L_ OL - M_ OM - N_ ON - O_ OO - P_ OP - Q_ OQ - R_ OR - S_ OS - T_ OT - U_ OU - V_ OV - W_ OW - X_ OX - Y_ OY - Z_ OZ +#[typle(Tuple for 1..=26)] +impl<'a, I, E, T, O> ParserSealed<'a, I, O, E> for Group +where + I: Input<'a>, + E: ParserExtra<'a, I>, + T: Tuple, + O: Tuple, + typle_bound!(i in ..T::LEN => T<{i}>): Parser<'a, I, O<{ i }>, E>, +{ + #[inline] + fn go(&self, inp: &mut InputRef<'a, '_, I, E>) -> PResult { + let outputs = typle_for!(i in ..T::LEN => self.parsers[[i]].go::(inp)?); + let result = typle_fold!( + M::map(outputs.0, |b| (b,)); + i in 1..T::LEN => |acc| M::combine( + acc, + outputs[[i]], + |a, b| typle_for!(j in ..=i => if typle_const!(j < i) { a[[j]] } else { b }) + ) + ); + Ok(result) + } + + go_extra!(typle_ty!(O)); }