This page is an English fallback. I have not published a Français translation of this generated reference yet.
My Standard Library Reference#
I provide these built-in functions. They are my core capabilities, available to you without external modules.
This file and my builtin registry in src/builtins_registry.c are kept in step mechanically: tests/check_stdlib_docs.sh (run by make check-stdlib-docs, make test-quick, and make test) fails when a builtin has no ### entry here, when an entry names a builtin I no longer have, or when a ## Section (N) count disagrees with the entries beneath it.
Core I/O (3)#
print(value: any) -> void#
I print a value without a trailing newline.
(print "Hello")
(print 42)
(print 3.14)
println(value: any) -> void#
I print a value with a trailing newline. I am polymorphic — I work with int, float, string, and bool.
(println "Hello, World!")
(println 42)
(println true)
range(start: int, end: int) -> iterator#
I provide this special function for use only in for loops. I create an iterator from start (inclusive) to end (exclusive).
for i in (range 0 10) {
(println i) # Prints 0, 1, 2, ..., 9
}
for i in (range 5 8) {
(println i) # Prints 5, 6, 7
}
I only allow range to be used in for-loop contexts.
Math (20)#
abs(x: int) -> int#
I return the absolute value of an integer.
(abs -5) # Returns 5
(abs 5) # Returns 5
(abs 0) # Returns 0
min(a: int, b: int) -> int#
I return the minimum of two integers.
(min 5 10) # Returns 5
(min -3 0) # Returns -3
(min 7 7) # Returns 7
max(a: int, b: int) -> int#
I return the maximum of two integers.
(max 5 10) # Returns 10
(max -3 0) # Returns 0
(max 7 7) # Returns 7
sqrt(x: float) -> float#
I return the square root of x.
(sqrt 16.0) # Returns 4.0
(sqrt 2.0) # Returns 1.41421...
(sqrt 9.0) # Returns 3.0
pow(base: float, exponent: float) -> float#
I return base raised to the power of exponent.
(pow 2.0 3.0) # Returns 8.0
(pow 5.0 2.0) # Returns 25.0
(pow 2.0 -1.0) # Returns 0.5
floor(x: float) -> float#
I return the largest integer ≤ x as a float.
(floor 3.7) # Returns 3.0
(floor 3.2) # Returns 3.0
(floor -2.3) # Returns -3.0
ceil(x: float) -> float#
I return the smallest integer ≥ x as a float.
(ceil 3.2) # Returns 4.0
(ceil 3.7) # Returns 4.0
(ceil -2.7) # Returns -2.0
round(x: float) -> float#
I round to the nearest integer as a float.
(round 3.4) # Returns 3.0
(round 3.6) # Returns 4.0
(round 3.5) # Returns 4.0
sin(x: float) -> float#
I return the sine of x in radians.
(sin 0.0) # Returns 0.0
(sin 1.5708) # Returns ≈1.0 (π/2)
(sin 3.14159) # Returns ≈0.0 (π)
cos(x: float) -> float#
I return the cosine of x in radians.
(cos 0.0) # Returns 1.0
(cos 3.14159) # Returns ≈-1.0 (π)
(cos 1.5708) # Returns ≈0.0 (π/2)
tan(x: float) -> float#
I return the tangent of x in radians.
(tan 0.0) # Returns 0.0
(tan 0.7854) # Returns ≈1.0 (π/4)
(tan 1.0) # Returns ≈1.5574
atan2(y: float, x: float) -> float#
I return the angle in radians between the positive x-axis and the point (x, y). I handle the quadrant correctly, unlike atan.
(atan2 1.0 1.0) # Returns ≈0.7854 (π/4, first quadrant)
(atan2 1.0 -1.0) # Returns ≈2.3562 (3π/4, second quadrant)
(atan2 0.0 1.0) # Returns 0.0
asin(x: float) -> float#
I return the arcsine of x (inverse sine) in radians. My domain is -1.0 to 1.0 and I return values in -π/2 to π/2.
(asin 1.0) # Returns ≈1.5708 (π/2)
(asin 0.5) # Returns ≈0.5236 (π/6)
(asin 0.0) # Returns 0.0
acos(x: float) -> float#
I return the arccosine of x (inverse cosine) in radians. My domain is -1.0 to 1.0 and I return values in 0 to π.
(acos 1.0) # Returns 0.0
(acos 0.0) # Returns ≈1.5708 (π/2)
(acos -1.0) # Returns ≈3.14159 (π)
atan(x: float) -> float#
I return the arctangent of x (inverse tangent) in radians. I return values in -π/2 to π/2.
(atan 1.0) # Returns ≈0.7854 (π/4)
(atan 0.0) # Returns 0.0
(atan -1.0) # Returns ≈-0.7854 (-π/4)
log(x: float) -> float#
I return the natural logarithm (base e) of x.
(log 1.0) # Returns 0.0
(log 2.718) # Returns ≈1.0
(log 10.0) # Returns ≈2.303
log2(x: float) -> float#
I return the base-2 logarithm of x.
(log2 1.0) # Returns 0.0
(log2 2.0) # Returns 1.0
(log2 8.0) # Returns 3.0
log10(x: float) -> float#
I return the base-10 logarithm of x.
(log10 1.0) # Returns 0.0
(log10 10.0) # Returns 1.0
(log10 100.0) # Returns 2.0
exp(x: float) -> float#
I return e raised to the power of x.
(exp 0.0) # Returns 1.0
(exp 1.0) # Returns ≈2.71828
(exp 2.0) # Returns ≈7.389
fmod(x: float, y: float) -> float#
I return the floating-point remainder of x divided by y.
(fmod 5.5 2.0) # Returns 1.5
(fmod 10.0 3.0) # Returns 1.0
(fmod -5.5 2.0) # Returns -1.5
Type Casting and Conversion (13)#
cast_int(value: any) -> int#
I cast any value to an integer. I truncate floats and parse strings.
(cast_int 3.14) # Returns 3
(cast_int "42") # Returns 42
(cast_int true) # Returns 1
cast_float(value: any) -> float#
I cast any value to a float. I parse strings and convert integers.
(cast_float 42) # Returns 42.0
(cast_float "3.14") # Returns 3.14
(cast_float false) # Returns 0.0
float_from_bits(bits: int) -> float#
I copy the 64 bits of my signed integer operand into a binary64 value.
float_to_bits(value: float) -> int#
I copy the 64 bits of my binary64 operand into a signed integer. The following representation and name-resolution rules apply to both operations.
I copy an exact binary64 representation. My signed integer carries the same 64 bits in two's-complement form; I do not perform a numeric cast. I preserve both zeros and every quiet/signaling NaN payload and sign, and evaluate my operand once. I require the exact declared input type.
assert (== (float_to_bits (float_from_bits 1)) 1)
I reserve these names against ordinary function redeclarations. My current source profile refuses calls through same-named local/global bindings instead of substituting intrinsic semantics. See my transport contract for backend and reconstruction boundaries.
cast_bool(value: any) -> bool#
I cast any value to a boolean. I treat 0, empty string, and null as false; everything else becomes true.
(cast_bool 1) # Returns true
(cast_bool 0) # Returns false
(cast_bool "hello") # Returns true
(cast_bool "") # Returns false
cast_string(value: any) -> string#
I cast any value to its string representation.
(cast_string 42) # Returns "42"
(cast_string 3.14) # Returns "3.14"
(cast_string true) # Returns "true"
to_string(value: any) -> string#
I convert any value to its string representation. I am an alias for cast_string.
(to_string 99) # Returns "99"
(to_string false) # Returns "false"
(to_string 1.5) # Returns "1.5"
int_to_string(n: int) -> string#
I convert an integer to its string representation.
(int_to_string 42) # Returns "42"
(int_to_string 0) # Returns "0"
(int_to_string -100) # Returns "-100"
float_to_string(f: float) -> string#
I convert a float to its string representation.
(float_to_string 3.14) # Returns "3.14"
(float_to_string 0.0) # Returns "0.0"
(float_to_string -1.5) # Returns "-1.5"
bool_to_string(b: bool) -> string#
I convert a boolean to its string representation.
(bool_to_string true) # Returns "true"
(bool_to_string false) # Returns "false"
string_to_int(s: string) -> int#
I parse a string to an integer. I return 0 if the string cannot be parsed.
(string_to_int "42") # Returns 42
(string_to_int "-100") # Returns -100
(string_to_int "abc") # Returns 0
string_to_float(s: string) -> float#
I parse a string to a float. I return 0.0 if the string cannot be parsed.
(string_to_float "3.14") # Returns 3.14
(string_to_float "1e-3") # Returns 0.001
(string_to_float "bad") # Returns 0.0
null_opaque() -> opaque#
I return a null opaque handle. I am useful as a sentinel value when working with foreign function interfaces.
let handle: opaque = (null_opaque)
String Operations (20)#
str_length(s: string) -> int#
I return the length of a string in bytes.
(str_length "Hello") # Returns 5
(str_length "") # Returns 0
(str_length "abc") # Returns 3
str_concat(s1: string, s2: string) -> string#
I concatenate two strings and return a new string.
(str_concat "Hello" " World") # Returns "Hello World"
(str_concat "foo" "bar") # Returns "foobar"
(str_concat "" "test") # Returns "test"
str_substring(s: string, start: int, length: int) -> string#
I extract a substring starting at start with the given length. The index is 0-based. I return until the end of the string if start + length exceeds the string length. I return an empty string if start is out of bounds.
(str_substring "Hello, World!" 0 5) # Returns "Hello"
(str_substring "Hello, World!" 7 5) # Returns "World"
(str_substring "Hello" 2 100) # Returns "llo"
str_contains(s: string, substr: string) -> bool#
I return true if string s contains substring substr.
(str_contains "The quick brown fox" "quick") # Returns true
(str_contains "The quick brown fox" "slow") # Returns false
(str_contains "hello" "") # Returns true
str_equals(s1: string, s2: string) -> bool#
I return true if both strings are exactly equal.
(str_equals "Hello" "Hello") # Returns true
(str_equals "Hello" "World") # Returns false
(str_equals "" "") # Returns true
char_at(s: string, index: int) -> int#
I return the ASCII value of the character at the specified 0-based index. I terminate with an error if the index is out of bounds.
(char_at "Hello" 0) # Returns 72 ('H')
(char_at "Hello" 1) # Returns 101 ('e')
(char_at "Hello" 4) # Returns 111 ('o')
string_from_char(c: int) -> string#
I create a single-character string from an ASCII value.
(string_from_char 65) # Returns "A"
(string_from_char 90) # Returns "Z"
(string_from_char 48) # Returns "0"
str_starts_with(s: string, prefix: string) -> bool#
I return true if s begins with prefix. Every string starts with the empty prefix.
(str_starts_with "hello world" "hello") # Returns true
(str_starts_with "hello" "world") # Returns false
(str_starts_with "hello" "") # Returns true
str_ends_with(s: string, suffix: string) -> bool#
I return true if s ends with suffix. Every string ends with the empty suffix.
(str_ends_with "hello.nano" ".nano") # Returns true
(str_ends_with "hello.nano" ".c") # Returns false
(str_ends_with "abc" "xabc") # Returns false (suffix is longer)
str_index_of(haystack: string, needle: string) -> int#
I return the byte index of the first occurrence of needle in haystack, or -1 if it does not occur. I return 0 for an empty needle.
(str_index_of "hello" "e") # Returns 1
(str_index_of "hello world" "world") # Returns 6
(str_index_of "hello" "x") # Returns -1
str_last_index_of(haystack: string, needle: string) -> int#
I return the byte index of the last occurrence, including overlapping matches, or -1 when no match exists. An empty needle matches at the byte length. I search NUL-terminated strings, not Unicode character positions.
(str_last_index_of "ababa" "aba") # Returns 2
(str_last_index_of "abc" "") # Returns 3
(str_last_index_of "abc" "x") # Returns -1
str_trim(s: string) -> string#
I return a copy of s with leading and trailing whitespace (space, tab, newline, carriage return) removed.
(str_trim " hello ") # Returns "hello"
(str_trim "\n line \t") # Returns "line"
str_trim_left(s: string) -> string#
I return a copy of s with leading whitespace removed.
(str_trim_left " hello ") # Returns "hello "
str_trim_right(s: string) -> string#
I return a copy of s with trailing whitespace removed.
(str_trim_right " hello ") # Returns " hello"
str_to_lower(s: string) -> string#
I return a copy of s with every ASCII A–Z character lowercased. I leave all other bytes, including non-ASCII UTF-8 sequences, untouched.
(str_to_lower "HELLO") # Returns "hello"
(str_to_lower "Hello World") # Returns "hello world"
str_to_upper(s: string) -> string#
I return a copy of s with every ASCII a–z character uppercased. I leave all other bytes, including non-ASCII UTF-8 sequences, untouched.
(str_to_upper "hello") # Returns "HELLO"
(str_to_upper "Hello World") # Returns "HELLO WORLD"
str_replace(s: string, old: string, new: string) -> string#
I replace every occurrence of old in s with new. I return s unchanged when old is empty or does not occur.
(str_replace "hello world" "world" "nano") # Returns "hello nano"
(str_replace "aaa" "a" "b") # Returns "bbb"
(str_replace "hello" "xyz" "abc") # Returns "hello"
str_split(s: string, delimiter: string) -> array<string>#
I split s on every occurrence of delimiter and return the pieces. A string with no delimiter yields a one-element array. An empty delimiter splits into single characters.
let parts: array<string> = (str_split "a,b,c" ",")
(array_length parts) # Returns 3
(at parts 0) # Returns "a"
(array_length (str_split "hello" ",")) # Returns 1
str_join(parts: array<string>, delimiter: string) -> string#
I concatenate the strings in parts, placing delimiter between adjacent elements. I return the empty string for an empty array.
let mut parts: array<string> = []
set parts (array_push parts "a")
set parts (array_push parts "b")
set parts (array_push parts "c")
(str_join parts "-") # Returns "a-b-c"
format(template: string, args: any...) -> string#
I am variadic. I substitute each %s, %d, %f, or %g placeholder in template with the next argument, converted to its string form. I copy any placeholder left over after the arguments run out verbatim, and I require at least the template argument.
My C-seed and NanoVirt frontends reject a non-string template during typechecking. My VM converts arguments to strings and calls a runtime scanner through a fixed string-array ABI. This is interpolation, not printf: %f does not request fixed decimal precision, and %% has no special escape rule. I evaluate extra arguments but do not substitute them after the template ends. Cross-backend conversion parity remains unfinished: whole floats have different decimal suffixes, and aggregate substitutions lack a consistent contract.
(format "Hello, %s!" "world") # Returns "Hello, world!"
(format "kind=%s seq=%d" "spawn" 7) # Returns "kind=spawn seq=7"
(println (format "%s scored %d" name score))
Character Classification (10)#
is_digit(c: int) -> bool#
I return true if the character code represents a decimal digit ('0'–'9').
(is_digit 48) # Returns true ('0')
(is_digit 53) # Returns true ('5')
(is_digit 65) # Returns false ('A')
is_alpha(c: int) -> bool#
I return true if the character code represents a letter (a–z, A–Z).
(is_alpha 65) # Returns true ('A')
(is_alpha 97) # Returns true ('a')
(is_alpha 48) # Returns false ('0')
is_alnum(c: int) -> bool#
I return true if the character code represents an alphanumeric character (digit or letter).
(is_alnum 48) # Returns true ('0')
(is_alnum 65) # Returns true ('A')
(is_alnum 32) # Returns false (' ')
is_space(c: int) -> bool#
I return true if the character code is a space character (ASCII 32).
(is_space 32) # Returns true (' ')
(is_space 65) # Returns false ('A')
(is_space 9) # Returns false ('\t')
is_whitespace(c: int) -> bool#
I return true if the character code represents any whitespace: space, tab, newline, or carriage return.
(is_whitespace 32) # Returns true (' ')
(is_whitespace 9) # Returns true ('\t')
(is_whitespace 10) # Returns true ('\n')
(is_whitespace 65) # Returns false ('A')
is_upper(c: int) -> bool#
I return true if the character code represents an uppercase letter (A–Z).
(is_upper 65) # Returns true ('A')
(is_upper 90) # Returns true ('Z')
(is_upper 97) # Returns false ('a')
is_lower(c: int) -> bool#
I return true if the character code represents a lowercase letter (a–z).
(is_lower 97) # Returns true ('a')
(is_lower 122) # Returns true ('z')
(is_lower 65) # Returns false ('A')
digit_value(c: int) -> int#
I convert a digit character code to its numeric value. I return -1 if it is not a digit.
(digit_value 48) # Returns 0 ('0' -> 0)
(digit_value 53) # Returns 5 ('5' -> 5)
(digit_value 57) # Returns 9 ('9' -> 9)
(digit_value 65) # Returns -1 ('A' is not a digit)
char_to_lower(c: int) -> int#
I convert an uppercase letter code to lowercase. I leave non-letters unchanged.
(char_to_lower 65) # Returns 97 ('A' -> 'a')
(char_to_lower 90) # Returns 122 ('Z' -> 'z')
(char_to_lower 48) # Returns 48 ('0' -> '0', unchanged)
char_to_upper(c: int) -> int#
I convert a lowercase letter code to uppercase. I leave non-letters unchanged.
(char_to_upper 97) # Returns 65 ('a' -> 'A')
(char_to_upper 122) # Returns 90 ('z' -> 'Z')
(char_to_upper 48) # Returns 48 ('0' -> '0', unchanged)
Array Operations (17)#
at(arr: array<T>, index: int) -> T#
I return the element at the specified 0-based index. I perform bounds-checking and terminate with an error if the index is out of bounds.
My C-seed and NanoVirt frontends require exactly two arguments: an array and an integer index (int or u8). I reject strings, floats and booleans as indices during typechecking, before publishing native or bytecode output. The same rule applies to array_get.
let nums: array<int> = [1, 2, 3, 4, 5]
(at nums 0) # Returns 1
(at nums 4) # Returns 5
array_get(arr: array<T>, index: int) -> T#
I return the element at the specified 0-based index. I am an alias for at.
let nums: array<int> = [10, 20, 30]
(array_get nums 0) # Returns 10
(array_get nums 2) # Returns 30
array_length(arr: array<T>) -> int#
I return the number of elements in an array.
let nums: array<int> = [10, 20, 30]
(array_length nums) # Returns 3
(array_length []) # Returns 0
array_new(size: int, default: T) -> array<T>#
I create a new array of the specified size, filled with the default value.
let zeros: array<int> = (array_new 5 0)
# [0, 0, 0, 0, 0]
let strs: array<string> = (array_new 3 "")
# ["", "", ""]
array_set(arr: array<T>, index: int, value: T) -> void#
I set the element at the specified 0-based index. I perform bounds-checking and terminate with an error if the index is out of bounds. I require a mutable array.
let mut nums: array<int> = [1, 2, 3]
(array_set nums 1 42)
# nums is now [1, 42, 3]
array_push(arr: array<T>, value: T) -> array<T>#
I append an element to the end of an array and return the updated array.
let mut numbers: array<int> = [1, 2, 3]
(array_push numbers 4)
# numbers is now [1, 2, 3, 4]
array_pop(arr: array<T>) -> T#
I remove and return the last element of the array.
let mut stack: array<int> = [1, 2, 3]
let last: int = (array_pop stack) # Returns 3
# stack is now [1, 2]
array_remove_at(arr: array<T>, index: int) -> array<T>#
I remove the element at the specified index, shifting remaining elements left, and return the updated array.
let mut items: array<int> = [10, 20, 30, 40]
(array_remove_at items 1)
# items is now [10, 30, 40]
array_slice(arr: array<T>, start: int, length: int) -> array<T>#
I create a new array from a portion of the original, starting at start with the given length.
let numbers: array<int> = [1, 2, 3, 4, 5]
let subset: array<int> = (array_slice numbers 1 3)
# subset is [2, 3, 4]
array_concat(arr1: array<T>, arr2: array<T>) -> array<T>#
I concatenate two arrays and return a new array containing all elements.
let a: array<int> = [1, 2, 3]
let b: array<int> = [4, 5, 6]
let c: array<int> = (array_concat a b)
# c is [1, 2, 3, 4, 5, 6]
array_map(arr: array<T>, f: fn(T) -> U) -> array<U>#
I apply a function to each element of an array and return a new array of the results.
fn square(x: int) -> int { return (* x x) }
let nums: array<int> = [1, 2, 3, 4]
let squares: array<int> = (array_map nums square)
# squares is [1, 4, 9, 16]
array_filter(arr: array<T>, pred: fn(T) -> bool) -> array<T>#
I return a new array containing only the elements for which the predicate returns true.
fn is_even(n: int) -> bool { return (== (% n 2) 0) }
let nums: array<int> = [1, 2, 3, 4, 5, 6]
let evens: array<int> = (array_filter nums is_even)
# evens is [2, 4, 6]
array_fold(arr: array<T>, init: U, f: fn(U, T) -> U) -> U#
I reduce an array to a single value by applying a function cumulatively, starting with init.
fn add(acc: int, x: int) -> int { return (+ acc x) }
let nums: array<int> = [1, 2, 3, 4]
let sum: int = (array_fold nums 0 add)
# sum is 10
array_sort(arr: array<T>) -> array<T>#
I return a new array, leaving the source unchanged. My C-seed interpreter, native emitter and VM share scalar ordering: int, u8 and float ascend, false precedes true, and strings compare bytewise. Float NaNs sort last; equal elements have no stable-order guarantee. My runtime rejects unsupported element layouts instead of returning an unsorted copy. Complete compile-time diagnostics and self-hosted-driver parity remain separate acceptance work.
let nums: array<int> = [3, 1, 2]
let sorted: array<int> = (array_sort nums)
(at sorted 0) # Returns 1
# nums is unchanged
array_reverse(arr: array<T>) -> array<T>#
I return a new array with the elements in reverse order. I leave the input untouched.
let nums: array<int> = [1, 2, 3]
let flipped: array<int> = (array_reverse nums)
(at flipped 0) # Returns 3
array_contains(arr: array<int>, value: int) -> bool#
I return true if value appears in the array. I compare integer elements.
let nums: array<int> = [10, 20, 30]
(array_contains nums 20) # Returns true
(array_contains nums 99) # Returns false
array_index_of(arr: array<int>, value: int) -> int#
I return the index of the first occurrence of value, or -1 if it is absent. I compare integer elements.
let nums: array<int> = [10, 20, 30]
(array_index_of nums 30) # Returns 2
(array_index_of nums 99) # Returns -1
Higher-Order Functions (3)#
filter(arr: array<T>, predicate: fn(T) -> bool) -> array<T>#
I create a new array with elements that satisfy the predicate. I am equivalent to array_filter.
fn is_even(n: int) -> bool { return (== (% n 2) 0) }
let numbers: array<int> = [1, 2, 3, 4, 5, 6]
let evens: array<int> = (filter numbers is_even)
# evens is [2, 4, 6]
map(arr: array<T>, f: fn(T) -> U) -> array<U>#
I transform each element using the provided function. I am equivalent to array_map.
fn square(x: int) -> int { return (* x x) }
let numbers: array<int> = [1, 2, 3, 4]
let squares: array<int> = (map numbers square)
# squares is [1, 4, 9, 16]
reduce(arr: array<T>, init: U, f: fn(U, T) -> U) -> U#
I reduce an array to a single value. I am equivalent to array_fold.
fn add(acc: int, x: int) -> int { return (+ acc x) }
let numbers: array<int> = [1, 2, 3, 4]
let sum: int = (reduce numbers 0 add)
# sum is 10
HashMap Operations (16)#
I provide HashMap as a key-value collection with O(1) average lookup.
hashmap_new() -> hashmap#
I create a new empty hashmap.
let hm: hashmap = (hashmap_new)
hashmap_get(hm: hashmap, key: string) -> any#
I return the value associated with key, or null if the key does not exist.
(hashmap_set hm "name" "Alice")
let val: string = (hashmap_get hm "name") # Returns "Alice"
hashmap_set(hm: hashmap, key: string, value: any) -> void#
I insert or update the value for key.
let hm: hashmap = (hashmap_new)
(hashmap_set hm "score" 100)
(hashmap_set hm "name" "Bob")
hashmap_has(hm: hashmap, key: string) -> bool#
I return true if key exists in the hashmap.
(hashmap_set hm "x" 42)
(hashmap_has hm "x") # Returns true
(hashmap_has hm "missing") # Returns false
hashmap_delete(hm: hashmap, key: string) -> void#
I remove the key-value pair for key. I do nothing if the key does not exist.
(hashmap_set hm "temp" 99)
(hashmap_delete hm "temp")
(hashmap_has hm "temp") # Returns false
hashmap_keys(hm: hashmap) -> array<string>#
I return all keys in the hashmap as an array of strings.
(hashmap_set hm "a" 1)
(hashmap_set hm "b" 2)
let keys: array<string> = (hashmap_keys hm)
# keys contains ["a", "b"] (order may vary)
hashmap_values(hm: hashmap) -> array<any>#
I return all values in the hashmap as an array.
(hashmap_set hm "x" 10)
(hashmap_set hm "y" 20)
let vals: array<int> = (hashmap_values hm)
# vals contains [10, 20] (order may vary)
hashmap_length(hm: hashmap) -> int#
I return the number of key-value pairs in the hashmap.
let hm: hashmap = (hashmap_new)
(hashmap_set hm "a" 1)
(hashmap_set hm "b" 2)
(hashmap_length hm) # Returns 2
map_new() -> hashmap#
I create a new empty hashmap. I am an alias for hashmap_new.
let m: hashmap = (map_new)
map_get(hm: hashmap, key: string) -> any#
I return the value for key. I am an alias for hashmap_get.
let score: int = (map_get hm "alice")
map_set(hm: hashmap, key: string, value: any) -> void#
I insert or update a key-value pair. I am an alias for hashmap_set.
(map_set hm "alice" 10)
(map_set hm "bob" 20)
map_has(hm: hashmap, key: string) -> bool#
I check if a key exists. I am an alias for hashmap_has.
if (map_has hm "alice") { (println "found") }
map_delete(hm: hashmap, key: string) -> void#
I remove a key-value pair. I am an alias for hashmap_delete.
(map_delete hm "temp")
map_keys(hm: hashmap) -> array<string>#
I return all keys as an array. I am an alias for hashmap_keys.
let keys: array<string> = (map_keys hm)
map_values(hm: hashmap) -> array<any>#
I return all values as an array. I am an alias for hashmap_values.
let vals: array<int> = (map_values hm)
map_length(hm: hashmap) -> int#
I return the number of entries. I am an alias for hashmap_length.
let count: int = (map_length hm)
Result Type Operations (7)#
My Result<T, E> type represents either success (Ok) or failure (Err).
result_is_ok(r: Result<T, E>) -> bool#
I return true if the result is an Ok value.
let r: Result<int, string> = (divide 10 2)
if (result_is_ok r) { (println "Success!") }
result_is_err(r: Result<T, E>) -> bool#
I return true if the result is an Err value.
let r: Result<int, string> = (divide 10 0)
if (result_is_err r) { (println "Error occurred") }
result_unwrap(r: Result<T, E>) -> T#
I extract the Ok value. I panic if the result is Err — use result_is_ok to check first.
let r: Result<int, string> = (divide 10 2)
let value: int = (result_unwrap r) # Returns 5
result_unwrap_err(r: Result<T, E>) -> E#
I extract the Err value. I panic if the result is Ok.
let r: Result<int, string> = (divide 10 0)
if (result_is_err r) {
let msg: string = (result_unwrap_err r)
(println msg)
}
result_unwrap_or(r: Result<T, E>, default: T) -> T#
I extract the Ok value, or return default if the result is Err.
let r: Result<int, string> = (divide 10 0)
let value: int = (result_unwrap_or r 0) # Returns 0 (the default)
result_map(r: Result<T, E>, f: fn(T) -> U) -> Result<U, E>#
I apply a function to the Ok value and return a new result. I pass Err values through unchanged.
fn double(x: int) -> int { return (* x 2) }
let r: Result<int, string> = (divide 10 2)
let r2: Result<int, string> = (result_map r double)
# r2 is Ok(10)
result_and_then(r: Result<T, E>, f: fn(T) -> Result<U, E>) -> Result<U, E>#
I apply a function that itself returns a Result, and flatten the result. I pass Err values through unchanged. I use this to chain fallible operations.
fn safe_sqrt(x: int) -> Result<float, string> {
if (< x 0) { return (Err "negative input") }
return (Ok (sqrt (cast_float x)))
}
let r: Result<int, string> = (divide 16 1)
let r2: Result<float, string> = (result_and_then r safe_sqrt)
# r2 is Ok(4.0)
File I/O (8)#
file_read(path: string) -> string#
I read text without seeking. My C-seed interpreter, native helper, VM bridge and std/fs.read share the reader. Open, read and close failures return empty text, as does embedded NUL data that my current string API cannot represent without truncation. Use file_read_bytes for binary input. Empty files and these failures are not distinguishable through this API. I do not impose a size limit or read deadline, or validate UTF-8. Foreign-string ownership remains a separate runtime boundary.
module "modules/std/fs.nano" as fs
let content: string = (fs.read "data.txt")
(println content)
I expose the public wrapper as fs.read; file_read is its foreign boundary.
file_read_bytes(path: string) -> array<u8>#
I read binary contents into byte-typed storage, including zero bytes. My C-seed interpreter, native emitter and VM bridge share a streaming reader that does not seek. An open, read or close failure returns an empty array; a read or close failure discards partial contents. Empty files and failures are therefore not distinguishable through this API. I do not impose a size limit or read deadline.
let data: array<u8> = (file_read_bytes "image.png")
let size: int = (array_length data)
file_write(path: string, content: string) -> int#
I write string content to a file, overwriting it if it already exists. I return 0 on success and 1 on failure.
let status: int = (file_write "output.txt" "Hello, World!")
if (== status 0) { (println "Write successful") }
file_append(path: string, content: string) -> int#
I append string content to the end of a file, creating it if it does not exist. I return 0 on success and 1 on failure.
let status: int = (file_append "log.txt" "New log entry\n")
file_remove(path: string) -> int#
I delete a file permanently. I return 0 on success and 1 on failure.
let status: int = (file_remove "temp.txt")
file_rename(old_path: string, new_path: string) -> int#
I rename or move a file. I return 0 on success and 1 on failure.
let status: int = (file_rename "old.txt" "new.txt")
file_exists(path: string) -> bool#
I return true if the file exists and is accessible.
if (file_exists "config.json") {
let config: string = (file_read "config.json")
} else {
(println "Config file not found")
}
file_size(path: string) -> int#
I return the file size in bytes, or -1 on error.
let size: int = (file_size "data.bin")
(println (str_concat "File size: " (int_to_string size)))
Directory and Navigation (10)#
dir_exists(path: string) -> bool#
I return true if the path exists and is a directory.
if (not (dir_exists "output")) {
(dir_create "output")
}
dir_create(path: string) -> int#
I create a directory. Parent directories must already exist. I return 0 on success and 1 on failure.
let status: int = (dir_create "build/output")
dir_remove(path: string) -> int#
I remove an empty directory. I return 0 on success and 1 on failure.
let status: int = (dir_remove "temp")
dir_list(path: string) -> array<string>#
I list all entries in a directory and return an array of filenames (not full paths). I return an empty array on error.
let entries: array<string> = (dir_list ".")
for entry in entries {
(println entry)
}
getcwd() -> string#
I return the current working directory as an absolute path.
let cwd: string = (getcwd)
(println cwd) # Prints e.g. "/home/user/project"
chdir(path: string) -> int#
I change the current working directory. I return 0 on success and 1 on failure.
let status: int = (chdir "/tmp")
let cwd: string = (getcwd)
(println cwd) # Prints "/tmp"
fs_walkdir(path: string) -> array<string>#
I recursively walk a directory tree and return an array of all file paths found.
let files: array<string> = (fs_walkdir ".")
for f in files {
(println f)
}
tmp_dir() -> string#
I return the system's temporary directory path.
let tmp: string = (tmp_dir)
(println tmp) # Prints e.g. "/tmp"
mktemp(prefix: string) -> string#
I create a new temporary file with the given prefix and return its path. The file is created and left open for writing.
let path: string = (mktemp "nano_work_")
(file_write path "some data")
mktemp_dir(prefix: string) -> string#
I create a new temporary directory with the given prefix and return its path.
let dir: string = (mktemp_dir "nano_build_")
let out: string = (path_join dir "output.txt")
Path Operations (6)#
path_isfile(path: string) -> bool#
I return true if the path exists and is a regular file.
if (path_isfile "config.json") {
(println "Found config file")
}
path_isdir(path: string) -> bool#
I return true if the path exists and is a directory.
if (path_isdir "src") {
(println "src directory exists")
}
path_join(a: string, b: string) -> string#
I join two path components with the appropriate separator. I use / on Unix.
let full: string = (path_join "/home/user" "documents")
# Returns "/home/user/documents"
let nested: string = (path_join "src" "main.nano")
# Returns "src/main.nano"
path_basename(path: string) -> string#
I extract the filename component from a path.
(path_basename "/path/to/file.txt") # Returns "file.txt"
(path_basename "src/main.nano") # Returns "main.nano"
path_dirname(path: string) -> string#
I extract the directory component from a path.
(path_dirname "/path/to/file.txt") # Returns "/path/to"
(path_dirname "src/main.nano") # Returns "src"
path_normalize(path: string) -> string#
I normalize a path by resolving ., .., and redundant separators.
(path_normalize "./foo/../bar/./baz") # Returns "bar/baz"
(path_normalize "/a//b/./c") # Returns "/a/b/c"
Process and Environment (5)#
system(command: string) -> int#
I execute a shell command and wait for it to complete. I return the exit code.
let status: int = (system "ls -la")
if (!= status 0) { (println "Command failed") }
exit(code: int) -> void#
I terminate the program immediately with the given exit code.
if (not (file_exists "required.txt")) {
(println "Error: required.txt not found")
(exit 1)
}
getenv(name: string) -> string#
I return the value of an environment variable. I return an empty string if it is not set.
let home: string = (getenv "HOME")
let path: string = (getenv "PATH")
setenv(name: string, value: string) -> int#
I set an environment variable for the current process and its children. I return 0 on success and 1 on failure.
let status: int = (setenv "MY_VAR" "my_value")
process_run(command: string) -> array<string>#
I execute shell code through /bin/sh -c and return exactly three strings: [exit_code, stdout, stderr]. I preserve complete text streams, including newlines. My module, interpreter, native and VM paths share file-backed capture so one full output pipe cannot block draining the other. Commands are passed without a fixed-size command buffer; the host's argument limit still applies. Shell launch failure returns 127; capture failures and signal termination return -1. I reject embedded NUL output rather than silently truncating it. I do not impose a command deadline or output-storage quota here. This API executes shell syntax; callers must quote untrusted arguments as data.
let result: array<string> = (process_run "echo hello")
let code: string = (at result 0) # "0" (exit code)
let output: string = (at result 1) # "hello\n"
let errors: string = (at result 2) # ""
Binary String and UTF-8 (5)#
bytes_from_string(s: string) -> array<int>#
I convert a string to an array of byte values (0–255), one per character.
let bytes: array<int> = (bytes_from_string "Hello")
(at bytes 0) # Returns 72 ('H')
string_from_bytes(bytes: array<int>) -> string#
I construct a string from an array of byte values.
let bytes: array<int> = [72, 101, 108, 108, 111]
let s: string = (string_from_bytes bytes) # Returns "Hello"
bstr_utf8_length(s: string) -> int#
I return the number of Unicode code points in a UTF-8 encoded string, which may differ from its byte length for multi-byte characters.
(bstr_utf8_length "Hello") # Returns 5
(bstr_utf8_length "café") # Returns 4 (4 code points, 5 bytes)
bstr_utf8_char_at(s: string, index: int) -> int#
I return the Unicode code point at the given character index (not byte index) in a UTF-8 string.
(bstr_utf8_char_at "Hello" 0) # Returns 72 ('H')
(bstr_utf8_char_at "café" 3) # Returns the code point for 'é'
bstr_validate_utf8(s: string) -> bool#
I return true if the string contains valid UTF-8 encoded text.
if (bstr_validate_utf8 content) {
(println "Valid UTF-8")
} else {
(println "Invalid encoding")
}
GPU Kernel Builtins (15)#
I make these available only inside gpu fn bodies. The PTX backend (--target ptx) lowers each of them to a GPU special register or instruction; they have no meaning in host code. See docs/AI_ML_GUIDE.md for the surrounding kernel-launch model.
gpu fn vec_add(a: int, b: int) -> int {
let tid = (thread_id_x)
let bid = (block_id_x)
let bsz = (block_dim_x)
let gid = (+ (* bid bsz) tid)
return (+ (+ a b) gid)
}
thread_id_x() -> int#
I return the calling thread's x index within its block (PTX %tid.x).
thread_id_y() -> int#
I return the calling thread's y index within its block (PTX %tid.y).
thread_id_z() -> int#
I return the calling thread's z index within its block (PTX %tid.z).
block_id_x() -> int#
I return the block's x index within the grid (PTX %ctaid.x).
block_id_y() -> int#
I return the block's y index within the grid (PTX %ctaid.y).
block_id_z() -> int#
I return the block's z index within the grid (PTX %ctaid.z).
block_dim_x() -> int#
I return the number of threads per block along x (PTX %ntid.x).
block_dim_y() -> int#
I return the number of threads per block along y (PTX %ntid.y).
block_dim_z() -> int#
I return the number of threads per block along z (PTX %ntid.z).
grid_dim_x() -> int#
I return the number of blocks in the grid along x (PTX %nctaid.x).
grid_dim_y() -> int#
I return the number of blocks in the grid along y (PTX %nctaid.y).
grid_dim_z() -> int#
I return the number of blocks in the grid along z (PTX %nctaid.z).
global_id_x() -> int#
I return the global x index of the calling thread. I compute (block_id_x * block_dim_x) + thread_id_x for you.
gpu fn scale_by_index(x: int) -> int {
return (* x (global_id_x))
}
global_id_y() -> int#
I return the global y index of the calling thread, computed as (block_id_y * block_dim_y) + thread_id_y.
gpu_barrier() -> void#
I synchronize every thread in the block before any of them continues (PTX bar.sync 0).
gpu fn staged(x: int) -> int {
(gpu_barrier)
return x
}