Skip to content

Commit dc53f04

Browse files
authored
Parse binary operations with correct operator precedence (#271)
* Parse binary operations with correct operator precedence * Add another test case
1 parent 822399e commit dc53f04

File tree

5 files changed

+470
-112
lines changed

5 files changed

+470
-112
lines changed

parser/src/parser/parser.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -889,7 +889,7 @@ impl<'a> Parser<'a> {
889889
// https://docs.python.org/3/reference/compound_stmts.html#literal-patterns
890890
fn parse_literal_pattern(&mut self) -> Result<MatchPattern, ParsingError> {
891891
let node = self.start_node();
892-
let value = self.parse_binary_arithmetic_operation()?;
892+
let value = self.parse_binary_arithmetic_operation(0)?;
893893
Ok(MatchPattern::MatchValue(MatchValue {
894894
node: self.finish_node(node),
895895
value,
@@ -2146,15 +2146,15 @@ impl<'a> Parser<'a> {
21462146
// https://docs.python.org/3/reference/expressions.html#shifting-operations
21472147
fn parse_shift_expr(&mut self) -> Result<Expression, ParsingError> {
21482148
let node = self.start_node();
2149-
let mut arith_expr = self.parse_binary_arithmetic_operation()?;
2149+
let mut arith_expr = self.parse_binary_arithmetic_operation(0)?;
21502150
if self.at(Kind::LeftShift) || self.at(Kind::RightShift) {
21512151
let op = if self.eat(Kind::LeftShift) {
21522152
BinaryOperator::LShift
21532153
} else {
21542154
self.bump(Kind::RightShift);
21552155
BinaryOperator::RShift
21562156
};
2157-
let lhs = self.parse_binary_arithmetic_operation()?;
2157+
let lhs = self.parse_binary_arithmetic_operation(0)?;
21582158
arith_expr = Expression::BinOp(Box::new(BinOp {
21592159
node: self.finish_node(node),
21602160
op,
@@ -2166,12 +2166,24 @@ impl<'a> Parser<'a> {
21662166
}
21672167

21682168
// https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations
2169-
fn parse_binary_arithmetic_operation(&mut self) -> Result<Expression, ParsingError> {
2169+
//
2170+
fn parse_binary_arithmetic_operation(
2171+
&mut self,
2172+
min_precedence: u8,
2173+
) -> Result<Expression, ParsingError> {
21702174
let node = self.start_node();
21712175
let mut lhs = self.parse_unary_arithmetic_operation()?;
2172-
while self.cur_kind().is_bin_arithmetic_op() {
2173-
let op = self.parse_bin_arithmetic_op()?;
2174-
let rhs = self.parse_unary_arithmetic_operation()?;
2176+
while let Some((op, precedence, associativity)) = self.cur_kind().bin_op_precedence() {
2177+
if precedence < min_precedence {
2178+
break;
2179+
}
2180+
self.bump_any();
2181+
let next_precedence = match associativity {
2182+
0 => precedence + 1,
2183+
1 => precedence,
2184+
_ => unreachable!(),
2185+
};
2186+
let rhs = self.parse_binary_arithmetic_operation(next_precedence)?;
21752187
lhs = Expression::BinOp(Box::new(BinOp {
21762188
node: self.finish_node(node),
21772189
op,

parser/src/token.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use std::fmt::Display;
22

3+
use crate::ast::BinaryOperator;
4+
35
#[derive(Debug, Clone, PartialEq)]
46
pub struct Token {
57
pub kind: Kind,
@@ -214,18 +216,18 @@ impl Kind {
214216
matches!(self, Kind::Not | Kind::BitNot | Kind::Minus | Kind::Plus)
215217
}
216218

217-
pub fn is_bin_arithmetic_op(&self) -> bool {
218-
matches!(
219-
self,
220-
Kind::Plus
221-
| Kind::Minus
222-
| Kind::Mul
223-
| Kind::MatrixMul
224-
| Kind::Div
225-
| Kind::Mod
226-
| Kind::Pow
227-
| Kind::IntDiv
228-
)
219+
pub fn bin_op_precedence(&self) -> Option<(BinaryOperator, u8, u8)> {
220+
match self {
221+
Kind::Plus => Some((BinaryOperator::Add, 9, 0)),
222+
Kind::Minus => Some((BinaryOperator::Sub, 9, 0)),
223+
Kind::Mul => Some((BinaryOperator::Mult, 10, 0)),
224+
Kind::MatrixMul => Some((BinaryOperator::MatMult, 10, 0)),
225+
Kind::Div => Some((BinaryOperator::Div, 10, 0)),
226+
Kind::Mod => Some((BinaryOperator::Mod, 10, 0)),
227+
Kind::Pow => Some((BinaryOperator::Pow, 10, 0)),
228+
Kind::IntDiv => Some((BinaryOperator::FloorDiv, 10, 0)),
229+
_ => None,
230+
}
229231
}
230232

231233
pub fn is_comparison_operator(&self) -> bool {

parser/test_data/inputs/binary_op.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
1 % 2
1212

13-
1 ** 2
13+
1**2
1414

1515
1 << 2
1616

@@ -25,3 +25,13 @@
2525
1 | 2 | 3
2626

2727
1 @ 2
28+
29+
1 + 2 * 3
30+
31+
1 * 2 + 3
32+
33+
1 ^ 2 + 3
34+
35+
3 + (1 + 2) * 3
36+
37+
(3 + 1) * 2**3 + 1
Lines changed: 86 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: parser/src/lexer/mod.rs
3-
description: "1 + 2\n\n1 - 2\n\n1 * 2\n\n1 / 2\n\n1 // 2\n\n1 % 2\n\n1 ** 2\n\n1 << 2\n\n1 >> 2\n\n1 & 2\n\n1 ^ 2\n\n1 | 2\n\n1 | 2 | 3\n\n1 @ 2\n"
3+
description: "1 + 2\n\n1 - 2\n\n1 * 2\n\n1 / 2\n\n1 // 2\n\n1 % 2\n\n1**2\n\n1 << 2\n\n1 >> 2\n\n1 & 2\n\n1 ^ 2\n\n1 | 2\n\n1 | 2 | 3\n\n1 @ 2\n\n1 + 2 * 3\n\n1 * 2 + 3\n\n1 ^ 2 + 3\n\n3 + (1 + 2) * 3\n\n(3 + 1) * 2**3 + 1\n"
44
input_file: parser/test_data/inputs/binary_op.py
55
---
66
0,1: Integer 1
@@ -34,43 +34,88 @@ input_file: parser/test_data/inputs/binary_op.py
3434
41,42: NewLine
3535
42,43: NL
3636
43,44: Integer 1
37-
45,47: **
38-
48,49: Integer 2
39-
49,50: NewLine
40-
50,51: NL
41-
51,52: Integer 1
42-
53,55: <<
43-
56,57: Integer 2
44-
57,58: NewLine
45-
58,59: NL
46-
59,60: Integer 1
47-
61,63: >>
48-
64,65: Integer 2
49-
65,66: NewLine
50-
66,67: NL
51-
67,68: Integer 1
52-
69,70: &
53-
71,72: Integer 2
54-
72,73: NewLine
55-
73,74: NL
56-
74,75: Integer 1
57-
76,77: ^
58-
78,79: Integer 2
59-
79,80: NewLine
60-
80,81: NL
61-
81,82: Integer 1
62-
83,84: |
63-
85,86: Integer 2
64-
86,87: NewLine
65-
87,88: NL
66-
88,89: Integer 1
67-
90,91: |
68-
92,93: Integer 2
69-
94,95: |
70-
96,97: Integer 3
71-
97,98: NewLine
72-
98,99: NL
73-
99,100: Integer 1
74-
101,102: @
75-
103,104: Integer 2
76-
104,105: NewLine
37+
44,46: **
38+
46,47: Integer 2
39+
47,48: NewLine
40+
48,49: NL
41+
49,50: Integer 1
42+
51,53: <<
43+
54,55: Integer 2
44+
55,56: NewLine
45+
56,57: NL
46+
57,58: Integer 1
47+
59,61: >>
48+
62,63: Integer 2
49+
63,64: NewLine
50+
64,65: NL
51+
65,66: Integer 1
52+
67,68: &
53+
69,70: Integer 2
54+
70,71: NewLine
55+
71,72: NL
56+
72,73: Integer 1
57+
74,75: ^
58+
76,77: Integer 2
59+
77,78: NewLine
60+
78,79: NL
61+
79,80: Integer 1
62+
81,82: |
63+
83,84: Integer 2
64+
84,85: NewLine
65+
85,86: NL
66+
86,87: Integer 1
67+
88,89: |
68+
90,91: Integer 2
69+
92,93: |
70+
94,95: Integer 3
71+
95,96: NewLine
72+
96,97: NL
73+
97,98: Integer 1
74+
99,100: @
75+
101,102: Integer 2
76+
102,103: NewLine
77+
103,104: NL
78+
104,105: Integer 1
79+
106,107: +
80+
108,109: Integer 2
81+
110,111: *
82+
112,113: Integer 3
83+
113,114: NewLine
84+
114,115: NL
85+
115,116: Integer 1
86+
117,118: *
87+
119,120: Integer 2
88+
121,122: +
89+
123,124: Integer 3
90+
124,125: NewLine
91+
125,126: NL
92+
126,127: Integer 1
93+
128,129: ^
94+
130,131: Integer 2
95+
132,133: +
96+
134,135: Integer 3
97+
135,136: NewLine
98+
136,137: NL
99+
137,138: Integer 3
100+
139,140: +
101+
141,142: (
102+
142,143: Integer 1
103+
144,145: +
104+
146,147: Integer 2
105+
147,148: )
106+
149,150: *
107+
151,152: Integer 3
108+
152,153: NewLine
109+
153,154: NL
110+
154,155: (
111+
155,156: Integer 3
112+
157,158: +
113+
159,160: Integer 1
114+
160,161: )
115+
162,163: *
116+
164,165: Integer 2
117+
165,167: **
118+
167,168: Integer 3
119+
169,170: +
120+
171,172: Integer 1
121+
172,173: NewLine

0 commit comments

Comments
 (0)