Commit Graph
3 Commits
Author SHA1 Message Date
Antonio PallaresandCursor e32933780b Arithmetic: drop divide-/modulo-by-zero short-circuit
The previous implementation intercepted `n / 0` and `n % 0` and returned
`.null`, on the rationale that `null` is friendlier for rule authors and
matches the engine's "missing value" convention. That's the last
remaining intentional deviation from `json-logic-js` in this file —
remove it for full spec parity.

`json-logic-js` delegates `/` and `%` to native JS arithmetic, which
follows IEEE 754: `n / 0` is `±Infinity` (sign matches the dividend),
`0 / 0` is `NaN`, and any `n % 0` is `NaN`. Swift's `Double` operators
and `truncatingRemainder(dividingBy:)` already produce these IEEE
values, so the fix is just dropping the divisor==0 short-circuit and
wrapping the result in `.float`.

Tests:
- Replace `testDivByZeroReturnsNull` with `testDivByZeroFollowsIeee754`
  pinning all three cases (`+Infinity`, `-Infinity`, `NaN`).
- Replace `testModByZeroReturnsNull` with `testModByZeroIsNan` covering
  `7 % 0` and `0 % 0`.
- Update the `1 / null` / `1 % null` cases inside
  `testSubDivAndModUseToNumberPerSpec` (divisor coerces to 0 →
  `+Infinity` / `NaN` instead of `.null`).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 10:27:32 +02:00
Antonio PallaresandCursor a1ad0bce09 Arithmetic: match json-logic-js coercion exactly
`json-logic-js` is asymmetric about which JS coercion arithmetic uses:
`+` and `*` go through `parseFloat(value)` (stringify, parse longest
numeric prefix); `-`, `/`, `%` go through native arithmetic which calls
`Number(value)` (a.k.a. `ToNumber`). The prior implementation routed
every operand through `Value.asNumber` (a partial `ToNumber`), which
diverged from the spec in two directions:

- For `+` / `*`: `null`, bools, the empty string, and `[1]` produced
  numbers instead of `NaN`.
- For `-` / `/` / `%`: arrays produced `NaN` instead of being coerced
  via `ToPrimitive("number")` → `toString` → recurse, so `[] - 1` and
  `[1] - 1` were `NaN` instead of `-1` and `0`.

Add `jsString(value)` (top-level JS `String()`) and `jsParseFloat(value)`
to `Value.swift`, extend `Value.asNumber` to coerce arrays/objects via
`jsString`, and route `+` / `*` through `jsParseFloat` while leaving
`-` / `/` / `%` on `asNumber ?? .nan`. The existing
divide-/modulo-by-zero short-circuit (`.null` instead of `±Infinity` /
`NaN`) is the only remaining intentional deviation in this file and is
documented as such.

Tests:
- Replace `testNullOperandIsTreatedAsZero` with two coercion-path tests
  that pin both branches against the spec, including the array cases
  (`[1] + 1 = 2`, `[1,2] + 0 = 1`, `[] - 1 = -1`, `[1] - 1 = 0`,
  `[1,2] - 0 = NaN`).
- Update `testAddOneArgActsAsNumericCast` and split
  `testAddCoercesStringsAndBools` into `testAddCoercesNumericStrings`
  to reflect that bools no longer bridge through `+` / `*`
  (`parseFloat("true") === NaN`) but trailing junk in numeric strings
  now parses (`parseFloat("3.14abc") === 3.14`).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 10:16:36 +02:00
Antonio Pallares ab6533db24 Merge remote-tracking branch 'origin/pallares/json-logic-evaluator' into pallares/json-logic-arithmetic-operators 2026-05-21 17:53:10 +02:00