Skip to content

Commit f1abba6

Browse files
committed
fix precedence parsing
1 parent 6c12469 commit f1abba6

File tree

3 files changed

+85
-80
lines changed

3 files changed

+85
-80
lines changed

src/precedence/mod.rs

+16-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! Combinators to parse expressions with operator precedence.
2-
#![cfg(feature="alloc")]
2+
#![cfg(feature = "alloc")]
33
#![cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
44

55
#[cfg(test)]
@@ -79,17 +79,11 @@ pub fn unary_op<I, O, E, P, Q>(
7979
mut parser: P,
8080
) -> impl FnMut(I) -> IResult<I, Unary<O, Q>, E>
8181
where
82-
P: Parser<I, O, E>,
82+
P: Parser<I, Output = O, Error = E>,
8383
Q: Ord + Copy,
8484
{
8585
move |input| match parser.parse(input) {
86-
Ok((i, value)) => Ok((
87-
i,
88-
Unary {
89-
value,
90-
precedence,
91-
},
92-
)),
86+
Ok((i, value)) => Ok((i, Unary { value, precedence })),
9387
Err(e) => Err(e),
9488
}
9589
}
@@ -107,7 +101,7 @@ pub fn binary_op<I, O, E, P, Q>(
107101
mut parser: P,
108102
) -> impl FnMut(I) -> IResult<I, Binary<O, Q>, E>
109103
where
110-
P: Parser<I, O, E>,
104+
P: Parser<I, Output = O, Error = E>,
111105
Q: Ord + Copy,
112106
{
113107
move |input| match parser.parse(input) {
@@ -124,7 +118,7 @@ where
124118
}
125119

126120
/// Parses an expression with operator precedence.
127-
///
121+
///
128122
/// Supports prefix, postfix and binary operators. Operators are applied in ascending precedence.
129123
///
130124
/// The parser will track its current position inside the expression and call the respective
@@ -146,7 +140,7 @@ where
146140
/// * `binary` Parser for binary operators.
147141
/// * `operand` Parser for operands.
148142
/// * `fold` Function that evaluates a single operation and returns the result.
149-
///
143+
///
150144
/// # Example
151145
/// ```rust
152146
/// # use nom::{Err, error::{Error, ErrorKind}, IResult};
@@ -156,11 +150,11 @@ where
156150
/// use nom::sequence::delimited;
157151
/// use nom::bytes::complete::tag;
158152
/// use nom::branch::alt;
159-
///
153+
///
160154
/// fn parser(i: &str) -> IResult<&str, i64> {
161155
/// precedence(
162156
/// unary_op(1, tag("-")),
163-
/// fail,
157+
/// fail(),
164158
/// alt((
165159
/// binary_op(2, Assoc::Left, tag("*")),
166160
/// binary_op(2, Assoc::Left, tag("/")),
@@ -189,19 +183,19 @@ where
189183
/// assert_eq!(parser("4-(2+2)"), Ok(("", 0)));
190184
/// assert_eq!(parser("3-(2*3)+7+2*2-(2*(2+4))"), Ok(("", -4)));
191185
/// ```
192-
///
186+
///
193187
/// # Evaluation order
194188
/// This parser reads expressions from left to right and folds operations as soon as possible. This
195189
/// behaviour is only important when using an operator grammar that allows for ambigious expressions.
196-
///
190+
///
197191
/// For example, the expression `-a++**b` is ambigious with the following precedence.
198-
///
192+
///
199193
/// | Operator | Position | Precedence | Associativity |
200194
/// |----------|----------|------------|---------------|
201195
/// | ** | Binary | 1 | Right |
202196
/// | - | Prefix | 2 | N/A |
203197
/// | ++ | Postfix | 3 | N/A |
204-
///
198+
///
205199
/// The expression can be parsed in two ways: `-((a++)**b)` or `((-a)++)**b`. This parser will always
206200
/// parse it as the latter because of how it evaluates expressions:
207201
/// * It reads, left-to-right, the first two operators `-a++`.
@@ -220,11 +214,11 @@ pub fn precedence<I, O, E, E2, F, G, H1, H3, H2, P1, P2, P3, Q>(
220214
where
221215
I: Clone + PartialEq,
222216
E: ParseError<I> + FromExternalError<I, E2>,
223-
F: Parser<I, O, E>,
217+
F: Parser<I, Output = O, Error = E>,
224218
G: FnMut(Operation<P1, P2, P3, O>) -> Result<O, E2>,
225-
H1: Parser<I, Unary<P1, Q>, E>,
226-
H2: Parser<I, Unary<P2, Q>, E>,
227-
H3: Parser<I, Binary<P3, Q>, E>,
219+
H1: Parser<I, Output = Unary<P1, Q>, Error = E>,
220+
H2: Parser<I, Output = Unary<P2, Q>, Error = E>,
221+
H3: Parser<I, Output = Binary<P3, Q>, Error = E>,
228222
Q: Ord + Copy,
229223
{
230224
move |mut i| {

src/precedence/tests.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ use crate::{
33
branch::alt,
44
bytes::complete::tag,
55
character::complete::digit1,
6-
combinator::{map_res, fail},
6+
combinator::{fail, map_res},
7+
error::ErrorKind,
78
internal::{Err, IResult},
89
sequence::delimited,
9-
error::ErrorKind,
1010
};
1111

1212
#[cfg(feature = "alloc")]
@@ -16,7 +16,7 @@ use crate::precedence::precedence;
1616
fn parser(i: &str) -> IResult<&str, i64> {
1717
precedence(
1818
unary_op(1, tag("-")),
19-
fail,
19+
fail(),
2020
alt((
2121
binary_op(2, Assoc::Left, tag("*")),
2222
binary_op(2, Assoc::Left, tag("/")),
@@ -50,9 +50,9 @@ fn precedence_test() {
5050
assert_eq!(parser("4-2*2"), Ok(("", 0)));
5151
assert_eq!(parser("(4-2)*2"), Ok(("", 4)));
5252
assert_eq!(parser("2*2/1"), Ok(("", 4)));
53-
53+
5454
let a = "a";
55-
55+
5656
assert_eq!(
5757
parser(a),
5858
Err(Err::Error(error_node_position!(
@@ -61,9 +61,9 @@ fn precedence_test() {
6161
error_position!(&a[..], ErrorKind::Tag)
6262
)))
6363
);
64-
64+
6565
let b = "3+b";
66-
66+
6767
assert_eq!(
6868
parser(b),
6969
Err(Err::Error(error_node_position!(

tests/expression_ast.rs

+62-51
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use nom::{
22
branch::alt,
33
bytes::complete::tag,
4-
character::complete::{digit1 as digit, alphanumeric1 as alphanumeric},
5-
combinator::{map_res, map},
4+
character::complete::{alphanumeric1 as alphanumeric, digit1 as digit},
5+
combinator::{map, map_res},
66
multi::separated_list0,
7+
precedence::{binary_op, precedence, unary_op, Assoc, Operation},
78
sequence::delimited,
8-
IResult,
9-
precedence::{precedence, Assoc, binary_op, unary_op, Operation},
9+
IResult, Parser,
1010
};
1111

1212
// Elements of the abstract syntax tree (ast) that represents an expression.
@@ -29,25 +29,25 @@ pub enum Expr {
2929

3030
// Prefix operators.
3131
enum PrefixOp {
32-
Identity, // +
33-
Negate, // -
32+
Identity, // +
33+
Negate, // -
3434
}
3535

3636
// Postfix operators.
3737
enum PostfixOp {
3838
// The function call operator. In addition to its own representation "()" it carries additional information that we need to keep here.
3939
// Specifically the vector of expressions that make up the parameters.
40-
Call(Vec<Expr>), // ()
40+
Call(Vec<Expr>), // ()
4141
}
4242

4343
// Binary operators.
4444
enum BinaryOp {
45-
Addition, // +
46-
Subtraction, // -
47-
Multiplication, // *
48-
Division, // /
45+
Addition, // +
46+
Subtraction, // -
47+
Multiplication, // *
48+
Division, // /
4949
// The ternary operator can contain a single expression.
50-
Ternary(Expr), // ?:
50+
Ternary(Expr), // ?:
5151
}
5252

5353
// Parser for function calls.
@@ -57,31 +57,28 @@ fn function_call(i: &str) -> IResult<&str, PostfixOp> {
5757
tag("("),
5858
// Subexpressions are evaluated by recursing back into the expression parser.
5959
separated_list0(tag(","), expression),
60-
tag(")")
60+
tag(")"),
6161
),
62-
|v: Vec<Expr>| PostfixOp::Call(v)
63-
)(i)
62+
|v: Vec<Expr>| PostfixOp::Call(v),
63+
)
64+
.parse(i)
6465
}
6566

6667
// The ternary operator is actually just a binary operator that contains another expression. So it can be
6768
// handled similarly to the function call operator except its in a binary position and can only contain
6869
// a single expression.
69-
//
70+
//
7071
// For example the expression "a<b ? a : b" is handled similarly to the function call operator, the
7172
// "?" is treated like an opening bracket and the ":" is treated like a closing bracket.
7273
//
7374
// For the outer expression the result looks like "a<b ?: b". Where "?:" is a single operator. The
7475
// subexpression is contained within the operator in the same way that the function call operator
7576
// contains subexpressions.
7677
fn ternary_operator(i: &str) -> IResult<&str, BinaryOp> {
77-
map(
78-
delimited(
79-
tag("?"),
80-
expression,
81-
tag(":")
82-
),
83-
|e: Expr| BinaryOp::Ternary(e)
84-
)(i)
78+
map(delimited(tag("?"), expression, tag(":")), |e: Expr| {
79+
BinaryOp::Ternary(e)
80+
})
81+
.parse(i)
8582
}
8683

8784
// The actual expression parser .
@@ -94,65 +91,79 @@ fn expression(i: &str) -> IResult<&str, Expr> {
9491
// Function calls are implemented as postfix unary operators.
9592
unary_op(1, function_call),
9693
alt((
97-
binary_op(3, Assoc::Left, alt((
98-
map(tag("*"), |_| BinaryOp::Multiplication),
99-
map(tag("/"), |_| BinaryOp::Division),
100-
))),
101-
binary_op(4, Assoc::Left, alt((
102-
map(tag("+"), |_| BinaryOp::Addition),
103-
map(tag("-"), |_| BinaryOp::Subtraction),
104-
))),
94+
binary_op(
95+
3,
96+
Assoc::Left,
97+
alt((
98+
map(tag("*"), |_| BinaryOp::Multiplication),
99+
map(tag("/"), |_| BinaryOp::Division),
100+
)),
101+
),
102+
binary_op(
103+
4,
104+
Assoc::Left,
105+
alt((
106+
map(tag("+"), |_| BinaryOp::Addition),
107+
map(tag("-"), |_| BinaryOp::Subtraction),
108+
)),
109+
),
105110
// Ternary operators are just binary operators with a subexpression.
106111
binary_op(5, Assoc::Right, ternary_operator),
107112
)),
108113
alt((
109-
map_res(digit,
110-
|s: &str| match s.parse::<i64>() {
111-
Ok(s) => Ok(Expr::Num(s)),
112-
Err(e) => Err(e),
113-
}
114-
),
114+
map_res(digit, |s: &str| match s.parse::<i64>() {
115+
Ok(s) => Ok(Expr::Num(s)),
116+
Err(e) => Err(e),
117+
}),
115118
map(alphanumeric, |s: &str| Expr::Iden(s.to_string())),
116119
delimited(tag("("), expression, tag(")")),
117120
)),
118121
|op: Operation<PrefixOp, PostfixOp, BinaryOp, Expr>| -> Result<Expr, ()> {
119122
use nom::precedence::Operation::*;
120-
use PrefixOp::*;
121-
use PostfixOp::*;
122123
use BinaryOp::*;
124+
use PostfixOp::*;
125+
use PrefixOp::*;
123126
match op {
124127
// The identity operator (prefix +) is ignored.
125128
Prefix(Identity, e) => Ok(e),
126-
129+
127130
// Unary minus gets evaluated to the same representation as a multiplication with -1.
128131
Prefix(Negate, e) => Ok(Expr::Mul(Expr::Num(-1).into(), e.into())),
129-
132+
130133
// The list of parameters are taken from the operator and placed into the ast.
131134
Postfix(e, Call(p)) => Ok(Expr::Call(e.into(), p)),
132-
135+
133136
// Meaning is assigned to the expressions of the ternary operator during evaluation.
134137
// The lhs becomes the condition, the contained expression is the true case, rhs the false case.
135138
Binary(lhs, Ternary(e), rhs) => Ok(Expr::Tern(lhs.into(), e.into(), rhs.into())),
136-
139+
137140
// Raw operators get turned into their respective ast nodes.
138141
Binary(lhs, Multiplication, rhs) => Ok(Expr::Mul(lhs.into(), rhs.into())),
139142
Binary(lhs, Division, rhs) => Ok(Expr::Div(lhs.into(), rhs.into())),
140143
Binary(lhs, Addition, rhs) => Ok(Expr::Add(lhs.into(), rhs.into())),
141144
Binary(lhs, Subtraction, rhs) => Ok(Expr::Sub(lhs.into(), rhs.into())),
142145
}
143-
}
146+
},
144147
)(i)
145148
}
146149

147150
#[test]
148151
fn expression_test() {
149152
assert_eq!(
150153
expression("-2*max(2,3)-2").map(|(i, x)| (i, format!("{:?}", x))),
151-
Ok(("", String::from("Sub(Mul(Mul(Num(-1), Num(2)), Call(Iden(\"max\"), [Num(2), Num(3)])), Num(2))")))
154+
Ok((
155+
"",
156+
String::from("Sub(Mul(Mul(Num(-1), Num(2)), Call(Iden(\"max\"), [Num(2), Num(3)])), Num(2))")
157+
))
158+
);
159+
160+
assert_eq!(
161+
expression("a?2+c:-2*2").map(|(i, x)| (i, format!("{:?}", x))),
162+
Ok((
163+
"",
164+
String::from(
165+
"Tern(Iden(\"a\"), Add(Num(2), Iden(\"c\")), Mul(Mul(Num(-1), Num(2)), Num(2)))"
166+
)
167+
))
152168
);
153-
154-
assert_eq!(
155-
expression("a?2+c:-2*2").map(|(i, x)| (i, format!("{:?}", x))),
156-
Ok(("", String::from("Tern(Iden(\"a\"), Add(Num(2), Iden(\"c\")), Mul(Mul(Num(-1), Num(2)), Num(2)))")))
157-
);
158169
}

0 commit comments

Comments
 (0)