Skip to content

Commit 7d836f0

Browse files
Fix unary minus precedence being higher than ^
Closes #37
1 parent e1e7826 commit 7d836f0

File tree

3 files changed

+71
-54
lines changed

3 files changed

+71
-54
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Changelog
22

3+
## Next
4+
- Fix negative unary `-` always having higher precedence than `^`. This resulted in `-3^2` returning `9` instead of `-9`
5+
36
## 1.9.2 - 2023 Jul 11
47
- Fix automatic light year unit not chosen for large distances (@gcomte)
58

src/lib.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub mod parser;
4848
#[rustfmt::skip]
4949
pub mod units;
5050

51-
#[derive(Clone, Debug)]
51+
#[derive(Clone, Debug, PartialEq)]
5252
/// A number with a `Unit`.
5353
///
5454
/// Example:
@@ -290,3 +290,23 @@ pub fn eval(
290290
Err(e) => Err(format!("Lexing error: {}", e)),
291291
}
292292
}
293+
294+
#[cfg(test)]
295+
mod tests {
296+
use super::*;
297+
298+
fn default_eval(input: &str) -> Number {
299+
eval(input, true, units::Unit::Celsius, false).unwrap()
300+
}
301+
302+
#[test]
303+
fn test_evaluations() {
304+
assert_eq!(default_eval("-2(-3)"), Number::new(d128!(6), Unit::NoUnit));
305+
assert_eq!(default_eval("-2(3)"), Number::new(d128!(-6), Unit::NoUnit));
306+
assert_eq!(default_eval("(3)-2"), Number::new(d128!(1), Unit::NoUnit));
307+
assert_eq!(default_eval("-1km to m"), Number::new(d128!(-1000), Unit::Meter));
308+
assert_eq!(default_eval("2*-3*0.5"), Number::new(d128!(-3), Unit::NoUnit));
309+
assert_eq!(default_eval("-3^2"), Number::new(d128!(-9), Unit::NoUnit));
310+
assert_eq!(default_eval("-1+2"), Number::new(d128!(1), Unit::NoUnit));
311+
}
312+
}

src/parser.rs

