diff --git a/CHANGELOG.md b/CHANGELOG.md index d3b6e37..79a6880 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## Next +- Add units of force +- Parse `kN` as `kilonewton` instead of `knot` if the N is uppercase - Add REPL to the `cpc` CLI - Add support for numbers with thousands separators `_`, ` ` and ` ` (thin space) - Format numbers with thousands separator diff --git a/src/evaluator.rs b/src/evaluator.rs index 6d674a7..fef10cd 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -480,6 +480,13 @@ mod tests { results_eq("1000 mA to A", "1 A"); results_eq("1000 mΩ to Ω", "1 Ω"); results_eq("1000 mV to V", "1 V"); + results_eq("1000 millinewtons to newtons", "1 N"); + results_eq("1000 n to kilonewtons", "1 kN"); + results_eq("1 kilonewton to newtons", "1000 N"); + results_eq("1 meganewton to newtons", "1000000 N"); + results_eq("1 newton to dynes", "100000 dyne"); + eval_test("4.4482216152605 newtons to lbf", "1 pound-force"); + eval_test("1 lbf to newtons", "4.4482216152605 newtons"); results_eq("1 bar to Pa", "100000 Pa"); results_eq("1 kHz to Hz", "1000 Hz"); eval_test( diff --git a/src/lexer.rs b/src/lexer.rs index 527dc1b..bc88bf3 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -422,7 +422,7 @@ fn lex_word(word: &str, lexer: &mut Lexer) -> Result<(), String> { "lb" | "lbs" => Token::unit(Pound), "pound" | "pounds" => match read_immediate_grapheme("-", lexer) { true => match lexer.read_immediate_word().as_str() { - "force" => Token::LexerKeyword(PoundForce), + "force" => Token::unit(PoundForce), other => { lexer.tokens.push(Token::unit(Pound)); lexer.tokens.push(Token::Operator(Minus)); @@ -533,16 +533,6 @@ fn lex_word(word: &str, lexer: &mut Lexer) -> Result<(), String> { "millijoule" | "millijoules" => Token::unit(Millijoule), "j" | "joule" | "joules" => Token::unit(Joule), "nm" => Token::unit(NewtonMeter), - "newton" => match read_immediate_grapheme("-", lexer) { - true => match lexer.read_immediate_word().as_str() { - "meter" | "meters" | "metre" | "metres" => Token::unit(NewtonMeter), - string => return Err(format!("Invalid string: {}", string)), - }, - false => match lexer.read_word().as_str() { - "meter" | "meters" | "metre" | "metres" => Token::unit(NewtonMeter), - string => return Err(format!("Invalid string: {}", string)), - }, - }, "kj" | "kilojoule" | "kilojoules" => Token::unit(Kilojoule), "mj" | "megajoule" | "megajoules" => Token::unit(Megajoule), "gj" | "gigajoule" | "gigajoules" => Token::unit(Gigajoule), @@ -638,9 +628,32 @@ fn lex_word(word: &str, lexer: &mut Lexer) -> Result<(), String> { "v" | "volt" | "volts" => Token::unit(Volt), "kv" | "kilovolt" | "kilovolts" => Token::unit(Kilovolt), - // for pound-force per square inch - "lbf" => Token::LexerKeyword(PoundForce), - "force" => Token::LexerKeyword(Force), + "mn" | "millinewton" | "millinewtons" => Token::unit(Millinewton), + "n" | "newtons" => Token::unit(Newton), + "newton" => match read_immediate_grapheme("-", lexer) { + true => match lexer.read_immediate_word().as_str() { + "meter" | "meters" | "metre" | "metres" => Token::unit(NewtonMeter), + other => { + lexer.tokens.push(Token::unit(Newton)); + lexer.tokens.push(Token::Operator(Minus)); + lex_word_if_non_empty(other, lexer)?; + return Ok(()); + } + }, + false => match lexer.read_word().as_str() { + "meter" | "meters" | "metre" | "metres" => Token::unit(NewtonMeter), + other => { + lexer.tokens.push(Token::unit(Newton)); + lex_word_if_non_empty(other, lexer)?; + return Ok(()); + } + }, + }, + "kn" if word.as_bytes()[1] == b'N' => Token::unit(Kilonewton), + "kilonewton" | "kilonewtons" => Token::unit(Kilonewton), + "meganewton" | "meganewtons" => Token::unit(Meganewton), + "dyn" | "dyne" | "dynes" => Token::unit(Dyne), + "lbf" => Token::unit(PoundForce), "pa" | "pascal" | "pascals" => Token::unit(Pascal), "kpa" | "kilopascal" | "kilopascals" => Token::unit(Kilopascal), @@ -1181,7 +1194,7 @@ mod tests { ); run_lex( "210 pounds-force", - vec![numtok!(210), Token::LexerKeyword(PoundForce)], + vec![numtok!(210), Token::unit(PoundForce)], &strip_operator_spacing, &strip_afterdigit_spacing, ); @@ -2789,7 +2802,7 @@ mod tests { ); run_lex( "20 lbf", - vec![numtok!(20), Token::LexerKeyword(PoundForce)], + vec![numtok!(20), Token::unit(PoundForce)], &strip_operator_spacing, &strip_afterdigit_spacing, ); diff --git a/src/lib.rs b/src/lib.rs index a1fc30e..f5e6098 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -301,8 +301,6 @@ pub enum FunctionIdentifier { pub enum LexerKeyword { PercentChar, In, - PoundForce, - Force, Revolution, } diff --git a/src/units.rs b/src/units.rs index df6784c..63efd46 100644 --- a/src/units.rs +++ b/src/units.rs @@ -37,6 +37,8 @@ pub enum UnitType { /// A unit of voltage, for example [`Volt`] Voltage, /// A unit of pressure, for example [`Bar`] + Force, + /// A unit of force, for example [`Hertz`] Pressure, /// A unit of frequency, for example [`Hertz`] Frequency, @@ -65,6 +67,7 @@ impl UnitType { ElectricCurrent => vec![(Ampere, 1)], Resistance => vec![(Meter, 2), (Kilogram, 1), (Second, -3), (Ampere, -2)], Voltage => vec![(Meter, 2), (Kilogram, 1), (Second, -3), (Ampere, -1)], + Force => vec![(Meter, 1), (Kilogram, 1), (Second, -2)], Pressure => vec![(Kilogram, 1), (Second, -2), (Meter, -1)], Frequency => vec![(Second, -1)], Speed => vec![(Meter, 1), (Second, -1)], @@ -407,6 +410,13 @@ create_units!( Volt: (Voltage, d!(1), "volt", "volts"), Kilovolt: (Voltage, d!(1000), "kilovolt", "kilovolts"), + Millinewton: (Force, d!(0.001), "millinewton", "millinewtons"), + Newton: (Force, d!(1), "newton", "newtons"), + Kilonewton: (Force, d!(1000), "kilonewton", "kilonewtons"), + Meganewton: (Force, d!(1000000), "meganewton", "meganewtons"), + Dyne: (Force, d!(0.00001), "dyne", "dynes"), + PoundForce: (Force, d!(4.4482216152605), "pound-force", "pound-force"), + Pascal: (Pressure, d!(1), "pascal", "pascals"), Kilopascal: (Pressure, d!(1000), "kilopascal", "kilopascals"), Atmosphere: (Pressure, d!(101325), "atmosphere", "atmospheres"), @@ -1407,6 +1417,12 @@ mod tests { assert_float_eq!(convert_test(1000.0, Millivolt, Volt), 1.0); assert_float_eq!(convert_test(1000.0, Volt, Kilovolt), 1.0); + assert_float_eq!(convert_test(1000.0, Millinewton, Newton), 1.0); + assert_float_eq!(convert_test(1000.0, Newton, Kilonewton), 1.0); + assert_float_eq!(convert_test(1000.0, Kilonewton, Meganewton), 1.0); + assert_float_eq!(convert_test(100000.0, Dyne, Newton), 1.0); + assert_float_eq!(convert_test(4.4482216152605, Newton, PoundForce), 1.0); + assert_float_eq!(convert_test(1000.0, Pascal, Kilopascal), 1.0); assert_float_eq!(convert_test(101325.0, Pascal, Atmosphere), 1.0); assert_float_eq!(convert_test(100.0, Pascal, Millibar), 1.0);