Quadrate is a stack-based programming language with a LLVM backend. This document is the definitive reference for its core language features, covering all available operations and their behavior.
In Quadrate, all computation is done through a stack. Instructions push, pop, and manipulate values on this stack, enabling structured programming without traditional variables. The language emphasizes simplicity and expressiveness for system-level programming.
This reference is aimed at developers writing programs directly in Quadrate.
Comments start with two slashes // and end at the end of the line. Block comments are supported with /* and */.
// This is a single-line comment
/*
This is a
block comment
*/
fn main( -- ) {
// Comments can appear anywhere
42 . nl /* even inline */
}
Functions are the building blocks of Quadrate programs. Every function has a signature that declares its stack effect.
fn function_name(input_params -- output_params) {
// Function body
}
The signature (a:i b:f -- result:f) means:
a and float bresult:i (integer), :f (float), :s (string), :p (pointer)// Simple function with no parameters
fn greet( -- ) {
"Hello, World!" . nl
}
// Function with inputs and outputs
fn add(a:i b:i -- sum:i) {
+ // Add top two stack values
}
// Function with multiple outputs
fn divmod(a:i b:i -- quotient:i remainder:i) {
dup2 / swap %
}
fn main( -- ) {
10 3 divmod // Leaves 3 1 on stack
print nl print nl
}
Every Quadrate program must have a main function, which serves as the entry point:
fn main( -- ) {
// Program starts here
}
The stack is the central data structure in Quadrate. Values are pushed onto the stack and operations manipulate these values.
Comments show stack state with topmost element on the right:
fn main( -- ) {
10 // Stack: (10)
20 // Stack: (10, 20)
+ // Stack: (30)
5 // Stack: (30, 5)
* // Stack: (150)
. nl // Prints: 150
}
The runtime tracks four value types:
i): 64-bit signed integersf): 64-bit floating-point numberss): UTF-8 text stringsp): Memory addresses42 // Decimal
0x2A // Hexadecimal
-100 // Negative
3.14 // Decimal
-0.5 // Negative
2.0e10 // Scientific notation
"Hello" // Simple string
"Line 1\nLine 2" // With escape sequences
"He said \"Hi\"" // Escaped quotes
Quadrate uses integers for booleans:
0 = false1) = true$ - Loop IteratorThe $ operator pushes the current loop iteration value onto the stack. Used in for constructs.
fn main( -- ) {
0 10 1 for {
$ print nl // Prints 0 through 9
}
}
:: - Scope ResolutionUsed to access functions from imported modules.
use math
fn main( -- ) {
45.0 math::sin . nl // Call sin from math module
}
& - Function PointerCreates a pointer to a function for use with call.
fn greet( -- ) {
"Hello!" . nl
}
fn main( -- ) {
&greet call // Call greet via function pointer
}
+ / addAdds two numbers.
fn main( -- ) {
5 3 + // Stack: (8)
. nl
}
- / subSubtracts top from second.
fn main( -- ) {
10 3 - // Stack: (7)
. nl
}
* / mulMultiplies two numbers.
fn main( -- ) {
4 5 * // Stack: (20)
. nl
}
/ / divDivides second by top.
fn main( -- ) {
20 4 / // Stack: (5)
. nl
}
% / modComputes modulo (remainder).
fn main( -- ) {
17 5 % // Stack: (2)
. nl
}
incIncrements top value by 1.
fn main( -- ) {
5 inc // Stack: (6)
. nl
}
decDecrements top value by 1.
fn main( -- ) {
5 dec // Stack: (4)
. nl
}
negNegates top value.
fn main( -- ) {
5 neg // Stack: (-5)
. nl
}
All comparison operations push 1 (true) or 0 (false) onto the stack.
== / eqTests equality.
fn main( -- ) {
5 5 == // Stack: (1)
. nl
}
!= / neqTests inequality.
fn main( -- ) {
5 3 != // Stack: (1)
. nl
}
< / ltLess than.
fn main( -- ) {
3 5 < // Stack: (1)
. nl
}
<= / lteLess than or equal.
fn main( -- ) {
5 5 <= // Stack: (1)
. nl
}
> / gtGreater than.
fn main( -- ) {
5 3 > // Stack: (1)
. nl
}
>= / gteGreater than or equal.
fn main( -- ) {
5 5 >= // Stack: (1)
. nl
}
withinChecks if value is in range [min, max).
fn main( -- ) {
5 0 10 within // Stack: (1) - 5 is in [0, 10)
. nl
}
dupDuplicates top element. ( a -- a a )
fn main( -- ) {
5 dup // Stack: (5, 5)
. nl . nl
}
dup2Duplicates top two elements. ( a b -- a b a b )
fn main( -- ) {
1 2 dup2 // Stack: (1, 2, 1, 2)
}
dupdDuplicates second element. ( a b -- a a b )
fn main( -- ) {
1 2 dupd // Stack: (1, 1, 2)
}
dropRemoves top element. ( a -- )
fn main( -- ) {
1 2 drop // Stack: (1)
. nl
}
drop2Removes top two elements. ( a b -- )
fn main( -- ) {
1 2 3 drop2 // Stack: (1)
. nl
}
swapSwaps top two elements. ( a b -- b a )
fn main( -- ) {
1 2 swap // Stack: (2, 1)
. nl . nl
}
swap2Swaps top two pairs. ( a b c d -- c d a b )
fn main( -- ) {
1 2 3 4 swap2 // Stack: (3, 4, 1, 2)
}
swapdSwaps second and third elements. ( a b c -- b a c )
fn main( -- ) {
1 2 3 swapd // Stack: (2, 1, 3)
}
overCopies second element to top. ( a b -- a b a )
fn main( -- ) {
1 2 over // Stack: (1, 2, 1)
}
over2Copies second pair to top. ( a b c d -- a b c d a b )
fn main( -- ) {
1 2 3 4 over2 // Stack: (1, 2, 3, 4, 1, 2)
}
overdCopies third element to top. ( a b c -- a b c a )
fn main( -- ) {
1 2 3 overd // Stack: (1, 2, 3, 1)
}
rotRotates top three elements. ( a b c -- b c a )
fn main( -- ) {
1 2 3 rot // Stack: (2, 3, 1)
}
nipRemoves second element. ( a b -- b )
fn main( -- ) {
1 2 nip // Stack: (2)
. nl
}
nipdRemoves third element. ( a b c -- a c )
fn main( -- ) {
1 2 3 nipd // Stack: (1, 3)
}
tuckCopies top element below second. ( a b -- b a b )
fn main( -- ) {
1 2 tuck // Stack: (2, 1, 2)
}
pickCopies nth element to top (0-indexed from top).
fn main( -- ) {
1 2 3 4
2 pick // Stack: (1, 2, 3, 4, 2)
}
rollMoves nth element to top (0-indexed from top).
fn main( -- ) {
1 2 3 4
2 roll // Stack: (1, 3, 4, 2)
}
depthPushes current stack depth.
fn main( -- ) {
1 2 3
depth // Stack: (1, 2, 3, 3)
. nl
}
clearRemoves all elements from stack.
fn main( -- ) {
1 2 3 4 5
clear // Stack: ()
}
callCalls function pointer.
fn greet( -- ) {
"Hello!" . nl
}
fn main( -- ) {
&greet call
}
. / printPrints top value without newline (leaves value on stack).
fn main( -- ) {
42 . nl // Prints: 42
}
nlPrints a newline.
fn main( -- ) {
"Hello" . nl // Prints: Hello\n
}
printsPrints entire stack with newlines.
fn main( -- ) {
1 2 3 prints // Prints: 1\n2\n3\n
}
printvPrints top value with type information.
fn main( -- ) {
42 printv // Prints: INT(42)
"Hi" printv // Prints: STRING("Hi")
}
printsvPrints entire stack with type information.
fn main( -- ) {
1 2.5 "test" printsv
// Prints type info for each
}
readReads a line from stdin and pushes as string.
fn main( -- ) {
"Enter name: " . read
"Hello, " . . nl
}
castiConverts top value to integer.
fn main( -- ) {
3.7 casti // Stack: (3)
. nl
}
castfConverts top value to float.
fn main( -- ) {
42 castf // Stack: (42.0)
. nl
}
castsConverts top value to string.
fn main( -- ) {
42 casts // Stack: ("42")
. nl
}
spawnCreates a new thread to execute function pointer.
fn worker( -- ) {
"Worker thread" . nl
}
fn main( -- ) {
&worker spawn
wait
}
waitWaits for spawned thread to complete.
detachDetaches a thread to run independently.
errorTriggers an error with top value as message.
fn main( -- ) {
"Something went wrong!" error
}
ifConditional execution based on top stack value.
fn main( -- ) {
10 5 > if {
"Greater" . nl
}
}
With else:
fn main( -- ) {
3 5 > if {
"Greater" . nl
} else {
"Not greater" . nl
}
}
forLoop with start, end, and step values.
fn main( -- ) {
// for (i = 0; i < 10; i += 1)
0 10 1 for {
$ print nl
}
}
Loop from 10 to 0 by -1:
fn main( -- ) {
10 0 -1 for {
$ print nl
}
}
loopInfinite loop (use break to exit).
fn main( -- ) {
0 loop {
dup print nl
inc
dup 10 >= if {
break
}
}
drop
}
breakExits the nearest enclosing for or loop.
fn main( -- ) {
0 10 1 for {
$ dup 5 == if {
drop break
}
print nl
}
}
continueSkips to next iteration of loop.
fn main( -- ) {
0 10 1 for {
$ dup 5 == if {
drop continue
}
print nl // Skips printing 5
}
}
returnReturns from current function immediately.
fn check(n:i -- ) {
dup 0 < if {
"Negative!" . nl
drop return
}
"Positive: " . . nl
}
fn main( -- ) {
-5 check
10 check
}
deferExecutes a statement when function exits (even on error).
fn process( -- ) {
"Opening file" . nl
defer {
"Closing file" . nl
}
// Do work here
"Processing..." . nl
// Deferred code runs when function exits
}
fn main( -- ) {
process
}
Output:
Opening file
Processing...
Closing file
Quadrate comes with several standard library modules. Import them with use:
use math
use str
use fmt
fn main( -- ) {
45.0 math::sin . nl
"hello" str::upper . nl
}
Available modules:
See the Standard Library documentation for details.
Create reusable code by organizing it into modules.
File: math_utils/module.qd
fn double(n:i -- result:i) {
2 *
}
fn triple(n:i -- result:i) {
3 *
}
use math_utils
fn main( -- ) {
5 math_utils::double . nl // Prints: 10
5 math_utils::triple . nl // Prints: 15
}
Modules are searched in:
$QUADRATE_ROOT (default: $HOME/quadrate/)/usr/lib/quadrate/ or /usr/local/lib/quadrate/)Putting it all together:
// BMI Calculator
fn bmi(weight:f height:f -- bmi:f) {
sq / // weight / (height * height)
}
fn classify(bmi_value:f -- ) {
dup 18.5 < if {
drop "Underweight" . nl
return
}
dup 25.0 < if {
drop "Normal weight" . nl
return
}
dup 30.0 < if {
drop "Overweight" . nl
return
}
drop "Obese" . nl
}
fn main( -- ) {
"Enter weight (kg): " . read casts castf
"Enter height (m): " . read casts castf
bmi
"Your BMI: " . dup . nl
classify
}
For more information, see the Installation Guide, Code Examples, and Standard Library documentation.