Switch indentation to tabs
This commit is contained in:
parent
1bb3819350
commit
4ac7e74c6b
@ -1 +1 @@
|
|||||||
tab_spaces=2
|
hard_tabs=true
|
||||||
|
|||||||
512
src/evaluator.rs
512
src/evaluator.rs
@ -11,8 +11,8 @@ use decimal::d128;
|
|||||||
|
|
||||||
/// Evaluate an [`AstNode`] into a [`Number`]
|
/// Evaluate an [`AstNode`] into a [`Number`]
|
||||||
pub fn evaluate(ast: &AstNode) -> Result<Number, String> {
|
pub fn evaluate(ast: &AstNode) -> Result<Number, String> {
|
||||||
let answer = evaluate_node(ast)?;
|
let answer = evaluate_node(ast)?;
|
||||||
Ok(answer)
|
Ok(answer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the factorial of a [`struct@d128`] up to `1000!` without doing any math
|
/// Returns the factorial of a [`struct@d128`] up to `1000!` without doing any math
|
||||||
@ -21,287 +21,289 @@ pub fn evaluate(ast: &AstNode) -> Result<Number, String> {
|
|||||||
///
|
///
|
||||||
/// All return values of this function are hard-coded.
|
/// All return values of this function are hard-coded.
|
||||||
pub fn factorial(input: d128) -> d128 {
|
pub fn factorial(input: d128) -> d128 {
|
||||||
lookup_factorial(input.into())
|
lookup_factorial(input.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the square root of a [`struct@d128`]
|
/// Returns the square root of a [`struct@d128`]
|
||||||
pub fn sqrt(input: d128) -> d128 {
|
pub fn sqrt(input: d128) -> d128 {
|
||||||
let mut n = d128!(1);
|
let mut n = d128!(1);
|
||||||
let half = d128!(0.5);
|
let half = d128!(0.5);
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
n = (n + input / n) * half;
|
n = (n + input / n) * half;
|
||||||
}
|
}
|
||||||
n
|
n
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the cube root of a [`struct@d128`]
|
/// Returns the cube root of a [`struct@d128`]
|
||||||
pub fn cbrt(input: d128) -> d128 {
|
pub fn cbrt(input: d128) -> d128 {
|
||||||
let mut n: d128 = input;
|
let mut n: d128 = input;
|
||||||
// hope that 20 iterations makes it accurate enough
|
// hope that 20 iterations makes it accurate enough
|
||||||
let three = d128!(3);
|
let three = d128!(3);
|
||||||
for _ in 0..20 {
|
for _ in 0..20 {
|
||||||
let z2 = n * n;
|
let z2 = n * n;
|
||||||
n = n - ((n * z2 - input) / (three * z2));
|
n = n - ((n * z2 - input) / (three * z2));
|
||||||
}
|
}
|
||||||
n
|
n
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the sine of a [`struct@d128`]
|
/// Returns the sine of a [`struct@d128`]
|
||||||
pub fn sin(mut input: d128) -> d128 {
|
pub fn sin(mut input: d128) -> d128 {
|
||||||
let pi = d128!(3.141592653589793238462643383279503);
|
let pi = d128!(3.141592653589793238462643383279503);
|
||||||
let pi2 = d128!(6.283185307179586476925286766559006);
|
let pi2 = d128!(6.283185307179586476925286766559006);
|
||||||
|
|
||||||
input %= pi2;
|
input %= pi2;
|
||||||
|
|
||||||
let negative_correction = if input.is_negative() {
|
let negative_correction = if input.is_negative() {
|
||||||
input -= pi;
|
input -= pi;
|
||||||
d128!(-1)
|
d128!(-1)
|
||||||
} else {
|
} else {
|
||||||
d128!(1)
|
d128!(1)
|
||||||
};
|
};
|
||||||
|
|
||||||
let one = d128!(1);
|
let one = d128!(1);
|
||||||
let two = d128!(2);
|
let two = d128!(2);
|
||||||
let neg_one = -one;
|
let neg_one = -one;
|
||||||
|
|
||||||
let precision = 37;
|
let precision = 37;
|
||||||
let mut result = d128!(0);
|
let mut result = d128!(0);
|
||||||
for i_int in 0..precision {
|
for i_int in 0..precision {
|
||||||
let i = d128::from(i_int);
|
let i = d128::from(i_int);
|
||||||
let calc_result = two * i + one;
|
let calc_result = two * i + one;
|
||||||
result += neg_one.pow(i) * (input.pow(calc_result) / factorial(calc_result));
|
result += neg_one.pow(i) * (input.pow(calc_result) / factorial(calc_result));
|
||||||
}
|
}
|
||||||
|
|
||||||
negative_correction * result
|
negative_correction * result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the cosine of a [`struct@d128`]
|
/// Returns the cosine of a [`struct@d128`]
|
||||||
pub fn cos(input: d128) -> d128 {
|
pub fn cos(input: d128) -> d128 {
|
||||||
let half_pi = d128!(1.570796326794896619231321691639751);
|
let half_pi = d128!(1.570796326794896619231321691639751);
|
||||||
sin(half_pi - input)
|
sin(half_pi - input)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the tangent of a [`struct@d128`]
|
/// Returns the tangent of a [`struct@d128`]
|
||||||
pub fn tan(input: d128) -> d128 {
|
pub fn tan(input: d128) -> d128 {
|
||||||
sin(input) / cos(input)
|
sin(input) / cos(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate an [`AstNode`] into a [`Number`]
|
/// Evaluate an [`AstNode`] into a [`Number`]
|
||||||
fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
|
fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
|
||||||
let token = &ast_node.token;
|
let token = &ast_node.token;
|
||||||
let children = &ast_node.children;
|
let children = &ast_node.children;
|
||||||
match token {
|
match token {
|
||||||
Token::Number(number) => Ok(Number::new(*number, Unit::NoUnit)),
|
Token::Number(number) => Ok(Number::new(*number, Unit::NoUnit)),
|
||||||
Token::Constant(constant) => match constant {
|
Token::Constant(constant) => match constant {
|
||||||
Pi => Ok(Number::new(
|
Pi => Ok(Number::new(
|
||||||
d128!(3.141592653589793238462643383279503),
|
d128!(3.141592653589793238462643383279503),
|
||||||
Unit::NoUnit,
|
Unit::NoUnit,
|
||||||
)),
|
)),
|
||||||
E => Ok(Number::new(
|
E => Ok(Number::new(
|
||||||
d128!(2.718281828459045235360287471352662),
|
d128!(2.718281828459045235360287471352662),
|
||||||
Unit::NoUnit,
|
Unit::NoUnit,
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
Token::FunctionIdentifier(function) => {
|
Token::FunctionIdentifier(function) => {
|
||||||
let child_node = children.get(0).ok_or("Paren has no child[0]")?;
|
let child_node = children.get(0).ok_or("Paren has no child[0]")?;
|
||||||
let child_answer = evaluate_node(child_node)?;
|
let child_answer = evaluate_node(child_node)?;
|
||||||
match function {
|
match function {
|
||||||
Cbrt => {
|
Cbrt => {
|
||||||
if child_answer.unit.category() == UnitType::NoType {
|
if child_answer.unit.category() == UnitType::NoType {
|
||||||
let result = cbrt(child_answer.value);
|
let result = cbrt(child_answer.value);
|
||||||
Ok(Number::new(result, child_answer.unit))
|
Ok(Number::new(result, child_answer.unit))
|
||||||
} else {
|
} else {
|
||||||
Err("log() only accepts UnitType::NoType".to_string())
|
Err("log() only accepts UnitType::NoType".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Sqrt => {
|
Sqrt => {
|
||||||
if child_answer.unit.category() == UnitType::NoType {
|
if child_answer.unit.category() == UnitType::NoType {
|
||||||
let result = sqrt(child_answer.value);
|
let result = sqrt(child_answer.value);
|
||||||
Ok(Number::new(result, child_answer.unit))
|
Ok(Number::new(result, child_answer.unit))
|
||||||
} else {
|
} else {
|
||||||
Err("log() only accepts UnitType::NoType".to_string())
|
Err("log() only accepts UnitType::NoType".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log => {
|
Log => {
|
||||||
if child_answer.unit.category() == UnitType::NoType {
|
if child_answer.unit.category() == UnitType::NoType {
|
||||||
let result = child_answer.value.log10();
|
let result = child_answer.value.log10();
|
||||||
Ok(Number::new(result, child_answer.unit))
|
Ok(Number::new(result, child_answer.unit))
|
||||||
} else {
|
} else {
|
||||||
Err("log() only accepts UnitType::NoType".to_string())
|
Err("log() only accepts UnitType::NoType".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ln => {
|
Ln => {
|
||||||
if child_answer.unit.category() == UnitType::NoType {
|
if child_answer.unit.category() == UnitType::NoType {
|
||||||
let result = child_answer.value.ln();
|
let result = child_answer.value.ln();
|
||||||
Ok(Number::new(result, child_answer.unit))
|
Ok(Number::new(result, child_answer.unit))
|
||||||
} else {
|
} else {
|
||||||
Err("ln() only accepts UnitType::NoType".to_string())
|
Err("ln() only accepts UnitType::NoType".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Exp => {
|
Exp => {
|
||||||
if child_answer.unit.category() == UnitType::NoType {
|
if child_answer.unit.category() == UnitType::NoType {
|
||||||
let result = child_answer.value.exp(child_answer.value);
|
let result = child_answer.value.exp(child_answer.value);
|
||||||
Ok(Number::new(result, child_answer.unit))
|
Ok(Number::new(result, child_answer.unit))
|
||||||
} else {
|
} else {
|
||||||
Err("exp() only accepts UnitType::NoType".to_string())
|
Err("exp() only accepts UnitType::NoType".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Round => {
|
Round => {
|
||||||
// .quantize() rounds .5 to nearest even integer, so we correct that
|
// .quantize() rounds .5 to nearest even integer, so we correct that
|
||||||
let mut result = child_answer.value.quantize(d128!(1));
|
let mut result = child_answer.value.quantize(d128!(1));
|
||||||
let rounding_change = result - child_answer.value;
|
let rounding_change = result - child_answer.value;
|
||||||
// If the result was rounded down by 0.5, correct by +1
|
// If the result was rounded down by 0.5, correct by +1
|
||||||
if rounding_change == d128!(-0.5) {
|
if rounding_change == d128!(-0.5) {
|
||||||
result += d128!(1);
|
result += d128!(1);
|
||||||
}
|
}
|
||||||
Ok(Number::new(result, child_answer.unit))
|
Ok(Number::new(result, child_answer.unit))
|
||||||
}
|
}
|
||||||
Ceil => {
|
Ceil => {
|
||||||
let mut result = child_answer.value.quantize(d128!(1));
|
let mut result = child_answer.value.quantize(d128!(1));
|
||||||
let rounding_change = result - child_answer.value;
|
let rounding_change = result - child_answer.value;
|
||||||
if rounding_change.is_negative() {
|
if rounding_change.is_negative() {
|
||||||
result += d128!(1);
|
result += d128!(1);
|
||||||
}
|
}
|
||||||
Ok(Number::new(result, child_answer.unit))
|
Ok(Number::new(result, child_answer.unit))
|
||||||
}
|
}
|
||||||
Floor => {
|
Floor => {
|
||||||
let mut result = child_answer.value.quantize(d128!(1));
|
let mut result = child_answer.value.quantize(d128!(1));
|
||||||
let rounding_change = result - child_answer.value;
|
let rounding_change = result - child_answer.value;
|
||||||
if !rounding_change.is_negative() {
|
if !rounding_change.is_negative() {
|
||||||
result -= d128!(1);
|
result -= d128!(1);
|
||||||
}
|
}
|
||||||
Ok(Number::new(result, child_answer.unit))
|
Ok(Number::new(result, child_answer.unit))
|
||||||
}
|
}
|
||||||
Abs => {
|
Abs => {
|
||||||
let mut result = child_answer.value.abs();
|
let mut result = child_answer.value.abs();
|
||||||
let rounding_change = result - child_answer.value;
|
let rounding_change = result - child_answer.value;
|
||||||
if rounding_change == d128!(-0.5) {
|
if rounding_change == d128!(-0.5) {
|
||||||
result += d128!(1);
|
result += d128!(1);
|
||||||
}
|
}
|
||||||
Ok(Number::new(result, child_answer.unit))
|
Ok(Number::new(result, child_answer.unit))
|
||||||
}
|
}
|
||||||
Sin => {
|
Sin => {
|
||||||
let result = sin(child_answer.value);
|
let result = sin(child_answer.value);
|
||||||
Ok(Number::new(result, child_answer.unit))
|
Ok(Number::new(result, child_answer.unit))
|
||||||
}
|
}
|
||||||
Cos => {
|
Cos => {
|
||||||
let result = cos(child_answer.value);
|
let result = cos(child_answer.value);
|
||||||
Ok(Number::new(result, child_answer.unit))
|
Ok(Number::new(result, child_answer.unit))
|
||||||
}
|
}
|
||||||
Tan => {
|
Tan => {
|
||||||
let result = tan(child_answer.value);
|
let result = tan(child_answer.value);
|
||||||
Ok(Number::new(result, child_answer.unit))
|
Ok(Number::new(result, child_answer.unit))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Token::Unit(unit) => {
|
Token::Unit(unit) => {
|
||||||
let child_node = children.get(0).ok_or("Unit has no child[0]")?;
|
let child_node = children.get(0).ok_or("Unit has no child[0]")?;
|
||||||
let child_answer = evaluate_node(child_node)?;
|
let child_answer = evaluate_node(child_node)?;
|
||||||
Ok(Number::new(child_answer.value, *unit))
|
Ok(Number::new(child_answer.value, *unit))
|
||||||
}
|
}
|
||||||
Token::Negative => {
|
Token::Negative => {
|
||||||
let child_node = children.get(0).ok_or("Negative has no child[0]")?;
|
let child_node = children.get(0).ok_or("Negative has no child[0]")?;
|
||||||
let child_answer = evaluate_node(child_node)?;
|
let child_answer = evaluate_node(child_node)?;
|
||||||
Ok(Number::new(-child_answer.value, child_answer.unit))
|
Ok(Number::new(-child_answer.value, child_answer.unit))
|
||||||
}
|
}
|
||||||
Token::Paren => {
|
Token::Paren => {
|
||||||
let child_node = children.get(0).ok_or("Paren has no child[0]")?;
|
let child_node = children.get(0).ok_or("Paren has no child[0]")?;
|
||||||
evaluate_node(child_node)
|
evaluate_node(child_node)
|
||||||
}
|
}
|
||||||
Token::UnaryOperator(operator) => {
|
Token::UnaryOperator(operator) => {
|
||||||
let child_node = children
|
let child_node = children
|
||||||
.get(0)
|
.get(0)
|
||||||
.ok_or(format!("Token {:?} has no child[0]", token))?;
|
.ok_or(format!("Token {:?} has no child[0]", token))?;
|
||||||
let child_answer = evaluate_node(child_node)?;
|
let child_answer = evaluate_node(child_node)?;
|
||||||
match operator {
|
match operator {
|
||||||
Percent => Ok(Number::new(
|
Percent => Ok(Number::new(
|
||||||
child_answer.value / d128!(100),
|
child_answer.value / d128!(100),
|
||||||
child_answer.unit,
|
child_answer.unit,
|
||||||
)),
|
)),
|
||||||
Factorial => {
|
Factorial => {
|
||||||
let result = factorial(child_answer.value);
|
let result = factorial(child_answer.value);
|
||||||
if result.is_nan() {
|
if result.is_nan() {
|
||||||
return Err("Can only perform factorial on integers from 0 to 1000".to_string());
|
return Err(
|
||||||
}
|
"Can only perform factorial on integers from 0 to 1000".to_string()
|
||||||
Ok(Number::new(result, child_answer.unit))
|
);
|
||||||
}
|
}
|
||||||
}
|
Ok(Number::new(result, child_answer.unit))
|
||||||
}
|
}
|
||||||
Token::NamedNumber(named_number) => {
|
}
|
||||||
let child_node = children
|
}
|
||||||
.get(0)
|
Token::NamedNumber(named_number) => {
|
||||||
.ok_or(format!("Token {:?} has no child[0]", token))?;
|
let child_node = children
|
||||||
let named_number_value = lookup_named_number(named_number);
|
.get(0)
|
||||||
if let Token::NamedNumber(child_nn) = &child_node.token {
|
.ok_or(format!("Token {:?} has no child[0]", token))?;
|
||||||
let child_nn_value = lookup_named_number(child_nn);
|
let named_number_value = lookup_named_number(named_number);
|
||||||
if child_nn_value > named_number_value {
|
if let Token::NamedNumber(child_nn) = &child_node.token {
|
||||||
return Err(format!("Unexpected smaller token {:?}", token));
|
let child_nn_value = lookup_named_number(child_nn);
|
||||||
}
|
if child_nn_value > named_number_value {
|
||||||
}
|
return Err(format!("Unexpected smaller token {:?}", token));
|
||||||
let child_answer = evaluate_node(child_node)?;
|
}
|
||||||
let result = child_answer.value * named_number_value;
|
}
|
||||||
Ok(Number::new(result, child_answer.unit))
|
let child_answer = evaluate_node(child_node)?;
|
||||||
}
|
let result = child_answer.value * named_number_value;
|
||||||
Token::TextOperator(operator) => {
|
Ok(Number::new(result, child_answer.unit))
|
||||||
let left_child = children
|
}
|
||||||
.get(0)
|
Token::TextOperator(operator) => {
|
||||||
.ok_or(format!("Token {:?} has no child[0]", token))?;
|
let left_child = children
|
||||||
let right_child = children
|
.get(0)
|
||||||
.get(1)
|
.ok_or(format!("Token {:?} has no child[0]", token))?;
|
||||||
.ok_or(format!("Token {:?} has no child[1]", token))?;
|
let right_child = children
|
||||||
|
.get(1)
|
||||||
|
.ok_or(format!("Token {:?} has no child[1]", token))?;
|
||||||
|
|
||||||
match operator {
|
match operator {
|
||||||
To => {
|
To => {
|
||||||
if let Token::Unit(right_unit) = right_child.token {
|
if let Token::Unit(right_unit) = right_child.token {
|
||||||
let left = evaluate_node(left_child)?;
|
let left = evaluate_node(left_child)?;
|
||||||
let result = convert(left, right_unit)?;
|
let result = convert(left, right_unit)?;
|
||||||
Ok(result)
|
Ok(result)
|
||||||
} else {
|
} else {
|
||||||
Err("Right side of To operator needs to be a unit".to_string())
|
Err("Right side of To operator needs to be a unit".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Of => {
|
Of => {
|
||||||
let left = evaluate_node(left_child)?;
|
let left = evaluate_node(left_child)?;
|
||||||
let right = evaluate_node(right_child)?;
|
let right = evaluate_node(right_child)?;
|
||||||
if left.unit == Unit::NoUnit {
|
if left.unit == Unit::NoUnit {
|
||||||
Ok(Number::new(left.value * right.value, right.unit))
|
Ok(Number::new(left.value * right.value, right.unit))
|
||||||
} else {
|
} else {
|
||||||
Err("Left side of the Of operator must be NoUnit".to_string())
|
Err("Left side of the Of operator must be NoUnit".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Token::Operator(operator) => {
|
Token::Operator(operator) => {
|
||||||
let left_child = children
|
let left_child = children
|
||||||
.get(0)
|
.get(0)
|
||||||
.ok_or(format!("Token {:?} has no child[0]", token))?;
|
.ok_or(format!("Token {:?} has no child[0]", token))?;
|
||||||
let right_child = children
|
let right_child = children
|
||||||
.get(1)
|
.get(1)
|
||||||
.ok_or(format!("Token {:?} has no child[1]", token))?;
|
.ok_or(format!("Token {:?} has no child[1]", token))?;
|
||||||
let left = evaluate_node(left_child)?;
|
let left = evaluate_node(left_child)?;
|
||||||
let right = evaluate_node(right_child)?;
|
let right = evaluate_node(right_child)?;
|
||||||
match operator {
|
match operator {
|
||||||
Plus => Ok(add(left, right)?),
|
Plus => Ok(add(left, right)?),
|
||||||
Minus => Ok(subtract(left, right)?),
|
Minus => Ok(subtract(left, right)?),
|
||||||
Multiply => {
|
Multiply => {
|
||||||
Ok(multiply(left, right)?)
|
Ok(multiply(left, right)?)
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
Divide => {
|
Divide => {
|
||||||
Ok(divide(left, right)?)
|
Ok(divide(left, right)?)
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
Modulo => {
|
Modulo => {
|
||||||
Ok(modulo(left, right)?)
|
Ok(modulo(left, right)?)
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
Caret => {
|
Caret => {
|
||||||
Ok(pow(left, right)?)
|
Ok(pow(left, right)?)
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
_ => Err(format!("Unexpected operator {:?}", operator)),
|
_ => Err(format!("Unexpected operator {:?}", operator)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(format!("Unexpected token {:?}", token)),
|
_ => Err(format!("Unexpected token {:?}", token)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2270
src/lexer.rs
2270
src/lexer.rs
File diff suppressed because it is too large
Load Diff
280
src/lib.rs
280
src/lib.rs
@ -1,10 +1,10 @@
|
|||||||
#![cfg_attr(
|
#![cfg_attr(
|
||||||
feature = "cargo-clippy",
|
feature = "cargo-clippy",
|
||||||
allow(
|
allow(
|
||||||
clippy::comparison_chain,
|
clippy::comparison_chain,
|
||||||
clippy::if_same_then_else,
|
clippy::if_same_then_else,
|
||||||
clippy::match_like_matches_macro,
|
clippy::match_like_matches_macro,
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
//! calculation + conversion
|
//! calculation + conversion
|
||||||
//!
|
//!
|
||||||
@ -63,110 +63,110 @@ pub mod units;
|
|||||||
/// };
|
/// };
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Number {
|
pub struct Number {
|
||||||
/// The number part of a [`Number`] struct
|
/// The number part of a [`Number`] struct
|
||||||
pub value: d128,
|
pub value: d128,
|
||||||
/// The unit of a [`Number`] struct. This can be [`NoType`](units::UnitType::NoType)
|
/// The unit of a [`Number`] struct. This can be [`NoType`](units::UnitType::NoType)
|
||||||
pub unit: Unit,
|
pub unit: Unit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Number {
|
impl Number {
|
||||||
pub const fn new(value: d128, unit: Unit) -> Number {
|
pub const fn new(value: d128, unit: Unit) -> Number {
|
||||||
Number { value, unit }
|
Number { value, unit }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Display for Number {
|
impl Display for Number {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
// 0.2/0.01 results in 2E+1, but if we add zero it becomes 20
|
// 0.2/0.01 results in 2E+1, but if we add zero it becomes 20
|
||||||
let fixed_value = self.value + d128!(0);
|
let fixed_value = self.value + d128!(0);
|
||||||
let output = match self.unit {
|
let output = match self.unit {
|
||||||
Unit::NoUnit => format!("{}", fixed_value),
|
Unit::NoUnit => format!("{}", fixed_value),
|
||||||
unit => format!("{} {:?}", fixed_value, unit),
|
unit => format!("{} {:?}", fixed_value, unit),
|
||||||
};
|
};
|
||||||
write!(f, "{}", output)
|
write!(f, "{}", output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
/// Math operators like [`Multiply`](Operator::Multiply), parentheses, etc.
|
/// Math operators like [`Multiply`](Operator::Multiply), parentheses, etc.
|
||||||
pub enum Operator {
|
pub enum Operator {
|
||||||
Plus,
|
Plus,
|
||||||
Minus,
|
Minus,
|
||||||
Multiply,
|
Multiply,
|
||||||
Divide,
|
Divide,
|
||||||
Modulo,
|
Modulo,
|
||||||
Caret,
|
Caret,
|
||||||
LeftParen, // lexer only
|
LeftParen, // lexer only
|
||||||
RightParen, // lexer only
|
RightParen, // lexer only
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
/// Unary operators like [`Percent`](UnaryOperator::Percent) and [`Factorial`](UnaryOperator::Factorial).
|
/// Unary operators like [`Percent`](UnaryOperator::Percent) and [`Factorial`](UnaryOperator::Factorial).
|
||||||
pub enum UnaryOperator {
|
pub enum UnaryOperator {
|
||||||
Percent,
|
Percent,
|
||||||
Factorial,
|
Factorial,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
/// A Text operator like [`To`](TextOperator::To) or [`Of`](TextOperator::Of).
|
/// A Text operator like [`To`](TextOperator::To) or [`Of`](TextOperator::Of).
|
||||||
pub enum TextOperator {
|
pub enum TextOperator {
|
||||||
To,
|
To,
|
||||||
Of,
|
Of,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
/// A named number like [`Million`](NamedNumber::Million).
|
/// A named number like [`Million`](NamedNumber::Million).
|
||||||
pub enum NamedNumber {
|
pub enum NamedNumber {
|
||||||
Hundred,
|
Hundred,
|
||||||
Thousand,
|
Thousand,
|
||||||
Million,
|
Million,
|
||||||
Billion,
|
Billion,
|
||||||
Trillion,
|
Trillion,
|
||||||
Quadrillion,
|
Quadrillion,
|
||||||
Quintillion,
|
Quintillion,
|
||||||
Sextillion,
|
Sextillion,
|
||||||
Septillion,
|
Septillion,
|
||||||
Octillion,
|
Octillion,
|
||||||
Nonillion,
|
Nonillion,
|
||||||
Decillion,
|
Decillion,
|
||||||
Undecillion,
|
Undecillion,
|
||||||
Duodecillion,
|
Duodecillion,
|
||||||
Tredecillion,
|
Tredecillion,
|
||||||
Quattuordecillion,
|
Quattuordecillion,
|
||||||
Quindecillion,
|
Quindecillion,
|
||||||
Sexdecillion,
|
Sexdecillion,
|
||||||
Septendecillion,
|
Septendecillion,
|
||||||
Octodecillion,
|
Octodecillion,
|
||||||
Novemdecillion,
|
Novemdecillion,
|
||||||
Vigintillion,
|
Vigintillion,
|
||||||
Centillion,
|
Centillion,
|
||||||
Googol,
|
Googol,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
/// A constant like [`Pi`](Constant::Pi) or [`E`](Constant::E).
|
/// A constant like [`Pi`](Constant::Pi) or [`E`](Constant::E).
|
||||||
pub enum Constant {
|
pub enum Constant {
|
||||||
Pi,
|
Pi,
|
||||||
E,
|
E,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
/// Functions identifiers like [`Sqrt`](FunctionIdentifier::Sqrt), [`Sin`](FunctionIdentifier::Sin), [`Round`](FunctionIdentifier::Round), etc.
|
/// Functions identifiers like [`Sqrt`](FunctionIdentifier::Sqrt), [`Sin`](FunctionIdentifier::Sin), [`Round`](FunctionIdentifier::Round), etc.
|
||||||
pub enum FunctionIdentifier {
|
pub enum FunctionIdentifier {
|
||||||
Sqrt,
|
Sqrt,
|
||||||
Cbrt,
|
Cbrt,
|
||||||
|
|
||||||
Log,
|
Log,
|
||||||
Ln,
|
Ln,
|
||||||
Exp,
|
Exp,
|
||||||
|
|
||||||
Round,
|
Round,
|
||||||
Ceil,
|
Ceil,
|
||||||
Floor,
|
Floor,
|
||||||
Abs,
|
Abs,
|
||||||
|
|
||||||
Sin,
|
Sin,
|
||||||
Cos,
|
Cos,
|
||||||
Tan,
|
Tan,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
@ -178,15 +178,15 @@ pub enum FunctionIdentifier {
|
|||||||
/// dependingon them, turns it into a [`Percent`](UnaryOperator::Percent) or
|
/// dependingon them, turns it into a [`Percent`](UnaryOperator::Percent) or
|
||||||
/// [`Modulo`](Operator::Modulo) [`Token`].
|
/// [`Modulo`](Operator::Modulo) [`Token`].
|
||||||
pub enum LexerKeyword {
|
pub enum LexerKeyword {
|
||||||
Per,
|
Per,
|
||||||
PercentChar,
|
PercentChar,
|
||||||
In,
|
In,
|
||||||
DoubleQuotes,
|
DoubleQuotes,
|
||||||
Mercury,
|
Mercury,
|
||||||
Hg,
|
Hg,
|
||||||
PoundForce,
|
PoundForce,
|
||||||
Force,
|
Force,
|
||||||
Revolution,
|
Revolution,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
@ -194,29 +194,29 @@ pub enum LexerKeyword {
|
|||||||
///
|
///
|
||||||
/// Strings can be divided up into these tokens by the [`lexer`], and then put into the [`parser`].
|
/// Strings can be divided up into these tokens by the [`lexer`], and then put into the [`parser`].
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
Operator(Operator),
|
Operator(Operator),
|
||||||
UnaryOperator(UnaryOperator),
|
UnaryOperator(UnaryOperator),
|
||||||
Number(d128),
|
Number(d128),
|
||||||
FunctionIdentifier(FunctionIdentifier),
|
FunctionIdentifier(FunctionIdentifier),
|
||||||
Constant(Constant),
|
Constant(Constant),
|
||||||
/// Used by the parser only
|
/// Used by the parser only
|
||||||
Paren,
|
Paren,
|
||||||
/// Used by the lexer only
|
/// Used by the lexer only
|
||||||
Per,
|
Per,
|
||||||
/// Used by the parser only
|
/// Used by the parser only
|
||||||
LexerKeyword(LexerKeyword),
|
LexerKeyword(LexerKeyword),
|
||||||
TextOperator(TextOperator),
|
TextOperator(TextOperator),
|
||||||
NamedNumber(NamedNumber),
|
NamedNumber(NamedNumber),
|
||||||
/// The `-` symbol, specifically when used as `-5` and not `5-5`. Used by the parser only
|
/// The `-` symbol, specifically when used as `-5` and not `5-5`. Used by the parser only
|
||||||
Negative,
|
Negative,
|
||||||
Unit(units::Unit),
|
Unit(units::Unit),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! numtok {
|
macro_rules! numtok {
|
||||||
( $num:literal ) => {
|
( $num:literal ) => {
|
||||||
Token::Number(d128!($num))
|
Token::Number(d128!($num))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluates a string into a resulting [`Number`].
|
/// Evaluates a string into a resulting [`Number`].
|
||||||
@ -237,48 +237,52 @@ macro_rules! numtok {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn eval(
|
pub fn eval(
|
||||||
input: &str,
|
input: &str,
|
||||||
allow_trailing_operators: bool,
|
allow_trailing_operators: bool,
|
||||||
default_degree: Unit,
|
default_degree: Unit,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
) -> Result<Number, String> {
|
) -> Result<Number, String> {
|
||||||
let lex_start = Instant::now();
|
let lex_start = Instant::now();
|
||||||
|
|
||||||
match lexer::lex(input, allow_trailing_operators, default_degree) {
|
match lexer::lex(input, allow_trailing_operators, default_degree) {
|
||||||
Ok(tokens) => {
|
Ok(tokens) => {
|
||||||
let lex_time = Instant::now().duration_since(lex_start).as_nanos() as f32;
|
let lex_time = Instant::now().duration_since(lex_start).as_nanos() as f32;
|
||||||
if verbose {
|
if verbose {
|
||||||
println!("Lexed TokenVector: {:?}", tokens);
|
println!("Lexed TokenVector: {:?}", tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
let parse_start = Instant::now();
|
let parse_start = Instant::now();
|
||||||
match parser::parse(&tokens) {
|
match parser::parse(&tokens) {
|
||||||
Ok(ast) => {
|
Ok(ast) => {
|
||||||
let parse_time = Instant::now().duration_since(parse_start).as_nanos() as f32;
|
let parse_time = Instant::now().duration_since(parse_start).as_nanos() as f32;
|
||||||
if verbose {
|
if verbose {
|
||||||
println!("Parsed AstNode: {:#?}", ast);
|
println!("Parsed AstNode: {:#?}", ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
let eval_start = Instant::now();
|
let eval_start = Instant::now();
|
||||||
match evaluator::evaluate(&ast) {
|
match evaluator::evaluate(&ast) {
|
||||||
Ok(answer) => {
|
Ok(answer) => {
|
||||||
let eval_time = Instant::now().duration_since(eval_start).as_nanos() as f32;
|
let eval_time =
|
||||||
|
Instant::now().duration_since(eval_start).as_nanos() as f32;
|
||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
println!("Evaluated value: {} {:?}", answer.value, answer.unit);
|
println!("Evaluated value: {} {:?}", answer.value, answer.unit);
|
||||||
println!("\u{23f1} {:.3}ms lexing", lex_time / 1000.0 / 1000.0);
|
println!("\u{23f1} {:.3}ms lexing", lex_time / 1000.0 / 1000.0);
|
||||||
println!("\u{23f1} {:.3}ms parsing", parse_time / 1000.0 / 1000.0);
|
println!("\u{23f1} {:.3}ms parsing", parse_time / 1000.0 / 1000.0);
|
||||||
println!("\u{23f1} {:.3}ms evaluation", eval_time / 1000.0 / 1000.0);
|
println!(
|
||||||
}
|
"\u{23f1} {:.3}ms evaluation",
|
||||||
|
eval_time / 1000.0 / 1000.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(answer)
|
Ok(answer)
|
||||||
}
|
}
|
||||||
Err(e) => Err(format!("Eval error: {}", e)),
|
Err(e) => Err(format!("Eval error: {}", e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => Err(format!("Parsing error: {}", e)),
|
Err(e) => Err(format!("Parsing error: {}", e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => Err(format!("Lexing error: {}", e)),
|
Err(e) => Err(format!("Lexing error: {}", e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2060
src/lookup.rs
2060
src/lookup.rs
File diff suppressed because it is too large
Load Diff
116
src/main.rs
116
src/main.rs
@ -6,70 +6,70 @@ use std::process::exit;
|
|||||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
fn print_help() {
|
fn print_help() {
|
||||||
println!(concat!(
|
println!(concat!(
|
||||||
"Usage: cpc '<expression>' [options]",
|
"Usage: cpc '<expression>' [options]",
|
||||||
"\n",
|
"\n",
|
||||||
"\nOptions:",
|
"\nOptions:",
|
||||||
"\n --verbose Enable verbose logging",
|
"\n --verbose Enable verbose logging",
|
||||||
"\n --version Show cpc version",
|
"\n --version Show cpc version",
|
||||||
"\n --help Show this help page",
|
"\n --help Show this help page",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_args() -> env::Args {
|
fn get_args() -> env::Args {
|
||||||
let mut args = env::args();
|
let mut args = env::args();
|
||||||
args.next(); // skip binary name
|
args.next(); // skip binary name
|
||||||
args
|
args
|
||||||
}
|
}
|
||||||
|
|
||||||
/// CLI interface
|
/// CLI interface
|
||||||
fn main() {
|
fn main() {
|
||||||
// parse these first so they work if there are unexpected args
|
// parse these first so they work if there are unexpected args
|
||||||
for arg in get_args() {
|
for arg in get_args() {
|
||||||
match arg.as_str() {
|
match arg.as_str() {
|
||||||
"--version" => {
|
"--version" => {
|
||||||
println!("{}", VERSION);
|
println!("{}", VERSION);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
"--help" => {
|
"--help" => {
|
||||||
print_help();
|
print_help();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut verbose = false;
|
let mut verbose = false;
|
||||||
let mut expression_opt = None;
|
let mut expression_opt = None;
|
||||||
for arg in get_args() {
|
for arg in get_args() {
|
||||||
match arg.as_str() {
|
match arg.as_str() {
|
||||||
"-v" | "--verbose" => verbose = true,
|
"-v" | "--verbose" => verbose = true,
|
||||||
_ => {
|
_ => {
|
||||||
if expression_opt.is_none() {
|
if expression_opt.is_none() {
|
||||||
expression_opt = Some(arg);
|
expression_opt = Some(arg);
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Unexpected argument: {}", arg);
|
eprintln!("Unexpected argument: {}", arg);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let expression = match expression_opt {
|
let expression = match expression_opt {
|
||||||
Some(expression) => expression,
|
Some(expression) => expression,
|
||||||
None => {
|
None => {
|
||||||
print_help();
|
print_help();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match eval(&expression, true, Unit::Celsius, verbose) {
|
match eval(&expression, true, Unit::Celsius, verbose) {
|
||||||
Ok(answer) => {
|
Ok(answer) => {
|
||||||
if !verbose {
|
if !verbose {
|
||||||
println!("{}", answer);
|
println!("{}", answer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
622
src/parser.rs
622
src/parser.rs
@ -7,295 +7,295 @@ use crate::UnaryOperator::{Factorial, Percent};
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// A struct with a [`Token`](AstNode::token) and [`AstNode`] [`children`](AstNode::children)
|
/// A struct with a [`Token`](AstNode::token) and [`AstNode`] [`children`](AstNode::children)
|
||||||
pub struct AstNode {
|
pub struct AstNode {
|
||||||
/// The children of the [`AstNode`]
|
/// The children of the [`AstNode`]
|
||||||
pub children: Vec<AstNode>,
|
pub children: Vec<AstNode>,
|
||||||
/// The token of the [`AstNode`]
|
/// The token of the [`AstNode`]
|
||||||
pub token: Token,
|
pub token: Token,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstNode {
|
impl AstNode {
|
||||||
pub const fn new(token: Token) -> AstNode {
|
pub const fn new(token: Token) -> AstNode {
|
||||||
AstNode {
|
AstNode {
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
token,
|
token,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse [`Token`]s into an Abstract Syntax Tree ([`AstNode`])
|
/// Parse [`Token`]s into an Abstract Syntax Tree ([`AstNode`])
|
||||||
pub fn parse(tokens: &[Token]) -> Result<AstNode, String> {
|
pub fn parse(tokens: &[Token]) -> Result<AstNode, String> {
|
||||||
parse_level_1(tokens, 0).and_then(|(ast, next_pos)| {
|
parse_level_1(tokens, 0).and_then(|(ast, next_pos)| {
|
||||||
if next_pos == tokens.len() {
|
if next_pos == tokens.len() {
|
||||||
Ok(ast)
|
Ok(ast)
|
||||||
} else {
|
} else {
|
||||||
Err(format!(
|
Err(format!(
|
||||||
"Expected end of input, found {:?} at {}",
|
"Expected end of input, found {:?} at {}",
|
||||||
tokens[next_pos], next_pos
|
tokens[next_pos], next_pos
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// level 1 precedence (lowest): to, of
|
// level 1 precedence (lowest): to, of
|
||||||
/// Parse [`To`](crate::TextOperator::To) and [`Of`](crate::TextOperator::Of)
|
/// Parse [`To`](crate::TextOperator::To) and [`Of`](crate::TextOperator::Of)
|
||||||
pub fn parse_level_1(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
pub fn parse_level_1(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
||||||
// do higher precedences first, then come back down
|
// do higher precedences first, then come back down
|
||||||
let (mut node, mut pos) = parse_level_2(tokens, pos)?;
|
let (mut node, mut pos) = parse_level_2(tokens, pos)?;
|
||||||
// now we loop through the next tokens
|
// now we loop through the next tokens
|
||||||
loop {
|
loop {
|
||||||
let token = tokens.get(pos);
|
let token = tokens.get(pos);
|
||||||
match token {
|
match token {
|
||||||
// if there's a match, we once again do higher precedences, then come
|
// if there's a match, we once again do higher precedences, then come
|
||||||
// back down again and continue the loop
|
// back down again and continue the loop
|
||||||
Some(&Token::TextOperator(To)) | Some(&Token::TextOperator(Of)) => {
|
Some(&Token::TextOperator(To)) | Some(&Token::TextOperator(Of)) => {
|
||||||
let (right_node, next_pos) = parse_level_2(tokens, pos + 1)?;
|
let (right_node, next_pos) = parse_level_2(tokens, pos + 1)?;
|
||||||
let mut new_node = AstNode::new(token.unwrap().clone());
|
let mut new_node = AstNode::new(token.unwrap().clone());
|
||||||
new_node.children.push(node);
|
new_node.children.push(node);
|
||||||
new_node.children.push(right_node);
|
new_node.children.push(right_node);
|
||||||
node = new_node;
|
node = new_node;
|
||||||
pos = next_pos;
|
pos = next_pos;
|
||||||
}
|
}
|
||||||
// if there's no match, we go down to a lower precedence (or, in this
|
// if there's no match, we go down to a lower precedence (or, in this
|
||||||
// case, we're done)
|
// case, we're done)
|
||||||
_ => {
|
_ => {
|
||||||
return Ok((node, pos));
|
return Ok((node, pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// level 2 precedence: +, -
|
// level 2 precedence: +, -
|
||||||
/// Parse [`Plus`](crate::Operator::Plus) and [`Minus`](crate::Operator::Minus)
|
/// Parse [`Plus`](crate::Operator::Plus) and [`Minus`](crate::Operator::Minus)
|
||||||
pub fn parse_level_2(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
pub fn parse_level_2(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
||||||
let (mut node, mut pos) = parse_level_3(tokens, pos)?;
|
let (mut node, mut pos) = parse_level_3(tokens, pos)?;
|
||||||
loop {
|
loop {
|
||||||
let token = tokens.get(pos);
|
let token = tokens.get(pos);
|
||||||
match token {
|
match token {
|
||||||
Some(&Token::Operator(Plus)) | Some(&Token::Operator(Minus)) => {
|
Some(&Token::Operator(Plus)) | Some(&Token::Operator(Minus)) => {
|
||||||
let (right_node, next_pos) = parse_level_3(tokens, pos + 1)?;
|
let (right_node, next_pos) = parse_level_3(tokens, pos + 1)?;
|
||||||
let mut new_node = AstNode::new(token.unwrap().clone());
|
let mut new_node = AstNode::new(token.unwrap().clone());
|
||||||
new_node.children.push(node);
|
new_node.children.push(node);
|
||||||
new_node.children.push(right_node);
|
new_node.children.push(right_node);
|
||||||
node = new_node;
|
node = new_node;
|
||||||
pos = next_pos;
|
pos = next_pos;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Ok((node, pos));
|
return Ok((node, pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// level 3 precedence: *, /, modulo, implicative multiplication, foot-inch 6'4"
|
// level 3 precedence: *, /, modulo, implicative multiplication, foot-inch 6'4"
|
||||||
/// Parse [`Multiply`](crate::Operator::Multiply), [`Divide`](crate::Operator::Divide), [`Modulo`](crate::Operator::Modulo) and implicative multiplication (for example`2pi`)
|
/// Parse [`Multiply`](crate::Operator::Multiply), [`Divide`](crate::Operator::Divide), [`Modulo`](crate::Operator::Modulo) and implicative multiplication (for example`2pi`)
|
||||||
pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
||||||
// parse foot-inch syntax 6'4"
|
// parse foot-inch syntax 6'4"
|
||||||
let token0 = tokens.get(pos);
|
let token0 = tokens.get(pos);
|
||||||
if let Some(Token::Number(_number)) = token0 {
|
if let Some(Token::Number(_number)) = token0 {
|
||||||
let token1 = tokens.get(pos + 1);
|
let token1 = tokens.get(pos + 1);
|
||||||
if let Some(Token::Unit(Foot)) = token1 {
|
if let Some(Token::Unit(Foot)) = token1 {
|
||||||
let token2 = tokens.get(pos + 2);
|
let token2 = tokens.get(pos + 2);
|
||||||
if let Some(Token::Number(_number)) = token2 {
|
if let Some(Token::Number(_number)) = token2 {
|
||||||
let token3 = tokens.get(pos + 3);
|
let token3 = tokens.get(pos + 3);
|
||||||
if let Some(Token::Unit(Inch)) = token3 {
|
if let Some(Token::Unit(Inch)) = token3 {
|
||||||
let new_node = AstNode {
|
let new_node = AstNode {
|
||||||
children: vec![
|
children: vec![
|
||||||
AstNode {
|
AstNode {
|
||||||
children: vec![AstNode::new(token0.unwrap().clone())],
|
children: vec![AstNode::new(token0.unwrap().clone())],
|
||||||
token: Token::Unit(Foot),
|
token: Token::Unit(Foot),
|
||||||
},
|
},
|
||||||
AstNode {
|
AstNode {
|
||||||
children: vec![AstNode::new(token2.unwrap().clone())],
|
children: vec![AstNode::new(token2.unwrap().clone())],
|
||||||
token: Token::Unit(Inch),
|
token: Token::Unit(Inch),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
token: Token::Operator(Plus),
|
token: Token::Operator(Plus),
|
||||||
};
|
};
|
||||||
return Ok((new_node, pos + 4));
|
return Ok((new_node, pos + 4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (mut node, mut pos) = parse_level_4(tokens, pos)?;
|
let (mut node, mut pos) = parse_level_4(tokens, pos)?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let token = tokens.get(pos);
|
let token = tokens.get(pos);
|
||||||
match token {
|
match token {
|
||||||
Some(&Token::Operator(Multiply))
|
Some(&Token::Operator(Multiply))
|
||||||
| Some(&Token::Operator(Divide))
|
| Some(&Token::Operator(Divide))
|
||||||
| Some(&Token::Operator(Modulo)) => {
|
| Some(&Token::Operator(Modulo)) => {
|
||||||
let (right_node, next_pos) = parse_level_4(tokens, pos + 1)?;
|
let (right_node, next_pos) = parse_level_4(tokens, pos + 1)?;
|
||||||
let mut new_node = AstNode::new(token.unwrap().clone());
|
let mut new_node = AstNode::new(token.unwrap().clone());
|
||||||
new_node.children.push(node);
|
new_node.children.push(node);
|
||||||
new_node.children.push(right_node);
|
new_node.children.push(right_node);
|
||||||
node = new_node;
|
node = new_node;
|
||||||
pos = next_pos;
|
pos = next_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Below is implicative multiplication, for example '2pi'. Constants and
|
// Below is implicative multiplication, for example '2pi'. Constants and
|
||||||
// such will only end up here if they were unable to be parsed as part of
|
// such will only end up here if they were unable to be parsed as part of
|
||||||
// other operators.
|
// other operators.
|
||||||
// Note that this match statement matches an AstNode token, but the
|
// Note that this match statement matches an AstNode token, but the
|
||||||
// matches nested inside check the [`Token`]s. That's why we for example
|
// matches nested inside check the [`Token`]s. That's why we for example
|
||||||
// match a FunctionIdentifier, and inside that, a RightParen.
|
// match a FunctionIdentifier, and inside that, a RightParen.
|
||||||
|
|
||||||
// pi2, )2
|
// pi2, )2
|
||||||
Some(&Token::Number(_)) => {
|
Some(&Token::Number(_)) => {
|
||||||
let last_token = tokens.get(pos - 1);
|
let last_token = tokens.get(pos - 1);
|
||||||
match last_token {
|
match last_token {
|
||||||
Some(&Token::Constant(_)) | Some(&Token::Operator(RightParen)) => {
|
Some(&Token::Constant(_)) | Some(&Token::Operator(RightParen)) => {
|
||||||
let (right_node, next_pos) = parse_level_4(tokens, pos)?;
|
let (right_node, next_pos) = parse_level_4(tokens, pos)?;
|
||||||
let mut new_node = AstNode::new(Token::Operator(Multiply));
|
let mut new_node = AstNode::new(Token::Operator(Multiply));
|
||||||
new_node.children.push(node);
|
new_node.children.push(node);
|
||||||
new_node.children.push(right_node);
|
new_node.children.push(right_node);
|
||||||
node = new_node;
|
node = new_node;
|
||||||
pos = next_pos;
|
pos = next_pos;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Ok((node, pos));
|
return Ok((node, pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 2pi, )pi
|
// 2pi, )pi
|
||||||
Some(&Token::Constant(_)) => {
|
Some(&Token::Constant(_)) => {
|
||||||
let last_token = tokens.get(pos - 1);
|
let last_token = tokens.get(pos - 1);
|
||||||
match last_token {
|
match last_token {
|
||||||
Some(&Token::Number(_)) | Some(&Token::Operator(RightParen)) => {
|
Some(&Token::Number(_)) | Some(&Token::Operator(RightParen)) => {
|
||||||
let (right_node, next_pos) = parse_level_4(tokens, pos)?;
|
let (right_node, next_pos) = parse_level_4(tokens, pos)?;
|
||||||
let mut new_node = AstNode::new(Token::Operator(Multiply));
|
let mut new_node = AstNode::new(Token::Operator(Multiply));
|
||||||
new_node.children.push(node);
|
new_node.children.push(node);
|
||||||
new_node.children.push(right_node);
|
new_node.children.push(right_node);
|
||||||
node = new_node;
|
node = new_node;
|
||||||
pos = next_pos;
|
pos = next_pos;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Ok((node, pos));
|
return Ok((node, pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 2log(1), )log(1)
|
// 2log(1), )log(1)
|
||||||
Some(&Token::FunctionIdentifier(_)) => {
|
Some(&Token::FunctionIdentifier(_)) => {
|
||||||
let last_token = tokens.get(pos - 1);
|
let last_token = tokens.get(pos - 1);
|
||||||
match last_token {
|
match last_token {
|
||||||
Some(&Token::Number(_)) | Some(&Token::Operator(RightParen)) => {
|
Some(&Token::Number(_)) | Some(&Token::Operator(RightParen)) => {
|
||||||
let (right_node, next_pos) = parse_level_4(tokens, pos)?;
|
let (right_node, next_pos) = parse_level_4(tokens, pos)?;
|
||||||
let mut new_node = AstNode::new(Token::Operator(Multiply));
|
let mut new_node = AstNode::new(Token::Operator(Multiply));
|
||||||
new_node.children.push(node);
|
new_node.children.push(node);
|
||||||
new_node.children.push(right_node);
|
new_node.children.push(right_node);
|
||||||
node = new_node;
|
node = new_node;
|
||||||
pos = next_pos;
|
pos = next_pos;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Ok((node, pos));
|
return Ok((node, pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 2(3), pi(3), )(3)
|
// 2(3), pi(3), )(3)
|
||||||
Some(&Token::Operator(LeftParen)) => {
|
Some(&Token::Operator(LeftParen)) => {
|
||||||
let last_token = tokens.get(pos - 1);
|
let last_token = tokens.get(pos - 1);
|
||||||
match last_token {
|
match last_token {
|
||||||
Some(&Token::Number(_))
|
Some(&Token::Number(_))
|
||||||
| Some(&Token::Constant(_))
|
| Some(&Token::Constant(_))
|
||||||
| Some(&Token::Operator(RightParen)) => {
|
| Some(&Token::Operator(RightParen)) => {
|
||||||
let (right_node, next_pos) = parse_level_4(tokens, pos)?;
|
let (right_node, next_pos) = parse_level_4(tokens, pos)?;
|
||||||
let mut new_node = AstNode::new(Token::Operator(Multiply));
|
let mut new_node = AstNode::new(Token::Operator(Multiply));
|
||||||
new_node.children.push(node);
|
new_node.children.push(node);
|
||||||
new_node.children.push(right_node);
|
new_node.children.push(right_node);
|
||||||
node = new_node;
|
node = new_node;
|
||||||
pos = next_pos;
|
pos = next_pos;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Ok((node, pos));
|
return Ok((node, pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Ok((node, pos));
|
return Ok((node, pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// level 4 precedence: ^
|
// level 4 precedence: ^
|
||||||
/// Parse [`Caret`](crate::Operator::Caret)
|
/// Parse [`Caret`](crate::Operator::Caret)
|
||||||
pub fn parse_level_4(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
pub fn parse_level_4(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
||||||
let (mut node, mut pos) = parse_level_5(tokens, pos)?;
|
let (mut node, mut pos) = parse_level_5(tokens, pos)?;
|
||||||
loop {
|
loop {
|
||||||
let token = tokens.get(pos);
|
let token = tokens.get(pos);
|
||||||
match token {
|
match token {
|
||||||
Some(&Token::Operator(Caret)) => {
|
Some(&Token::Operator(Caret)) => {
|
||||||
let (right_node, next_pos) = parse_level_5(tokens, pos + 1)?;
|
let (right_node, next_pos) = parse_level_5(tokens, pos + 1)?;
|
||||||
let mut new_node = AstNode::new(token.unwrap().clone());
|
let mut new_node = AstNode::new(token.unwrap().clone());
|
||||||
new_node.children.push(node);
|
new_node.children.push(node);
|
||||||
new_node.children.push(right_node);
|
new_node.children.push(right_node);
|
||||||
node = new_node;
|
node = new_node;
|
||||||
pos = next_pos;
|
pos = next_pos;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Ok((node, pos));
|
return Ok((node, pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// level 5 precedence: - (as in -5, but not 4-5)
|
// level 5 precedence: - (as in -5, but not 4-5)
|
||||||
/// Parse [`Negative`](Token::Negative)
|
/// Parse [`Negative`](Token::Negative)
|
||||||
pub fn parse_level_5(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
pub fn parse_level_5(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
||||||
// Here we parse the negative unary operator. If the current token
|
// Here we parse the negative unary operator. If the current token
|
||||||
// is a minus, we wrap the right_node inside a Negative AstNode.
|
// is a minus, we wrap the right_node inside a Negative AstNode.
|
||||||
//
|
//
|
||||||
// Why doesn't this parse 4-5? First, we will first get a 4. In which case,
|
// Why doesn't this parse 4-5? First, we will first get a 4. In which case,
|
||||||
// we just return the result of parse_level_6(), which will include the pos
|
// we just return the result of parse_level_6(), which will include the pos
|
||||||
// of +. This will then go down to level 2 and be parsed as a normal minus
|
// of +. This will then go down to level 2 and be parsed as a normal minus
|
||||||
// operator.
|
// operator.
|
||||||
// The difference is that in other levels, we parse higher priorities
|
// The difference is that in other levels, we parse higher priorities
|
||||||
// immediately, while in this one we instead check if the current token
|
// immediately, while in this one we instead check if the current token
|
||||||
// is a minus, and if not, we then return the higher priority as normal.
|
// is a minus, and if not, we then return the higher priority as normal.
|
||||||
let token = tokens.get(pos);
|
let token = tokens.get(pos);
|
||||||
match token {
|
match token {
|
||||||
Some(&Token::Operator(Minus)) => {
|
Some(&Token::Operator(Minus)) => {
|
||||||
let (right_node, next_pos) = parse_level_6(tokens, pos + 1)?;
|
let (right_node, next_pos) = parse_level_6(tokens, pos + 1)?;
|
||||||
let mut new_node = AstNode::new(Token::Negative);
|
let mut new_node = AstNode::new(Token::Negative);
|
||||||
new_node.children.push(right_node);
|
new_node.children.push(right_node);
|
||||||
Ok((new_node, next_pos))
|
Ok((new_node, next_pos))
|
||||||
}
|
}
|
||||||
_ => parse_level_6(tokens, pos),
|
_ => parse_level_6(tokens, pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// level 6 precedence: !, percent, units attached to values
|
// level 6 precedence: !, percent, units attached to values
|
||||||
/// Parse [`Factorial`](crate::UnaryOperator::Factorial) and [`Percent`](crate::UnaryOperator::Percent)
|
/// Parse [`Factorial`](crate::UnaryOperator::Factorial) and [`Percent`](crate::UnaryOperator::Percent)
|
||||||
pub fn parse_level_6(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
pub fn parse_level_6(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
||||||
let (mut node, mut pos) = parse_level_7(tokens, pos)?;
|
let (mut node, mut pos) = parse_level_7(tokens, pos)?;
|
||||||
loop {
|
loop {
|
||||||
let token = tokens.get(pos);
|
let token = tokens.get(pos);
|
||||||
match token {
|
match token {
|
||||||
Some(&Token::UnaryOperator(Factorial))
|
Some(&Token::UnaryOperator(Factorial))
|
||||||
| Some(&Token::UnaryOperator(Percent))
|
| Some(&Token::UnaryOperator(Percent))
|
||||||
| Some(&Token::NamedNumber(_)) => {
|
| Some(&Token::NamedNumber(_)) => {
|
||||||
// Here we are handling unary operators, aka stuff written as
|
// Here we are handling unary operators, aka stuff written as
|
||||||
// "Number Operator" (3!) instead of "Number Operator Number" (3+3).
|
// "Number Operator" (3!) instead of "Number Operator Number" (3+3).
|
||||||
// Therefore, if we find a match, we don't parse what comes after it.
|
// Therefore, if we find a match, we don't parse what comes after it.
|
||||||
let mut new_node = AstNode::new(token.unwrap().clone());
|
let mut new_node = AstNode::new(token.unwrap().clone());
|
||||||
new_node.children.push(node);
|
new_node.children.push(node);
|
||||||
node = new_node;
|
node = new_node;
|
||||||
pos += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
Some(&Token::Unit(_unit)) => {
|
Some(&Token::Unit(_unit)) => {
|
||||||
// We won't allow units to repeat, like "1min min", so we end the loop if it's found.
|
// We won't allow units to repeat, like "1min min", so we end the loop if it's found.
|
||||||
let mut new_node = AstNode::new(token.unwrap().clone());
|
let mut new_node = AstNode::new(token.unwrap().clone());
|
||||||
new_node.children.push(node);
|
new_node.children.push(node);
|
||||||
return Ok((new_node, pos + 1));
|
return Ok((new_node, pos + 1));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// let's say we parse 1+2. parse_level_7 then returns 1, and token
|
// let's say we parse 1+2. parse_level_7 then returns 1, and token
|
||||||
// is set to plus. Plus has lower precedence than level 4, so we
|
// is set to plus. Plus has lower precedence than level 4, so we
|
||||||
// don't do anything, and pass the number down to a lower precedence.
|
// don't do anything, and pass the number down to a lower precedence.
|
||||||
return Ok((node, pos));
|
return Ok((node, pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// level 7 precedence: numbers, standalone units, constants, functions, parens
|
// level 7 precedence: numbers, standalone units, constants, functions, parens
|
||||||
@ -305,66 +305,68 @@ pub fn parse_level_6(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
|
|||||||
/// [`FunctionIdentifier`](Token::FunctionIdentifier),
|
/// [`FunctionIdentifier`](Token::FunctionIdentifier),
|
||||||
/// [`Paren`](Token::Paren)
|
/// [`Paren`](Token::Paren)
|
||||||
pub fn parse_level_7(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
pub fn parse_level_7(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> {
|
||||||
let token: &Token = tokens
|
let token: &Token = tokens
|
||||||
.get(pos)
|
.get(pos)
|
||||||
.ok_or(format!("Unexpected end of input at {}", pos))?;
|
.ok_or(format!("Unexpected end of input at {}", pos))?;
|
||||||
match token {
|
match token {
|
||||||
&Token::Number(_number) => {
|
&Token::Number(_number) => {
|
||||||
let node = AstNode::new(token.clone());
|
let node = AstNode::new(token.clone());
|
||||||
Ok((node, pos + 1))
|
Ok((node, pos + 1))
|
||||||
}
|
}
|
||||||
&Token::Unit(_unit) => {
|
&Token::Unit(_unit) => {
|
||||||
let node = AstNode::new(token.clone());
|
let node = AstNode::new(token.clone());
|
||||||
Ok((node, pos + 1))
|
Ok((node, pos + 1))
|
||||||
}
|
}
|
||||||
Token::Constant(_constant) => {
|
Token::Constant(_constant) => {
|
||||||
let node = AstNode::new(token.clone());
|
let node = AstNode::new(token.clone());
|
||||||
Ok((node, pos + 1))
|
Ok((node, pos + 1))
|
||||||
}
|
}
|
||||||
Token::FunctionIdentifier(_function_identifier) => {
|
Token::FunctionIdentifier(_function_identifier) => {
|
||||||
let left_paren_pos = pos + 1;
|
let left_paren_pos = pos + 1;
|
||||||
let left_paren_token = tokens.get(left_paren_pos);
|
let left_paren_token = tokens.get(left_paren_pos);
|
||||||
// check if '(' comes after function identifier, like 'log('
|
// check if '(' comes after function identifier, like 'log('
|
||||||
match left_paren_token {
|
match left_paren_token {
|
||||||
Some(&Token::Operator(LeftParen)) => {
|
Some(&Token::Operator(LeftParen)) => {
|
||||||
// parse everything inside as you would with normal parentheses,
|
// parse everything inside as you would with normal parentheses,
|
||||||
// then put it inside an ast node.
|
// then put it inside an ast node.
|
||||||
parse_level_1(tokens, left_paren_pos + 1).and_then(|(node, next_pos)| {
|
parse_level_1(tokens, left_paren_pos + 1).and_then(|(node, next_pos)| {
|
||||||
if let Some(&Token::Operator(RightParen)) = tokens.get(next_pos) {
|
if let Some(&Token::Operator(RightParen)) = tokens.get(next_pos) {
|
||||||
let mut function_node = AstNode::new(token.clone());
|
let mut function_node = AstNode::new(token.clone());
|
||||||
function_node.children.push(node);
|
function_node.children.push(node);
|
||||||
Ok((function_node, next_pos + 1))
|
Ok((function_node, next_pos + 1))
|
||||||
} else {
|
} else {
|
||||||
Err(format!(
|
Err(format!(
|
||||||
"Expected closing paren at {} but found {:?}",
|
"Expected closing paren at {} but found {:?}",
|
||||||
next_pos,
|
next_pos,
|
||||||
tokens.get(next_pos)
|
tokens.get(next_pos)
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => Err(format!(
|
_ => Err(format!(
|
||||||
"Expected ( after {} at {:?} but found {:?}",
|
"Expected ( after {} at {:?} but found {:?}",
|
||||||
left_paren_pos, token, left_paren_token
|
left_paren_pos, token, left_paren_token
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Token::Operator(LeftParen) => parse_level_1(tokens, pos + 1).and_then(|(node, next_pos)| {
|
Token::Operator(LeftParen) => {
|
||||||
if let Some(&Token::Operator(RightParen)) = tokens.get(next_pos) {
|
parse_level_1(tokens, pos + 1).and_then(|(node, next_pos)| {
|
||||||
let mut paren_node = AstNode::new(Token::Paren);
|
if let Some(&Token::Operator(RightParen)) = tokens.get(next_pos) {
|
||||||
paren_node.children.push(node);
|
let mut paren_node = AstNode::new(Token::Paren);
|
||||||
Ok((paren_node, next_pos + 1))
|
paren_node.children.push(node);
|
||||||
} else {
|
Ok((paren_node, next_pos + 1))
|
||||||
Err(format!(
|
} else {
|
||||||
"Expected closing paren at {} but found {:?}",
|
Err(format!(
|
||||||
next_pos,
|
"Expected closing paren at {} but found {:?}",
|
||||||
tokens.get(next_pos)
|
next_pos,
|
||||||
))
|
tokens.get(next_pos)
|
||||||
}
|
))
|
||||||
}),
|
}
|
||||||
_ => Err(format!(
|
})
|
||||||
"Unexpected token {:?}, expected paren or number",
|
}
|
||||||
token
|
_ => Err(format!(
|
||||||
)),
|
"Unexpected token {:?}, expected paren or number",
|
||||||
}
|
token
|
||||||
|
)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1730
src/units.rs
1730
src/units.rs
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user