Support for using "in" as To operator (like "5in in cm")

This commit is contained in:
Kasper 2020-01-12 03:03:26 +01:00
parent efa76e93c5
commit 02e74806e1
3 changed files with 45 additions and 19 deletions

View File

@ -5,6 +5,7 @@ use crate::Operator::{Caret, Divide, LeftParen, Minus, Modulo, Multiply, Plus, R
use crate::UnaryOperator::{Percent, Factorial}; use crate::UnaryOperator::{Percent, Factorial};
use crate::TextOperator::{Of, To}; use crate::TextOperator::{Of, To};
use crate::Constant::{E, Pi}; use crate::Constant::{E, Pi};
use crate::LexerKeyword::{In, PercentChar, Per};
use crate::FunctionIdentifier::{Cbrt, Ceil, Cos, Exp, Abs, Floor, Ln, Log, Round, Sin, Sqrt, Tan}; use crate::FunctionIdentifier::{Cbrt, Ceil, Cos, Exp, Abs, Floor, Ln, Log, Round, Sin, Sqrt, Tan};
use crate::units::Unit::*; use crate::units::Unit::*;
@ -25,7 +26,7 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
'-' => tokens.push(Token::Operator(Minus)), '-' => tokens.push(Token::Operator(Minus)),
'*' => tokens.push(Token::Operator(Multiply)), '*' => tokens.push(Token::Operator(Multiply)),
'/' => tokens.push(Token::Operator(Divide)), '/' => tokens.push(Token::Operator(Divide)),
'%' => tokens.push(Token::Operator(Modulo)), '%' => tokens.push(Token::LexerKeyword(PercentChar)),
'^' => tokens.push(Token::Operator(Caret)), '^' => tokens.push(Token::Operator(Caret)),
'!' => tokens.push(Token::UnaryOperator(Factorial)), '!' => tokens.push(Token::UnaryOperator(Factorial)),
'(' => { '(' => {
@ -100,7 +101,7 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
"cos" => tokens.push(Token::FunctionIdentifier(Cos)), "cos" => tokens.push(Token::FunctionIdentifier(Cos)),
"tan" => tokens.push(Token::FunctionIdentifier(Tan)), "tan" => tokens.push(Token::FunctionIdentifier(Tan)),
"per" => tokens.push(Token::Per), "per" => tokens.push(Token::LexerKeyword(Per)),
"ns" | "nanosec" | "nanosecs" | "nanosecond" | "nanoseconds" => tokens.push(Token::Unit(Nanosecond)), "ns" | "nanosec" | "nanosecs" | "nanosecond" | "nanoseconds" => tokens.push(Token::Unit(Nanosecond)),
"μs" | "microsec" | "microsecs" | "microsecond" | "microseconds" => tokens.push(Token::Unit(Microsecond)), "μs" | "microsec" | "microsecs" | "microsecond" | "microseconds" => tokens.push(Token::Unit(Microsecond)),
@ -122,7 +123,8 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
"dm" | "decimeter" | "decimeters" => tokens.push(Token::Unit(Centimeter)), "dm" | "decimeter" | "decimeters" => tokens.push(Token::Unit(Centimeter)),
"m" | "meter" | "meters" => tokens.push(Token::Unit(Meter)), "m" | "meter" | "meters" => tokens.push(Token::Unit(Meter)),
"km" | "kilometer" | "kilometers" => tokens.push(Token::Unit(Kilometer)), "km" | "kilometer" | "kilometers" => tokens.push(Token::Unit(Kilometer)),
"in" | "inch" | "inches" => tokens.push(Token::Unit(Inch)), "in" => tokens.push(Token::LexerKeyword(In)),
"inch" | "inches" => tokens.push(Token::Unit(Inch)),
"ft" | "foot" | "feet" => tokens.push(Token::Unit(Foot)), "ft" | "foot" | "feet" => tokens.push(Token::Unit(Foot)),
"yd" | "yard" | "yards" => tokens.push(Token::Unit(Yard)), "yd" | "yard" | "yards" => tokens.push(Token::Unit(Yard)),
"mi" | "mile" | "miles" => tokens.push(Token::Unit(Mile)), "mi" | "mile" | "miles" => tokens.push(Token::Unit(Mile)),
@ -309,60 +311,76 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
let mut token_index = 0; let mut token_index = 0;
for _i in 1..tokens.len() { for _i in 1..tokens.len() {
// the lexer parses percentages as modulo, so here modulos become percentages // decide if % is percent or modulo
match tokens[token_index] { match tokens[token_index] {
Token::Operator(Modulo) => { Token::LexerKeyword(PercentChar) => {
match &tokens[token_index + 1] { match &tokens[token_index + 1] {
Token::TextOperator(Of) => { Token::TextOperator(Of) => {
// for example "10% of 1km" should be a percentage, not modulo // "10% of 1km" should be percentage
tokens[token_index] = Token::UnaryOperator(Percent); tokens[token_index] = Token::UnaryOperator(Percent);
}, },
Token::Operator(operator) => { Token::Operator(operator) => {
match operator { match operator {
LeftParen => {}, LeftParen => {
// "10%(2)" should be modulo
tokens[token_index] = Token::Operator(Modulo);
},
_ => { _ => {
// for example "10%*2" should be a percentage, but "10%(2)" should be modulo // "10%*2" should be a percentage
tokens[token_index] = Token::UnaryOperator(Percent); tokens[token_index] = Token::UnaryOperator(Percent);
} }
} }
}, },
Token::UnaryOperator(_operator) => { Token::UnaryOperator(_operator) => {
// for example "10%!" should be a percentage, but "10%(2)" should be modulo // "10%!" should be a percentage
tokens[token_index] = Token::UnaryOperator(Percent); tokens[token_index] = Token::UnaryOperator(Percent);
}, },
_ => {}, _ => {
// "10%2" should be modulo
tokens[token_index] = Token::Operator(Modulo);
},
}
},
Token::LexerKeyword(In) => {
match &tokens[token_index + 1] {
Token::Unit(_) => {
tokens[token_index] = Token::TextOperator(To);
},
_ => {
tokens[token_index] = Token::Unit(Inch);
},
} }
} }
_ => {}, _ => {},
} }
token_index += 1; token_index += 1;
// parse units like km per h // parse units like km/h, km per h
if token_index >= 2 { if token_index >= 2 {
let token1 = &tokens[token_index-2]; let token1 = &tokens[token_index-2];
let token2 = match &tokens[token_index-1] { let token2 = match &tokens[token_index-1] {
// treat km/h the same as km per h // treat km/h the same as km per h
Token::Operator(Divide) => &Token::Per, Token::Operator(Divide) => &Token::LexerKeyword(Per),
_ => &tokens[token_index-1], _ => &tokens[token_index-1],
}; };
let token3 = &tokens[token_index]; let token3 = &tokens[token_index];
let mut replaced = true; let mut replaced = true;
match (token1, token2, token3) { match (token1, token2, token3) {
(Token::Unit(Kilometer), Token::Per, Token::Unit(Hour)) => { (Token::Unit(Kilometer), Token::LexerKeyword(Per), Token::Unit(Hour)) => {
tokens[token_index-2] = Token::Unit(KilometersPerHour); tokens[token_index-2] = Token::Unit(KilometersPerHour);
}, },
(Token::Unit(Mile), Token::Per, Token::Unit(Hour)) => { (Token::Unit(Mile), Token::LexerKeyword(Per), Token::Unit(Hour)) => {
tokens[token_index-2] = Token::Unit(MilesPerHour); tokens[token_index-2] = Token::Unit(MilesPerHour);
}, },
(Token::Unit(Meter), Token::Per, Token::Unit(Second)) => { (Token::Unit(Meter), Token::LexerKeyword(Per), Token::Unit(Second)) => {
tokens[token_index-2] = Token::Unit(MetersPerSecond); tokens[token_index-2] = Token::Unit(MetersPerSecond);
}, },
(Token::Unit(Foot), Token::Per, Token::Unit(Second)) => { (Token::Unit(Foot), Token::LexerKeyword(Per), Token::Unit(Second)) => {
tokens[token_index-2] = Token::Unit(FeetPerSecond); tokens[token_index-2] = Token::Unit(FeetPerSecond);
}, },
(Token::Unit(BritishThermalUnit), Token::Per, Token::Unit(Minute)) => { (Token::Unit(BritishThermalUnit), Token::LexerKeyword(Per), Token::Unit(Minute)) => {
tokens[token_index-2] = Token::Unit(BritishThermalUnitsPerMinute); tokens[token_index-2] = Token::Unit(BritishThermalUnitsPerMinute);
}, },
(Token::Unit(BritishThermalUnit), Token::Per, Token::Unit(Hour)) => { (Token::Unit(BritishThermalUnit), Token::LexerKeyword(Per), Token::Unit(Hour)) => {
tokens[token_index-2] = Token::Unit(BritishThermalUnitsPerHour); tokens[token_index-2] = Token::Unit(BritishThermalUnitsPerHour);
}, },
_ => { _ => {

View File

@ -50,6 +50,13 @@ pub enum FunctionIdentifier {
Tan, Tan,
} }
#[derive(Clone, Debug)]
pub enum LexerKeyword {
Per,
PercentChar,
In,
}
mod units; mod units;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -61,6 +68,7 @@ pub enum Token {
Constant(Constant), Constant(Constant),
Paren, // parser only Paren, // parser only
Per, // lexer only Per, // lexer only
LexerKeyword(LexerKeyword),
TextOperator(TextOperator), TextOperator(TextOperator),
Negative, // parser only Negative, // parser only
Unit(units::Unit), Unit(units::Unit),

View File

@ -263,7 +263,7 @@ fn parse_level_7(tokens: &TokenVector, pos: usize) -> Result<(AstNode, usize), S
&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))