# Operator Expression Tests

Empirically confirmed test results against the Architect runtime.
These results are the authoritative source for the operator precedence table and
type rules documented in the language specification.

---

## Type ranges

| Type | Minimum | Maximum | Notes |
|---|---|---|---|
| `SHORT` | −32,768 | 32,767 | 16-bit signed integer |
| `INTEGER` | −2,147,483,648 | 2,147,483,647 | 32-bit signed integer; synonym for LONG |
| `LONG` | −2,147,483,648 | 2,147,483,647 | 32-bit signed integer; synonym for INTEGER |
| `BIGINT` | −9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 | 64-bit signed integer |
| `DOUBLE` | −1.79769313486232E+308 | 1.79769313486232E+308 | 64-bit IEEE 754 double |
| `DATE` | 00/00/0000 | 31/12/9998 | DD/MM/YYYY format |
| `TIME` | 00:00:00:00 | 29:59:59:99 | HH:MM:SS:CC format (CC = centiseconds) |
| `STRING` | — | — | Arbitrary text |
| `BOOLEAN` | — | — | `TRUE` or `FALSE` |

---

## Operator precedence

| Expression | Result | Confirms |
|---|---|---|
| `2 * 3 ** 2` | `18` | `**` binds tighter than `*` |
| `2 ** 3 ** 2` | `64` | `**` is left-associative (`(2**3)**2 = 8**2 = 64`, not `2**(3**2) = 512`) |
| `-2 ** 2` | `4` | Unary `-` binds tighter than `**` (`(-2)**2 = 4`, not `-(2**2) = -4`) |
| `3 + 4 MOD 3` | `4` | `MOD` binds tighter than `+` (`3 + (4 MOD 3) = 3 + 1 = 4`) |
| `5 / 2` | `2` | `/` is integer division (truncates toward zero) |
| `-5 / 2` | `−2` | Integer division truncates toward zero (not floor) |
| `TRUE OR FALSE AND FALSE` | `TRUE` | `AND` binds tighter than `OR` (`TRUE OR (FALSE AND FALSE) = TRUE`) |
| `NOT TRUE AND FALSE` | `FALSE` | `NOT` binds tighter than `AND` (`(NOT TRUE) AND FALSE = FALSE`) |

---

## Type rules

| Expression | Assignment target | Result | Confirms |
|---|---|---|---|
| `2 * 2 * 2` | `VAR sX : SHORT` | `8` | `*` with integer operands is SHORT-compatible |
| `2 ** 3` | `VAR sX : SHORT` | Type mismatch | `**` always returns DOUBLE regardless of operand types |
| `2.5 ** 2` | — | `6.25` | DOUBLE operands with `**` return DOUBLE |
| `1 + "item"` | — | `Invalid type of Operand` | Mixed numeric + STRING with `+` is a runtime error |
| `"item" + 1` | — | `Invalid type of Operand` | Mixed STRING + numeric with `+` is a runtime error |
| `-9 MOD 5` | — | `−4` | `MOD` result carries the sign of the dividend |
| `sX = lY` (SHORT vs LONG) | `VAR bResult : BOOLEAN` | valid (`TRUE` possible) | Cross-type numeric comparisons are valid; types are widened |
| `dX + 1` | — | Type error | DATE + numeric is not supported |
| `01/01/2000 + 1` | — | Type error | DATE literal + numeric is not supported |
| `01 / 01 / 2000 + 1` | `1` | `1` | Spaces around `/` → parsed as integer divisions: `((1/1)/2000)+1 = 0+1 = 1` |
| `01/01/0001 + 01/01/0001` | `02/02/0002` | `02/02/0002` | DATE + DATE is valid; components are added with calendar-aware rollover |
| `28/03/2026 + 30/00/0000` | `27/04/2026` | `27/04/2026` | Second DATE acts as a duration offset; `00` month/year components are zero — March 28 + 30 days = April 27 |
| `29/03/2026 - 30/00/0000` | `26/02/2026` | `26/02/2026` | DATE - DATE is valid; right-hand DATE is subtracted as a duration offset |
| `01/01/0001 - 01/01/0001` | `30/11/6552` | `30/11/6552` | **Suspected runtime bug** — underflow wraps to a large date; do not rely on `DATE - DATE` producing `00/00/0000` when operands are equal |
| `01/01/2000 * 01/01/2000` | — | `invalid operand` | `*` is not valid for DATE operands |
| `01/01/2000 / 01/01/2000` | — | `invalid operand` | `/` is not valid for DATE operands |

