Built-Ins

Stack comes with quite a few built-in functions (called intrinsics internally). They provide baseline functinoality for Stack.

Lists vs Functions

Unless explicitly stated in the description of a function, for all mentions of the type list, functions are also supported (e.g.: (fn 2 2 +) len).

Arithmetic

Note: Stack uses wrapping arithmetic, unlike Rust, which uses bounds checking in safe modes.

Add (+)

Signature: ([a: int] [b: int] -- int)

Equivalent Rust: a + b

Examples:

1 2 +
;; 3

Subtract (-)

Signature: ([a: int] [b: int] -- int)

Equivalent Rust: a - b

Examples:

2 1 -
;; 1

1 2 -
;; -1

Multiply (*)

Signature: ([a: int] [b: int] -- int)

Equivalent Rust: a * b

Examples:

2 3 *
;; 6

Divide (/)

Signature: ([a: int] [b: int] -- int)

Equivalent Rust: a / b

Examples:

6 3 /
;; 2

Remainder (%)

Signature: ([a: int] [b: int] -- int)

Equivalent Rust: a % b

Examples:

10 5 %
;; 0

11 5 %
;; 1

Comparison

Equal (=)

Signature: ([a] [b] -- bool)

Equivalent Rust: a == b

Examples:

2 2 =
;; true

"hello" "world" =
;; false

'(1 2) '(1 2) =
;; true

Not Equal (!=)

Signature: ([a] [b] -- bool)

Equivalent Rust: a != b

Examples:

2 2 !=
;; false

"hello" "world" !=
;; true

'(1 2) '(1 2) !=
;; false

Less Than (<)

Signature: ([a] [b] -- bool)

Equivalent Rust: a < b

Examples:

1 2 <
;; true

2 1 <
;; false

Less Than or Equal To (<=)

Signature: ([a] [b] -- bool)

Equivalent Rust: a <= b

Examples:

1 2 <=
;; true

2 2 <=
;; true

2 1 <=
;; false

Greater Than (>)

Signature: ([a] [b] -- bool)

Equivalent Rust: a > b

Examples:

1 2 >
;; false

2 1 >
;; true

Greater Than or Equal To (>=)

Signature: ([a] [b] -- bool)

Equivalent Rust: a >= b

Examples:

1 2 >=
;; false

2 2 >=
;; true

2 1 >=
;; true

Boolean

Or (or)

Signature: ([a: bool] [b: bool] -- bool)

Equivalent Rust: a || b

Examples:

false false or
;; false

false true or
;; true

true false or
;; true

true true or
;; true

And (and)

Signature: ([a: bool] [b: bool] -- bool)

Equivalent Rust: a && b

Examples:

false false and
;; false

false true and
;; false

true false and
;; false

true true and
;; true

Not (not)

Signature: ([a: bool] -- bool)

Equivalent Rust: !a

Examples:

false not
;; true

true not
;; false

Stack Ops

Drop (drop)

Signature: ([a] --)

Drops a from the stack

Examples:

"hey"
;; ["hey"]

drop
;; []

Duplicate (dupe)

Signature: ([a] -- a a)

Duplicates a on the stack

Examples:

"hey"
;; ["hey"]

dupe
;; ["hey" "hey"]

Swap (swap)

Signature: ([a] [b] -- b a)

Swaps a and b on the stack

Examples:

"hello" "world"
;; ["hello" "world"]

swap
;; ["world" "hello"]

Rotate (rot)

Signature: ([a] [b] [c] -- b c a)

Rotates a, b, and c on the stack

Examples:

"a" "b" "c"
;; ["a" "b" "c"]

rot
;; ["b" "c" "a"]

Lists

Length (len)

Signature: ([a: list|string|function] -- int)

Equivalent Rust: a.len()

Examples:

'[1 2 3] len
;; 3

"123" len
;; 3

Get at Index (nth)

Signature: ([a: list|function] [b: int] -- a any) or ([a: string] [b: int] -- a string)

Equivalent Rust: a[b] or a.get(b)

Examples:

