# Mini arithmetic language (tickets 15, 16, and 17)

A small language written entirely in ASPL. It reads one program from a
file, tokenizes and parses it, executes it with integer arithmetic,
named variables, and control flow, reports source errors with scalar
line/column positions, and generates runnable ASPL source with matching
output. Ticket 15 delivered expression interpretation; ticket 16 extended
this same program with variables and control flow; ticket 17 extends it
further with ASPL source generation.

## Files

- `mini-arith.aspl` — the single-file implementation: tokenizer, parser,
  syntax-node storage, variable table, interpreter, ASPL source
  generator, and the file/operation driver.
- `calc.txt` — valid expression sample: `(2 + 3) * 4 - 10 / 2`,
  evaluates to `15` (ticket 15 form, still accepted).
- `calc-error.txt` — malformed expression: `2 + * 3`, fails at line 1,
  column 5.
- `calc-unicode.txt` — mixed Unicode sample: an Arabic-letter identifier
  followed by an emoji, fails with `invalid-character` at line 1, column 6
  (scalar counting; a byte count would report column 11).
- `calc-name.txt` — identifier sample: `7 + متغير`, fails with
  `syntax-error` at line 1, column 5. A bare expression keeps ticket 15
  behavior: identifiers are tokenized with their spelling kept but are
  rejected in expression position.
- `demo.txt` — statement demonstration: declares `i`, `مجموع`, and
  `limit`, accumulates 1 through 10 in a `while` loop, selects the `if`
  branch on `مجموع == 55`, and prints `55`.
- `branch.txt` — else-branch sample using `.` terminators: `x = 3` takes
  the `else` side and prints `2`, so the `then` side never executes.
- `undeclared.txt` — variable error: assigns `y` with only `x` declared,
  fails with `undeclared-variable` at line 2, column 1, naming `y`.
- `duplicate.txt` — variable error: declares `x` twice, fails with
  `duplicate-variable` at line 1, column 8, naming `x`.
- `badstmt.txt` — malformed statement: `x = ;` fails with `syntax-error`
  at line 2, column 5.

## Grammar

```text
program     := expression | var_section? statement+
expression  := term (("+" | "-") term)*
term        := factor (("*" | "/" | "%") factor)*
factor      := "-" factor | primary
primary     := number | variable | "(" expression ")"
var_section := ("var" name ("," name)* (";" | "."))+
statement   := assignment | print_statement | if_statement | while_statement
assignment  := name "=" expression (";" | ".")
print_statement := "print" expression (";" | ".")
if_statement    := "if" "(" condition ")" "{" statement* "}"
                   ("else" "{" statement* "}")?
while_statement := "while" "(" condition ")" "{" statement* "}"
condition   := expression ("==" | "!=" | "<" | "<=" | ">" | ">=") expression
```

A program whose first token is an identifier parses as the statement
form; anything else parses as the ticket 15 bare expression. A bare
expression prints its value directly, while the statement form prints
only through `print` statements.

Precedence, highest first: parentheses, unary minus, `*` `/` `%`, then
`+` `-`. Operators on one level associate to the left. Comparisons take
two integer expressions and produce a boolean used only for the branch
or loop decision; they cannot be nested or used inside `print`.
Numbers are ASCII digit runs parsed with `parse_i64`, so the full signed
64-bit range works and overflow reports `number-out-of-range`.
Whitespace (space, tab, carriage return, line feed) separates tokens;
anything else outside a number or identifier is `invalid-character`.

Division truncates toward zero and `%` takes the sign of its left side,
exactly like ASPL. Arithmetic uses ASPL's own checked operations, so
overflow and division by zero trap exactly as they do in an ASPL program
(`integer-overflow`, `integer-zero-divisor`) instead of becoming
recoverable values.

## Variables and control flow

- Declarations live only in the initial program-wide `var` section, one
  or more `var` lines before any statement. There are no block scopes
  and no declarations inside loop bodies; a `var` line anywhere else is
  a `syntax-error`.
- Every declared variable is visible to all later statements and starts
  at `0`. Later statements assign declared variables with `=`.
- Reserved words and never variable names: `var`, `print`, `if`,
  `else`, `while`.
- Both `;` and `.` terminate simple statements, as in ASPL.
- `if` runs the `then` block when the condition holds and the `else`
  block otherwise (`else` may be absent, in which case nothing runs).
  `while` repeats its body until the condition is false, using ASPL's
  own loops, so a runaway example stays stoppable through the existing
  execution controls (Stop, cancellation, and time limits).

## Identifier alphabet

