Use rustfmt for some files
This commit is contained in:
parent
a64081ac73
commit
1bb3819350
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
tab_spaces=2
|
||||||
160
src/evaluator.rs
160
src/evaluator.rs
@ -1,13 +1,13 @@
|
|||||||
use decimal::d128;
|
|
||||||
use crate::{Token, Number};
|
|
||||||
use crate::units::{Unit, UnitType, convert, add, subtract, multiply, divide, modulo, pow};
|
|
||||||
use crate::parser::AstNode;
|
|
||||||
use crate::Operator::{Caret, Divide, Minus, Modulo, Multiply, Plus};
|
|
||||||
use crate::Constant::{Pi, E};
|
|
||||||
use crate::UnaryOperator::{Percent, Factorial};
|
|
||||||
use crate::TextOperator::{To, Of};
|
|
||||||
use crate::FunctionIdentifier::*;
|
|
||||||
use crate::lookup::{lookup_factorial, lookup_named_number};
|
use crate::lookup::{lookup_factorial, lookup_named_number};
|
||||||
|
use crate::parser::AstNode;
|
||||||
|
use crate::units::{add, convert, divide, modulo, multiply, pow, subtract, Unit, UnitType};
|
||||||
|
use crate::Constant::{Pi, E};
|
||||||
|
use crate::FunctionIdentifier::*;
|
||||||
|
use crate::Operator::{Caret, Divide, Minus, Modulo, Multiply, Plus};
|
||||||
|
use crate::TextOperator::{Of, To};
|
||||||
|
use crate::UnaryOperator::{Factorial, Percent};
|
||||||
|
use crate::{Number, Token};
|
||||||
|
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> {
|
||||||
@ -29,7 +29,7 @@ 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
|
||||||
}
|
}
|
||||||
@ -40,8 +40,8 @@ pub fn cbrt(input: d128) -> d128 {
|
|||||||
// 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
|
||||||
}
|
}
|
||||||
@ -68,12 +68,11 @@ pub fn sin(mut input: d128) -> d128 {
|
|||||||
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`]
|
||||||
@ -92,18 +91,16 @@ 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) => {
|
Token::Number(number) => Ok(Number::new(*number, Unit::NoUnit)),
|
||||||
Ok(Number::new(*number, Unit::NoUnit))
|
Token::Constant(constant) => match constant {
|
||||||
},
|
Pi => Ok(Number::new(
|
||||||
Token::Constant(constant) => {
|
d128!(3.141592653589793238462643383279503),
|
||||||
match constant {
|
Unit::NoUnit,
|
||||||
Pi => {
|
)),
|
||||||
Ok(Number::new(d128!(3.141592653589793238462643383279503), Unit::NoUnit))
|
E => Ok(Number::new(
|
||||||
},
|
d128!(2.718281828459045235360287471352662),
|
||||||
E => {
|
Unit::NoUnit,
|
||||||
Ok(Number::new(d128!(2.718281828459045235360287471352662), 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]")?;
|
||||||
@ -116,7 +113,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
|
|||||||
} 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);
|
||||||
@ -124,7 +121,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
|
|||||||
} 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();
|
||||||
@ -132,7 +129,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
|
|||||||
} 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();
|
||||||
@ -140,7 +137,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
|
|||||||
} 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);
|
||||||
@ -148,79 +145,92 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
|
|||||||
} 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) { result += d128!(1); }
|
if rounding_change == d128!(-0.5) {
|
||||||
|
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() { result += d128!(1); }
|
if rounding_change.is_negative() {
|
||||||
|
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() { result -= d128!(1); }
|
if !rounding_change.is_negative() {
|
||||||
|
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) { result += d128!(1); }
|
if rounding_change == d128!(-0.5) {
|
||||||
|
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.get(0).ok_or(format!("Token {:?} has no child[0]", token))?;
|
let child_node = children
|
||||||
|
.get(0)
|
||||||
|
.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 => {
|
Percent => Ok(Number::new(
|
||||||
Ok(Number::new(child_answer.value / d128!(100), child_answer.unit))
|
child_answer.value / d128!(100),
|
||||||
},
|
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) => {
|
Token::NamedNumber(named_number) => {
|
||||||
let child_node = children.get(0).ok_or(format!("Token {:?} has no child[0]", token))?;
|
let child_node = children
|
||||||
|
.get(0)
|
||||||
|
.ok_or(format!("Token {:?} has no child[0]", token))?;
|
||||||
let named_number_value = lookup_named_number(named_number);
|
let named_number_value = lookup_named_number(named_number);
|
||||||
if let Token::NamedNumber(child_nn) = &child_node.token {
|
if let Token::NamedNumber(child_nn) = &child_node.token {
|
||||||
let child_nn_value = lookup_named_number(child_nn);
|
let child_nn_value = lookup_named_number(child_nn);
|
||||||
@ -233,8 +243,12 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
|
|||||||
Ok(Number::new(result, child_answer.unit))
|
Ok(Number::new(result, child_answer.unit))
|
||||||
}
|
}
|
||||||
Token::TextOperator(operator) => {
|
Token::TextOperator(operator) => {
|
||||||
let left_child = children.get(0).ok_or(format!("Token {:?} has no child[0]", token))?;
|
let left_child = children
|
||||||
let right_child = children.get(1).ok_or(format!("Token {:?} has no child[1]", token))?;
|
.get(0)
|
||||||
|
.ok_or(format!("Token {:?} has no child[0]", token))?;
|
||||||
|
let right_child = children
|
||||||
|
.get(1)
|
||||||
|
.ok_or(format!("Token {:?} has no child[1]", token))?;
|
||||||
|
|
||||||
match operator {
|
match operator {
|
||||||
To => {
|
To => {
|
||||||
@ -245,7 +259,7 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
|
|||||||
} 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)?;
|
||||||
@ -254,44 +268,40 @@ fn evaluate_node(ast_node: &AstNode) -> Result<Number, String> {
|
|||||||
} 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.get(0).ok_or(format!("Token {:?} has no child[0]", token))?;
|
let left_child = children
|
||||||
let right_child = children.get(1).ok_or(format!("Token {:?} has no child[1]", token))?;
|
.get(0)
|
||||||
|
.ok_or(format!("Token {:?} has no child[0]", token))?;
|
||||||
|
let right_child = children
|
||||||
|
.get(1)
|
||||||
|
.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 => {
|
Plus => Ok(add(left, right)?),
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
49
src/lib.rs
49
src/lib.rs
@ -30,20 +30,23 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
use crate::units::Unit;
|
||||||
|
use decimal::d128;
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use decimal::d128;
|
|
||||||
use crate::units::Unit;
|
|
||||||
|
|
||||||
/// Units, and functions you can use with them
|
|
||||||
pub mod units;
|
|
||||||
/// Turns a string into [`Token`]s
|
|
||||||
pub mod lexer;
|
|
||||||
/// Turns [`Token`]s into an [`AstNode`](parser::AstNode)
|
|
||||||
pub mod parser;
|
|
||||||
/// Turns an [`AstNode`](parser::AstNode) into a [`Number`]
|
/// Turns an [`AstNode`](parser::AstNode) into a [`Number`]
|
||||||
pub mod evaluator;
|
pub mod evaluator;
|
||||||
|
/// Turns a string into [`Token`]s
|
||||||
|
#[rustfmt::skip]
|
||||||
|
pub mod lexer;
|
||||||
|
#[rustfmt::skip]
|
||||||
mod lookup;
|
mod lookup;
|
||||||
|
/// Turns [`Token`]s into an [`AstNode`](parser::AstNode)
|
||||||
|
pub mod parser;
|
||||||
|
/// Units, and functions you can use with them
|
||||||
|
#[rustfmt::skip]
|
||||||
|
pub mod units;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
/// A number with a `Unit`.
|
/// A number with a `Unit`.
|
||||||
@ -213,7 +216,7 @@ pub enum Token {
|
|||||||
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`].
|
||||||
@ -233,20 +236,28 @@ macro_rules! numtok {
|
|||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn eval(input: &str, allow_trailing_operators: bool, default_degree: Unit, verbose: bool) -> Result<Number, String> {
|
pub fn eval(
|
||||||
|
input: &str,
|
||||||
|
allow_trailing_operators: bool,
|
||||||
|
default_degree: Unit,
|
||||||
|
verbose: bool,
|
||||||
|
) -> 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 { println!("Lexed TokenVector: {:?}", tokens); }
|
if verbose {
|
||||||
|
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 { println!("Parsed AstNode: {:#?}", ast); }
|
if verbose {
|
||||||
|
println!("Parsed AstNode: {:#?}", ast);
|
||||||
|
}
|
||||||
|
|
||||||
let eval_start = Instant::now();
|
let eval_start = Instant::now();
|
||||||
match evaluator::evaluate(&ast) {
|
match evaluator::evaluate(&ast) {
|
||||||
@ -255,19 +266,19 @@ pub fn eval(input: &str, allow_trailing_operators: bool, default_degree: Unit, v
|
|||||||
|
|
||||||
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)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/main.rs
14
src/main.rs
@ -1,7 +1,7 @@
|
|||||||
use cpc::eval;
|
use cpc::eval;
|
||||||
use cpc::units::Unit;
|
use cpc::units::Unit;
|
||||||
use std::process::exit;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
@ -30,12 +30,12 @@ fn main() {
|
|||||||
"--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;
|
||||||
@ -58,7 +58,7 @@ fn main() {
|
|||||||
None => {
|
None => {
|
||||||
print_help();
|
print_help();
|
||||||
exit(0);
|
exit(0);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match eval(&expression, true, Unit::Celsius, verbose) {
|
match eval(&expression, true, Unit::Celsius, verbose) {
|
||||||
@ -66,10 +66,10 @@ fn main() {
|
|||||||
if !verbose {
|
if !verbose {
|
||||||
println!("{}", answer);
|
println!("{}", answer);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
exit(1);
|
exit(1);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
137
src/parser.rs
137
src/parser.rs
@ -1,8 +1,8 @@
|
|||||||
use crate::Token;
|
|
||||||
use crate::Operator::{Caret, Divide, LeftParen, Minus, Modulo, Multiply, Plus, RightParen};
|
|
||||||
use crate::UnaryOperator::{Percent, Factorial};
|
|
||||||
use crate::TextOperator::{To, Of};
|
|
||||||
use crate::units::Unit::{Foot, Inch};
|
use crate::units::Unit::{Foot, Inch};
|
||||||
|
use crate::Operator::{Caret, Divide, LeftParen, Minus, Modulo, Multiply, Plus, RightParen};
|
||||||
|
use crate::TextOperator::{Of, To};
|
||||||
|
use crate::Token;
|
||||||
|
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)
|
||||||
@ -24,10 +24,15 @@ impl AstNode {
|
|||||||
|
|
||||||
/// 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)| if next_pos == tokens.len() {
|
parse_level_1(tokens, 0).and_then(|(ast, next_pos)| {
|
||||||
|
if next_pos == tokens.len() {
|
||||||
Ok(ast)
|
Ok(ast)
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Expected end of input, found {:?} at {}", tokens[next_pos], next_pos))
|
Err(format!(
|
||||||
|
"Expected end of input, found {:?} at {}",
|
||||||
|
tokens[next_pos], next_pos
|
||||||
|
))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,12 +54,12 @@ pub fn parse_level_1(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
|
|||||||
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));
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,10 +78,10 @@ pub fn parse_level_2(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
|
|||||||
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));
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,21 +101,17 @@ pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
|
|||||||
let new_node = AstNode {
|
let new_node = AstNode {
|
||||||
children: vec![
|
children: vec![
|
||||||
AstNode {
|
AstNode {
|
||||||
children: vec![
|
children: vec![AstNode::new(token0.unwrap().clone())],
|
||||||
AstNode::new(token0.unwrap().clone()),
|
|
||||||
],
|
|
||||||
token: Token::Unit(Foot),
|
token: Token::Unit(Foot),
|
||||||
},
|
},
|
||||||
AstNode {
|
AstNode {
|
||||||
children: vec![
|
children: vec![AstNode::new(token2.unwrap().clone())],
|
||||||
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,14 +122,16 @@ pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
|
|||||||
loop {
|
loop {
|
||||||
let token = tokens.get(pos);
|
let token = tokens.get(pos);
|
||||||
match token {
|
match token {
|
||||||
Some(&Token::Operator(Multiply)) | Some(&Token::Operator(Divide)) | Some(&Token::Operator(Modulo)) => {
|
Some(&Token::Operator(Multiply))
|
||||||
|
| Some(&Token::Operator(Divide))
|
||||||
|
| 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
|
||||||
@ -148,12 +151,12 @@ pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
|
|||||||
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);
|
||||||
@ -165,12 +168,12 @@ pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
|
|||||||
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);
|
||||||
@ -182,32 +185,34 @@ pub fn parse_level_3(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
|
|||||||
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::Constant(_)) | Some(&Token::Operator(RightParen)) => {
|
Some(&Token::Number(_))
|
||||||
|
| 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));
|
return Ok((node, pos));
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Ok((node, pos));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,10 +231,10 @@ pub fn parse_level_4(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
|
|||||||
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));
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -254,8 +259,8 @@ pub fn parse_level_5(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
|
|||||||
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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,7 +271,9 @@ pub fn parse_level_6(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
|
|||||||
loop {
|
loop {
|
||||||
let token = tokens.get(pos);
|
let token = tokens.get(pos);
|
||||||
match token {
|
match token {
|
||||||
Some(&Token::UnaryOperator(Factorial)) | Some(&Token::UnaryOperator(Percent)) | Some(&Token::NamedNumber(_)) => {
|
Some(&Token::UnaryOperator(Factorial))
|
||||||
|
| Some(&Token::UnaryOperator(Percent))
|
||||||
|
| 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.
|
||||||
@ -274,19 +281,19 @@ pub fn parse_level_6(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
|
|||||||
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));
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,20 +305,22 @@ 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.get(pos).ok_or(format!("Unexpected end of input at {}", pos))?;
|
let token: &Token = tokens
|
||||||
|
.get(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);
|
||||||
@ -326,28 +335,36 @@ pub fn parse_level_7(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), S
|
|||||||
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!("Expected closing paren at {} but found {:?}", next_pos, tokens.get(next_pos)))
|
Err(format!(
|
||||||
|
"Expected closing paren at {} but found {:?}",
|
||||||
|
next_pos,
|
||||||
|
tokens.get(next_pos)
|
||||||
|
))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
_ => {
|
_ => Err(format!(
|
||||||
Err(format!("Expected ( after {} at {:?} but found {:?}", left_paren_pos, token, left_paren_token))
|
"Expected ( after {} at {:?} but found {:?}",
|
||||||
|
left_paren_pos, token, left_paren_token
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
Token::Operator(LeftParen) => parse_level_1(tokens, pos + 1).and_then(|(node, next_pos)| {
|
||||||
Token::Operator(LeftParen) => {
|
|
||||||
parse_level_1(tokens, 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 paren_node = AstNode::new(Token::Paren);
|
let mut paren_node = AstNode::new(Token::Paren);
|
||||||
paren_node.children.push(node);
|
paren_node.children.push(node);
|
||||||
Ok((paren_node, next_pos + 1))
|
Ok((paren_node, next_pos + 1))
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Expected closing paren at {} but found {:?}", next_pos, tokens.get(next_pos)))
|
Err(format!(
|
||||||
|
"Expected closing paren at {} but found {:?}",
|
||||||
|
next_pos,
|
||||||
|
tokens.get(next_pos)
|
||||||
|
))
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
},
|
_ => Err(format!(
|
||||||
_ => {
|
"Unexpected token {:?}, expected paren or number",
|
||||||
Err(format!("Unexpected token {:?}, expected paren or number", token))
|
token
|
||||||
},
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user