Skip to content

Commit ce14cc9

Browse files
committed
docs(comb): Add documentation to Pratt parser
1 parent c92af6d commit ce14cc9

File tree

1 file changed

+112
-2
lines changed

1 file changed

+112
-2
lines changed

src/combinator/expression.rs

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,59 @@ use crate::{
88
};
99

1010
/// Parses an expression based on operator precedence.
11+
///
12+
/// It uses a Pratt parsing algorithm, where operators are
13+
/// associated with a binding power. The higher the power,
14+
/// the more tightly an operator will bind to its operands.
15+
///
16+
/// This method returns an [`Expression`], which configures
17+
/// the Pratt parser.
18+
///
19+
/// Each operator type is configured with [`Prefix`], [`Postfix`],
20+
/// and [`Infix`]. These describe the operator's binding power,
21+
/// a function that applies the operator to its operand, and the
22+
/// operator's associativity (infix only).
23+
///
24+
/// # Example
25+
///
26+
/// Parsing a simple arithmetic expression without parenthesis.
27+
///
28+
/// ```rust
29+
/// # use winnow::prelude::*;
30+
/// # use winnow::error::ContextError;
31+
/// # use winnow::ascii::digit1;
32+
/// # use winnow::combinator::{dispatch, fail};
33+
/// # use winnow::token::any;
34+
/// use winnow::combinator::expression;
35+
/// use winnow::combinator::{Prefix, Postfix, Infix};
36+
///
37+
/// fn parser<'i>() -> impl Parser<&'i str, i32, ContextError> {
38+
/// move |i: &mut &str| {
39+
/// use Infix::*;
40+
/// expression(digit1.parse_to::<i32>()) // operands are 32-bit integers
41+
/// .prefix(dispatch! {any;
42+
/// '-' => Prefix(12, |_, a: i32| Ok(-a)),
43+
/// _ => fail,
44+
/// })
45+
/// .infix(dispatch! {any;
46+
/// '+' => Left(5, |_, a, b| Ok(a + b)),
47+
/// '-' => Left(5, |_, a, b| Ok(a - b)),
48+
/// '*' => Left(7, |_, a, b| Ok(a * b)),
49+
/// '/' => Left(7, |_, a: i32, b| Ok(a.checked_div(b).unwrap_or_default())),
50+
/// _ => fail,
51+
/// })
52+
/// .postfix(dispatch! {any;
53+
/// '!' => Postfix(15, |_, a| if a < 1 { Ok(1) } else { Ok((1..=a).fold(1, |acc, a| acc*a)) }),
54+
/// _ => fail,
55+
/// })
56+
/// .parse_next(i)
57+
/// }
58+
/// }
59+
///
60+
/// assert_eq!(parser().parse("1+1"), Ok(2));
61+
/// assert_eq!(parser().parse("0!"), Ok(1));
62+
/// assert_eq!(parser().parse("-1*5*2*10+30/3!"), Ok(-95));
63+
/// ```
1164
#[doc(alias = "pratt")]
1265
#[doc(alias = "separated")]
1366
#[doc(alias = "shunting_yard")]
@@ -41,6 +94,15 @@ where
4194
}
4295
}
4396

97+
/// A helper struct for [`expression()`].
98+
///
99+
/// Holds the configuration for the Pratt parser, including
100+
/// the operator and operand parsers. A precedence level can
101+
/// also be set, which is useful to disambiguate parse trees
102+
/// based on the parent operator's precedence.
103+
///
104+
/// Implements [`Parser`]. When parsing an input, it applies
105+
/// the Pratt parser.
44106
pub struct Expression<I, O, ParseOperand, Pre, Post, Pix, E>
45107
where
46108
I: Stream + StreamIsPartial,
@@ -63,6 +125,12 @@ where
63125
I: Stream + StreamIsPartial,
64126
E: ParserError<I>,
65127
{
128+
/// Sets the prefix operator parser.
129+
///
130+
/// The parser should parse the input to a [`Prefix`],
131+
/// which contains the operator's binding power and
132+
/// a fold function which applies the operator to its
133+
/// operands.
66134
#[inline(always)]
67135
pub fn prefix<NewParsePrefix>(
68136
self,
@@ -83,6 +151,12 @@ where
83151
}
84152
}
85153

