diff --git a/README.md b/README.md index 061245b..40bcf92 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/src/lexer.rs b/src/lexer.rs index d302f63..76fb93c 100644 --- a/src/lexer.rs +++ b/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 { 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 { "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 { "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 { "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 { } 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 { } } }, - 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 { 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 { token_index -= 2; } } + if token_index == tokens.len()-1 { + break; + } else { + token_index += 1; + } } - println!("XKXK {:?}", tokens); + println!("TOKENS {:?}", tokens); Ok(tokens) } diff --git a/src/main.rs b/src/main.rs index c9ebab7..6e65173 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,6 +55,12 @@ pub enum LexerKeyword { Per, PercentChar, In, + DoubleQuotes, + Mercury, + Hg, + PoundForce, + PoundWord, + Force, } mod units; diff --git a/src/units.rs b/src/units.rs index beef493..1ce9e85 100644 --- a/src/units.rs +++ b/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);