!~ATH INTERPRETER

an esoteric language where everything is about death
inspired by Homestuck's fictional ~ATH
CODE:
INPUT (stdin for HEED), newline separates inputs:
OUTPUT:
EXAMPLE PROGRAMS:
DOWNLOAD: Run !~ATH programs locally with the Python interpreter (includes process, connection, watcher entities and file I/O)

LANGUAGE REFERENCE (full spec)

!~ATH (pronounced "until death") is an esoteric programming language where all control flow is predicated on waiting for things to die. Inspired by the fictional ~ATH language from Homestuck. Everything is about death. Loops wait for entities to die. Computation happens in death callbacks. The language is deliberately inconvenient.

ENTITIES

Entities are mortal things that can be waited upon. Each entity is either alive or dead. Create entities with import:

import timer T(1s);        // dies after 1 second
import timer T2(500ms);    // dies after 500 milliseconds

import process P("cmd"); // dies when process exits [NOT IN BROWSER]
import connection C("host", 80); // dies when connection closes [NOT IN BROWSER]
import watcher W("file.txt"); // dies when file is deleted [NOT IN BROWSER]

THIS is an implicit entity representing the program itself. Kill entities manually with .DIE(). The program ends when THIS.DIE(); is called.

~ATH LOOPS

The fundamental control structure. Waits for an entity to die, then runs the EXECUTE clause:

import timer T(1s);
~ATH(T) {
} EXECUTE(UTTER("Timer died!"));
THIS.DIE();

Combine entities with && (both must die), || (either dies), or ! (dies immediately when created):

~ATH(T1 && T2) { } EXECUTE(...);  // wait for both
~ATH(T1 || T2) { } EXECUTE(...);  // wait for either
~ATH(!T) { } EXECUTE(...);        // runs immediately

BIFURCATION

Split execution into concurrent branches:

bifurcate THIS[LEFT, RIGHT];

~ATH(LEFT) {
    // code for left branch
} EXECUTE(VOID);

~ATH(RIGHT) {
    // code for right branch
} EXECUTE(VOID);

[LEFT, RIGHT].DIE();

VARIABLES

BIRTH x WITH 5;           // mutable variable
ENTOMB PI WITH 3.14159;   // constant (immutable)
x = x + 1;                // reassignment

DATA TYPES

42, -7                    // INTEGER
3.14, -0.5                // FLOAT
"hello\nworld"            // STRING (escapes: \\ \" \n \t)
ALIVE, DEAD               // BOOLEAN (truthy/falsy)
VOID                      // absence of value
[1, 2, 3]                 // ARRAY
{name: "Karkat", age: 6}  // MAP

OPERATORS

+ - * / %                     // arithmetic (/ is integer div for ints)
== != < > <= >=               // comparison
AND OR NOT                    // logical (short-circuit)
arr[0]  map["key"]  map.key   // indexing

CONTROL FLOW

SHOULD condition {
    // if truthy
} LEST {
    // else
}

No loops in the expression language. Use ~ATH with timers for iteration:

RITE countdown(n) {
    SHOULD n > 0 {
        UTTER(n);
        import timer T(1s);
        ~ATH(T) { } EXECUTE(countdown(n - 1));
    }
}
countdown(5);
THIS.DIE();

FUNCTIONS (RITES)

RITE add(a, b) {
    BEQUEATH a + b;       // return value
}
BIRTH sum WITH add(2, 3);

ERROR HANDLING

ATTEMPT {
    BIRTH x WITH PARSE_INT("bad");
} SALVAGE error {
    UTTER("Error: " + error);
}

CONDEMN "Something went wrong";  // throw error

BUILT-IN RITES

I/O:

UTTER("Hello", x);        // print (space-separated, newline appended)
BIRTH line WITH HEED();   // read line from input

SCRY("file.txt") // read file [NOT IN BROWSER]
INSCRIBE("file.txt", s) // write file [NOT IN BROWSER]

Type operations:

TYPEOF(x)                 // "INTEGER", "FLOAT", "STRING", etc.
LENGTH(arr), LENGTH(str)  // length of array or string
PARSE_INT("42")           // string to integer
PARSE_FLOAT("3.14")       // string to float
STRING(42)                // value to string
INT(3.7)                  // float to integer (truncates)
FLOAT(42)                 // integer to float

Array operations:

APPEND(arr, val)          // add to end (returns new array)
PREPEND(arr, val)         // add to start
SLICE(arr, start, end)    // subsequence
FIRST(arr), LAST(arr)     // first/last element
CONCAT(arr1, arr2)        // concatenate arrays

Map operations:

KEYS(map), VALUES(map)    // get keys/values as arrays
HAS(map, key)             // check if key exists
SET(map, key, val)        // set key (returns new map)
DELETE(map, key)          // remove key

String operations:

SPLIT("a,b,c", ",")       // split to array
JOIN(arr, ",")            // join array to string
SUBSTRING(s, start, end)  // extract substring
UPPERCASE(s), LOWERCASE(s), TRIM(s)
REPLACE(s, old, new)      // replace all occurrences

Utility:

RANDOM()                  // random float 0 to 1
RANDOM_INT(min, max)      // random integer in range
TIME()                    // Unix timestamp in ms