'[1 2 3] 0 nth
;; [[1 2 3] 1]

'[1 2 3] 2 nth
;; [[1 2 3] 3]

"123" 0 nth
;; ["123" "1"]

"123" 2 nth
;; ["123" "3"]

Split (split)

Signature: ([a: list] [b: int] -- list list) or ([a: string] [b: int] -- string string)

Splits a at the separator b and returns both chunks.

Examples:

'[1 2 3] 1 split
;; [1] [2 3]

"123" 1 split
;; "1" "23"

Concat (concat)

Signatures:

  • ([a: list] [b: list] -- list)
  • ([a: string] [b: string] -- string)
  • ([a: function] [b: function] -- function)
  • ([a: function] [b: list] -- function)
  • ([a: list] [b: function] -- list)

Concats a and b together (concats the two lists or two strings)

Examples:

'[1] '[2 3] concat
;; [1 2 3]

"1" "23" concat
;; "123"

Push (push)

Signature: ([a] [b: list|function] -- b) or ([a: string] [b: string] -- string)

Equivalent Rust: b.push(a)

Examples:

3 '[1 2] push
;; [1 2 3]

"3" "12" len
;; "123"

Pop (pop)

Signature: ([a: list|function] -- a any) or ([a: string] -- string)

Equivalent Rust: a.pop()

Examples:

'[1 2 3] pop
;; 3

"123" len
;; "3"

Records

Insert (insert)

Signature: ([value] [key] [c: record] -- record)

Equivalent Rust: c.insert(key, value)

Examples:

"value" "key" {} insert
;; {key: "value"}

true 'key {} insert
;; {key: true}

2 1 {} insert
;; {1: 2}

Property (prop)

Signature: ([a: record] [b] -- a any)

Equivalent Rust: a.get(b)

Examples:

{key "value"} "key" prop
;; [{key "value"} "value"]

{key "value"} "foo" prop
;; [{key "value"} nil]

{key "value"} 'key prop
;; [{key "value"} "value"]

{key "value"} 'foo prop
;; [{key "value"} nil]

{1 2} 1 prop
;; [{1 2} 2]

{1 2} 2 prop
;; [{1 2} nil]

Has (has)

Signature: ([a: record] [b] -- a bool)

Equivalent Rust: a.has(b)

Examples:

{key "value"} "key" has
;; [{key "value"} true]

{key "value"} "foo" has
;; [{key "value"} false]

{key "value"} 'key has
;; [{key "value"} true]

{key "value"} 'foo has
;; [{key "value"} false]

{1 2} 1 has
;; [{1 2} true]

{1 2} 2 has
;; [{1 2} false]

Remove (remove)

Signature: ([a: record] [b: string] -- record)

Equivalent Rust: a.remove(b)

Examples:

{key "value" foo "bar"} "foo" remove
;; [{key "value"}]

{key "value" foo "bar"} "bar" remove
;; [{key "value" foo "bar"}]

Keys (keys)

Signature: ([a: record] -- a list(symbol))

Equivalent Rust: a.keys()

Examples:

{key "value" foo "bar"} keys
;; [{key "value" foo "bar"} (key foo)]

{"key" "value" "foo" "bar"} keys
;; [{key "value" foo "bar"} (key foo)]

Values (values)

Signature: ([a: record] -- a list)

Equivalent Rust: a.values()

Examples:

{key "value" foo "bar"} values
;; [{key "value" foo "bar"} ["value" "bar"]]

{f (fn 2 2 +)} values
;; [{key (fn 2 2 +)} ((fn 2 2 +))]

