Language Reference
Cryn 0.3.0. The definition of every word of the language is the table below. Supplementary tables follow for types, literals, conditions, open modes, defined behavior, limits, and diagnostics.
Ground rules, briefly: a program is one file of world cells and transformations. Whitespace has no meaning. Comments are [# ... #]. Names are letters, digits, and underscores, starting with a letter or underscore, case sensitive; every word in the tables below is reserved. Execution starts at main, which takes no parameters. Every program can be written on one line.
The language
Legend: wv is the working value. "stack" is the value stack. "focus" is the focused cell. Read the "needs" column as a compile time requirement unless the faults column says runtime.
| Group | Word | Forms | Needs | Does | wv after | Faults |
|---|---|---|---|---|---|---|
| navigation | move | move name | a cell name in frame or world | focuses name, resets focus history | unchanged | compile: unknown cell |
| navigation | move | move n | focus on array, n in range | focuses element n | unchanged | compile: not an array, index out of range, negative or over 2^31-1 |
| navigation | move | move index | focus on array, wv an integer | focuses element number wv | unchanged | compile: not an array, wv not integer. runtime: index out of bounds |
| navigation | move | move at | wv an address | focuses the cell at that address | unchanged | compile: wv not an address |
| navigation | back | back | focus deeper than frame root | undoes one relative move | unchanged | compile: already at root |
| navigation | home | home | nothing | focus to frame root, history cleared | unchanged | never |
| addresses | address | address | focus not on frame root | wv becomes address of focused cell | address of cell type | compile: frame root |
| addresses | address | address name | a cell or transformation name | wv becomes its address, or a transform reference | address type, or transform | compile: no such name |
| addresses | address | (arithmetic) add n, subtract n, or stack forms | wv an address and other operand an integer, or both addresses for subtract | moves address by n elements, or gives element distance | address, or int64 for address minus address | compile: other operations on addresses, mixed operands |
| cells | make | make <type> [name] | a scalar type | named: zeroes the frame cell. unnamed: allocates one zeroed heap cell | unnamed: address of the cell | compile: unsized array cell |
| cells | make | make array n of <type> [name] | n at least 1 | named: zeroes the array cell. unnamed: allocates on heap | unnamed: address of array | compile: bad size or element |
| cells | make | make string "text" [name] | string on one line | named: fills the cell with text plus a zero byte. unnamed: uses a static writable byte array | unnamed: address of bytes | never |
| cells | make | make <size> of <type> [name] | size: literal or integer cell name; element a value type | allocates a zeroed heap block of size elements; optional trailing name binds the address into a cell | address of unsized array of element | compile: array or transform element. runtime: negative or oversized count, out of memory |
| cells | make | top level make ... | outside all transformations | creates a world cell, zeroed, or string filled | n/a | compile: duplicate name, name collides with transformation, unsized array |
| cells | free | free | wv an address of a heap block | releases the block | unchanged | compile: wv not an address. runtime: not a heap block, null address |
| cells | read | read | focus on a single cell | wv becomes the cell value, with the cell type | the cell type | compile: frame root or array in focus |
| cells | write | write | focus on a single cell, wv same type, or integer literal that fits, or float64 into float32 | stores wv into the cell | unchanged | compile: frame root or array in focus, type mismatch |
| wv | set | set <literal> | any literal, or set nothing | wv becomes that literal | the literal type, or nothing | compile: no literal given |
| wv | add | add [literal] | numeric operands, matching types | wv = wv + other (literal or popped) | the pair type | compile: non numeric, mixed types needing convert, literal out of range. runtime: division class errors do not apply |
| wv | subtract | subtract [literal] | same as add | wv = wv - other | the pair type | as add |
| wv | multiply | multiply [literal] | same as add | wv = wv * other | the pair type | as add |
| wv | divide | divide [literal] | same as add | wv = wv / other, cut toward zero | the pair type | compile: constant zero divisor. runtime: division by zero |
| wv | remainder | remainder [literal] | same as add | wv = remainder of wv / other, sign of wv | the pair type | compile: constant zero divisor. runtime: remainder by zero |
| wv | negate | negate | wv a signed integer or float | wv = -wv | same type | compile: unsigned or non numeric |
| stack | push | push | nothing | puts wv on the stack | unchanged | compile: stack deeper than 4096 |
| stack | pop | pop | non empty stack | wv becomes the top stack value | that slot's type | compile: empty stack |
| stack | swap | swap | non empty stack | exchanges wv with the stack top | old top | compile: empty stack |
| comparison | compare | compare [literal] | numeric or char or bool wv, or address against address or nonnegative literal; other from literal or stack | remembers the three way answer | unchanged | compile: empty stack, incompatible operands |
| conversion | convert | convert <type> | scalar wv, scalar target | changes wv type, wrapping integers, saturating floats, reusing bits for address pairs | target type | compile: arrays, transform refs, nothing, float to address or back |
| control | jump | jump label | a label in this transformation | continues at the label | unchanged at the label, joined | compile: unknown label |
| control | jump | jump label when cond | label plus a valid comparison on every path | jumps only when the remembered comparison matches cond | joined at the label | compile: unknown label, no comparison in reach |
| control | call | call name | arguments pushed left to right, matching types | runs the transformation, pops the arguments | its result, or unchanged if result is nothing | compile: wrong argument count or types, unknown name |
| control | call | call | wv a transform reference | runs it if it takes no parameters | unchanged, result discarded | runtime: reference to a transformation with parameters |
| control | return | return | empty stack, one result type | ends the transformation, wv is the result | n/a | compile: stack not empty, result type disagrees with other returns or the declaration |
| control | stop | stop | nothing | ends the program, exit code wv and 0xFF | n/a | never |
| io | input | input | nothing | reads one byte from the current input stream | int64, or -1 at end | never |
| io | output | output | numeric, bool, char, or address wv | prints wv to the current output stream, no separator | unchanged | compile: nothing, array, transform |
| io | output | output text | wv address to bytes or byte array | prints the bytes at wv up to the first zero | unchanged | compile: wv not a byte address |
| files | open | open read | wv address to a zero ended path | opens for reading, wv becomes the handle | int64 handle, 1 or more, or -1 | runtime: none, failure gives -1 |
| files | open | open write | as above | opens for writing, truncates | int64 handle or -1 | as above |
| files | open | open both | as above | opens for reading and writing, truncates | int64 handle or -1 | as above |
| files | open | open append | as above | opens for writing at the end | int64 handle or -1 | as above |
| files | use | use | wv an integer handle | routes input from the readable side, output to the writable side; unsupported sides keep their stream | unchanged | runtime: handle not open in this context |
| files | use | use n | n a nonnegative literal | same, with handle n; use 0 selects keyboard and terminal | unchanged | compile: negative literal. runtime: handle not open |
| files | flush | flush | nothing | pushes buffered bytes of the output stream out | unchanged | runtime: flush failure |
| files | seek | seek [n] | an open handle routed with use; n a nonnegative literal or an integer wv | positions the handle's streams at byte offset n (or wv) from the start; both sides of an open both stream are one file and move together | int64: 0 success, -1 failure | compile: negative literal, wv not an integer. runtime: no current stream belongs to a handle |
| files | close | close | wv an integer handle | closes it, streams routed through it return to standard | int64, 0 success, -1 close error | runtime: handle not open, closing 0 |
| external | system | system | wv address to a zero ended command | runs it with the system shell | int64 status from the host | never |
| concurrency | spawn | spawn name | a zero parameter transformation | starts a thread running it in a fresh context | unchanged | compile: parameters, unknown name. runtime: more than 256 outstanding |
| concurrency | wait | wait | nothing | joins every context this one spawned | int64 0 | never |
| atomics | exchange | exchange | focus a single cell of at most 8 bytes; wv exactly the cell type, or an integer literal that fits, or float64 into float32 | swaps wv with the cell in one indivisible acquire release step | the cell type | compile: frame root, array, nothing, transform cell, type mismatch |
| program | define | define name {type param}... [result type] | unique name | declares a transformation; the body runs until end | n/a | compile: duplicate, array parameter or result |
| program | end | end | inside a define | closes the transformation | n/a | compile: stray end |
| program | mark | mark label | unique label in this transformation | places a jump target | unchanged at the label, joined | compile: duplicate or unreachable label |
Conditions for jump ... when: less, equal, greater, different, less-or-equal, greater-or-equal. They read the answer left by compare, which compares wv with the other value.
Types
| Type word | Size | Values |
|---|---|---|
nothing | 0 | no value |
bool | 1 | false, true |
char | 1 | one byte, 0 to 255 |
int8 int16 int32 int64 | 1, 2, 4, 8 | signed whole numbers |
uint8 uint16 uint32 uint64 | 1, 2, 4, 8 | unsigned whole numbers |
float32 float64 | 4, 8 | IEEE 754 floats |
address <type> | 8 | location of one <type>, or of nothing for a bare address |
array n of <type> | n times element | fixed row of cells; array of <type> without n only as a pointee of an address, meaning a runtime sized block |
transform | 16 | a transformation reference for indirect call |
Integer literal typing: a plain literal like 5 keeps its exact value until it meets a concrete type, then widens if it fits. Two plain literals together give int64. Joining a literal with a float type is rejected; materialize with set or convert first.
Literals
| Form | Examples | Notes |
|---|---|---|
| integer | 0, 42, -7 | decimal, magnitude up to 2^64-1 |
| hexadecimal | 0x1F, 0xFFff | no negative hex |
| float | 2.5, 0.5e-2, 1e3 | digit required both sides of the dot |
| character | 'a', '\n', '\'' | exactly one character or escape |
| string | "hi\n" | one line, zero ended when stored |
| boolean | true, false |
Escapes in characters and strings: \n \t \r \0 \\ \' \".
Open modes
| Mode | Opens as | Reading | Writing | Position |
|---|---|---|---|---|
read | "rb" | yes | no | start |
write | "wb" | no | yes | start, content discarded |
both | "w+b" | yes | yes | start, content discarded |
append | "ab" | no | yes | end, content kept |
Handle numbers run 1 to 256 per context, are plain integers, and a closed handle's number may be handed out again. Handle 0 is the standard keyboard and terminal. Spawned contexts have their own tables and cannot use the parent's handles. seek is the positioning command: it works on the current streams of one context and only when those streams belong to an open handle. Standard streams cannot be positioned.
Defined behavior
| Situation | Definition |
|---|---|
| integer overflow | wraps modulo the width |
| division or remainder by zero | runtime abort |
| minimum integer divided by -1 | wraps to itself, no abort |
| float division by zero | IEEE infinity or NaN |
| float remainder | C fmod semantics |
| float to integer conversion | saturates at the target range |
| integer conversions | keep the low bits |
| indexing out of range | runtime abort naming index and size |
| freeing, or bounds checking through, a stale address (already freed, shifted into a block, or never a block) | runtime abort; never an undefined read |
| freeing a non heap address | runtime abort |
| racing load or store, cell of 8 bytes or less | yields some value of the cell's type, never a torn mix |
| racing access to 16 byte transform cells | undefined |
| ordering between threads | only wait and exchange |
mixing reads and writes on one open both file | position with seek between direction changes; without it behavior follows the host C library |
exchange | indivisible, acquire release, never tears |
| exit status | stop value and 0xFF, 0 at normal end of main, 1 on runtime abort |
Output formats
| Type | Printed as |
|---|---|
| signed integers | -5 |
| unsigned integers | 18446744073709551615 |
| floats | shortest digits that round trip, 2.75, nan |
bool | true, false |
char, uint8 | one raw byte |
address | 0x7ffd00 style hexadecimal |
| integer literal | signed if it fits int64, else unsigned |
Limits
| Limit | Value |
|---|---|
| focus history per context | 1024 entries |
| labels per transformation | 4096 |
| cells per transformation | 4096 |
| value stack depth proved by the checker | 4096 |
| open files per context | 256 |
| outstanding spawned contexts per context | 256 |
one make <size> of allocation | 16 TiB (2^44 bytes), overflow safe |
| distinct types in one program | memory bound |
Diagnostics
Compile errors print file:line:col: error: message and list every problem found. Runtime aborts print cryn: runtime error: message and exit with code 1.