Added unit of pressure, improved lexer "post-fix" loop

Previously the lexer's post-fix loop was really weird, incrementing in the middle of the function. No more
This commit is contained in:
Kasper 2020-01-12 22:57:49 +01:00
parent 02e74806e1
commit 5360318bfc
4 changed files with 106 additions and 17 deletions

View File

@ -38,7 +38,7 @@ create_units!(
The number associated with a unit is it's "weight". For example, if a second's weight is `1`, then a minute's weight is `1000`. The number associated with a unit is it's "weight". For example, if a second's weight is `1`, then a minute's weight is `1000`.
I have found [translatorscafe.com](https://www.translatorscafe.com/unit-converter) to be a good website for accurate and comprehensive unit convertion. Wikipedia is worth looking at as well. I have found [translatorscafe.com](https://www.translatorscafe.com/unit-converter) and [calculateme.com](https://www.calculateme.com/) to be good websites for unit convertion. Wikipedia is worth looking at as well.
### 2. Add a test for the unit ### 2. Add a test for the unit
Make sure to also add a test for each unit. The tests look like this: Make sure to also add a test for each unit. The tests look like this:

View File

@ -5,7 +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::LexerKeyword::{In, PercentChar, Per, Mercury, Hg, PoundForce, PoundWord, Force, DoubleQuotes};
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::*;
@ -38,6 +38,7 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
tokens.push(Token::Operator(RightParen)); tokens.push(Token::Operator(RightParen));
}, },
'π' => tokens.push(Token::Constant(Pi)), 'π' => tokens.push(Token::Constant(Pi)),
'"' | '“' | '”' | '″' => tokens.push(Token::LexerKeyword(DoubleQuotes)),
value if value.is_whitespace() => {}, value if value.is_whitespace() => {},
value if value.is_alphabetic() => { value if value.is_alphabetic() => {
@ -102,6 +103,7 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
"tan" => tokens.push(Token::FunctionIdentifier(Tan)), "tan" => tokens.push(Token::FunctionIdentifier(Tan)),
"per" => tokens.push(Token::LexerKeyword(Per)), "per" => tokens.push(Token::LexerKeyword(Per)),
"hg" => tokens.push(Token::LexerKeyword(Hg)), // can be hectogram or mercury
"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)),
@ -170,11 +172,12 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
"mg" | "milligram" | "milligrams" => tokens.push(Token::Unit(Milligram)), "mg" | "milligram" | "milligrams" => tokens.push(Token::Unit(Milligram)),
"g" | "gram" | "grams" => tokens.push(Token::Unit(Gram)), "g" | "gram" | "grams" => tokens.push(Token::Unit(Gram)),
"hg" | "hectogram" | "hectograms" => tokens.push(Token::Unit(Hectogram)), "hectogram" | "hectograms" => tokens.push(Token::Unit(Hectogram)),
"kg" | "kilo" | "kilos" | "kilogram" | "kilograms" => tokens.push(Token::Unit(Kilogram)), "kg" | "kilo" | "kilos" | "kilogram" | "kilograms" => tokens.push(Token::Unit(Kilogram)),
"t" | "tonne" | "tonnes" | "metric ton" | "metric tons" | "metric tonne" | "metric tonnes" => tokens.push(Token::Unit(MetricTon)), "t" | "tonne" | "tonnes" | "metric ton" | "metric tons" | "metric tonne" | "metric tonnes" => tokens.push(Token::Unit(MetricTon)),
"oz" | "ounces" => tokens.push(Token::Unit(Ounce)), "oz" | "ounces" => tokens.push(Token::Unit(Ounce)),
"lb" | "lbs" | "pound" | "pounds" => tokens.push(Token::Unit(Pound)), "lb" | "lbs" | "pounds" => tokens.push(Token::Unit(Pound)),
"pound" => tokens.push(Token::LexerKeyword(PoundWord)),
"st" | "ton" | "tons" | "short ton" | "short tons" | "short tonne" | "short tonnes" => tokens.push(Token::Unit(ShortTon)), "st" | "ton" | "tons" | "short ton" | "short tons" | "short tonne" | "short tonnes" => tokens.push(Token::Unit(ShortTon)),
"lt" | "long ton" | "long tons" | "long tonne" | "long tonnes" => tokens.push(Token::Unit(LongTon)), "lt" | "long ton" | "long tons" | "long tonne" | "long tonnes" => tokens.push(Token::Unit(LongTon)),
@ -240,6 +243,20 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
"hp" | "hps" | "horsepower" | "horsepowers" => tokens.push(Token::Unit(Horsepower)), "hp" | "hps" | "horsepower" | "horsepowers" => tokens.push(Token::Unit(Horsepower)),
"mhp" | "hpm" | "metric hp" | "metric hps" | "metric horsepower" | "metric horsepowers" => tokens.push(Token::Unit(MetricHorsepower)), "mhp" | "hpm" | "metric hp" | "metric hps" | "metric horsepower" | "metric horsepowers" => tokens.push(Token::Unit(MetricHorsepower)),
// for pound-force per square inch
"lbf" => tokens.push(Token::LexerKeyword(PoundForce)),
"force" => tokens.push(Token::LexerKeyword(Force)),
"pa" | "pascal" | "pascals" => tokens.push(Token::Unit(Pascal)),
"kpa" | "kilopascal" | "kilopascals" => tokens.push(Token::Unit(Kilopascal)),
"atm" | "atms" | "atmosphere" | "atmospheres" => tokens.push(Token::Unit(Atmosphere)),
"mbar" | "mbars" | "millibar" | "millibars" => tokens.push(Token::Unit(Millibar)),
"bar" | "bars" => tokens.push(Token::Unit(Bar)),
"inhg" => tokens.push(Token::Unit(InchOfMercury)),
"mercury" => tokens.push(Token::LexerKeyword(Mercury)),
"psi" => tokens.push(Token::Unit(PoundsPerSquareInch)),
"torr" | "torrs" => tokens.push(Token::Unit(Torr)),
"kph" | "kmh" => tokens.push(Token::Unit(KilometersPerHour)), "kph" | "kmh" => tokens.push(Token::Unit(KilometersPerHour)),
"mps" => tokens.push(Token::Unit(MetersPerSecond)), "mps" => tokens.push(Token::Unit(MetersPerSecond)),
"mph" => tokens.push(Token::Unit(MilesPerHour)), "mph" => tokens.push(Token::Unit(MilesPerHour)),
@ -310,16 +327,16 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
} }
let mut token_index = 0; let mut token_index = 0;
for _i in 1..tokens.len() { loop {
// decide if % is percent or modulo
match tokens[token_index] { match tokens[token_index] {
// decide if % is percent or modulo
Token::LexerKeyword(PercentChar) => { Token::LexerKeyword(PercentChar) => {
match &tokens[token_index + 1] { match tokens.get(token_index + 1) {
Token::TextOperator(Of) => { Some(Token::TextOperator(Of)) => {
// "10% of 1km" should be percentage // "10% of 1km" should be percentage
tokens[token_index] = Token::UnaryOperator(Percent); tokens[token_index] = Token::UnaryOperator(Percent);
}, },
Token::Operator(operator) => { Some(Token::Operator(operator)) => {
match operator { match operator {
LeftParen => { LeftParen => {
// "10%(2)" should be modulo // "10%(2)" should be modulo
@ -331,30 +348,55 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
} }
} }
}, },
Token::UnaryOperator(_operator) => { Some(Token::UnaryOperator(_operator)) => {
// "10%!" should be a percentage // "10%!" should be a percentage
tokens[token_index] = Token::UnaryOperator(Percent); tokens[token_index] = Token::UnaryOperator(Percent);
}, },
None => {
// percent if there's no element afterwards
tokens[token_index] = Token::UnaryOperator(Percent);
},
_ => { _ => {
// "10%2" should be modulo // everything else should be modulo, for example if the % is
// before a number, function or constants
tokens[token_index] = Token::Operator(Modulo); tokens[token_index] = Token::Operator(Modulo);
}, },
} }
}, },
// decide if " is inch of inch of mercury
Token::LexerKeyword(DoubleQuotes) => {
match tokens.get(token_index + 1) {
Some(Token::LexerKeyword(Hg)) => {
// "hg should be inch of mercury
tokens[token_index] = Token::Unit(InchOfMercury);
tokens.remove(token_index + 1);
},
_ => {
// otherwise, Inch
tokens[token_index] = Token::Unit(InchOfMercury);
},
}
},
// if hg wasn't already turned into inch of mercury, it's hectogram
Token::LexerKeyword(Hg) => {
tokens[token_index] = Token::Unit(Hectogram);
},
// decide if "in" is Inch or To
Token::LexerKeyword(In) => { Token::LexerKeyword(In) => {
match &tokens[token_index + 1] { match tokens.get(token_index + 1) {
Token::Unit(_) => { Some(Token::Unit(_)) => {
// "in" should be To
tokens[token_index] = Token::TextOperator(To); tokens[token_index] = Token::TextOperator(To);
}, },
_ => { _ => {
// otherwise, Inch
tokens[token_index] = Token::Unit(Inch); tokens[token_index] = Token::Unit(Inch);
}, },
} }
} },
_ => {}, _ => {},
} }
token_index += 1; // parse units like km/h, lbf per square inch
// 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] {
@ -365,24 +407,42 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
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) {
// km/h
(Token::Unit(Kilometer), Token::LexerKeyword(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);
}, },
// mi/h
(Token::Unit(Mile), Token::LexerKeyword(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);
}, },
// m/s
(Token::Unit(Meter), Token::LexerKeyword(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);
}, },
// ft/s
(Token::Unit(Foot), Token::LexerKeyword(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);
}, },
// btu/min
(Token::Unit(BritishThermalUnit), Token::LexerKeyword(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);
}, },
// btu/h
(Token::Unit(BritishThermalUnit), Token::LexerKeyword(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);
}, },
// pound-force
(Token::LexerKeyword(PoundWord), Token::Operator(Minus), Token::LexerKeyword(Force)) => {
tokens[token_index-2] = Token::LexerKeyword(PoundForce);
},
// lbs/sqin
(Token::LexerKeyword(PoundForce), Token::LexerKeyword(Per), Token::Unit(SquareInch)) => {
tokens[token_index-2] = Token::Unit(PoundsPerSquareInch);
},
// inch of mercury
(Token::Unit(Inch), Token::TextOperator(Of), Token::LexerKeyword(Mercury)) => {
tokens[token_index-2] = Token::Unit(InchOfMercury);
},
_ => { _ => {
replaced = false; replaced = false;
}, },
@ -393,8 +453,13 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
token_index -= 2; token_index -= 2;
} }
} }
if token_index == tokens.len()-1 {
break;
} else {
token_index += 1;
} }
println!("XKXK {:?}", tokens); }
println!("TOKENS {:?}", tokens);
Ok(tokens) Ok(tokens)
} }