Lines changed: 47 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ impl AstNode {
2424

2525
/// Parse [`Token`]s into an Abstract Syntax Tree ([`AstNode`])
2626
pub fn parse(tokens: &[Token]) -> Result<AstNode, String> {
27-
parse_level_1(tokens, 0).and_then(|(ast, next_pos)| {
27+
parse_text_operators(tokens, 0).and_then(|(ast, next_pos)| {
2828
if next_pos == tokens.len() {
2929
Ok(ast)
3030
} else {
@@ -38,41 +38,39 @@ pub fn parse(tokens: &[Token]) -> Result<AstNode, String> {
3838

3939
// level 1 precedence (lowest): to, of
4040
/// Parse [`To`](crate::TextOperator::To) and [`Of`](crate::TextOperator::Of)
41-
pub fn parse_level_1(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
41+
pub fn parse_text_operators(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
4242
// do higher precedences first, then come back down
43-
let (mut node, mut pos) = parse_level_2(tokens, pos)?;
43+
let (mut node, mut pos) = parse_plus(tokens, pos)?;
4444
// now we loop through the next tokens
4545
loop {
4646
let token = tokens.get(pos);
4747
match token {
4848
// if there's a match, we once again do higher precedences, then come
4949
// back down again and continue the loop
5050
Some(&Token::TextOperator(To)) | Some(&Token::TextOperator(Of)) => {
51-
let (right_node, next_pos) = parse_level_2(tokens, pos + 1)?;
51+
let (right_node, next_pos) = parse_plus(tokens, pos + 1)?;
5252
let mut new_node = AstNode::new(token.unwrap().clone());
5353
new_node.children.push(node);
5454
new_node.children.push(right_node);
5555
node = new_node;
5656
pos = next_pos;
5757
}
58-
// if there's no match, we go down to a lower precedence (or, in this
59-
// case, we're done)
58+
// if there's no match, we go down to a lower precedence
6059
_ => {
6160
return Ok((node, pos));
6261
}
6362
}
6463
}
6564
}
6665

67-
// level 2 precedence: +, -
68-
/// Parse [`Plus`](crate::Operator::Plus) and [`Minus`](crate::Operator::Minus)
69-
pub fn parse_level_2(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
70-
let (mut node, mut pos) = parse_level_3(tokens, pos)?;
66+
/// Parse [`+`](crate::Operator::Plus), [`-`](crate::Operator::Minus)
67+
pub fn parse_plus(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
68+
let (mut node, mut pos) = parse_unary(tokens, pos)?;
7169
loop {
7270
let token = tokens.get(pos);
7371
match token {
7472
Some(&Token::Operator(Plus)) | Some(&Token::Operator(Minus)) => {
75-
let (right_node, next_pos) = parse_level_3(tokens, pos + 1)?;
73+
let (right_node, next_pos) = parse_unary(tokens, pos + 1)?;
7674
let mut new_node = AstNode::new(token.unwrap().clone());
7775
new_node.children.push(node);
7876
new_node.children.push(right_node);
@@ -86,9 +84,23 @@ pub fn parse_level_2(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
8684
}
8785
}
8886

89-
// level 3 precedence: *, /, modulo, implicative multiplication, foot-inch 6'4"
90-
/// Parse [`Multiply`](crate::Operator::Multiply), [`Divide`](crate::Operator::Divide), [`Modulo`](crate::Operator::Modulo) and implicative multiplication (for example`2pi`)
91-
pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
87+
/// Parse [`unary -`](Token::Negative) (for example -5)
88+
pub fn parse_unary(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
89+
// Since a unary operator has no left side, we parse the the unary operator immediately
90+
let token = tokens.get(pos);
91+
match token {
92+
Some(&Token::Operator(Minus)) => {
93+
let (right_node, next_pos) = parse_mult_level(tokens, pos + 1)?;
94+
let mut new_node = AstNode::new(Token::Negative);
95+
new_node.children.push(right_node);
96+
Ok((new_node, next_pos))
97+
}
98+
_ => parse_mult_level(tokens, pos),
99+
}
100+
}
101+
102+
/// Parse [`*`](crate::Operator::Multiply), [`/`](crate::Operator::Divide), [`Modulo`](crate::Operator::Modulo), implicative multiplication (for example`2pi`), foot-inch syntax (for example `6'4"`)
103+
pub fn parse_mult_level(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
92104
// parse foot-inch syntax 6'4"
93105
let token0 = tokens.get(pos);
94106
if let Some(Token::Number(_number)) = token0 {
@@ -117,15 +129,15 @@ pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
117129
}
118130
}
119131

120-
let (mut node, mut pos) = parse_level_4(tokens, pos)?;
132+
let (mut node, mut pos) = parse_caret(tokens, pos)?;
121133

122134
loop {
123135
let token = tokens.get(pos);
124136
match token {
125137
Some(&Token::Operator(Multiply))
126138
| Some(&Token::Operator(Divide))
127139
| Some(&Token::Operator(Modulo)) => {
128-
let (right_node, next_pos) = parse_level_4(tokens, pos + 1)?;
140+
let (right_node, next_pos) = parse_caret(tokens, pos + 1)?;
129141
let mut new_node = AstNode::new(token.unwrap().clone());
130142
new_node.children.push(node);
131143
new_node.children.push(right_node);
@@ -145,7 +157,7 @@ pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
145157
let last_token = tokens.get(pos - 1);
146158
match last_token {
147159
Some(&Token::Constant(_)) | Some(&Token::Operator(RightParen)) => {
148-
let (right_node, next_pos) = parse_level_4(tokens, pos)?;
160+
let (right_node, next_pos) = parse_caret(tokens, pos)?;
149161
let mut new_node = AstNode::new(Token::Operator(Multiply));
150162
new_node.children.push(node);
151163
new_node.children.push(right_node);
@@ -162,7 +174,7 @@ pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
162174
let last_token = tokens.get(pos - 1);
163175
match last_token {
164176
Some(&Token::Number(_)) | Some(&Token::Operator(RightParen)) => {
165-
let (right_node, next_pos) = parse_level_4(tokens, pos)?;
177+
let (right_node, next_pos) = parse_caret(tokens, pos)?;
166178
let mut new_node = AstNode::new(Token::Operator(Multiply));
167179
new_node.children.push(node);
168180
new_node.children.push(right_node);
@@ -179,7 +191,7 @@ pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
179191
let last_token = tokens.get(pos - 1);
180192
match last_token {
181193
Some(&Token::Number(_)) | Some(&Token::Operator(RightParen)) => {
182-
let (right_node, next_pos) = parse_level_4(tokens, pos)?;
194+
let (right_node, next_pos) = parse_caret(tokens, pos)?;
183195
let mut new_node = AstNode::new(Token::Operator(Multiply));
184196
new_node.children.push(node);
185197
new_node.children.push(right_node);
@@ -198,7 +210,7 @@ pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
198210
Some(&Token::Number(_))
199211
| Some(&Token::Constant(_))
200212
| Some(&Token::Operator(RightParen)) => {
201-
let (right_node, next_pos) = parse_level_4(tokens, pos)?;
213+
let (right_node, next_pos) = parse_caret(tokens, pos)?;
202214
let mut new_node = AstNode::new(Token::Operator(Multiply));
203215
new_node.children.push(node);
204216
new_node.children.push(right_node);
@@ -217,15 +229,14 @@ pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
217229
}
218230
}
219231

220-
// level 4 precedence: ^
221-
/// Parse [`Caret`](crate::Operator::Caret)
222-
pub fn parse_level_4(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
223-
let (mut node, mut pos) = parse_level_5(tokens, pos)?;
232+
/// Parse [`^`](crate::Operator::Caret)
233+
pub fn parse_caret(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
234+
let (mut node, mut pos) = parse_unary_high(tokens, pos)?;
224235
loop {
225236
let token = tokens.get(pos);
226237
match token {
227238
Some(&Token::Operator(Caret)) => {
228-
let (right_node, next_pos) = parse_level_5(tokens, pos + 1)?;
239+
let (right_node, next_pos) = parse_unary_high(tokens, pos + 1)?;
229240
let mut new_node = AstNode::new(token.unwrap().clone());
230241
new_node.children.push(node);
231242
new_node.children.push(right_node);
@@ -239,35 +250,23 @@ pub fn parse_level_4(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
239250
}
240251
}
241252

242-
// level 5 precedence: - (as in -5, but not 4-5)
243-
/// Parse [`Negative`](Token::Negative)
244-
pub fn parse_level_5(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
245-
// Here we parse the negative unary operator. If the current token
246-
// is a minus, we wrap the right_node inside a Negative AstNode.
247-
//
248-
// Why doesn't this parse 4-5? First, we will first get a 4. In which case,
249-
// we just return the result of parse_level_6(), which will include the pos
250-
// of +. This will then go down to level 2 and be parsed as a normal minus
251-
// operator.
252-
// The difference is that in other levels, we parse higher priorities
253-
// immediately, while in this one we instead check if the current token
254-
// is a minus, and if not, we then return the higher priority as normal.
253+
/// Parse [`unary -`](Token::Negative) at high precedence (for example in 3^-2)
254+
pub fn parse_unary_high(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
255255
let token = tokens.get(pos);
256256
match token {
257257
Some(&Token::Operator(Minus)) => {
258-
let (right_node, next_pos) = parse_level_6(tokens, pos + 1)?;
258+
let (right_node, next_pos) = parse_suffix(tokens, pos + 1)?;
259259
let mut new_node = AstNode::new(Token::Negative);
260260
new_node.children.push(right_node);
261261
Ok((new_node, next_pos))
262262
}
263-
_ => parse_level_6(tokens, pos),
263+
_ => parse_suffix(tokens, pos),
264264
}
265265
}
266266

267-
// level 6 precedence: !, percent, units attached to values
268-
/// Parse [`Factorial`](crate::UnaryOperator::Factorial) and [`Percent`](crate::UnaryOperator::Percent)
269-
pub fn parse_level_6(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
270-
let (mut node, mut pos) = parse_level_7(tokens, pos)?;
267+
/// Parse [`!!`](crate::UnaryOperator::Factorial), [`Percent`](crate::UnaryOperator::Percent), units attached to values
268+
pub fn parse_suffix(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
269+
let (mut node, mut pos) = parse_highest(tokens, pos)?;
271270
loop {
272271
let token = tokens.get(pos);
273272
match token {
@@ -298,13 +297,8 @@ pub fn parse_level_6(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
298297
}
299298
}
300299

301-
// level 7 precedence: numbers, standalone units, constants, functions, parens
302-
/// Parse [`Number`](Token::Number),
303-
/// [`Unit`](Token::Unit),
304-
/// [`Constant`](Token::Constant),
305-
/// [`FunctionIdentifier`](Token::FunctionIdentifier),
306-
/// [`Paren`](Token::Paren)
307-
pub fn parse_level_7(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
300+
/// Parse [`Number`](Token::Number), standalone [`Unit`](Token::Unit), [`Constant`](Token::Constant), [`FunctionIdentifier`](Token::FunctionIdentifier), [`Paren`](Token::Paren)
301+
pub fn parse_highest(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
308302
let token: &Token = tokens
309303
.get(pos)
310304
.ok_or(format!("Unexpected end of input at {}", pos))?;
@@ -329,7 +323,7 @@ pub fn parse_level_7(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
329323
Some(&Token::Operator(LeftParen)) => {
330324
// parse everything inside as you would with normal parentheses,
331325
// then put it inside an ast node.
332-
parse_level_1(tokens, left_paren_pos + 1).and_then(|(node, next_pos)| {
326+
parse_text_operators(tokens, left_paren_pos + 1).and_then(|(node, next_pos)| {
333327
if let Some(&Token::Operator(RightParen)) = tokens.get(next_pos) {
334328
let mut function_node = AstNode::new(token.clone());
335329
function_node.children.push(node);
@@ -350,7 +344,7 @@ pub fn parse_level_7(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
350344
}
351345
}
352346
Token::Operator(LeftParen) => {
353-
parse_level_1(tokens, pos + 1).and_then(|(node, next_pos)| {
347+
parse_text_operators(tokens, pos + 1).and_then(|(node, next_pos)| {
354348
if let Some(&Token::Operator(RightParen)) = tokens.get(next_pos) {
355349
let mut paren_node = AstNode::new(Token::Paren);
356350
paren_node.children.push(node);

0 commit comments

Comments
 (0)