Xlerb Language Specification
A stack-oriented language for the BEAM
Data Types
1 2 3
-42
100_000Integers
Whole numbers, optionally negative. Use optional underscores to separate digit groups.
3.14
-0.001
1e10
2.5e-3Floats
Decimals, with scientific notation.
"hello, xlerb!"
"line one\nline two"
"µ-ziq"Strings
UTF-8 binary strings.
:ok
:error
:my_atomAtoms
Efficiently interned strings.
true
falseBooleans
Forth uses `-1`, `0` for `true`/`false`, Xlerb uses standard BEAM booleans.
Words
: square dup * ;Basic Definition
Words are defined with : and end ;
This defines square which duplicates the top-of-stack, and multiplies the top two elements.
: 0 factorial drop 1 ;
: _ factorial dup 1 - factorial * ;Pattern Matching
Patterns before the word name match the top of stack. _ matches anything.
Multiple definitions create multi-clause words—first match wins.
: :ok _ unwrap! swap drop ;
: :error _ unwrap! raise ;example: Tagged Dispatch
You can match on an arbitrary number of stack elements.
Unwrap the value from the result, or raise.
: ^2 dup * ;
: +=+ [Bitwise bxor 2] elixir ;Allowed Symbols
Word names can contain most characters.
Quotations
[dup *]
[1 2 +]Syntax
Quotations are delimited by [ and ]
They represent code as data that can be passed around and executed later.
200 [dup *] i
\ => 40000Invoking Quotations
The word i invokes a quotation, executing its contents against the current stack.
[2 + 3] rev
\ => [3 + 2]Quotation Manipulation
Quotations can be transformed like any list-like data structure. For example, rev reverses the elements of a quotation.
[3 + 2] [swap] do
\ => [+ 3 2]Do
The word do runs a quotation using another quotation as the stack.
[whatever receive .s flush recurse] spawnRecurse
Inside a quotation, recurse performs a tail-call, for anonymous recursion.
Modules
: math [
: square dup * ;
: cube dup dup * * ;
] ; moduleDefinition
Modules group related words. Define a module by giving a name to a quotation, and following it with module.
Compiles directly to BEAM modules, which export functions from list to list.
5 math:square
\ => 25Calling
Module words are called with module:word syntax.
: utils [
: helper dup + ; private
: double helper ;
] ; modulePrivate Words
The private keyword after a definition makes it module-private.
Case
[
: 0 -> "zero" ;
: 1 -> "one" ;
: _ -> "other" ;
] caseSyntax
Case expressions are just a quotation defining overloads of -> which match on the current stack.
: get-result [
: :ok _ -> swap drop "success: " ++ ;
: :error -> drop "failed" ;
] case ;Example: Tagged Results
Like Elixir/Erlang, pattern match for. Patterns are read with the top of the stack rightmost.
[
: 1 -> "monday" ;
: 2 -> "tuesday" ;
: 3 -> "wednesday" ;
: 4 -> "thursday" ;
: 5 -> "friday" ;
: _ -> "weekend" ;
] caseMultiple Branches
First matching branch executes. Use _ as a catch-all.
Compiles directly to Erlang's case expression.
Receive
[ : -> ; ] receive
whatever receiveReceive Anything
Waits for a message and pushes it onto the stack. An empty -> pattern matches any message.
The word whatever pushes this quotation.
[
: _ :ping -> ! :pong send drop ;
: _ _ -> drop drop ;
] receivePattern Matching
Define multiple -> clauses to handle different message shapes. All Xlerb messages are stacks.
pid ! :hello "world" 42 sendSending Messages
! marks message start. Elements are pushed, then send packs and dispatches to the PID before the marker.
Sends [:hello, "world", 42] to pid.
[quotation] spawn
selfProcesses
spawn executes a quotation in a new process and pushes its PID.
self pushes the current process's PID.
Pack & Unpack
1 2 3 &{&1, &2, &3} pack
\ => {3, 2, 1}Pack Tuples
& introduces a capture pattern. &1, &2, etc. reference stack positions (&1 is the top of stack).
pack constructs the structure from stack values.
\ Stack: {10, 20, 30}
&{&1, &2, &3} unpack
\ Stack: 10 20 30Unpack Tuples
unpack destructures the top of stack according to the pattern, pushing captured values.
100 200 &%{x: &1, y: &2} pack
\ => %{x: 200, y: 100}Pack Maps
Maps use %{key: &n} or %{&n => &m} syntax. Atom keys use the shorthand colon syntax.
%{name: "alice", age: 30} &%{name: &1} unpack
\ => "alice"Unpack Maps
Extract specific keys from maps. Unmatched keys are ignored.
1 2 3 &[&1, &2, &3] pack
\ => [3, 2, 1]
[1, 2, 3] &[&1 | &2] unpack
\ Stack: 1 [2, 3]Lists
List patterns support both element and cons (|) syntax for head/tail destructuring.
10 20 &%{point: {&1, &2}} pack
\ => %{point: {20, 10}}Nested Structures
Patterns can be nested arbitrarily—maps containing tuples, lists of maps, etc.
Interop
"hello" "world" [String concat 2] elixir
\ => "helloworld"Elixir Calls
elixir calls an Elixir function. The quotation specifies [Module function arity].
arity arguments are popped taken from the stack, and the result pushed.
\ Stack: [1, 2, 3]
[lists reverse 1] erlang
\ => [3, 2, 1]Erlang Calls
erlang calls an Erlang function. Module names are lowercase atoms.
Same calling convention as elixir.
: sha256 :sha256 [crypto hash 2] erlang ;
"hash-this-value" sha256Example: Wrapping FFI calls
Keep FFI calls contained by wrapping them in a definition.
: puts [IO puts 1] elixir ;
: ++ [Kernel <> 2] elixir ;Common Patterns
Wrap frequently-used Elixir/Erlang functions as words. Operators like <> work as function names.
Stack Operations
dup \ a -- a a
drop \ a --
swap \ a b -- b a
rot \ a b c -- c a bPrimitives
Core stack manipulation words. Stack effect notation shows before/after states.
. \ print and pop top
.s \ print entire stackInspection
Debug words for examining stack state.
+ - *Arithmetic
Binary operators pop two values, push result. a b - computes a - b.