View File

@ -55,6 +55,12 @@ pub enum LexerKeyword {
Per, Per,
PercentChar, PercentChar,
In, In,
DoubleQuotes,
Mercury,
Hg,
PoundForce,
PoundWord,
Force,
} }
mod units; mod units;

View File

@ -11,6 +11,7 @@ pub enum UnitType {
Information, Information,
Energy, Energy,
Power, Power,
Pressure,
Speed, Speed,
Temperature, Temperature,
} }
@ -184,6 +185,15 @@ create_units!(
Horsepower: (Power, d128!(745.69987158227022)), // exact according to wikipedia Horsepower: (Power, d128!(745.69987158227022)), // exact according to wikipedia
MetricHorsepower: (Power, d128!(735.49875)), MetricHorsepower: (Power, d128!(735.49875)),
Pascal: (Pressure, d128!(1)),
Kilopascal: (Pressure, d128!(1000)),
Atmosphere: (Pressure, d128!(101325)),
Millibar: (Pressure, d128!(100)),
Bar: (Pressure, d128!(100000)),
InchOfMercury: (Pressure, d128!(3386.389)),
PoundsPerSquareInch: (Pressure, d128!(6894.757293168361)), // inexact
Torr: (Pressure, d128!(162.12)),
KilometersPerHour: (Speed, d128!(1)), KilometersPerHour: (Speed, d128!(1)),
MetersPerSecond: (Speed, d128!(3.6)), MetersPerSecond: (Speed, d128!(3.6)),
MilesPerHour: (Speed, d128!(1.609344)), MilesPerHour: (Speed, d128!(1.609344)),
@ -386,6 +396,14 @@ mod tests {
assert_eq!(convert_test(745.6998715822702, Watt, Horsepower), 1.0); assert_eq!(convert_test(745.6998715822702, Watt, Horsepower), 1.0);
assert_eq!(convert_test(735.49875, Watt, MetricHorsepower), 1.0); assert_eq!(convert_test(735.49875, Watt, MetricHorsepower), 1.0);
assert_eq!(convert_test(1000.0, Pascal, Kilopascal), 1.0);
assert_eq!(convert_test(101325.0, Pascal, Atmosphere), 1.0);
assert_eq!(convert_test(100.0, Pascal, Millibar), 1.0);
assert_eq!(convert_test(1000.0, Millibar, Bar), 1.0);
assert_eq!(convert_test(3386.389, Pascal, InchOfMercury), 1.0);
assert_eq!(convert_test(6894.757293168361, Pascal, PoundsPerSquareInch), 1.0);
assert_eq!(convert_test(162.12, Pascal, Torr), 1.0);
assert_eq!(convert_test(3.6, KilometersPerHour, MetersPerSecond), 1.0); assert_eq!(convert_test(3.6, KilometersPerHour, MetersPerSecond), 1.0);
assert_eq!(convert_test(0.3048, MetersPerSecond, FeetPerSecond), 1.0); assert_eq!(convert_test(0.3048, MetersPerSecond, FeetPerSecond), 1.0);
assert_eq!(convert_test(1.609344, KilometersPerHour, MilesPerHour), 1.0); assert_eq!(convert_test(1.609344, KilometersPerHour, MilesPerHour), 1.0);