mirror of
https://github.com/rm-dr/daisy
synced 2025-08-02 01:34:50 -07:00
Restructured packages
This commit is contained in:
@ -1,174 +1,21 @@
|
||||
use std::collections::VecDeque;
|
||||
mod stage;
|
||||
|
||||
mod pretoken;
|
||||
mod parsererror;
|
||||
mod token;
|
||||
|
||||
mod tokenize;
|
||||
mod treeify;
|
||||
mod groupify;
|
||||
mod find_subs;
|
||||
|
||||
|
||||
use crate::parser::tokenize::tokenize;
|
||||
use crate::parser::groupify::groupify;
|
||||
use crate::parser::treeify::treeify;
|
||||
use crate::parser::find_subs::find_subs;
|
||||
|
||||
use crate::quantity::Quantity;
|
||||
use crate::quantity::Unit;
|
||||
|
||||
use crate::tokens::Token;
|
||||
|
||||
/// Specifies the location of a token in an input string.
|
||||
/// Used to locate ParserErrors.
|
||||
#[derive(Debug)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct LineLocation {
|
||||
pub pos: usize,
|
||||
pub len: usize
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum PreToken {
|
||||
PreNumber(LineLocation, String),
|
||||
PreWord(LineLocation, String),
|
||||
PreOperator(LineLocation, String),
|
||||
|
||||
PreGroupStart(LineLocation),
|
||||
PreGroupEnd(LineLocation),
|
||||
PreGroup(LineLocation, VecDeque<PreToken>),
|
||||
|
||||
Container(Token)
|
||||
}
|
||||
|
||||
impl PreToken {
|
||||
#[inline(always)]
|
||||
pub fn get_line_location(&self) -> &LineLocation {
|
||||
match self {
|
||||
PreToken::PreNumber(l, _)
|
||||
| PreToken::PreWord(l, _)
|
||||
| PreToken::PreOperator(l, _)
|
||||
| PreToken::PreGroupStart(l)
|
||||
| PreToken::PreGroupEnd(l)
|
||||
| PreToken::PreGroup(l, _)
|
||||
=> l,
|
||||
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_mut_line_location(&mut self) -> &mut LineLocation {
|
||||
match self {
|
||||
PreToken::PreNumber(l, _)
|
||||
| PreToken::PreWord(l, _)
|
||||
| PreToken::PreOperator(l, _)
|
||||
| PreToken::PreGroupStart(l)
|
||||
| PreToken::PreGroupEnd(l)
|
||||
| PreToken::PreGroup(l, _)
|
||||
=> l,
|
||||
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn to_token(self) -> Result<Token, (LineLocation, ParserError)>{
|
||||
match self {
|
||||
PreToken::PreNumber(l, mut s) => {
|
||||
|
||||
// The length check here ensures that
|
||||
// `.` is not parsed as `0.`
|
||||
// That should be a syntax error.
|
||||
if s.len() != 1 && &s[0..1] == "." {
|
||||
s.insert(0, '0');
|
||||
}
|
||||
|
||||
let r = Quantity::new_rational_from_string(&s);
|
||||
if r.is_none() {
|
||||
return Err((l, ParserError::BadNumber))
|
||||
}
|
||||
return Ok(Token::Number(r.unwrap()));
|
||||
},
|
||||
|
||||
PreToken::PreWord(l, s) => {
|
||||
let c = match &s[..] {
|
||||
// Mathematical constants
|
||||
// 100 digits of each.
|
||||
"π"|"pi" => { Some((Quantity::new_float_from_string(
|
||||
"3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067"
|
||||
).unwrap(), String::from("π")))},
|
||||
|
||||
"e" => { Some((Quantity::new_float_from_string(
|
||||
"2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427"
|
||||
).unwrap(), String::from("e"))) },
|
||||
|
||||
"phi"|"φ" => { Some((Quantity::new_float_from_string(
|
||||
"1.618033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137"
|
||||
).unwrap(), String::from("φ"))) },
|
||||
|
||||
_ => { None }
|
||||
};
|
||||
|
||||
if c.is_some() {
|
||||
let (a, b) = c.unwrap();
|
||||
return Ok(Token::Constant(a, b));
|
||||
}
|
||||
|
||||
let c = Unit::from_string(&s);
|
||||
if c.is_some() { return Ok(Token::Number(c.unwrap())); }
|
||||
|
||||
return Err((l, ParserError::Undefined(s)));
|
||||
}
|
||||
|
||||
PreToken::Container(v) => { return Ok(v); }
|
||||
|
||||
PreToken::PreOperator(_,_)
|
||||
| PreToken::PreGroupStart(_)
|
||||
| PreToken::PreGroupEnd(_)
|
||||
| PreToken::PreGroup(_, _)
|
||||
=> panic!()
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Types of parser errors.
|
||||
/// If we cannot parse a string, one of these is returned.
|
||||
#[derive(Debug)]
|
||||
pub enum ParserError {
|
||||
//MissingCloseParen,
|
||||
ExtraCloseParen,
|
||||
EmptyGroup,
|
||||
Syntax,
|
||||
Undefined(String),
|
||||
BadNumber
|
||||
}
|
||||
|
||||
impl ParserError {
|
||||
pub fn to_message(&self) -> String {
|
||||
match self {
|
||||
//ParserError::MissingCloseParen => {
|
||||
// String::from("This group is never closed")
|
||||
//},
|
||||
ParserError::ExtraCloseParen => {
|
||||
String::from("Extra close parenthesis")
|
||||
},
|
||||
ParserError::EmptyGroup => {
|
||||
String::from("Groups can't be empty")
|
||||
},
|
||||
ParserError::Syntax => {
|
||||
String::from("Syntax")
|
||||
},
|
||||
ParserError::Undefined(s) => {
|
||||
format!("\"{s}\" isn't defined")
|
||||
},
|
||||
ParserError::BadNumber => {
|
||||
String::from("Invalid number")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
use self::{
|
||||
pretoken::PreToken,
|
||||
parsererror::ParserError,
|
||||
parsererror::LineLocation
|
||||
};
|
||||
|
||||
pub use self::{
|
||||
token::Token,
|
||||
token::Constant,
|
||||
token::Operator,
|
||||
token::Function,
|
||||
};
|
||||
|
||||
|
||||
pub fn parse(
|
||||
@ -178,10 +25,10 @@ pub fn parse(
|
||||
(LineLocation, ParserError)
|
||||
> {
|
||||
|
||||
let tokens = tokenize(s);
|
||||
let (_, tokens) = find_subs(tokens);
|
||||
let g = groupify(tokens)?;
|
||||
let g = treeify(g)?;
|
||||
let tokens = stage::tokenize(s);
|
||||
let (_, tokens) = stage::find_subs(tokens);
|
||||
let g = stage::groupify(tokens)?;
|
||||
let g = stage::treeify(g)?;
|
||||
|
||||
return Ok(g);
|
||||
}
|
||||
@ -198,8 +45,8 @@ pub fn substitute(
|
||||
let mut new_s = s.clone();
|
||||
|
||||
let l = s.chars().count();
|
||||
let tokens = tokenize(s);
|
||||
let (subs, _) = find_subs(tokens);
|
||||
let tokens = stage::tokenize(s);
|
||||
let (subs, _) = stage::find_subs(tokens);
|
||||
let mut new_c = l - c;
|
||||
|
||||
for r in subs.iter() {
|
||||
|
45
src/parser/parsererror.rs
Normal file
45
src/parser/parsererror.rs
Normal file
@ -0,0 +1,45 @@
|
||||
/// Specifies the location of a token in an input string.
|
||||
/// Used to locate ParserErrors.
|
||||
#[derive(Debug)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct LineLocation {
|
||||
pub pos: usize,
|
||||
pub len: usize
|
||||
}
|
||||
|
||||
/// Types of parser errors.
|
||||
/// If we cannot parse a string, one of these is returned.
|
||||
#[derive(Debug)]
|
||||
pub enum ParserError {
|
||||
//MissingCloseParen,
|
||||
ExtraCloseParen,
|
||||
EmptyGroup,
|
||||
Syntax,
|
||||
Undefined(String),
|
||||
BadNumber
|
||||
}
|
||||
|
||||
impl ToString for ParserError {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
//ParserError::MissingCloseParen => {
|
||||
// String::from("This group is never closed")
|
||||
//},
|
||||
ParserError::ExtraCloseParen => {
|
||||
String::from("Extra close parenthesis")
|
||||
},
|
||||
ParserError::EmptyGroup => {
|
||||
String::from("Groups can't be empty")
|
||||
},
|
||||
ParserError::Syntax => {
|
||||
String::from("Syntax")
|
||||
},
|
||||
ParserError::Undefined(s) => {
|
||||
format!("\"{s}\" isn't defined")
|
||||
},
|
||||
ParserError::BadNumber => {
|
||||
String::from("Invalid number")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
104
src/parser/pretoken.rs
Normal file
104
src/parser/pretoken.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use std::collections::VecDeque;
|
||||
use crate::quantity::Unit;
|
||||
use crate::quantity::Quantity;
|
||||
|
||||
use super::{
|
||||
LineLocation,
|
||||
ParserError,
|
||||
Token,
|
||||
Constant
|
||||
};
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PreToken {
|
||||
PreQuantity(LineLocation, String),
|
||||
PreWord(LineLocation, String),
|
||||
PreOperator(LineLocation, String),
|
||||
|
||||
PreGroupStart(LineLocation),
|
||||
PreGroupEnd(LineLocation),
|
||||
PreGroup(LineLocation, VecDeque<PreToken>),
|
||||
|
||||
Container(Token)
|
||||
}
|
||||
|
||||
impl PreToken {
|
||||
#[inline(always)]
|
||||
pub fn get_line_location(&self) -> &LineLocation {
|
||||
match self {
|
||||
PreToken::PreQuantity(l, _)
|
||||
| PreToken::PreWord(l, _)
|
||||
| PreToken::PreOperator(l, _)
|
||||
| PreToken::PreGroupStart(l)
|
||||
| PreToken::PreGroupEnd(l)
|
||||
| PreToken::PreGroup(l, _)
|
||||
=> l,
|
||||
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_mut_line_location(&mut self) -> &mut LineLocation {
|
||||
match self {
|
||||
PreToken::PreQuantity(l, _)
|
||||
| PreToken::PreWord(l, _)
|
||||
| PreToken::PreOperator(l, _)
|
||||
| PreToken::PreGroupStart(l)
|
||||
| PreToken::PreGroupEnd(l)
|
||||
| PreToken::PreGroup(l, _)
|
||||
=> l,
|
||||
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn to_token(self) -> Result<Token, (LineLocation, ParserError)>{
|
||||
match self {
|
||||
PreToken::PreQuantity(l, mut s) => {
|
||||
|
||||
// The length check here ensures that
|
||||
// `.` is not parsed as `0.`
|
||||
// That should be a syntax error.
|
||||
if s.len() != 1 && &s[0..1] == "." {
|
||||
s.insert(0, '0');
|
||||
}
|
||||
|
||||
let r = Quantity::new_rational_from_string(&s);
|
||||
if r.is_none() {
|
||||
return Err((l, ParserError::BadNumber))
|
||||
}
|
||||
return Ok(Token::Quantity(r.unwrap()));
|
||||
},
|
||||
|
||||
PreToken::PreWord(l, s) => {
|
||||
let c = match &s[..] {
|
||||
"π"|"pi" => { Some(Constant::Pi)},
|
||||
"e" => { Some(Constant::E) },
|
||||
"phi"|"φ" => { Some(Constant::Phi) },
|
||||
_ => { None }
|
||||
};
|
||||
|
||||
if c.is_some() {
|
||||
return Ok(Token::Constant(c.unwrap()));
|
||||
}
|
||||
|
||||
let c = Unit::from_string(&s);
|
||||
if c.is_some() { return Ok(Token::Quantity(c.unwrap())); }
|
||||
|
||||
return Err((l, ParserError::Undefined(s)));
|
||||
}
|
||||
|
||||
PreToken::Container(v) => { return Ok(v); }
|
||||
|
||||
PreToken::PreOperator(_,_)
|
||||
| PreToken::PreGroupStart(_)
|
||||
| PreToken::PreGroupEnd(_)
|
||||
| PreToken::PreGroup(_, _)
|
||||
=> panic!()
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::parser::PreToken;
|
||||
use crate::parser::LineLocation;
|
||||
use super::super::{
|
||||
PreToken,
|
||||
LineLocation
|
||||
};
|
||||
|
||||
|
||||
pub(in crate::parser) fn find_subs(
|
||||
pub fn find_subs(
|
||||
mut g: VecDeque<PreToken>,
|
||||
) -> (
|
||||
Vec<(LineLocation, String)>,
|
@ -1,10 +1,11 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::parser::PreToken;
|
||||
use crate::parser::LineLocation;
|
||||
use crate::parser::ParserError;
|
||||
|
||||
use crate::tokens::Operator;
|
||||
use super::super::{
|
||||
PreToken,
|
||||
LineLocation,
|
||||
ParserError,
|
||||
Operator
|
||||
};
|
||||
|
||||
|
||||
fn lookback_signs(
|
||||
@ -99,7 +100,6 @@ fn lookback(
|
||||
|
||||
lookback_signs(g)?;
|
||||
|
||||
|
||||
let mut i: usize = 0;
|
||||
while i < g.len() {
|
||||
if i >= 1 {
|
||||
@ -109,12 +109,12 @@ fn lookback(
|
||||
match (&a, &b) {
|
||||
// Insert ImplicitMultiply
|
||||
(PreToken::PreGroup(_,_), PreToken::PreGroup(l ,_))
|
||||
| (PreToken::PreGroup(_,_), PreToken::PreNumber(l,_))
|
||||
| (PreToken::PreNumber(_,_), PreToken::PreGroup(l,_))
|
||||
| (PreToken::PreGroup(_,_), PreToken::PreQuantity(l,_))
|
||||
| (PreToken::PreQuantity(_,_), PreToken::PreGroup(l,_))
|
||||
| (PreToken::PreGroup(_,_), PreToken::PreWord(l,_))
|
||||
| (PreToken::PreWord(_,_), PreToken::PreGroup(l,_))
|
||||
| (PreToken::PreNumber(_,_), PreToken::PreWord(l,_))
|
||||
| (PreToken::PreWord(_,_), PreToken::PreNumber(l,_))
|
||||
| (PreToken::PreQuantity(_,_), PreToken::PreWord(l,_))
|
||||
| (PreToken::PreWord(_,_), PreToken::PreQuantity(l,_))
|
||||
| (PreToken::PreWord(_,_), PreToken::PreWord(l,_))
|
||||
=> {
|
||||
let loc = LineLocation{pos: l.pos-1, len: 0};
|
||||
@ -128,7 +128,7 @@ fn lookback(
|
||||
},
|
||||
|
||||
// Insert implicit multiplications for right-unary operators
|
||||
(PreToken::PreNumber(_,_), PreToken::PreOperator(l,s))
|
||||
(PreToken::PreQuantity(_,_), PreToken::PreOperator(l,s))
|
||||
| (PreToken::PreGroup(_,_), PreToken::PreOperator(l,s))
|
||||
| (PreToken::PreWord(_,_), PreToken::PreOperator(l,s))
|
||||
=> {
|
||||
@ -149,7 +149,7 @@ fn lookback(
|
||||
},
|
||||
|
||||
// Insert implicit multiplications for left-unary operators.
|
||||
(PreToken::PreOperator(_,s), PreToken::PreNumber(l,_))
|
||||
(PreToken::PreOperator(_,s), PreToken::PreQuantity(l,_))
|
||||
| (PreToken::PreOperator(_,s), PreToken::PreGroup(l,_))
|
||||
| (PreToken::PreOperator(_,s), PreToken::PreWord(l,_))
|
||||
=> {
|
||||
@ -170,7 +170,7 @@ fn lookback(
|
||||
},
|
||||
|
||||
// The following are syntax errors
|
||||
(PreToken::PreNumber(la,_), PreToken::PreNumber(lb,_))
|
||||
(PreToken::PreQuantity(la,_), PreToken::PreQuantity(lb,_))
|
||||
=> {
|
||||
return Err((
|
||||
LineLocation{pos: la.pos, len: lb.pos - la.pos + lb.len},
|
||||
@ -188,7 +188,7 @@ fn lookback(
|
||||
}
|
||||
|
||||
|
||||
pub(in crate::parser) fn groupify(
|
||||
pub fn groupify(
|
||||
mut g: VecDeque<PreToken>
|
||||
) -> Result<
|
||||
PreToken,
|
11
src/parser/stage/mod.rs
Normal file
11
src/parser/stage/mod.rs
Normal file
@ -0,0 +1,11 @@
|
||||
mod tokenize;
|
||||
mod find_subs;
|
||||
mod groupify;
|
||||
mod treeify;
|
||||
|
||||
pub (in super) use self::{
|
||||
tokenize::tokenize,
|
||||
find_subs::find_subs,
|
||||
groupify::groupify,
|
||||
treeify::treeify,
|
||||
};
|
@ -1,9 +1,10 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::parser::PreToken;
|
||||
use crate::parser::LineLocation;
|
||||
|
||||
use crate::tokens::Operator;
|
||||
use super::super::{
|
||||
PreToken,
|
||||
LineLocation,
|
||||
Operator
|
||||
};
|
||||
|
||||
// Called whenever a token is finished.
|
||||
#[inline(always)]
|
||||
@ -16,7 +17,7 @@ fn push_token(g: &mut VecDeque<PreToken>, t: Option<PreToken>, stop_i: usize) {
|
||||
PreToken::PreGroupStart(ref mut l)
|
||||
| PreToken::PreGroupEnd(ref mut l)
|
||||
| PreToken::PreOperator(ref mut l, _)
|
||||
| PreToken::PreNumber(ref mut l, _)
|
||||
| PreToken::PreQuantity(ref mut l, _)
|
||||
| PreToken::PreWord(ref mut l, _)
|
||||
=> {
|
||||
*l = LineLocation{
|
||||
@ -33,10 +34,10 @@ fn push_token(g: &mut VecDeque<PreToken>, t: Option<PreToken>, stop_i: usize) {
|
||||
|
||||
// `2e` isn't exponential notation, it's 2*e.
|
||||
// If a number ends in `e`, disconnect the `e` and make it a word.
|
||||
if let PreToken::PreNumber(l, s) = &t {
|
||||
if let PreToken::PreQuantity(l, s) = &t {
|
||||
let last = &s[s.len()-1..];
|
||||
if last == "e" {
|
||||
g.push_back(PreToken::PreNumber(
|
||||
g.push_back(PreToken::PreQuantity(
|
||||
LineLocation { pos: l.pos, len: l.len-1 },
|
||||
String::from(&s[0..s.len()-1])
|
||||
));
|
||||
@ -60,7 +61,7 @@ fn push_token(g: &mut VecDeque<PreToken>, t: Option<PreToken>, stop_i: usize) {
|
||||
}
|
||||
|
||||
/// Turns a string into Tokens. First stage of parsing.
|
||||
pub(in crate::parser) fn tokenize(input: &String) -> VecDeque<PreToken> {
|
||||
pub fn tokenize(input: &String) -> VecDeque<PreToken> {
|
||||
let mut t: Option<PreToken> = None; // The current token we're reading
|
||||
let mut g: VecDeque<PreToken> = VecDeque::with_capacity(32);
|
||||
|
||||
@ -73,7 +74,7 @@ pub(in crate::parser) fn tokenize(input: &String) -> VecDeque<PreToken> {
|
||||
match &mut t {
|
||||
// If we're already building a number,
|
||||
// append.
|
||||
Some(PreToken::PreNumber(_, val)) => {
|
||||
Some(PreToken::PreQuantity(_, val)) => {
|
||||
val.push(if c == ',' {'.'} else {c});
|
||||
},
|
||||
|
||||
@ -81,7 +82,7 @@ pub(in crate::parser) fn tokenize(input: &String) -> VecDeque<PreToken> {
|
||||
// previous token and start one.
|
||||
_ => {
|
||||
push_token(&mut g, t, i);
|
||||
t = Some(PreToken::PreNumber(LineLocation{pos: i, len: 0}, String::from(c)));
|
||||
t = Some(PreToken::PreQuantity(LineLocation{pos: i, len: 0}, String::from(c)));
|
||||
}
|
||||
};
|
||||
},
|
||||
@ -91,7 +92,7 @@ pub(in crate::parser) fn tokenize(input: &String) -> VecDeque<PreToken> {
|
||||
'e' => {
|
||||
match &mut t {
|
||||
Some(PreToken::PreWord(_, val)) => { val.push(c); },
|
||||
Some(PreToken::PreNumber(_, val)) => { val.push(c); },
|
||||
Some(PreToken::PreQuantity(_, val)) => { val.push(c); },
|
||||
|
||||
_ => {
|
||||
push_token(&mut g, t, i);
|
||||
@ -105,7 +106,7 @@ pub(in crate::parser) fn tokenize(input: &String) -> VecDeque<PreToken> {
|
||||
// or it can specify a negative exponent.
|
||||
'-' | '+' => {
|
||||
match &mut t {
|
||||
Some(PreToken::PreNumber(_, val)) => {
|
||||
Some(PreToken::PreQuantity(_, val)) => {
|
||||
if &val[val.len()-1..] == "e" {
|
||||
// If the current number ends in an `e`,
|
||||
// this negative specifies a negative exponent
|
@ -1,11 +1,12 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::parser::PreToken;
|
||||
use crate::parser::ParserError;
|
||||
use crate::parser::LineLocation;
|
||||
|
||||
use crate::tokens::Token;
|
||||
use crate::tokens::Operator;
|
||||
use super::super::{
|
||||
PreToken,
|
||||
ParserError,
|
||||
LineLocation,
|
||||
Token,
|
||||
Operator
|
||||
};
|
||||
|
||||
fn treeify_binary(
|
||||
i: usize,
|
||||
@ -269,7 +270,7 @@ fn treeify_unary(
|
||||
}
|
||||
|
||||
|
||||
pub(in crate::parser) fn treeify(
|
||||
pub fn treeify(
|
||||
mut g: PreToken,
|
||||
) -> Result<Token, (LineLocation, ParserError)> {
|
||||
|
17
src/parser/token/constant.rs
Normal file
17
src/parser/token/constant.rs
Normal file
@ -0,0 +1,17 @@
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone)]
|
||||
pub enum Constant {
|
||||
Pi,
|
||||
Phi,
|
||||
E
|
||||
}
|
||||
|
||||
impl Constant {
|
||||
pub fn to_string(&self) -> String {
|
||||
match self {
|
||||
Constant::Pi => { String::from("π") },
|
||||
Constant::Phi => { String::from("φ") },
|
||||
Constant::E => { String::from("e") },
|
||||
}
|
||||
}
|
||||
}
|
62
src/parser/token/function.rs
Normal file
62
src/parser/token/function.rs
Normal file
@ -0,0 +1,62 @@
|
||||
#[derive(Debug)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Function {
|
||||
Abs,
|
||||
Floor,
|
||||
Ceil,
|
||||
Round,
|
||||
|
||||
NaturalLog,
|
||||
TenLog,
|
||||
|
||||
Sin,
|
||||
Cos,
|
||||
Tan,
|
||||
Asin,
|
||||
Acos,
|
||||
Atan,
|
||||
Csc,
|
||||
Sec,
|
||||
Cot,
|
||||
|
||||
Sinh,
|
||||
Cosh,
|
||||
Tanh,
|
||||
Asinh,
|
||||
Acosh,
|
||||
Atanh,
|
||||
Csch,
|
||||
Sech,
|
||||
Coth,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn to_string(&self) -> String {
|
||||
match self {
|
||||
Function::Abs => { String::from("abs") },
|
||||
Function::Floor => { String::from("floor") },
|
||||
Function::Ceil => { String::from("ceil") },
|
||||
Function::Round => { String::from("round") },
|
||||
Function::NaturalLog => { String::from("ln") },
|
||||
Function::TenLog => { String::from("log") },
|
||||
Function::Sin => { String::from("sin") },
|
||||
Function::Cos => { String::from("cos") },
|
||||
Function::Tan => { String::from("tan") },
|
||||
Function::Asin => { String::from("asin") },
|
||||
Function::Acos => { String::from("acos") },
|
||||
Function::Atan => { String::from("atan") },
|
||||
Function::Csc => { String::from("csc") },
|
||||
Function::Sec => { String::from("sec") },
|
||||
Function::Cot => { String::from("cot") },
|
||||
Function::Sinh => { String::from("sinh") },
|
||||
Function::Cosh => { String::from("cosh") },
|
||||
Function::Tanh => { String::from("tanh") },
|
||||
Function::Asinh => { String::from("asinh") },
|
||||
Function::Acosh => { String::from("acosh") },
|
||||
Function::Atanh => { String::from("atanh") },
|
||||
Function::Csch => { String::from("csch") },
|
||||
Function::Sech => { String::from("sech") },
|
||||
Function::Coth => { String::from("coth") },
|
||||
}
|
||||
}
|
||||
}
|
9
src/parser/token/mod.rs
Normal file
9
src/parser/token/mod.rs
Normal file
@ -0,0 +1,9 @@
|
||||
mod operator;
|
||||
mod function;
|
||||
mod token;
|
||||
mod constant;
|
||||
|
||||
pub use self::operator::Operator;
|
||||
pub use self::function::Function;
|
||||
pub use self::token::Token;
|
||||
pub use self::constant::Constant;
|
320
src/parser/token/operator.rs
Normal file
320
src/parser/token/operator.rs
Normal file
@ -0,0 +1,320 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::VecDeque;
|
||||
use crate::quantity::Quantity;
|
||||
|
||||
use super::Token;
|
||||
use super::Function;
|
||||
|
||||
|
||||
/// Operator types, in order of increasing priority.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone)]
|
||||
#[repr(usize)]
|
||||
pub enum Operator {
|
||||
ModuloLong = 0, // Mod invoked with "mod"
|
||||
UnitConvert,
|
||||
Subtract,
|
||||
Add,
|
||||
Divide,
|
||||
Multiply,
|
||||
Modulo, // Mod invoked with %
|
||||
Negative,
|
||||
|
||||
Sqrt,
|
||||
ImplicitMultiply,
|
||||
|
||||
Power,
|
||||
Factorial,
|
||||
|
||||
Function(Function),
|
||||
|
||||
// Not accessible from prompt
|
||||
Flip,
|
||||
}
|
||||
|
||||
impl PartialEq for Operator {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_int() == other.as_int()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Operator {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match (self, other) {
|
||||
(Operator::Add, Operator::Subtract)
|
||||
| (Operator::Subtract, Operator::Add)
|
||||
| (Operator::Multiply, Operator::Divide)
|
||||
| (Operator::Divide, Operator::Multiply)
|
||||
=> {Some(Ordering::Equal)}
|
||||
|
||||
_ => { self.as_int().partial_cmp(&other.as_int()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
#[inline(always)]
|
||||
pub fn as_int(&self) -> usize {
|
||||
unsafe { *<*const _>::from(self).cast::<usize>() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_string(s: &str) -> Option<Operator> {
|
||||
match s {
|
||||
"+" => {Some( Operator::Add )},
|
||||
"-" => {Some( Operator::Subtract )},
|
||||
"neg" => {Some( Operator::Negative )},
|
||||
"*"|"×" => {Some( Operator::Multiply )},
|
||||
"/"|"÷" => {Some( Operator::Divide )},
|
||||
"i*" => {Some( Operator::ImplicitMultiply )},
|
||||
"%" => {Some( Operator::Modulo )},
|
||||
"mod" => {Some( Operator::ModuloLong )},
|
||||
"to" => {Some( Operator::UnitConvert )},
|
||||
"^"|"**" => {Some( Operator::Power )},
|
||||
"!" => {Some( Operator::Factorial )},
|
||||
"sqrt"|"rt"|"√" => {Some( Operator::Sqrt )},
|
||||
|
||||
"abs" => {Some( Operator::Function(Function::Abs) )},
|
||||
"floor" => {Some( Operator::Function(Function::Floor) )},
|
||||
"ceil" => {Some( Operator::Function(Function::Ceil) )},
|
||||
"round" => {Some( Operator::Function(Function::Round) )},
|
||||
"ln" => {Some( Operator::Function(Function::NaturalLog) )},
|
||||
"log" => {Some( Operator::Function(Function::TenLog) )},
|
||||
"sin" => {Some( Operator::Function(Function::Sin) )},
|
||||
"cos" => {Some( Operator::Function(Function::Cos) )},
|
||||
"tan" => {Some( Operator::Function(Function::Tan) )},
|
||||
"asin" => {Some( Operator::Function(Function::Asin) )},
|
||||
"acos" => {Some( Operator::Function(Function::Acos) )},
|
||||
"atan" => {Some( Operator::Function(Function::Atan) )},
|
||||
"csc" => {Some( Operator::Function(Function::Csc) )},
|
||||
"secant" => {Some( Operator::Function(Function::Sec) )},
|
||||
"cot" => {Some( Operator::Function(Function::Cot) )},
|
||||
"sinh" => {Some( Operator::Function(Function::Sinh) )},
|
||||
"cosh" => {Some( Operator::Function(Function::Cosh) )},
|
||||
"tanh" => {Some( Operator::Function(Function::Tanh) )},
|
||||
"asinh" => {Some( Operator::Function(Function::Asinh) )},
|
||||
"acosh" => {Some( Operator::Function(Function::Acosh) )},
|
||||
"atanh" => {Some( Operator::Function(Function::Atanh) )},
|
||||
"csch" => {Some( Operator::Function(Function::Csch) )},
|
||||
"sech" => {Some( Operator::Function(Function::Sech) )},
|
||||
"coth" => {Some( Operator::Function(Function::Coth) )},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_binary(&self) -> bool {
|
||||
match self {
|
||||
Operator::Negative
|
||||
| Operator::Factorial
|
||||
| Operator::Sqrt
|
||||
| Operator::Function(_)
|
||||
=> false,
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn is_left_associative(&self) -> bool {
|
||||
match self {
|
||||
Operator::Negative
|
||||
| Operator::Sqrt
|
||||
| Operator::Function(_)
|
||||
=> false,
|
||||
_ => true
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn into_token(self, mut args: VecDeque<Token>) -> Token {
|
||||
match self {
|
||||
Operator::Subtract => {
|
||||
if args.len() != 2 { panic!() }
|
||||
let a = args.pop_front().unwrap();
|
||||
let b = args.pop_front().unwrap();
|
||||
|
||||
let b_new;
|
||||
if let Token::Quantity(q) = b {
|
||||
b_new = Token::Quantity(-q);
|
||||
} else {
|
||||
b_new = Token::Operator(Operator::Negative, VecDeque::from(vec!(b)));
|
||||
}
|
||||
|
||||
Token::Operator(
|
||||
Operator::Add,
|
||||
VecDeque::from(vec!(a,b_new))
|
||||
)
|
||||
},
|
||||
|
||||
Operator::Divide => {
|
||||
if args.len() != 2 { panic!() }
|
||||
let a = args.pop_front().unwrap();
|
||||
let b = args.pop_front().unwrap();
|
||||
let b = Token::Operator(Operator::Flip, VecDeque::from(vec!(b)));
|
||||
|
||||
Token::Operator(
|
||||
Operator::Multiply,
|
||||
VecDeque::from(vec!(a,b))
|
||||
)
|
||||
},
|
||||
|
||||
Operator::Sqrt => {
|
||||
if args.len() != 1 { panic!() }
|
||||
let a = args.pop_front().unwrap();
|
||||
|
||||
Token::Operator(
|
||||
Operator::Power,
|
||||
VecDeque::from(vec!(a, Token::Quantity(Quantity::new_rational_from_string("0.5").unwrap())))
|
||||
)
|
||||
},
|
||||
|
||||
Operator::ImplicitMultiply
|
||||
=> { Token::Operator(Operator::Multiply, args) },
|
||||
|
||||
Operator::Function(_)
|
||||
| Operator::Factorial
|
||||
| Operator::Negative
|
||||
| Operator::Flip
|
||||
| Operator::Add
|
||||
| Operator::Multiply
|
||||
| Operator::Modulo
|
||||
| Operator::Power
|
||||
| Operator::ModuloLong
|
||||
| Operator::UnitConvert
|
||||
=> { Token::Operator(self, args) },
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[inline(always)]
|
||||
fn add_parens_to_arg(&self, arg: &Token) -> String {
|
||||
let mut astr: String = arg.to_string();
|
||||
if let Token::Operator(o,_) = arg {
|
||||
if o < self {
|
||||
astr = format!("({})", astr);
|
||||
}
|
||||
}
|
||||
return astr;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn add_parens_to_arg_strict(&self, arg: &Token) -> String {
|
||||
let mut astr: String = arg.to_string();
|
||||
if let Token::Operator(o,_) = arg {
|
||||
if o <= self {
|
||||
astr = format!("({})", astr);
|
||||
}
|
||||
}
|
||||
return astr;
|
||||
}
|
||||
|
||||
|
||||
pub fn print(&self, args: &VecDeque<Token>) -> String {
|
||||
match self {
|
||||
Operator::ImplicitMultiply |
|
||||
Operator::Sqrt |
|
||||
Operator::Divide |
|
||||
Operator::Subtract => { panic!() }
|
||||
|
||||
Operator::Flip => {
|
||||
return format!("{}⁻¹", Operator::Divide.add_parens_to_arg(&args[0]));
|
||||
},
|
||||
|
||||
Operator::Negative => {
|
||||
return format!("-{}", self.add_parens_to_arg(&args[0]));
|
||||
},
|
||||
|
||||
Operator::ModuloLong => {
|
||||
return format!(
|
||||
"{} mod {}",
|
||||
self.add_parens_to_arg(&args[0]),
|
||||
self.add_parens_to_arg(&args[1])
|
||||
);
|
||||
},
|
||||
|
||||
Operator::UnitConvert => {
|
||||
return format!(
|
||||
"{} to {}",
|
||||
self.add_parens_to_arg(&args[0]),
|
||||
self.add_parens_to_arg(&args[1])
|
||||
);
|
||||
},
|
||||
|
||||
Operator::Modulo => {
|
||||
return format!(
|
||||
"{} % {}",
|
||||
self.add_parens_to_arg(&args[0]),
|
||||
self.add_parens_to_arg(&args[1])
|
||||
);
|
||||
},
|
||||
|
||||
Operator::Power => {
|
||||
return format!(
|
||||
"{}^{}",
|
||||
self.add_parens_to_arg_strict(&args[0]),
|
||||
self.add_parens_to_arg_strict(&args[1])
|
||||
);
|
||||
},
|
||||
|
||||
Operator::Factorial => {
|
||||
return format!("{}!", self.add_parens_to_arg(&args[0]));
|
||||
},
|
||||
|
||||
Operator::Add => {
|
||||
let a = &args[0];
|
||||
|
||||
let b; let sub;
|
||||
if let Token::Operator(o,ar) = &args[1] {
|
||||
if let Operator::Negative = o {
|
||||
sub = true;
|
||||
b = &ar[0];
|
||||
} else { sub = false; b = &args[1]; }
|
||||
} else { sub = false; b = &args[1]; }
|
||||
|
||||
if sub {
|
||||
return format!(
|
||||
"{} - {}",
|
||||
self.add_parens_to_arg(a),
|
||||
self.add_parens_to_arg(b)
|
||||
);
|
||||
} else {
|
||||
return format!(
|
||||
"{} + {}",
|
||||
self.add_parens_to_arg(a),
|
||||
self.add_parens_to_arg(b)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
Operator::Multiply => {
|
||||
let a = &args[0];
|
||||
|
||||
let b; let div;
|
||||
if let Token::Operator(o,ar) = &args[1] {
|
||||
if let Operator::Flip = o {
|
||||
div = true;
|
||||
b = &ar[0];
|
||||
} else { div = false; b = &args[1]; }
|
||||
} else { div = false; b = &args[1]; }
|
||||
|
||||
if div {
|
||||
return format!("{} ÷ {}",
|
||||
self.add_parens_to_arg_strict(a),
|
||||
self.add_parens_to_arg_strict(b)
|
||||
);
|
||||
} else {
|
||||
return format!("{} × {}",
|
||||
self.add_parens_to_arg_strict(a),
|
||||
self.add_parens_to_arg_strict(b)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
Operator::Function(s) => {
|
||||
return format!("{}({})", s.to_string(), args[0].to_string());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
56
src/parser/token/token.rs
Normal file
56
src/parser/token/token.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use std::collections::VecDeque;
|
||||
use crate::quantity::Quantity;
|
||||
|
||||
use super::Operator;
|
||||
use super::Constant;
|
||||
|
||||
/// Tokens represent logical objects in an expession.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone)]
|
||||
pub enum Token {
|
||||
Quantity(Quantity),
|
||||
Constant(Constant),
|
||||
Operator(Operator, VecDeque<Token>),
|
||||
}
|
||||
|
||||
impl ToString for Token {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Token::Quantity(v) => v.to_string(),
|
||||
Token::Constant(c) => c.to_string(),
|
||||
Token::Operator(o,a) => o.print(a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Token {
|
||||
// This is called only when this is the outermost token.
|
||||
// This sometimes leads to different--usually more verbose--behavior.
|
||||
pub fn to_string_outer(&self) -> String {
|
||||
match self {
|
||||
Token::Quantity(v) => v.to_string_outer(),
|
||||
Token::Constant(c) => c.to_string(),
|
||||
Token::Operator(o,a) => o.print(a)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_args_mut(&mut self) -> Option<&mut VecDeque<Token>> {
|
||||
match self {
|
||||
Token::Operator(_, ref mut a) => Some(a),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_at_coords<'a>(g: &'a mut Token, coords: &Vec<usize>) -> &'a mut Token {
|
||||
let mut h = &mut *g;
|
||||
|
||||
for t in coords.iter() {
|
||||
let inner = h.get_args_mut().unwrap();
|
||||
h = &mut inner[*t];
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user