vextoly/cryn
Delete LANGUAGE.md
Commit 2639d3f · Vextoly · 2026-10-04T15:40:04+02:00
Comments
No comments yet.
Diff
diff --git a/LANGUAGE.md b/LANGUAGE.md
deleted file mode 100644
index 26e9950..0000000
--- a/LANGUAGE.md
+++ /dev/null
@@ -1,439 +0,0 @@
-# The Cryn Programming Language Reference
-
-This document defines Cryn precisely enough for an independent
-implementation. It describes the language implemented by the `cryn`
-compiler (bootstrap version).
-
-Cryn is a compiled, statically typed systems language. Its execution
-model is a small abstract machine consisting of **cells**, named and
-indexed **connections**, a **focus**, a **working value**, a **value
-stack**, and a **comparison state** inside each **execution context**,
-plus a shared **world** of cells.
-
----
-
-## 1. Lexical rules
-
-### 1.1 Source encoding
-
-Source files are byte streams. Tokens are formed from ASCII words,
-digits, and the delimiters below. Non-ASCII bytes may appear only inside
-string and character literals and comments.
-
-### 1.2 Whitespace
-
-Space, tab, carriage return, and newline separate tokens and have **no
-semantic meaning**. Any two programs with the same token sequence are the
-same program, regardless of layout.
-
-### 1.3 Comments
-
-A comment starts with the exact sequence `[#` and ends with the exact
-sequence `#]`, possibly spanning lines. Comments nest nothing, produce no
-tokens, and are the only comment syntax. An unterminated comment is a
-compile error. `#` or `]` alone are not comment delimiters; outside
-strings they are invalid characters.
-
-### 1.4 Identifiers and keywords
-
-An identifier is `[A-Za-z_][A-Za-z0-9_]*`. Identifiers containing hyphens
-do not exist; hyphens appear only in the two condition keywords
-`less-or-equal` and `greater-or-equal`.
-
-All of the following are reserved words and cannot be identifiers:
-
-* commands: `move back home address make free read write set add subtract
- multiply divide remainder negate push pop swap compare convert jump call
- return stop input output open close system spawn wait define end mark`
-* type words: `nothing bool char int8 int16 int32 int64 uint8 uint16
- uint32 uint64 float32 float64 address array string transform`
-* value words: `true false`
-* argument words: `of when result index at text both less equal greater
- different less-or-equal greater-or-equal`
-
-### 1.5 Literals
-
-* **Integer:** decimal digits or `0x` hex digits, optionally preceded by
- `-`. Range: magnitude must fit in `uint64`; the value range of a use is
- checked contextually (see §7).
-* **Float:** `digits [. digits] [e[+|-]digits]`, optionally negated;
- IEEE 754 double precision.
-* **Character:** `'a'`, `'\n'`, `'\t'`, `'\r'`, `'\0'`, `'\\'`, `'\''`.
- A character literal denotes its byte value (0–255).
-* **String:** `"..."` with the same escapes; may contain any other byte.
- Strings are NUL-terminated byte arrays (§5.4); therefore string
- literals cannot contain `\0`.
-* **Boolean:** `true`, `false`. **Void:** `nothing`.
-
-### 1.6 Locations
-
-Diagnostics carry 1-based line and column positions.
-
-## 2. Program structure
-
-```text
-program := { define | worldmake }
-worldmake := "make" ( basetype name
- | "array" int "of" type name
- | "string" stringlit name )
-define := "define" name params [ "result" type ] { command } "end"
-params := { paramtype name }
-```
-
-A program is a set of transformations and an optional world model (shared
-cells created by top-level `make`). Exactly one transformation named
-`main` with zero parameters must exist; execution starts there.
-
-Parameter lists are `{ type name }` pairs. They end at the first token
-that cannot begin a parameter; `address` only opens a parameter when a
-pointee type follows it. This makes bodies unambiguous without
-punctuation.
-
-## 3. Types
-
-| Type | Meaning | Size |
-|-----------------|-------------------------------------------|------|
-| `nothing` | absence of a value | – |
-| `bool` | `false` / `true`, stored as 0/1 | 1 |
-| `char` | byte value 0–255 (codepoint in v1) | 4 |
-| `int8..int64` | two's-complement signed integers | 1/2/4/8 |
-| `uint8..uint64` | unsigned integers | 1/2/4/8 |
-| `float32/64` | IEEE 754 binary32/64 | 4/8 |
-| `address T` | address of a cell/memory of type `T`; `address` alone means `address nothing` | 8 |
-| `array N of T` | fixed array of N T (N ≥ 1) | N·sizeof(T) |
-| `array of T` | unsized array; only as the pointee of `address`, produced by dynamic `make` | element-sized |
-| `transform` | reference to a transformation | 16 (ref) |
-
-Arrays cannot be parameters or results; pass an address. Unsized arrays
-cannot be parameters, results, or cells; they exist only behind
-addresses.
-
-The static type system also has an internal *integer literal* type that
-carries a literal's value; it is not writable in source.
-
-### 3.1 Type equality
-
-Two types are equal iff constructed identically. Array sizes are part of
-the type.
-
-## 4. The machine
-
-### 4.1 World and cells
-
-A **cell** has identity, a type, and an optional value. Cells are:
-
-* **Frame cells**: a transformation's parameters and every named cell
- made with `make ... name` in its body. They belong to the frame
- (automatic storage): they are created zeroed each time the `make`
- executes and live until the frame returns. Taking their address is
- legal but the address is only valid during the frame's lifetime.
-* **World cells**: created by top-level `make`; zero-initialized at
- program start; shared by all execution contexts.
-* **Heap blocks**: created by dynamic `make` (§6.3); exist until `free`.
-
-Two cells with equal values remain distinct cells.
-
-### 4.2 Connections
-
-Cells connect to other cells by **key**: a name (frame and world cells;
-each frame connects to all world cells) or an integer index (arrays and
-blocks, indices `0..N-1`). `move` follows connections. Linked structures
-are built by storing address values in cells.
-
-### 4.3 Execution context
-
-Each running transformation invocation is an execution context holding:
-
-* a program counter,
-* the **focus**: a stack of cell references, bottom entry being the
- frame root,
-* the **working value** (one typed value; initially `nothing`),
-* the **value stack** (unbounded stack of typed values),
-* the **comparison state** (invalid until a `compare`),
-* the current input/output streams (stdin/stdout until `open`),
-* spawn state for `wait`.
-
-Contexts share world cells and heap blocks and nothing else. Focus,
-working value, value stack, and streams are per context.
-
-### 4.4 Focus
-
-The focus identifies the currently selected cell. Its abstract state is a
-stack:
-
-* `move name` **anchors**: the history is reset to `[frame, cell]`.
-* `move <index>`, `move index`, `move at` **push** the previous focus
- onto the history and focus the target.
-* `back` pops one relative entry (error if only `[frame]` remains — at
- runtime it restores the frame root focus when the history is empty at
- the current frame level is impossible; the checker guarantees it).
-* `home` resets to `[frame]`.
-
-`read` and `write` operate on the focused cell; `address` (no argument)
-yields its address.
-
-### 4.5 Working value
-
-One typed value. Set by `set`, `read`, `pop`, arithmetic, `input`,
-`open`, `close`, `system`, `wait`, calls (see §8), conversions. It is
-also the **return value** of a transformation: at `return` (or implicit
-return at `end`) the working value's type must be identical at every
-return point of the transformation; that type is the transformation's
-result type. A call to a transformation whose result is `nothing`
-preserves the caller's working value; any other result replaces it.
-
-Integer literals adapt: a literal working value may be written into,
-compared with, or passed to any integer/char/bool/float context it fits
-(or any float context) without `convert`; otherwise `convert` is required.
-
-### 4.6 Value stack
-
-Holds typed values for preservation across navigation and for argument
-passing. The checker tracks the stack shape at every program point; all
-paths joining at a label must have identical stack depth and slot types,
-the working value type must join (identical, or literal widening), and
-the focus stack must join. The stack must be empty at `return`.
-
-## 5. Values and their semantics
-
-### 5.1 Integers
-
-Two's complement; signed overflow wraps (defined); unsigned overflow
-wraps modulo 2^N.
-
-### 5.2 Division
-
-* Division or remainder by zero: the program aborts with a runtime
- diagnostic and exit status 1.
-* Signed `INT_MIN / -1` yields `INT_MIN`; `INT_MIN % -1` yields `0`
- (defined wrap).
-* Truncation is toward zero (C semantics).
-
-### 5.3 Floats
-
-IEEE 754 binary32/binary64 with round-to-nearest-even. Operations on
-`float32` operands compute in single precision. Division by zero yields
-infinities/NaN per IEEE. **NaN comparison:** for `compare`, NaN yields
-`equal` (and not `different`, `less`, or `greater`).
-
-### 5.4 Strings
-
-A string literal used with `make string` creates a byte array of
-length+1 with a terminating NUL. Strings have no other magic; see §7
-`output text` and §6.3 for address-based access.
-
-### 5.5 Addresses
-
-Addresses are 8-byte values. `address nothing` is the type of `null`
-(produce with `set 0; convert address`). `add`/`subtract` on
-`address T` with an integer scales by `sizeof(T)` (for `address array of
-T`, by `sizeof(T)`). `subtract` between two `address T` (same or any
-pointee in v1) yields the element distance as `int64`. Addresses compare
-as unsigned 64-bit integers. Converting between `int64`/`uint64` and any
-address type reinterprets bits.
-
-### 5.6 Conversions (`convert`)
-
-Between any scalar numeric/bool/char/address types, with these rules:
-
-* int → int / char: wrap modulo 2^N.
-* int/uint → float: round to nearest (IEEE).
-* float → int: truncate toward zero; if out of target range, **saturate
- at the target width** (`float32 1e30 → int32` gives `2147483647`).
-* float32 ↔ float64: round.
-* anything numeric → bool: 0 → `false`, else `true`.
-* char ↔ int: codepoint value.
-* address ↔ int64/uint64 / other address types: bit reinterpretation.
-
-Conversions to or from `nothing`, `transform`, or arrays are errors.
-
-## 6. Commands
-
-34 commands total.
-
-### 6.1 Navigation
-
-| Command | Effect |
-|---|---|
-| `move name` | Focus the frame's cell `name` (params, local cells, world cells). Anchors the focus history to `[frame, cell]`. |
-| `move i` | From an array/block focus, focus element `i` (literal). Compile-time bounds check. Pushes focus. |
-| `move index` | As above, using the working value as index (any integer type; interpreted as unsigned). Runtime bounds check. Working value unchanged. |
-| `move at` | Focus the cell at the address in the working value (`address T` → cell of `T`; `address array of T` → the block). Pushes focus. |
-| `back` | Undo the last relative move. |
-| `home` | Reset focus to `[frame]`. |
-
-### 6.2 Cells and addresses
-
-| Command | Effect |
-|---|---|
-| `make T name` | Create/zero frame cell `name` of type `T`. |
-| `make T` | Create an anonymous heap cell of `T`; working value := its address (`address T`). |
-| `make array N of T name` / `make array N of T` | Frame array / anonymous heap array. |
-| `make string "s" name` | Frame byte array with the bytes and NUL. |
-| `make string "s"` | Working value := address of a static NUL-terminated byte array. |
-| `make n of T` / `make 8 of T` / `make n of T p` | Heap block of n elements (n from an integer cell or literal); working value := `address array of T`; optional `p` additionally binds a frame cell of that type. |
-| `free` | Free the heap block whose address is in the working value. Aborts if the address is not a heap block. |
-| `read` | Working value := value of focused cell. |
-| `write` | Focused cell := working value (literal adaptation applies; else types must match exactly or `convert` first). |
-| `address` | Working value := `address T` of the focused cell. |
-| `address name` | Working value := address of frame/world cell `name`, or (for a transformation name) a `transform` reference to it. |
-
-Frame cells store their values directly; address cells store addresses.
-Writing through an address requires `move at` first.
-
-### 6.3 Working value operations
-
-| Command | Effect |
-|---|---|
-| `set lit` | Working value := literal. |
-| `add`/`subtract`/`multiply`/`divide`/`remainder` [`lit`] | With operand: combine working value with literal (adapted). Without: pop one value and combine. Operand types must match after adaptation (mixed width/sign pairs widen per §7). Address ± integer scales. |
-| `negate` | Two's-complement / float negation. |
-| `convert T` | See §5.6. |
-
-### 6.4 Value stack
-
-| Command | Effect |
-|---|---|
-| `push` | Copy the working value onto the stack. Working value unchanged. |
-| `pop` | Working value := popped value. |
-| `swap` | Exchange working value with the stack top. |
-
-### 6.5 Comparison and control flow
-
-| Command | Effect |
-|---|---|
-| `compare` [`lit`] | Compare working value with popped value (or literal). Sets comparison state to `less`, `equal`, or `greater`. |
-| `jump label` | Unconditional. |
-| `jump label when cond` | cond ∈ `less equal greater different less-or-equal greater-or-equal`. Requires a prior `compare`. |
-| `mark label` | Define label (transformation scoped). |
-| `call name` / `call` | See §8. |
-| `return` | End transformation; result = working value type. |
-| `stop` | End program; exit status = low 8 bits of the working value if numeric, else 0. |
-
-There is no `if`/`while`/`for`; conditional jumps over marks are the
-control flow. A conditional jump **may** jump backward or forward.
-Unreachable commands and labels with no live incoming edge are compile
-errors.
-
-### 6.6 External world
-
-| Command | Effect |
-|---|---|
-| `input` | Read one byte from the current input stream; working value := `int64` 0–255, or `-1` at end of input. |
-| `output` | Write the working value to the current output stream, formatted by its static type: signed ints decimal; unsigned decimal; `uint8` and `char` as one raw byte; `bool` as `true`/`false`; floats shortest-round-trip decimal; `address` as `0x`-hex. |
-| `output text` | Write the NUL-terminated byte string at the working value's address (pointee `uint8` or array of `uint8`). |
-| `open read`/`open write`/`open both` | Open the path (working value address, NUL-terminated) for reading / writing (create/truncate) / both. On success the handle (`int64`) becomes the working value and the context's input and/or output stream is routed through the file; negative means failure and streams are unchanged. |
-| `close` | Close the handle in the working value; restores default streams if they pointed at it. Working value := 0 or -1. |
-| `system` | Run the shell command (working value address, NUL-terminated); working value := exit status as `int64`. |
-
-### 6.7 Concurrency
-
-| Command | Effect |
-|---|---|
-| `spawn name` | Start a new execution context (OS thread) running transformation `name`, which must take zero parameters. The new context starts with working value `nothing`; communication happens through world cells and heap blocks. The spawner's working value is unchanged. Up to 64 outstanding spawns per context. |
-| `wait` | Join all contexts spawned by this one. Working value := `int64 0`. This is the only synchronization guarantee: all memory writes performed by spawned contexts are visible after `wait`. |
-
-Racy access without `wait` yields unspecified values of the cell's type
-(no tearing for aligned ≤ 8-byte cells).
-
-## 7. Arithmetic and comparison typing
-
-* integer literal + any numeric → the numeric type (literal must fit, or
- `convert` first; float targets always accept).
-* two integer literals → `int64`.
-* same type → that type.
-* integer types of different width/sign → the wider type; mixed
- signedness becomes signed of the wider width. `uint64` vs signed uses
- the signed type of width 64; explicit `convert` is recommended.
-* any integer + float → float (float32 only when both are float32).
-* `char` participates as an unsigned 32-bit value.
-* `bool` compares only with `bool` (and literals 0/1).
-* addresses compare with addresses (unsigned) or integer literals.
-
-## 8. Transformations, calls, references
-
-* `define name { type param } result T? ... end`
-* Arguments: push values in parameter order (first parameter deepest),
- then `call name` — the call pops the parameters. Callee working value
- starts as `nothing`.
-* Result: the callee's working value at return becomes the caller's
- working value unless the result type is `nothing` (then the caller's
- working value is preserved). Result types are inferred from the first
- syntactic `return`; all return points must agree; recursion requires an
- explicit `result <type>` declaration.
-* The value stack must be empty at every return.
-* Recursion, mutual recursion, and nested calls are ordinary C-stack
- recursion — one frame per call.
-* `address name-of-transformation` yields a `transform` reference value.
- `call` without a name calls the referenced transformation; the
- reference must denote a zero-parameter transformation (checked
- statically when known, at runtime otherwise). The result of an indirect
- call is `nothing` in v1; return data through world cells or memory.
- Transformations can be passed as arguments (type `transform`) —
- callbacks.
-
-## 9. Numeric summary
-
-| Situation | Behavior |
-|---|---|
-| signed overflow | wrap (two's complement) |
-| unsigned overflow | wrap modulo 2^N |
-| `/` or `%` by zero | runtime abort, exit 1 |
-| `INT_MIN / -1` | `INT_MIN` (wrap) |
-| float → int out of range | saturate at target width |
-| int → float | round to nearest |
-| index out of bounds | runtime abort, exit 1 |
-| freeing a non-heap address | runtime abort, exit 1 |
-| negative dynamic allocation size | runtime abort, exit 1 |
-
-## 10. Grammar (informal)
-
-```text
-command :=
- "move" ( name | int | "index" | "at" )
- | "back" | "home"
- | "address" [ name ]
- | "make" ( type [name]
- | "array" int "of" type [name]
- | "string" str [name]
- | (name|int) "of" type [name] )
- | "free" | "read" | "write"
- | "set" (int|float|char|true|false|nothing)
- | ("add"|"subtract"|"multiply"|"divide"|"remainder") [int|float]
- | "negate"
- | "push" | "pop" | "swap"
- | "compare" [int|float|char|true|false]
- | "convert" type
- | "jump" name [ "when" cond ]
- | "call" [name]
- | "return" | "stop"
- | "input" | "output" ["text"]
- | "open" ("read"|"write"|"both") | "close" | "system"
- | "spawn" name | "wait"
- | "mark" name
-
-type := basetype | "address" [type] | "array" [int] "of" type
-cond := "less"|"equal"|"greater"|"different"|"less-or-equal"|"greater-or-equal"
-```
-
-## 11. Errors
-
-Compile errors report file, line, column, and message; the first error
-stops compilation of a transformation. Runtime errors print
-`cryn: runtime error: <message>` to stderr and exit with status 1.
-Defined runtime errors: division by zero, remainder by zero, index out
-of bounds, free of a non-heap address, negative or oversized allocation,
-focus history overflow, spawn limit exceeded, indirect call of a
-parameterized transformation, thread creation failure, out of memory.
-
-## 12. Limits
-
-| Limit | Value |
-|---|---|
-| focus history | 256 per frame |
-| value stack depth (static) | 1024 |
-| cells per transformation | 256 |
-| labels per transformation | 256 |
-| outstanding spawns per context | 64 |
-| open files per context | 64 |
-| dynamic allocation | ≤ 2^40 bytes |