---

## TIME literals and arithmetic

| Expression | Result | Confirms |
|---|---|---|
| `12:30:00:00` | `12:30:00:00` | `HH:MM:SS:CC` (no spaces) is a valid TIME literal |
| `12:3:0:0` | `12:03:00:00` | Single-digit components are valid; runtime zero-pads on display |
| `12:30::00` | Error | All four components are required; empty component is invalid |
| `12 : 30:00:00` | Error | Any space around a colon invalidates the literal |
| `tX = 12:30:00:00` then `tX += 0:30:0:0` | `13:00:00:00` | TIME + TIME arithmetic works; 60 minutes carries into hours |
| `TIME - TIME` | valid | Subtraction follows the same duration-offset pattern as addition |
| `12:90:00:00` | `12:90:00:00` | Out-of-range but 2-digit components are accepted — no semantic range validation |
| `12:90:00:00 + 0:1:0:0` | `13:31:00:00` | Arithmetic normalises: 90+1=91 min → 1hr 31min carry |
| `12:100:100:00` | Error | 3-digit components are rejected — each component must be 1–2 digits (0–99) |
| `99:00:00:00 + 0:1:0:0` | `03:01:00:00` | Hours wrap at 24 on overflow (`99 mod 24 = 3`); analogous to DATE underflow bug |

---

## Notes

**DATE literal syntax:** `DD/MM/YYYY` with no spaces around the slashes is a DATE literal.
`DD / MM / YYYY` with spaces is parsed as three integer division operations.
The lexer disambiguates based on the presence or absence of surrounding whitespace.

**TIME literal syntax:** `HH:MM:SS:CC` with no spaces around any colon is a TIME literal.
All four components are required. Single-digit components are valid (`12:3:0:0` = `12:03:00:00`).
Any space around a colon produces an error — unlike DATE where spaces fall through to integer
division, a spaced TIME literal has no meaningful arithmetic interpretation (`:` is not an
arithmetic operator). Each component must be 1–2 digits (0–99); 3-digit components produce an error (`12:100:100:00`
errors). Semantic range is not validated — `12:90:00:00` is accepted as-is; arithmetic
normalises via carry (`12:90:00:00 + 0:1:0:0 = 13:31:00:00`).

**TIME arithmetic:** `TIME + TIME` and `TIME - TIME` are valid and return TIME, following the
same duration-offset pattern as DATE. `tX += 0:30:0:0` carries correctly across component
boundaries (60 minutes → 1 hour). Hours wrap at 24 on overflow (`99:00:00:00 + 0:1:0:0 = 03:01:00:00`).

**Out-of-range TIME literals:** Accepting values like `12:90:00:00` and arithmetic overflow/underflow
are edge cases unlikely to appear in real Architect programs. A future diagnostic warning for
out-of-range components (> 59 for minutes/seconds/centiseconds, > 23 for hours) may be worth
adding but is not a current priority.

**DATE arithmetic:** `DATE + DATE` and `DATE - DATE` are valid and return DATE. The right-hand
DATE acts as a duration offset — components are added/subtracted with calendar-aware rollover.
`00/00/0000` is the zero-duration identity value. `DATE + numeric` and `DATE - numeric` are
type errors. `DATE * DATE` and `DATE / DATE` produce an "invalid operand" error.

**Known runtime bug:** `DATE - DATE` where the result underflows (e.g. `01/01/0001 - 01/01/0001`)
wraps to a large date (`30/11/6552`) rather than producing `00/00/0000` or an error. Do not
rely on date subtraction when the result may be at or below the minimum date.

**MOD sign behaviour:** The result carries the sign of the dividend (truncated division semantics),
consistent with `-5 / 2 = -2` (not floor division).
