feat: extend math engine expressions by antonreshetov · Pull Request #713 · massCodeIO/massCode

added 15 commits

March 28, 2026 19:42
… math functions

- Operator phrases: `to the power of`, `remainder of X divided by Y`, `divided by`
- Rounding directives: `to N dp`, `rounded`, `rounded up/down`, `to nearest X`
- Percentage extensions: `X to Y is what %`, `X is what % off Y`, `0.35 as %`, `X/Y as %`
- Strip units: `$100 as number`, `20% as dec`, `50% to decimal`
- Math functions: exp(), log2(), log10() via native math.js
- Scientific notation input: 1e5, 2.5e3
- Fix implicit multiplication breaking function names with digits (log2, log10)
- Block aggregates: `median` and `count` keywords (like sum/average)
- Inline aggregates: `total of 3, 4, 7 and 9`, `average of X, Y and Z`,
  `count of X, Y, Z`, `median of X, Y and Z`
- `square root of X`, `cube root of X`, `root N of X`
- `log X base Y`
- `X is Y to what power` (reverse logarithm)
- Degree trig: sind, cosd, tand, asind, acosd, atand
- Degrees as parameter: sin(90 degrees) → sin(90 deg)
- Hyperbolic inverse: asinh, acosh, atanh (native math.js)
Both already supported natively by math.js, just adding tests.
- if-then-else: `if 5 > 3 then 10 else 20`
- Postfix if/unless: `42 if 5 > 3`, `42 unless 5 < 3`
- Comparison operators: ==, !=, >, <, >=, <=
- Boolean values: true, false
- Logical operators: and, or, &&, ||
- Boolean assignment and compound conditions
- `X as fraction`: convert decimal to simplified fraction (2/10 → 1/5)
- `X% as fraction`: convert percentage to fraction (50% → 1/2)
- `X/Y of Z`: fraction of value (2/3 of 600 → 400)
- `Z is X/Y of what`: reverse fraction (50 is 1/5 of what → 250)
- `20/5 as multiplier` → 4x
- `50 as x of 5` → 10x
- `2 as multiplier on 1` → 1x (markup)
- `1 as x off 2` → 0.5x (discount)
- `50 to 75 is what x` → 1.5x (change)
- `20 to 40 as x` → 2x
…ions

New custom units: mph, km/h, knot, calorie, kcal, fathom, light year,
microgram, bushel.
Native math.js units verified: Hz/kHz/MHz/GHz, W/kW/hp, J/kWh,
GiB/MiB/KiB, deg/rad, ns/ms, nm/um/dm.
- Reverse: `meters in 10 km` → 10,000 m, `days in 3 weeks` → 21 days
- Shorthand: `km m` → 1,000 m (two unit names show conversion factor)
- Verified: larger unit wins (1km + 1000m = 2km), area from mult (10m * 10m)
- `per` keyword: `$99 per week` → $99/week
- `at` phrase: `30 hours at $30/hour` → rate multiplication
- `a day for a year` phrase: `$24 a day for a year`
- Math.js compound units handle rate arithmetic natively
- days since/till/until: `days since January 1`, `days till December 25`
- days between: `days between March 1 and March 31`
- Relative dates: `5 days from now`, `3 days ago`
- Day of week: `day of the week on January 24, 1984` → Tuesday
- Week numbers: `week of year`, `week number on March 12, 2021`
- Days in period: `days in February 2020` → 29, `days in Q3` → 92
- Date offsets: `3 weeks after March 14, 2019`, `28 days before March 12`
- Current timestamp: `current timestamp`
$30 * 4 days now shows "120 USD days" instead of "120 USD mcday"
Grouped number regex now uses lookahead instead of word boundary at the
end, so "1 000m" correctly groups to "1000m" when a unit follows without
a space.
$30 * 4 days now returns "120 USD" instead of "120 USD days".
When multiplying currency by time, the result is treated as an implicit
rate and the time unit is removed from the output.