From 5abd7955cec76e1178358a3cb9e3c39ab511421b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Thu, 22 May 2025 19:40:49 +0200 Subject: [PATCH 1/5] Rename binding_power to precedence Binding power is an implementation detail, what users specify is the precedence level. --- src/pratt.rs | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/pratt.rs b/src/pratt.rs index e9b1f503..78bc5253 100644 --- a/src/pratt.rs +++ b/src/pratt.rs @@ -434,32 +434,32 @@ where } } -/// Defines the [associativity](https://en.wikipedia.org/wiki/Associative_property) and binding power of an [`infix`] -/// operator (see [`left`] and [`right`]). +/// Defines the [associativity](https://en.wikipedia.org/wiki/Associative_property) and precedence of an [`infix`] +/// operator (see [`left`], [`right`] and [`none`]). /// -/// Higher binding powers should be used for higher precedence operators. +/// Higher numbers should be used for higher precedence operators. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Associativity { - /// Specifies that the operator should be left-associative, with the given binding power (see [`left`]). + /// Specifies that the operator should be left-associative, with the given precedence (see [`left`]). Left(u16), - /// Specifies that the operator should be right-associative, with the given binding power (see [`right`]). + /// Specifies that the operator should be right-associative, with the given precedence (see [`right`]). Right(u16), } -/// Specifies a left [`Associativity`] with the given binding power. +/// Specifies a left [`Associativity`] with the given precedence. /// /// Left-associative operators are evaluated from the left-most terms, moving rightward. For example, the expression /// `a + b + c + d` will be evaluated as `((a + b) + c) + d` because addition is conventionally left-associative. -pub fn left(binding_power: u16) -> Associativity { - Associativity::Left(binding_power) +pub fn left(precedence: u16) -> Associativity { + Associativity::Left(precedence) } -/// Specifies a right [`Associativity`] with the given binding power. +/// Specifies a right [`Associativity`] with the given precedence. /// /// Right-associative operators are evaluated from the right-most terms, moving leftward. For example, the expression /// `a ^ b ^ c ^ d` will be evaluated as `a ^ (b ^ (c ^ d))` because exponents are conventionally right-associative. -pub fn right(binding_power: u16) -> Associativity { - Associativity::Right(binding_power) +pub fn right(precedence: u16) -> Associativity { + Associativity::Right(precedence) } impl Associativity { @@ -499,7 +499,7 @@ impl Clone for Infix<'_, A, F, Atom, Op, I, } } -/// Specify a binary infix operator for a pratt parser with the given associativity, binding power, and +/// Specify a binary infix operator for a pratt parser with the given associativity, precedence, and /// [fold function](crate::pratt#fold-functions). /// /// Operators like addition, subtraction, multiplication, division, remainder, exponentiation, etc. are infix binary @@ -581,7 +581,7 @@ where pub struct Prefix<'src, A, F, Atom, Op, I, E> { op_parser: A, fold: F, - binding_power: u16, + binding_power: u32, #[allow(dead_code)] phantom: EmptyPhantom<&'src (Atom, Op, I, E)>, } @@ -598,7 +598,7 @@ impl Clone for Prefix<'_, A, F, Atom, Op, I, } } -/// Specify a unary prefix operator for a pratt parser with the given binding power and +/// Specify a unary prefix operator for a pratt parser with the given precedence and /// [fold function](crate::pratt#fold-functions). /// /// Operators like negation, not, dereferencing, etc. are prefix unary operators in most languages. @@ -610,7 +610,7 @@ impl Clone for Prefix<'_, A, F, Atom, Op, I, /// impl Fn(Op, Atom, &mut MapExtra<'src, '_, I, E>) -> O /// ``` pub const fn prefix<'src, A, F, Atom, Op, I, E>( - binding_power: u16, + precedence: u16, op_parser: A, fold: F, ) -> Prefix<'src, A, F, Atom, Op, I, E> @@ -620,7 +620,7 @@ where Prefix { op_parser, fold, - binding_power, + binding_power: precedence as u32 * 2, phantom: EmptyPhantom::new(), } } @@ -643,7 +643,7 @@ where Self: Sized, { match self.op_parser.go::(inp) { - Ok(op) => match f(inp, Associativity::Left(self.binding_power).left_power()) { + Ok(op) => match f(inp, self.binding_power) { Ok(rhs) => Ok(M::combine(op, rhs, |op, rhs| { (self.fold)(op, rhs, &mut MapExtra::new(pre_expr.cursor(), inp)) })), @@ -666,7 +666,7 @@ where pub struct Postfix<'src, A, F, Atom, Op, I, E> { op_parser: A, fold: F, - binding_power: u16, + binding_power: u32, #[allow(dead_code)] phantom: EmptyPhantom<&'src (Atom, Op, I, E)>, } @@ -683,7 +683,7 @@ impl Clone for Postfix<'_, A, F, Atom, Op, I } } -/// Specify a unary postfix operator for a pratt parser with the given binding power and +/// Specify a unary postfix operator for a pratt parser with the given precedence and /// [fold function](crate::pratt#fold-functions). /// /// Operators like factorial, field access, etc. are postfix unary operators in most languages. @@ -695,7 +695,7 @@ impl Clone for Postfix<'_, A, F, Atom, Op, I /// impl Fn(Atom, Op, &mut MapExtra<'src, '_, I, E>) -> O /// ``` pub const fn postfix<'src, A, F, Atom, Op, I, E>( - binding_power: u16, + precedence: u16, op_parser: A, fold: F, ) -> Postfix<'src, A, F, Atom, Op, I, E> @@ -705,7 +705,7 @@ where Postfix { op_parser, fold, - binding_power, + binding_power: precedence as u32 * 2 + 1, phantom: EmptyPhantom::new(), } } @@ -729,7 +729,7 @@ where where Self: Sized, { - if Associativity::Left(self.binding_power).right_power() >= min_power { + if self.binding_power >= min_power { match self.op_parser.go::(inp) { Ok(op) => Ok(M::combine(lhs, op, |lhs, op| { (self.fold)(lhs, op, &mut MapExtra::new(pre_expr, inp)) From b09ffbef7ad199994080c742effa04d60c3a4b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Sat, 24 May 2025 15:52:18 +0200 Subject: [PATCH 2/5] Change binding power to i32 The binding power zero is used as a special marker for the first invocation. User can specify a precedence of zero resulting in a binding power of zero, which would mimic this marker. Use an i32 and i32:MIN as a marker instead. --- src/pratt.rs | 112 ++++++++++++++++++++++++------------------------- src/private.rs | 24 +++++------ 2 files changed, 68 insertions(+), 68 deletions(-) diff --git a/src/pratt.rs b/src/pratt.rs index 78bc5253..7d885d68 100644 --- a/src/pratt.rs +++ b/src/pratt.rs @@ -99,7 +99,7 @@ macro_rules! op_check_and_emit { I, >::Checkpoint, >, - f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult { self.do_parse_prefix::(inp, pre_expr, &f) } @@ -113,7 +113,7 @@ macro_rules! op_check_and_emit { I, >::Checkpoint, >, - f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult { self.do_parse_prefix::(inp, pre_expr, &f) } @@ -129,7 +129,7 @@ macro_rules! op_check_and_emit { >::Checkpoint, >, lhs: (), - min_power: u32, + min_power: i32, ) -> Result<(), ()> { self.do_parse_postfix::(inp, pre_expr, pre_op, lhs, min_power) } @@ -145,7 +145,7 @@ macro_rules! op_check_and_emit { >::Checkpoint, >, lhs: O, - min_power: u32, + min_power: i32, ) -> Result { self.do_parse_postfix::(inp, pre_expr, pre_op, lhs, min_power) } @@ -161,8 +161,8 @@ macro_rules! op_check_and_emit { >::Checkpoint, >, lhs: (), - min_power: u32, - f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: i32, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result<(), ()> { self.do_parse_infix::(inp, pre_expr, pre_op, lhs, min_power, &f) } @@ -178,8 +178,8 @@ macro_rules! op_check_and_emit { >::Checkpoint, >, lhs: O, - min_power: u32, - f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: i32, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result { self.do_parse_infix::(inp, pre_expr, pre_op, lhs, min_power, &f) } @@ -211,7 +211,7 @@ where I, >::Checkpoint, >, - _f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + _f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult where Self: Sized, @@ -227,7 +227,7 @@ where _pre_expr: &input::Cursor<'src, 'parse, I>, _pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: M::Output, - _min_power: u32, + _min_power: i32, ) -> Result, M::Output> where Self: Sized, @@ -243,8 +243,8 @@ where _pre_expr: &input::Cursor<'src, 'parse, I>, _pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: M::Output, - _min_power: u32, - _f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + _min_power: i32, + _f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result, M::Output> where Self: Sized, @@ -257,14 +257,14 @@ where &self, inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult; #[doc(hidden)] fn do_parse_prefix_emit<'parse>( &self, inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult; #[doc(hidden)] fn do_parse_postfix_check<'parse>( @@ -273,7 +273,7 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: (), - min_power: u32, + min_power: i32, ) -> Result<(), ()>; #[doc(hidden)] fn do_parse_postfix_emit<'parse>( @@ -282,7 +282,7 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: O, - min_power: u32, + min_power: i32, ) -> Result; #[doc(hidden)] fn do_parse_infix_check<'parse>( @@ -291,8 +291,8 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: (), - min_power: u32, - f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: i32, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result<(), ()>; #[doc(hidden)] fn do_parse_infix_emit<'parse>( @@ -301,8 +301,8 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: O, - min_power: u32, - f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: i32, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result; } @@ -325,7 +325,7 @@ where &self, inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult where Self: Sized, @@ -340,7 +340,7 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: M::Output, - min_power: u32, + min_power: i32, ) -> Result, M::Output> where Self: Sized, @@ -355,8 +355,8 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: M::Output, - min_power: u32, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: i32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result, M::Output> where Self: Sized, @@ -369,7 +369,7 @@ where &self, inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult { self.0.do_parse_prefix_check(inp, pre_expr, f) } @@ -378,7 +378,7 @@ where &self, inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult { self.0.do_parse_prefix_emit(inp, pre_expr, f) } @@ -389,7 +389,7 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: (), - min_power: u32, + min_power: i32, ) -> Result<(), ()> { self.0 .do_parse_postfix_check(inp, pre_expr, pre_op, lhs, min_power) @@ -401,7 +401,7 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: O, - min_power: u32, + min_power: i32, ) -> Result { self.0 .do_parse_postfix_emit(inp, pre_expr, pre_op, lhs, min_power) @@ -413,8 +413,8 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: (), - min_power: u32, - f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: i32, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result<(), ()> { self.0 .do_parse_infix_check(inp, pre_expr, pre_op, lhs, min_power, &f) @@ -426,8 +426,8 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: O, - min_power: u32, - f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: i32, + f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result { self.0 .do_parse_infix_emit(inp, pre_expr, pre_op, lhs, min_power, &f) @@ -463,17 +463,17 @@ pub fn right(precedence: u16) -> Associativity { } impl Associativity { - fn left_power(&self) -> u32 { + fn left_power(&self) -> i32 { match self { - Self::Left(x) => *x as u32 * 2, - Self::Right(x) => *x as u32 * 2 + 1, + Self::Left(x) => *x as i32 * 2, + Self::Right(x) => *x as i32 * 2 + 1, } } - fn right_power(&self) -> u32 { + fn right_power(&self) -> i32 { match self { - Self::Left(x) => *x as u32 * 2 + 1, - Self::Right(x) => *x as u32 * 2, + Self::Left(x) => *x as i32 * 2 + 1, + Self::Right(x) => *x as i32 * 2, } } } @@ -543,8 +543,8 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: M::Output, - min_power: u32, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: i32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result, M::Output> where Self: Sized, @@ -581,7 +581,7 @@ where pub struct Prefix<'src, A, F, Atom, Op, I, E> { op_parser: A, fold: F, - binding_power: u32, + binding_power: i32, #[allow(dead_code)] phantom: EmptyPhantom<&'src (Atom, Op, I, E)>, } @@ -620,7 +620,7 @@ where Prefix { op_parser, fold, - binding_power: precedence as u32 * 2, + binding_power: precedence as i32 * 2, phantom: EmptyPhantom::new(), } } @@ -637,7 +637,7 @@ where &self, inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult where Self: Sized, @@ -666,7 +666,7 @@ where pub struct Postfix<'src, A, F, Atom, Op, I, E> { op_parser: A, fold: F, - binding_power: u32, + binding_power: i32, #[allow(dead_code)] phantom: EmptyPhantom<&'src (Atom, Op, I, E)>, } @@ -705,7 +705,7 @@ where Postfix { op_parser, fold, - binding_power: precedence as u32 * 2 + 1, + binding_power: precedence as i32 * 2 + 1, phantom: EmptyPhantom::new(), } } @@ -724,7 +724,7 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: M::Output, - min_power: u32, + min_power: i32, ) -> Result, M::Output> where Self: Sized, @@ -773,7 +773,7 @@ macro_rules! impl_operator_for_tuple { &self, inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult where Self: Sized, @@ -795,7 +795,7 @@ macro_rules! impl_operator_for_tuple { pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, mut lhs: M::Output, - min_power: u32, + min_power: i32, ) -> Result, M::Output> where Self: Sized, @@ -817,8 +817,8 @@ macro_rules! impl_operator_for_tuple { pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, mut lhs: M::Output, - min_power: u32, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: i32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result, M::Output> where Self: Sized, @@ -852,7 +852,7 @@ where &self, inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult where Self: Sized, @@ -872,7 +872,7 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, mut lhs: M::Output, - min_power: u32, + min_power: i32, ) -> Result, M::Output> where Self: Sized, @@ -893,8 +893,8 @@ where pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, mut lhs: M::Output, - min_power: u32, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: i32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result, M::Output> where Self: Sized, @@ -917,7 +917,7 @@ impl<'src, Atom, Ops> Pratt { fn pratt_go( &self, inp: &mut InputRef<'src, '_, I, E>, - min_power: u32, + min_power: i32, ) -> PResult where I: Input<'src>, @@ -986,7 +986,7 @@ where Ops: Operator<'src, I, O, E>, { fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { - self.pratt_go::(inp, 0) + self.pratt_go::(inp, i32::MIN) } go_extra!(O); diff --git a/src/private.rs b/src/private.rs index 1e76cdeb..4b0bb8d3 100644 --- a/src/private.rs +++ b/src/private.rs @@ -81,7 +81,7 @@ pub trait Mode { op: &Op, inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult where Op: pratt::Operator<'src, I, O, E>, @@ -94,7 +94,7 @@ pub trait Mode { pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: Self::Output, - min_power: u32, + min_power: i32, ) -> Result, Self::Output> where Op: pratt::Operator<'src, I, O, E>, @@ -107,8 +107,8 @@ pub trait Mode { pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: Self::Output, - min_power: u32, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: i32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result, Self::Output> where Op: pratt::Operator<'src, I, O, E>, @@ -193,7 +193,7 @@ impl Mode for Emit { op: &Op, inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult where Op: pratt::Operator<'src, I, O, E>, @@ -210,7 +210,7 @@ impl Mode for Emit { pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: Self::Output, - min_power: u32, + min_power: i32, ) -> Result, Self::Output> where Op: pratt::Operator<'src, I, O, E>, @@ -227,8 +227,8 @@ impl Mode for Emit { pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: Self::Output, - min_power: u32, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: i32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result, Self::Output> where Op: pratt::Operator<'src, I, O, E>, @@ -304,7 +304,7 @@ impl Mode for Check { op: &Op, inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> PResult where Op: pratt::Operator<'src, I, O, E>, @@ -321,7 +321,7 @@ impl Mode for Check { pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: Self::Output, - min_power: u32, + min_power: i32, ) -> Result, Self::Output> where Op: pratt::Operator<'src, I, O, E>, @@ -338,8 +338,8 @@ impl Mode for Check { pre_expr: &input::Cursor<'src, 'parse, I>, pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: Self::Output, - min_power: u32, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, u32) -> PResult, + min_power: i32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, ) -> Result, Self::Output> where Op: pratt::Operator<'src, I, O, E>, From 4e20ac1a42afe62ed4b1ecba3cd2a45e6b8cdaa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Thu, 22 May 2025 19:47:34 +0200 Subject: [PATCH 3/5] Add Associativity::None This designates an infox operator as non-associative. Chained operator result in a parse error because without an associativity they can't be unambigiously parsed. Also add some test cases that will fail for now. --- src/pratt.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/pratt.rs b/src/pratt.rs index 7d885d68..87ce0ced 100644 --- a/src/pratt.rs +++ b/src/pratt.rs @@ -444,6 +444,8 @@ pub enum Associativity { Left(u16), /// Specifies that the operator should be right-associative, with the given precedence (see [`right`]). Right(u16), + /// Specifies that the operator is non-associative, with the given precedence (see [`none`]). + None(u16), } /// Specifies a left [`Associativity`] with the given precedence. @@ -462,11 +464,20 @@ pub fn right(precedence: u16) -> Associativity { Associativity::Right(precedence) } +/// Specifies no [`Associativity`] with the given precedence. +/// +/// Non-associative operators can't be chained. For example, the expression +/// `a < b < c` will produce an error, because comparisons are conventionally non-associative. +pub fn none(precedence: u16) -> Associativity { + Associativity::None(precedence) +} + impl Associativity { fn left_power(&self) -> i32 { match self { Self::Left(x) => *x as i32 * 2, Self::Right(x) => *x as i32 * 2 + 1, + Self::None(x) => *x as i32 * 2, } } @@ -474,6 +485,7 @@ impl Associativity { match self { Self::Left(x) => *x as i32 * 2 + 1, Self::Right(x) => *x as i32 * 2, + Self::None(x) => *x as i32 * 2, } } } @@ -1053,6 +1065,7 @@ mod tests { Confusion(Box), Factorial(Box), Value(Box), + Less(Box, Box), Add(Box, Box), Sub(Box, Box), Mul(Box, Box), @@ -1068,6 +1081,7 @@ mod tests { Self::Confusion(right) => write!(f, "(§{right})"), Self::Factorial(right) => write!(f, "({right}!)"), Self::Value(right) => write!(f, "({right}$)"), + Self::Less(left, right) => write!(f, "({left} < {right})"), Self::Add(left, right) => write!(f, "({left} + {right})"), Self::Sub(left, right) => write!(f, "({left} - {right})"), Self::Mul(left, right) => write!(f, "({left} * {right})"), @@ -1254,4 +1268,37 @@ mod tests { Ok("(((§(1 + (-(~(2!)))))$) * 3)".to_string()), ) } + + fn non_associative_parser<'src>() -> impl Parser<'src, &'src str, String, Err>> { + let atom = text::int(10).from_str().unwrapped().map(Expr::Literal); + + atom.pratt(( + infix(none(1), just('<'), |l, _, r, _| i(Expr::Less, l, r)), + infix(left(2), just('+'), |l, _, r, _| i(Expr::Add, l, r)), + infix(left(2), just('-'), |l, _, r, _| i(Expr::Sub, l, r)), + infix(right(3), just('*'), |l, _, r, _| i(Expr::Mul, l, r)), + infix(right(3), just('/'), |l, _, r, _| i(Expr::Div, l, r)), + )) + .map(|x| x.to_string()) + } + + #[test] + fn with_non_associative_infix_ops() { + assert_eq!( + non_associative_parser().parse("1+2*3<10/2").into_result(), + Ok("((1 + (2 * 3)) < (10 / 2))".to_string()), + ) + } + + #[test] + fn with_chained_non_associative_infix_ops() { + assert_eq!( + non_associative_parser().parse("1<2<3").into_result(), + Err(vec![dbg!(unexpected(Some('<'.into()), 3..4))]) + ); + assert_eq!( + non_associative_parser().parse("1+2*3<10/2<42").into_result(), + Err(vec![dbg!(unexpected(Some('<'.into()), 10..11))]) + ) + } } From 17985a47081b51398b811860ffbed4f420322a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Sat, 24 May 2025 16:00:07 +0200 Subject: [PATCH 4/5] Implement the None associativity All pratt operators can now return two types of errors, non-fatal (`NoMatch`), which allow the parse to continue and fatal (`Err`) errors, that abort the parse. If a non-associative infix operator encounters an operator with the same binding strength it returns a fatal error because of an ambigious input. For this to work, the infix operator mus first parse it's operator before checking the binding power. --- src/pratt.rs | 197 ++++++++++++++++++++++++++++--------------------- src/private.rs | 18 ++--- 2 files changed, 122 insertions(+), 93 deletions(-) diff --git a/src/pratt.rs b/src/pratt.rs index 87ce0ced..76ed8f22 100644 --- a/src/pratt.rs +++ b/src/pratt.rs @@ -87,6 +87,16 @@ use super::*; +/// The result of calling [`Operator::do_parse_prefix`], [`Operator::do_parse_postfix`] or [`Operator::do_parse_infix`] +pub enum OperatorResult { + /// Input was parsed + Ok(T), + /// Input could not be parsed, not fatal + NoMatch(E), + /// Input could not be parsed, fatal error + Err(E), +} + macro_rules! op_check_and_emit { () => { #[inline(always)] @@ -100,7 +110,7 @@ macro_rules! op_check_and_emit { >::Checkpoint, >, f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult { + ) -> OperatorResult<::Output, ()> { self.do_parse_prefix::(inp, pre_expr, &f) } #[inline(always)] @@ -114,7 +124,7 @@ macro_rules! op_check_and_emit { >::Checkpoint, >, f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult { + ) -> OperatorResult<::Output, ()> { self.do_parse_prefix::(inp, pre_expr, &f) } #[inline(always)] @@ -130,7 +140,7 @@ macro_rules! op_check_and_emit { >, lhs: (), min_power: i32, - ) -> Result<(), ()> { + ) -> OperatorResult<(), ()> { self.do_parse_postfix::(inp, pre_expr, pre_op, lhs, min_power) } #[inline(always)] @@ -146,7 +156,7 @@ macro_rules! op_check_and_emit { >, lhs: O, min_power: i32, - ) -> Result { + ) -> OperatorResult { self.do_parse_postfix::(inp, pre_expr, pre_op, lhs, min_power) } #[inline(always)] @@ -163,7 +173,7 @@ macro_rules! op_check_and_emit { lhs: (), min_power: i32, f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result<(), ()> { + ) -> OperatorResult<(), ()> { self.do_parse_infix::(inp, pre_expr, pre_op, lhs, min_power, &f) } #[inline(always)] @@ -180,7 +190,7 @@ macro_rules! op_check_and_emit { lhs: O, min_power: i32, f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result { + ) -> OperatorResult { self.do_parse_infix::(inp, pre_expr, pre_op, lhs, min_power, &f) } }; @@ -212,11 +222,11 @@ where >::Checkpoint, >, _f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult + ) -> OperatorResult, ()> where Self: Sized, { - Err(()) + OperatorResult::NoMatch(()) } #[doc(hidden)] @@ -228,11 +238,11 @@ where _pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: M::Output, _min_power: i32, - ) -> Result, M::Output> + ) -> OperatorResult, M::Output> where Self: Sized, { - Err(lhs) + OperatorResult::NoMatch(lhs) } #[doc(hidden)] @@ -245,11 +255,11 @@ where lhs: M::Output, _min_power: i32, _f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result, M::Output> + ) -> OperatorResult, M::Output> where Self: Sized, { - Err(lhs) + OperatorResult::NoMatch(lhs) } #[doc(hidden)] @@ -258,14 +268,14 @@ where inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult; + ) -> OperatorResult<::Output, ()>; #[doc(hidden)] fn do_parse_prefix_emit<'parse>( &self, inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult; + ) -> OperatorResult<::Output, ()>; #[doc(hidden)] fn do_parse_postfix_check<'parse>( &self, @@ -274,7 +284,7 @@ where pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: (), min_power: i32, - ) -> Result<(), ()>; + ) -> OperatorResult<(), ()>; #[doc(hidden)] fn do_parse_postfix_emit<'parse>( &self, @@ -283,7 +293,7 @@ where pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: O, min_power: i32, - ) -> Result; + ) -> OperatorResult; #[doc(hidden)] fn do_parse_infix_check<'parse>( &self, @@ -293,7 +303,7 @@ where lhs: (), min_power: i32, f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result<(), ()>; + ) -> OperatorResult<(), ()>; #[doc(hidden)] fn do_parse_infix_emit<'parse>( &self, @@ -303,7 +313,7 @@ where lhs: O, min_power: i32, f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result; + ) -> OperatorResult; } /// A boxed pratt parser operator. See [`Operator`]. @@ -326,7 +336,7 @@ where inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult + ) -> OperatorResult, ()> where Self: Sized, { @@ -341,7 +351,7 @@ where pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: M::Output, min_power: i32, - ) -> Result, M::Output> + ) -> OperatorResult, M::Output> where Self: Sized, { @@ -357,7 +367,7 @@ where lhs: M::Output, min_power: i32, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result, M::Output> + ) -> OperatorResult, M::Output> where Self: Sized, { @@ -370,7 +380,7 @@ where inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult { + ) -> OperatorResult<::Output, ()> { self.0.do_parse_prefix_check(inp, pre_expr, f) } #[inline(always)] @@ -379,7 +389,7 @@ where inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult { + ) -> OperatorResult<::Output, ()> { self.0.do_parse_prefix_emit(inp, pre_expr, f) } #[inline(always)] @@ -390,7 +400,7 @@ where pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: (), min_power: i32, - ) -> Result<(), ()> { + ) -> OperatorResult<(), ()> { self.0 .do_parse_postfix_check(inp, pre_expr, pre_op, lhs, min_power) } @@ -402,7 +412,7 @@ where pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: O, min_power: i32, - ) -> Result { + ) -> OperatorResult { self.0 .do_parse_postfix_emit(inp, pre_expr, pre_op, lhs, min_power) } @@ -415,7 +425,7 @@ where lhs: (), min_power: i32, f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result<(), ()> { + ) -> OperatorResult<(), ()> { self.0 .do_parse_infix_check(inp, pre_expr, pre_op, lhs, min_power, &f) } @@ -428,7 +438,7 @@ where lhs: O, min_power: i32, f: &dyn Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result { + ) -> OperatorResult { self.0 .do_parse_infix_emit(inp, pre_expr, pre_op, lhs, min_power, &f) } @@ -557,32 +567,41 @@ where lhs: M::Output, min_power: i32, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result, M::Output> + ) -> OperatorResult, M::Output> where Self: Sized, { - if self.associativity.left_power() >= min_power { - match self.op_parser.go::(inp) { - Ok(op) => match f(inp, self.associativity.right_power()) { - Ok(rhs) => Ok(M::combine( - M::combine(lhs, rhs, |lhs, rhs| (lhs, rhs)), - op, - |(lhs, rhs), op| { - (self.fold)(lhs, op, rhs, &mut MapExtra::new(pre_expr, inp)) - }, - )), - Err(()) => { - inp.rewind(pre_op.clone()); - Err(lhs) + match self.op_parser.go::(inp) { + Ok(op) => { + if self.associativity.left_power() > min_power { + match f(inp, self.associativity.right_power()) { + Ok(rhs) => OperatorResult::Ok(M::combine( + M::combine(lhs, rhs, |lhs, rhs| (lhs, rhs)), + op, + |(lhs, rhs), op| { + (self.fold)(lhs, op, rhs, &mut MapExtra::new(pre_expr, inp)) + }, + )), + Err(()) => { + inp.rewind(pre_op.clone()); + OperatorResult::NoMatch(lhs) + } } - }, - Err(()) => { + } else { inp.rewind(pre_op.clone()); - Err(lhs) + + if self.associativity.left_power() == min_power { + // TODO: Add error "Ambigious operator order" + OperatorResult::Err(lhs) + } else { + OperatorResult::NoMatch(lhs) + } } } - } else { - Err(lhs) + Err(()) => { + inp.rewind(pre_op.clone()); + OperatorResult::NoMatch(lhs) + } } } @@ -650,23 +669,23 @@ where inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult + ) -> OperatorResult, ()> where Self: Sized, { match self.op_parser.go::(inp) { Ok(op) => match f(inp, self.binding_power) { - Ok(rhs) => Ok(M::combine(op, rhs, |op, rhs| { + Ok(rhs) => OperatorResult::Ok(M::combine(op, rhs, |op, rhs| { (self.fold)(op, rhs, &mut MapExtra::new(pre_expr.cursor(), inp)) })), Err(()) => { inp.rewind(pre_expr.clone()); - Err(()) + OperatorResult::NoMatch(()) } }, Err(()) => { inp.rewind(pre_expr.clone()); - Err(()) + OperatorResult::NoMatch(()) } } } @@ -737,22 +756,22 @@ where pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: M::Output, min_power: i32, - ) -> Result, M::Output> + ) -> OperatorResult, M::Output> where Self: Sized, { if self.binding_power >= min_power { match self.op_parser.go::(inp) { - Ok(op) => Ok(M::combine(lhs, op, |lhs, op| { + Ok(op) => OperatorResult::Ok(M::combine(lhs, op, |lhs, op| { (self.fold)(lhs, op, &mut MapExtra::new(pre_expr, inp)) })), Err(()) => { inp.rewind(pre_op.clone()); - Err(lhs) + OperatorResult::NoMatch(lhs) } } } else { - Err(lhs) + OperatorResult::NoMatch(lhs) } } @@ -786,18 +805,18 @@ macro_rules! impl_operator_for_tuple { inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult + ) -> OperatorResult, ()> where Self: Sized, { let ($($X,)*) = self; $( match $X.do_parse_prefix::(inp, pre_expr, f) { - Ok(out) => return Ok(out), - Err(()) => {}, + OperatorResult::NoMatch(out) => {}, + result => return result, } )* - Err(()) + OperatorResult::NoMatch(()) } #[inline] @@ -808,18 +827,18 @@ macro_rules! impl_operator_for_tuple { pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, mut lhs: M::Output, min_power: i32, - ) -> Result, M::Output> + ) -> OperatorResult, M::Output> where Self: Sized, { let ($($X,)*) = self; $( match $X.do_parse_postfix::(inp, pre_expr, pre_op, lhs, min_power) { - Ok(out) => return Ok(out), - Err(out) => lhs = out, + OperatorResult::NoMatch(out) => lhs = out, + result => return result, } )* - Err(lhs) + OperatorResult::NoMatch(lhs) } #[inline] @@ -831,18 +850,18 @@ macro_rules! impl_operator_for_tuple { mut lhs: M::Output, min_power: i32, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result, M::Output> + ) -> OperatorResult, M::Output> where Self: Sized, { let ($($X,)*) = self; $( match $X.do_parse_infix::(inp, pre_expr, pre_op, lhs, min_power, f) { - Ok(out) => return Ok(out), - Err(out) => lhs = out, + OperatorResult::NoMatch(out) => lhs = out, + result => return result, } )* - Err(lhs) + OperatorResult::NoMatch(lhs) } op_check_and_emit!(); @@ -865,16 +884,17 @@ where inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult + ) -> OperatorResult, ()> where Self: Sized, { for op in self { - if let Ok(out) = op.do_parse_prefix::(inp, pre_expr, f) { - return Ok(out); + match op.do_parse_prefix::(inp, pre_expr, f) { + OperatorResult::NoMatch(()) => {}, + result => return result, } } - Err(()) + OperatorResult::NoMatch(()) } #[inline] @@ -885,17 +905,17 @@ where pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, mut lhs: M::Output, min_power: i32, - ) -> Result, M::Output> + ) -> OperatorResult, M::Output> where Self: Sized, { for op in self { match op.do_parse_postfix::(inp, pre_expr, pre_op, lhs, min_power) { - Ok(out) => return Ok(out), - Err(out) => lhs = out, + OperatorResult::NoMatch(out) => lhs = out, + result => return result, } } - Err(lhs) + OperatorResult::NoMatch(lhs) } #[inline] @@ -907,17 +927,17 @@ where mut lhs: M::Output, min_power: i32, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result, M::Output> + ) -> OperatorResult, M::Output> where Self: Sized, { for op in self { match op.do_parse_infix::(inp, pre_expr, pre_op, lhs, min_power, f) { - Ok(out) => return Ok(out), - Err(out) => lhs = out, + OperatorResult::NoMatch(out) => lhs = out, + result => return result, } } - Err(lhs) + OperatorResult::NoMatch(lhs) } op_check_and_emit!(); @@ -944,8 +964,9 @@ impl<'src, Atom, Ops> Pratt { .do_parse_prefix::(inp, &pre_expr, &|inp, min_power| { recursive::recurse(|| self.pratt_go::(inp, min_power)) }) { - Ok(out) => out, - Err(()) => self.atom.go::(inp)?, + OperatorResult::Ok(out) => out, + OperatorResult::NoMatch(()) => self.atom.go::(inp)?, + OperatorResult::Err(()) => return Err(()) }; loop { @@ -956,11 +977,15 @@ impl<'src, Atom, Ops> Pratt { .ops .do_parse_postfix::(inp, pre_expr.cursor(), &pre_op, lhs, min_power) { - Ok(out) => { + OperatorResult::Ok(out) => { lhs = out; continue; } - Err(out) => lhs = out, + OperatorResult::NoMatch(out) => lhs = out, + OperatorResult::Err(out) => { + inp.rewind(pre_op); + return Err(()); + }, } // Infix binary operators @@ -974,11 +999,15 @@ impl<'src, Atom, Ops> Pratt { recursive::recurse(|| self.pratt_go::(inp, min_power)) }, ) { - Ok(out) => { + OperatorResult::Ok(out) => { lhs = out; continue; } - Err(out) => lhs = out, + OperatorResult::NoMatch(out) => lhs = out, + OperatorResult::Err(out) => { + inp.rewind(pre_op); + return Err(()); + } } inp.rewind(pre_op); diff --git a/src/private.rs b/src/private.rs index 4b0bb8d3..ea01373f 100644 --- a/src/private.rs +++ b/src/private.rs @@ -82,7 +82,7 @@ pub trait Mode { inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult + ) -> pratt::OperatorResult, ()> where Op: pratt::Operator<'src, I, O, E>, I: Input<'src>, @@ -95,7 +95,7 @@ pub trait Mode { pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: Self::Output, min_power: i32, - ) -> Result, Self::Output> + ) -> pratt::OperatorResult, Self::Output> where Op: pratt::Operator<'src, I, O, E>, I: Input<'src>, @@ -109,7 +109,7 @@ pub trait Mode { lhs: Self::Output, min_power: i32, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result, Self::Output> + ) -> pratt::OperatorResult, Self::Output> where Op: pratt::Operator<'src, I, O, E>, I: Input<'src>, @@ -194,7 +194,7 @@ impl Mode for Emit { inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult + ) -> pratt::OperatorResult, ()> where Op: pratt::Operator<'src, I, O, E>, I: Input<'src>, @@ -211,7 +211,7 @@ impl Mode for Emit { pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: Self::Output, min_power: i32, - ) -> Result, Self::Output> + ) -> pratt::OperatorResult, Self::Output> where Op: pratt::Operator<'src, I, O, E>, I: Input<'src>, @@ -229,7 +229,7 @@ impl Mode for Emit { lhs: Self::Output, min_power: i32, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result, Self::Output> + ) -> pratt::OperatorResult, Self::Output> where Op: pratt::Operator<'src, I, O, E>, I: Input<'src>, @@ -305,7 +305,7 @@ impl Mode for Check { inp: &mut InputRef<'src, 'parse, I, E>, pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> PResult + ) -> pratt::OperatorResult, ()> where Op: pratt::Operator<'src, I, O, E>, I: Input<'src>, @@ -322,7 +322,7 @@ impl Mode for Check { pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, lhs: Self::Output, min_power: i32, - ) -> Result, Self::Output> + ) -> pratt::OperatorResult, Self::Output> where Op: pratt::Operator<'src, I, O, E>, I: Input<'src>, @@ -340,7 +340,7 @@ impl Mode for Check { lhs: Self::Output, min_power: i32, f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> Result, Self::Output> + ) -> pratt::OperatorResult, Self::Output> where Op: pratt::Operator<'src, I, O, E>, I: Input<'src>, From 56a3e22e21d628659c5e35520c37dcca48ca58c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Althaus?= Date: Sun, 25 May 2025 12:19:26 +0200 Subject: [PATCH 5/5] Only check ambiguity with non-associative infix operators --- src/pratt.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/pratt.rs b/src/pratt.rs index 76ed8f22..90b6190b 100644 --- a/src/pratt.rs +++ b/src/pratt.rs @@ -573,7 +573,14 @@ where { match self.op_parser.go::(inp) { Ok(op) => { - if self.associativity.left_power() > min_power { + let binding_power = self.associativity.left_power(); + + let power_check = if let Associativity::None(_) = self.associativity { + binding_power > min_power + } else { + binding_power >= min_power + }; + if power_check { match f(inp, self.associativity.right_power()) { Ok(rhs) => OperatorResult::Ok(M::combine( M::combine(lhs, rhs, |lhs, rhs| (lhs, rhs)), @@ -590,7 +597,7 @@ where } else { inp.rewind(pre_op.clone()); - if self.associativity.left_power() == min_power { + if binding_power == min_power { // TODO: Add error "Ambigious operator order" OperatorResult::Err(lhs) } else {