From 431ea5b67e5e360c4b767ce8adba0f38a7ceffac Mon Sep 17 00:00:00 2001 From: electria Date: Tue, 21 Jul 2026 14:23:37 -0700 Subject: [PATCH 1/6] feat: recognize dedicated minus sign MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > As per Unicode 17.0.0, the ASCII hyphen-minus is not a mathematical symbol. To express the minus sign in math, U+2212 − MINUS SIGN is used instead. https://en.wikipedia.org/wiki/Mathematical_operators_and_symbols_in_Unicode#cite_ref-7 --- src/nodes/from_str.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nodes/from_str.rs b/src/nodes/from_str.rs index ba94dfa..f07f99f 100644 --- a/src/nodes/from_str.rs +++ b/src/nodes/from_str.rs @@ -22,7 +22,7 @@ impl TryFrom for Operator { fn try_from(value: char) -> Result { match value { '+' => Ok(Self::Plus), - '-' => Ok(Self::Minus), + '-' | '−' => Ok(Self::Minus), '*' | '×' => Ok(Self::Mult), '/' | '÷' => Ok(Self::Div), '^' => Ok(Self::Exp), From 442dc5fd913c44396822ea0841c542dbfdd8d6da Mon Sep 17 00:00:00 2001 From: electria Date: Fri, 24 Jul 2026 11:24:22 -0700 Subject: [PATCH 2/6] fix: 1e-1 edge case the "wrap up any unfinished parsing" section doesn't need to be changed, since this edge case is only valid when there is a number after the '-' which there never is in the case of only one extra character in other words, 1e- is not valid: `NumberParsing("1e-", ParseFloatError { kind: ExpNoDigits })` --- src/nodes/from_str.rs | 5 ++++- src/nodes/tests.rs | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/nodes/from_str.rs b/src/nodes/from_str.rs index f07f99f..5db581e 100644 --- a/src/nodes/from_str.rs +++ b/src/nodes/from_str.rs @@ -112,7 +112,10 @@ impl FromStr for Nodes { } } ParsingState::ReadingNumber(start_index) => { - if !is_number_char(c) { + if !is_number_char(c) + // don't stop reading number on a special case, eg "1e-1" + && !(filtered_string.chars().nth(i - 1) == Some('e') && c == '-') + { nodes.push(Node::Number( Float::parse(&filtered_string[start_index..i]) .map_err(|e| { diff --git a/src/nodes/tests.rs b/src/nodes/tests.rs index 405efe5..2acaac0 100644 --- a/src/nodes/tests.rs +++ b/src/nodes/tests.rs @@ -5,6 +5,11 @@ use rug::Float; use crate::nodes::{Function, Node, Nodes, Operator, PREC}; +#[test] +fn e_edge_case() { + assert_eq!(Nodes::from_str("1e-1").unwrap().evaluate().to_f64(), 0.1) +} + #[test] fn implicit_mult() { assert_eq!( From 4dcbc94e0fc415e8d08af96907b29986feda7701 Mon Sep 17 00:00:00 2001 From: electria Date: Fri, 24 Jul 2026 11:38:51 -0700 Subject: [PATCH 3/6] style: use Self where possible --- src/nodes/from_str.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nodes/from_str.rs b/src/nodes/from_str.rs index 5db581e..dac434c 100644 --- a/src/nodes/from_str.rs +++ b/src/nodes/from_str.rs @@ -184,6 +184,6 @@ impl FromStr for Nodes { } } - Ok(Nodes(nodes)) + Ok(Self(nodes)) } } From 0dc140909f348da58dff16510bc508013acc8aa4 Mon Sep 17 00:00:00 2001 From: electria Date: Fri, 24 Jul 2026 14:23:28 -0700 Subject: [PATCH 4/6] docs: reflow comments --- src/nodes/from_str.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/nodes/from_str.rs b/src/nodes/from_str.rs index dac434c..14e4c9a 100644 --- a/src/nodes/from_str.rs +++ b/src/nodes/from_str.rs @@ -79,11 +79,12 @@ impl FromStr for Nodes { } /// A function to check if the given `char` should be used by the parser. /// - /// We ignore whitespace since it has no semantic significance in typst aside from variables, - /// (which are not implemented) + /// We ignore whitespace since it has no semantic significance in typst + /// (aside from variables, which are not implemented) /// and `&` because it is only used for visual alignment. /// - /// Newlines are also ignored because they're likely accidental; like in the case of `echo 1+1 | tylc`. + /// Newlines are also ignored because they're likely accidental; + /// like in the case of `echo 1+1 | tylc`. fn is_not_ignored_char(c: &char) -> bool { !matches!(c, ' ' | '&' | '\n') } From 704ef81816e18bd798da2071c5ed9ab9dfda3bec Mon Sep 17 00:00:00 2001 From: electria Date: Fri, 24 Jul 2026 21:50:58 -0700 Subject: [PATCH 5/6] feat: ignore and add back single equal sign more than one is likely a user mistake and a panic still makes sense should also add back '&' in the same way, if applicable --- src/main.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 5a73914..ff8bb70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,9 +18,33 @@ fn main() { } }; + println!("{}", parse_and_calc_humanreadable(input)); +} + +pub fn parse_and_calc_humanreadable(mut input: String) -> String { + let should_add_back_equals = if let Some((i, _)) = input + .chars() + .enumerate() + .filter(|(_, c)| !matches!(c, ' ' | '&' | '\n')) + .find(|(_, c)| *c == '=') + { + input.remove(i); + true + } else { + false + }; + let result = parse_and_calc(&input); - println!("{result:.0$}", get_significant_digits(&result)); + format!( + "{0}{result:.1$}", + if should_add_back_equals { "= " } else { "" }, + get_significant_digits(&result), + ) +} +#[test] +fn test_equals_readdition() { + assert_eq!(parse_and_calc_humanreadable(" = 11".into()), "= 11"); } pub fn parse_and_calc(s: &str) -> Float { From 262c55b91ad354e040c07d8e4989d203b4c3a3ab Mon Sep 17 00:00:00 2001 From: electria Date: Fri, 24 Jul 2026 21:57:30 -0700 Subject: [PATCH 6/6] feat: add back single ampersand --- src/main.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index ff8bb70..84c33ab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,17 +34,31 @@ pub fn parse_and_calc_humanreadable(mut input: String) -> String { false }; + // no need to remove the character here, + // as we ignore the ampersand in all other logic already + let should_add_back_alignment = input.contains('&'); + let result = parse_and_calc(&input); format!( "{0}{result:.1$}", - if should_add_back_equals { "= " } else { "" }, + if should_add_back_equals && should_add_back_alignment { + "& = " + } else if should_add_back_equals { + "= " + } else if should_add_back_alignment { + "& " + } else { + "" + }, get_significant_digits(&result), ) } #[test] fn test_equals_readdition() { assert_eq!(parse_and_calc_humanreadable(" = 11".into()), "= 11"); + assert_eq!(parse_and_calc_humanreadable(" &= 11".into()), "& = 11"); + assert_eq!(parse_and_calc_humanreadable(" & 11".into()), "& 11"); } pub fn parse_and_calc(s: &str) -> Float {