Built-in Methods
Built-in methods use dot notation on values. The compiler translates each method call directly into a dedicated VM opcode — there is no function call overhead at runtime.
Array
arr.len()
Returns the number of elements as i32.
arr.push(value)
Appends an element to the end. Returns void.
Typed arrays enforce element type at runtime:
arr.pop()
Removes and returns the last element. Raises an error if the array is empty.
arr.contains(value)
Returns true if the array contains the value, false otherwise. Comparison is by value for primitives and strings, by reference for structs.
var arr: array<number> = [1.0, 2.0, 3.0]
assert(arr.contains(2.0) == true)
assert(arr.contains(99.0) == false)
arr.indexOf(value)
Returns the index of the first match as i32, or -1 if not found.
var arr: array<number> = [10.0, 20.0, 30.0]
assert(arr.indexOf(20.0) == 1)
assert(arr.indexOf(99.0) == -1)
arr.slice(start, end)
Returns a new array containing elements from start (inclusive) to end (exclusive). Out-of-bounds indices are clamped.
var arr: array<number> = [1.0, 2.0, 3.0, 4.0, 5.0]
var mid: array = arr.slice(1, 4) # [2.0, 3.0, 4.0]
assert(mid.len() == 3)
arr.sort()
Sorts the array in place. Works with number, i32, and string arrays.
var nums: array<i32> = [3, 1, 4, 1, 5]
nums.sort()
assert(nums[0] == 1)
assert(nums[4] == 5)
var words: array<string> = ["banana", "apple", "cherry"]
words.sort()
assert(words[0] == "apple")
arr.reverse()
Reverses the array in place.
arr.join(separator)
Joins all elements into a string with the given separator.
var arr: array<string> = ["a", "b", "c"]
var result: string = arr.join(", ")
assert(result == "a, b, c")
var nums: array<i32> = [1, 2, 3]
var s: string = nums.join("-")
assert(s == "1-2-3")
String
str.len()
Returns the byte length as i32.
str.upper()
Returns a new string with all ASCII letters uppercased.
str.lower()
Returns a new string with all ASCII letters lowercased.
str.trim()
Returns a new string with leading and trailing whitespace removed (space, tab, newline, carriage return).
str.contains(substring)
Returns true if the string contains the substring, false otherwise.
var s: string = "Hello World"
assert(s.contains("World") == true)
assert(s.contains("xyz") == false)
str.indexOf(substring)
Returns the byte index of the first occurrence as i32, or -1 if not found.
str.split(separator)
Splits the string by the separator and returns an array<string>.
var csv: string = "a,b,c"
var parts: array = csv.split(",")
assert(parts.len() == 3)
assert(parts[0] == "a")
assert(parts[1] == "b")
assert(parts[2] == "c")
str.substring(start, end)
Returns a new string from byte index start (inclusive) to end (exclusive).
var s: string = "Hello World"
assert(s.substring(0, 5) == "Hello")
assert(s.substring(6, 11) == "World")
Dict
dict.len()
Returns the number of key-value pairs as i32.
dict.has(key)
Returns true if the key exists, false otherwise.
var d: dict = {"host": "localhost", "port": 8080}
assert(d.has("host") == true)
assert(d.has("missing") == false)
dict.keys()
Returns all keys as a typed array. If the dict is dict<string, i32>, keys returns array<string>.
dict.values()
Returns all values as a typed array. If the dict is dict<string, i32>, values returns array<i32>.
dict.delete(key)
Removes a key-value pair. Returns void. No error if the key doesn't exist.
var d: dict = {"a": 1, "b": 2, "c": 3}
d.delete("b")
assert(d.len() == 2)
assert(d.has("b") == false)
Native Modules
Native modules provide additional functions implemented in C. They are imported like regular modules but dispatch via OP_NATIVE_CALL with zero overhead.
math
Requires require ../dev-libs/math v0.1 in star.mod.
| Function | Args | Returns | Description |
|---|---|---|---|
sin(x) |
number | number | Sine |
cos(x) |
number | number | Cosine |
tan(x) |
number | number | Tangent |
sqrt(x) |
number | number | Square root |
abs(x) |
number | number | Absolute value |
floor(x) |
number | number | Round down |
ceil(x) |
number | number | Round up |
round(x) |
number | number | Round to nearest |
pow(base, exp) |
number, number | number | Exponentiation |
log(x) |
number | number | Natural logarithm |
log10(x) |
number | number | Base-10 logarithm |
atan2(y, x) |
number, number | number | Two-argument arctangent |
min(a, b) |
number, number | number | Minimum |
max(a, b) |
number, number | number | Maximum |
See Modules for how to create native modules.
Global Functions
console.log(value)
Prints a value to stdout followed by a newline. Accepts any type. In release mode (compile), console.log calls are stripped entirely — zero bytecode, zero overhead.
runtime.guard(opcodes, heap_bytes, max_event_chain)
Configures VM safety guards. Each parameter sets a hard limit — exceeding it traps the script with a runtime error. The VM itself stays alive; only the script is killed.
| Parameter | Type | Description |
|---|---|---|
opcodes |
number |
Max VM instructions before trap. Catches infinite loops. |
heap_bytes |
number |
Max heap allocation in bytes. Catches memory exhaustion. |
max_event_chain |
number |
Max consecutive same-type event emits during a handler. Catches event storms. |
A value of 0 means unlimited — that specific guard is disabled. All three default to 0 (no limits). Set only the guards you need:
runtime.guard(50000, 0, 0) # opcode budget only
runtime.guard(0, 32768, 0) # heap limit only
runtime.guard(0, 0, 5) # event storm only
runtime.guard(10000, 4096, 3) # all three active
Guard errors are catchable with try:
runtime.guard(5000, 0, 0)
var r: result<void> = try dangerousWork()
if (r.failed) {
console.log(r.error) # "opcode budget exceeded"
}
When a guard trips:
| Guard | Error | Behavior |
|---|---|---|
| Opcode budget | opcode budget exceeded |
Script halts after N instructions without yield |
| Heap limit | heap limit exceeded |
GC runs first — if still over limit, script halts |
| Event chain | event storm detected |
Script halts when handler emits same event type N times |
If no try frame is on the call stack, the guard kills the script entirely.
Host-side Guards (Floor)
From C (host/firmware side), use star_vm_configure_guards() to set immutable floor values:
Host guards act as a floor — scripts can tighten limits but never loosen them:
# host set: 100000 opcodes, 65536 heap, 10 event chain
runtime.guard(50000, 0, 0) # OK — tighter than host (50k < 100k)
runtime.guard(200000, 0, 0) # clamped to 100000 — cannot exceed host floor
runtime.guard(0, 0, 0) # ignored — 0 means "don't change", host floor stays
Guards apply in both debug and release mode. On ESP32, the host sets floor values before loading any script — a runaway script must not kill the firmware.
gc.collect()
Triggers a manual garbage collection cycle. Normally the GC runs automatically, but this can be used for deterministic memory management in performance-critical sections.
toStr(value)
Converts any value to its string representation.
toNum(value)
Converts a string to a number. Returns 0 for non-numeric strings. Bools convert to 1/0.
Summary
| Type | Method | Args | Returns | Description |
|---|---|---|---|---|
| array | len() |
— | i32 | Element count |
| array | push(val) |
1 | void | Append element |
| array | pop() |
— | element | Remove and return last |
| array | contains(val) |
1 | bool | Value existence check |
| array | indexOf(val) |
1 | i32 | First index or -1 |
| array | slice(start, end) |
2 | array | Sub-array (new copy) |
| array | sort() |
— | void | Sort in place |
| array | reverse() |
— | void | Reverse in place |
| array | join(sep) |
1 | string | Join elements with separator |
| string | len() |
— | i32 | Byte length |
| string | upper() |
— | string | ASCII uppercase |
| string | lower() |
— | string | ASCII lowercase |
| string | trim() |
— | string | Strip whitespace |
| string | contains(sub) |
1 | bool | Substring check |
| string | indexOf(sub) |
1 | i32 | First byte index or -1 |
| string | split(sep) |
1 | array<string> | Split by separator |
| string | substring(s, e) |
2 | string | Sub-string by byte index |
| dict | len() |
— | i32 | Pair count |
| dict | has(key) |
1 | bool | Key existence check |
| dict | keys() |
— | array<K> | All keys (typed) |
| dict | values() |
— | array<V> | All values (typed) |
| dict | delete(key) |
1 | void | Remove key-value pair |
| — | toStr(val) |
1 | string | Convert any value to string |
| — | toNum(val) |
1 | number | Convert to number |
| — | console.log(val) |
1 | void | Print value (debug only) |
| — | runtime.guard(ops, heap, chain) |
3 | void | Set VM safety limits (0 = unlimited) |
| — | gc.collect() |
— | void | Manual GC cycle |