forked from 1b-agent/opencadstudio-upstream
Merge pull request #984 from NgoQuocViet2001/fix-power-right-assoc
fix(expr-eval): make ^ right-associative
This commit is contained in:
1 file changed
+23
-2
+23
-2
@@ -207,7 +207,13 @@ impl Parser {
|
|||||||
Ok(left)
|
Ok(left)
|
||||||
}
|
}
|
||||||
|
|
||||||
// power → unary ('^' unary)?
|
// power → unary ('^' power)?
|
||||||
|
//
|
||||||
|
// The exponent recurses into `parse_power`, not `parse_unary`, so `^` is
|
||||||
|
// right-associative — `2^3^2` is `2^(3^2)`, the standard reading. Binding
|
||||||
|
// it to the left both gave the wrong number and left the trailing `^2`
|
||||||
|
// unconsumed, which the caller's scan then handed back as literal text:
|
||||||
|
// `2^3^2` evaluated to the string "8^2" instead of 512.
|
||||||
fn parse_power(&mut self, depth: usize) -> Result<f64, ()> {
|
fn parse_power(&mut self, depth: usize) -> Result<f64, ()> {
|
||||||
if depth > MAX_RECURSION {
|
if depth > MAX_RECURSION {
|
||||||
return Err(());
|
return Err(());
|
||||||
@@ -215,7 +221,7 @@ impl Parser {
|
|||||||
let base = self.parse_unary(depth + 1)?;
|
let base = self.parse_unary(depth + 1)?;
|
||||||
if let Some(Token::Caret) = self.peek() {
|
if let Some(Token::Caret) = self.peek() {
|
||||||
self.advance();
|
self.advance();
|
||||||
let exp = self.parse_unary(depth + 1)?;
|
let exp = self.parse_power(depth + 1)?;
|
||||||
return Ok(base.powf(exp));
|
return Ok(base.powf(exp));
|
||||||
}
|
}
|
||||||
Ok(base)
|
Ok(base)
|
||||||
@@ -591,6 +597,21 @@ mod tests {
|
|||||||
assert_eq!(eval_to_string("(((10)))"), "10");
|
assert_eq!(eval_to_string("(((10)))"), "10");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `^` is right-associative, so a chained exponent evaluates as one number
|
||||||
|
// instead of stopping at the first `^` and leaving the rest as text.
|
||||||
|
#[test]
|
||||||
|
fn power_is_right_associative() {
|
||||||
|
assert_eq!(eval_to_string("2^3^2"), "512");
|
||||||
|
assert_eq!(eval_to_string("2^3^2+1"), "513");
|
||||||
|
assert_eq!(eval_to_string("4^0.5^2"), "1.4142135623730951"); // 4^(1/4)
|
||||||
|
assert_eq!(eval_to_string("2^2^2^2"), "65536");
|
||||||
|
// A single exponent, and a parenthesised one, are unaffected.
|
||||||
|
assert_eq!(eval_to_string("2^3"), "8");
|
||||||
|
assert_eq!(eval_to_string("2^(3^2)"), "512");
|
||||||
|
assert_eq!(eval_to_string("(2^3)^2"), "64");
|
||||||
|
assert_eq!(eval_to_string("2^-1"), "0.5");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unary_ops() {
|
fn unary_ops() {
|
||||||
assert_eq!(eval_to_string("--5"), "5");
|
assert_eq!(eval_to_string("--5"), "5");
|
||||||
|
|||||||
Reference in new issue
Block a user