154+
/// Sets the prefix operator parser.
155+
///
156+
/// The parser should parse the input to a [`Postfix`],
157+
/// which contains the operator's binding power and
158+
/// a fold function which applies the operator to its
159+
/// operands.
86160
#[inline(always)]
87161
pub fn postfix<NewParsePostfix>(
88162
self,
@@ -103,6 +177,12 @@ where
103177
}
104178
}
105179

180+
/// Sets the prefix operator parser.
181+
///
182+
/// The parser should parse the input to a [`Infix`],
183+
/// which contains the operator's binding power and
184+
/// a fold function which applies the operator to its
185+
/// operands.
106186
#[inline(always)]
107187
pub fn infix<NewParseInfix>(
108188
self,
@@ -123,6 +203,10 @@ where
123203
}
124204
}
125205

206+
/// Sets the precedence level of the parser.
207+
///
208+
/// This is useful to disambiguate grammars based on the parent operator's
209+
/// precedence.
126210
#[inline(always)]
127211
pub fn precedence_level(
128212
mut self,
@@ -158,6 +242,7 @@ where
158242
}
159243
}
160244

245+
// Opaque implementation of the Pratt parser.
161246
fn expression_impl<I, O, Pop, Pre, Post, Pix, E>(
162247
i: &mut I,
163248
parse_operand: &mut Pop,
@@ -247,7 +332,13 @@ where
247332
Ok(operand)
248333
}
249334

250-
pub struct Prefix<I, O, E>(i64, fn(&mut I, O) -> Result<O, E>);
335+
/// A helper struct for [`expression()`].
336+
///
337+
/// Represents a prefix operator.
338+
///
339+
/// It requires an operator binding power, as well as a
340+
/// fold function which applies the operator.
341+
pub struct Prefix<I, O, E>(pub i64, pub fn(&mut I, O) -> Result<O, E>);
251342

252343
impl<I, O, E> Clone for Prefix<I, O, E> {
253344
#[inline(always)]
@@ -263,7 +354,13 @@ impl<I: Stream, O, E: ParserError<I>> Parser<I, Prefix<I, O, E>, E> for Prefix<I
263354
}
264355
}
265356

266-
pub struct Postfix<I, O, E>(i64, fn(&mut I, O) -> Result<O, E>);
357+
/// A helper struct for [`expression()`].
358+
///
359+
/// Represents a postfix operator.
360+
///
361+
/// It requires an operator binding power, as well as a
362+
/// fold function which applies the operator.
363+
pub struct Postfix<I, O, E>(pub i64, pub fn(&mut I, O) -> Result<O, E>);
267364

268365
impl<I, O, E> Clone for Postfix<I, O, E> {
269366
#[inline(always)]
@@ -279,9 +376,22 @@ impl<I: Stream, O, E: ParserError<I>> Parser<I, Postfix<I, O, E>, E> for Postfix
279376
}
280377
}
281378

379+
/// A helper struct for [`expression()`].
380+
///
381+
/// Represents a prefix operator.
382+
///
383+
/// It requires an operator binding power, as well as a
384+
/// fold function which applies the operator.
385+
///
386+
/// Infix operators also have an associativity. Left-associative
387+
/// operators will bind more tightly to their rightmost operands,
388+
/// and it is the opposite for right-associative operators.
282389
pub enum Infix<I, O, E> {
390+
/// Left-associative operator.
283391
Left(i64, fn(&mut I, O, O) -> Result<O, E>),
392+
/// Right-associative operator.
284393
Right(i64, fn(&mut I, O, O) -> Result<O, E>),
394+
/// Neither left or right associative.
285395
Neither(i64, fn(&mut I, O, O) -> Result<O, E>),
286396
}
287397

0 commit comments

Comments
 (0)