{f '(fn 2 2 +)} values
;; [{key '(fn 2 2 +)} ('(fn 2 2 +))]

Types

Cast (cast)

Signature: ([a] [b: string] -- any)

Converts a to the type: b and returns the new type

Type of (typeof)

Signature: ([a] -- string)

Gets the type of a and pushes it as a string to the stack

Lazy (lazy)

Signature: ([a] -- lazy(a))

Wraps a with a lazy expression, making it lazy.

Examples:

1 lazy
;; '1

'[]
;; []
lazy
;; '[]

Control Flow

If (if)

Signature: ([a: bool] [b: list] --)

Equivalent Rust: if a { b }

Examples:

'["true"]
true
if
;; "true"
[4 4 =]
;; [true]
'["true"]
;; [true ["true"]]
if
;; ["true"]

Halt (halt)

Signature: (--)

Equivalent Rust: Halts execution.

Examples:

2 2 halt +
;; halts before the "+"

Recur (recur)

Signature: (-- symbol)

A QoL helper intrinsic that pushes the symbol: recur to the stack. Used to allow recur to be called without escaping with a lazy (such as 'recur).

Examples:

;; Define i
0 'i def

;; Function isn't lazy so it runs right away
(fn
  ;; Check if i is less than 5 (condition)
  i 5 <

  ;; Our if block
  '[
    ;; Push i to the stack
    i

    ;; Add 1 to i
    i 1 + 'i set

    ;; Recur
    recur
  ]

  ;; Run the if
  if
)
;; [0 1 2 3 4]

OrElse ('orelse')

Signature: ([a] [b] -- a|b)

Equivalent Rust: a.or(b)

If a is nil, returns b. Else, returns a.

Examples:

nil 2 orelse
;; 2

1 2 orelse
;; 1

Scopes and Variables

Define (def)

Signature: ([a] [b: symbol] --)

Equivalent Rust: let b = a

Examples:

0 'a def
a
;; 0

'(fn +) 'add def
2 2 add
;; 4

Set (set)

Signature: ([a] [b: symbol] --)

Equivalent Rust: b = a

Examples:

0 'a def
1 'a set
a
;; 1

1 'a set
;; throws since `a` is not defined

Call (call)

Signature: ([a] --)

Calls a and:

  • If a is a function: Runs the function
  • If a is a list: Runs each item in the list
  • If a is a symbol: Calls the symbol from the scope
  • If a is anything else: Pushes it back onto the stack

Examples:

2 2 
'(fn +) call
;; 4

'[2 2 +] call
;; 4

'[2 2 +] 'add def
add
;; [[2 2 +]]
call
;; [4]

'(fn +) 'add def
2 2 'add call
;; 4

0 'a def
'a call
;; 0

"foo" 'a def
'a call
;; "foo"

Let (let)

Signature: ([a: list] [b: list(symbol)] --)

Pops b.len() items off of the stack, assigning each item the corresponding symbol in b. Then, runs the code block a, injecting the symbols into the scope.

If list b was (first second), then they would be popped from the stack in order, following this signature: ([first] [second] --).

Important Note: Functions cannot be used as the block of a let (a). To use functions within lets, wrap them within the let block: 0 '((fn a)) '(a) let. Lets create create their own scopes, so any def will be isolated to that let.

Examples:

10 2 '[a b -] '[a b] let
;; 8

10 2
'[
  (fn a b -)
] '[a b] let
;; 8

10 2
(fn
  '[a b -]
  '[a b]
  let
) call
;; 8

Get (get)

Signature: ([a: symbol] -- any)

Equivalent Rust: a

Examples:

0 'a def
'a get
;; 0

'(fn +) 'add def
2 2 add
;; 4

'(fn +) 'add def
'add get
;; (fn +)

'(fn +) 'add def
2 2 
'add get call
;; 4

Debugging and I/O

Debug (debug)

Signature: ([a] -- a)

Equivalent Rust: dbg!(format!("{}", a))

Examples:

0 debug
;; prints 0

"hey" debug
;; prints "hey"

Assert (assert)

Signature: ([a] [b: bool] -- a)

Equivalent Rust: assert!(b, format!("{}", a))

Examples:

"my test" 2 2 = assert
;; nothing (it passes)

"my test" 1 2 = assert
;; error: assertion failed caused by my test

Import (import)

Signature: ([a: string] --)

Runs the file from path a in the current environment. Variables and stack changes will persist from file a.

Examples:

;; lib.stack
'(fn +) 'add def

;; main.stack
"lib.stack" import
2 2 add
;; 4