The first scalar is an ASCII letter, `_`, or an Arabic letter from
U+0627 through U+064A. Later scalars may also be ASCII digits. This is
a small documented Arabic-letter subset on purpose; it does not claim
ASPL's full Unicode identifier profile. `demo.txt` declares `مجموع`
and the variable errors quote the offending spelling with its source
bytes preserved.

## Running it

The program asks for two lines on standard input: the source file name,
then the operation (`interpret` or `generate`). Generation asks for a
third line: the output file name.

```sh
aspl run docs/examples/mini-arith/mini-arith.aspl
# File: docs/examples/mini-arith/demo.txt
# Operation: interpret
# File: Operation: 55
```

Generate runnable ASPL, then check, run, and build it with the ordinary
toolchain (no compiler runs inside the example itself):

```sh
aspl run docs/examples/mini-arith/mini-arith.aspl
# File: docs/examples/mini-arith/demo.txt
# Operation: generate
# Output: /tmp/demo-generated.aspl
# File: Operation: Output:
aspl check /tmp/demo-generated.aspl
aspl run /tmp/demo-generated.aspl
# 55
aspl build -o /tmp/demo-generated /tmp/demo-generated.aspl
/tmp/demo-generated
# 55
```

The generated program declares each mini-language variable as an indexed
`m_var_N: i64` binding (by declaration order), so Arabic names, ASPL
reserved words, and collisions with generated temporaries keep their
meaning. Its `start_main` takes no arguments, returns `0`, prints
integers through `format_i64`, and reproduces the interpreter's output
for the same input, including the bare-expression form.

Build a generated native program the usual way; it behaves identically:

```sh
aspl build -o mini-arith docs/examples/mini-arith/mini-arith.aspl
printf 'docs/examples/mini-arith/demo.txt\ninterpret\n' | ./mini-arith
```

In the browser playground, import `demo.txt` through the file controls,
paste `mini-arith.aspl` into the editor, run it, answer `demo.txt` and
`interpret` at the two input prompts, and expect
`File: Operation: 55`. For generation, answer `demo.txt`, `generate`,
and an output name such as `generated.aspl` at the three prompts; the
generated bytes appear as a virtual file available through the
established download controls. The browser does not compile the
generated text within the same run.

## Diagnostics

Handled failures print one line and exit with status 0:

```text
error: <code>                  # file and operation failures, no position
error: <code> <line>:<column>         # source failures, 1-based scalar position
error: <code> <line>:<column> <name>  # variable failures, with source spelling
```

Stable codes: `not-found`, `invalid-utf8`, `not-regular-file`,
`file-too-large`, `permission-denied`, `invalid-path`, `io-error`
(forwarded from `read_text_file`); the same write-side codes are
forwarded from `write_text_file` when generation cannot publish its
output, and a failed write preserves the previous destination;
`invalid-character`, `invalid-number`,
`number-out-of-range`, `syntax-error` (source failures, with positions);
`duplicate-variable`, `undeclared-variable` (variable failures, with
positions and the offending spelling); `unknown-operation` (anything but
`interpret` or `generate`). A malformed source fails before any output
file is published.

## Implementation notes

- Tokens keep kind, original spelling, and scalar position in `Token`
  records inside a typed `list<Token>`; syntax nodes live in a
  `list<Node>` and link children by integer index (`-1` means none).
  Expression nodes reuse the operator codes, comparisons reuse the
  `==`/`!=`/`<`/`<=`/`>`/`>=` token codes, variable loads carry the
  source spelling in the node `name` field, and statements (assignment,
  print, if/else, while) chain through the node `right` link with branch
  heads in `left`/`extra`/`number`.
- The variable table is a `list<Binding>` of name/value records with
  linear lookup by exact byte equality. Parser functions take
  `ParserState` (tokens, position, nodes, declared names, statement-mode
  flag) by value and return updated state through `result<ParseStep>`
  and `result<ParserState>`; execution threads the binding list through
  `exec_list`/`exec_one` return values. There are no hash maps, no
  recursive record types, and no shared mutable parameter aliases.
- Every `::value` access is guarded by `::ok`; every `::error` access
  sits on the failure branch. Numbers are formatted only with
  `format_i64`.
- The generator walks the checked syntax-node list, emits one indexed
  `m_var_N` per declared name, parenthesizes every expression and
  condition, renders mini-language `==` as ASPL `matches`, and prints
  integers as `print(format_i64(...))` plus `print("\n")`. Fragments
  accumulate in a typed `list<string>` joined once with `join`, numbers
  format through `format_i64`, and the file is written through
  `write_text_file` only after successful parsing.
