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:
parent
02e74806e1
commit
5360318bfc
@ -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`.
|
||||
|
||||
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
|
||||
Make sure to also add a test for each unit. The tests look like this:
|
||||
|
||||
97
src/lexer.rs
97
src/lexer.rs
@ -5,7 +5,7 @@ use crate::Operator::{Caret, Divide, LeftParen, Minus, Modulo, Multiply, Plus, R
|
||||
use crate::UnaryOperator::{Percent, Factorial};
|
||||
use crate::TextOperator::{Of, To};
|
||||
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::units::Unit::*;
|
||||
|
||||
@ -38,6 +38,7 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
|
||||
tokens.push(Token::Operator(RightParen));
|
||||
},
|
||||
'π' => tokens.push(Token::Constant(Pi)),
|
||||
'"' | '“' | '”' | '″' => tokens.push(Token::LexerKeyword(DoubleQuotes)),
|
||||
value if value.is_whitespace() => {},
|
||||
value if value.is_alphabetic() => {
|
||||
|
||||
@ -102,6 +103,7 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
|
||||
"tan" => tokens.push(Token::FunctionIdentifier(Tan)),
|
||||
|
||||
"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)),
|
||||
"μ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)),
|
||||
"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)),
|
||||
"t" | "tonne" | "tonnes" | "metric ton" | "metric tons" | "metric tonne" | "metric tonnes" => tokens.push(Token::Unit(MetricTon)),
|
||||
"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)),
|
||||
"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)),
|
||||
"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)),
|
||||
"mps" => tokens.push(Token::Unit(MetersPerSecond)),
|
||||
"mph" => tokens.push(Token::Unit(MilesPerHour)),
|
||||
@ -310,16 +327,16 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
|
||||
}
|
||||
|
||||
let mut token_index = 0;
|
||||
for _i in 1..tokens.len() {
|
||||
// decide if % is percent or modulo
|
||||
loop {
|
||||
match tokens[token_index] {
|
||||
// decide if % is percent or modulo
|
||||
Token::LexerKeyword(PercentChar) => {
|
||||
match &tokens[token_index + 1] {
|
||||
Token::TextOperator(Of) => {
|
||||
match tokens.get(token_index + 1) {
|
||||
Some(Token::TextOperator(Of)) => {
|
||||
// "10% of 1km" should be percentage
|
||||
tokens[token_index] = Token::UnaryOperator(Percent);
|
||||
},
|
||||
Token::Operator(operator) => {
|
||||
Some(Token::Operator(operator)) => {
|
||||
match operator {
|
||||
LeftParen => {
|
||||
// "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
|
||||
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);
|
||||
},
|
||||
}
|
||||
},
|
||||
// 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) => {
|
||||
match &tokens[token_index + 1] {
|
||||
Token::Unit(_) => {
|
||||
match tokens.get(token_index + 1) {
|
||||
Some(Token::Unit(_)) => {
|
||||
// "in" should be To
|
||||
tokens[token_index] = Token::TextOperator(To);
|
||||
},
|
||||
_ => {
|
||||
// otherwise, Inch
|
||||
tokens[token_index] = Token::Unit(Inch);
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
token_index += 1;
|
||||
// parse units like km/h, km per h
|
||||
// parse units like km/h, lbf per square inch
|
||||
if token_index >= 2 {
|
||||
let token1 = &tokens[token_index-2];
|
||||
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 mut replaced = true;
|
||||
match (token1, token2, token3) {
|
||||
// km/h
|
||||
(Token::Unit(Kilometer), Token::LexerKeyword(Per), Token::Unit(Hour)) => {
|
||||
tokens[token_index-2] = Token::Unit(KilometersPerHour);
|
||||
},
|
||||
// mi/h
|
||||
(Token::Unit(Mile), Token::LexerKeyword(Per), Token::Unit(Hour)) => {
|
||||
tokens[token_index-2] = Token::Unit(MilesPerHour);
|
||||
},
|
||||
// m/s
|
||||
(Token::Unit(Meter), Token::LexerKeyword(Per), Token::Unit(Second)) => {
|
||||
tokens[token_index-2] = Token::Unit(MetersPerSecond);
|
||||
},
|
||||
// ft/s
|
||||
(Token::Unit(Foot), Token::LexerKeyword(Per), Token::Unit(Second)) => {
|
||||
tokens[token_index-2] = Token::Unit(FeetPerSecond);
|
||||
},
|
||||
// btu/min
|
||||
(Token::Unit(BritishThermalUnit), Token::LexerKeyword(Per), Token::Unit(Minute)) => {
|
||||
tokens[token_index-2] = Token::Unit(BritishThermalUnitsPerMinute);
|
||||
},
|
||||
// btu/h
|
||||
(Token::Unit(BritishThermalUnit), Token::LexerKeyword(Per), Token::Unit(Hour)) => {
|
||||
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;
|
||||
},
|
||||
@ -393,8 +453,13 @@ pub fn lex(input: &str) -> Result<TokenVector, String> {
|
||||
token_index -= 2;
|
||||
}
|
||||
}
|
||||
if token_index == tokens.len()-1 {
|
||||
break;
|
||||
} else {
|
||||
token_index += 1;
|
||||
}
|
||||
println!("XKXK {:?}", tokens);
|
||||
}
|
||||
println!("TOKENS {:?}", tokens);
|
||||
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
@ -55,6 +55,12 @@ pub enum LexerKeyword {
|
||||
Per,
|
||||
PercentChar,
|
||||
In,
|
||||
DoubleQuotes,
|
||||
Mercury,
|
||||
Hg,
|
||||
PoundForce,
|
||||
PoundWord,
|
||||
Force,
|
||||
}
|
||||
|
||||
mod units;
|
||||
|
||||
18
src/units.rs
18
src/units.rs
@ -11,6 +11,7 @@ pub enum UnitType {
|
||||
Information,
|
||||
Energy,
|
||||
Power,
|
||||
Pressure,
|
||||
Speed,
|
||||
Temperature,
|
||||
}
|
||||
@ -184,6 +185,15 @@ create_units!(
|
||||
Horsepower: (Power, d128!(745.69987158227022)), // exact according to wikipedia
|
||||
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)),
|
||||
MetersPerSecond: (Speed, d128!(3.6)),
|
||||
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(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(0.3048, MetersPerSecond, FeetPerSecond), 1.0);
|
||||
assert_eq!(convert_test(1.609344, KilometersPerHour, MilesPerHour), 1.0);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user