Builtins
Generated from the language spec, which is normative.
Builtins are available without import. They are resolved only when the name is not bound by a variable in scope or a declared function; such a binding shadows the builtin entirely (a shadowed builtin is not callable through any other path).
print(v1, ..., vn)
Takes zero or more arguments of any single-value types, renders each
canonically, joins them with single spaces, and writes the result followed by
a line feed to standard output. Canonical rendering (shared by print, the
%v verb, and str()):
| Type | Rendering |
|---|---|
int | decimal |
byte | decimal (byte(7) renders as 7) |
float | shortest decimal that round-trips; integral values render without a fractional part (3.0 renders as 3); infinities as inf/-inf, NaN as NaN |
bool | true / false |
str | the string itself, unquoted |
list | [e1, e2, ...], elements rendered recursively |
map | {k1: v1, k2: v2} in insertion order |
| struct | Name{f1: v1, f2: v2} in declaration order |
none | none |
error | error(<msg>) |
py | Python str() of the object |
fn | fn |
printf and sprintf
printf(format, a1, ..., an) // writes to standard output, no implicit newline
sprintf(format, a1, ..., an) str // returns the formatted string
The format string uses Go-style verbs:
| Verb | Argument type | Output |
|---|---|---|
%v | any | canonical rendering (section 14.1); renders byte as decimal |
%d | int | decimal; byte is not accepted, widen with int(b) |
%s | str | the string |
%t | bool | true / false |
%q | str | double-quoted, backslash-escaped |
%f | float | fixed-point, default 6 fractional digits |
%% | none | literal % |
A verb may carry a minimum width (%5s) and a precision (%.2f), both
decimal digit sequences, in the form %[width][.precision]verb. Width pads
on the left with spaces to the given count of characters (not bytes).
Precision is honored by %f; on other verbs it is accepted and ignored.
There is no left-align or zero-pad flag. A width or precision exceeding the
implementation’s pad limit (2^20 in the reference implementation) is
rejected: a compile-time diagnostic when the format string is a literal
(section 14.3), a fault otherwise (chapter 12).
Static and dynamic format checking
When the format argument is a string literal, the format is checked at compile time: verb count must equal argument count, each argument’s type must match its verb, verbs must be from the table, and the format must not end inside a verb. Violations are compile-time errors.
When the format is not a literal, the same checks happen at runtime and a violation is a fault (chapter 12).
len
len(x) int
For str, the number of characters; for list (including []byte), the
element count; for map, the entry count. Any other argument type is a
compile-time error.
charcode
charcode(c str) int
The Unicode code point (the character’s number) of c, which must be
exactly one character; any other argument value is a runtime fault.
char
char(n int) str
The one-character string for the Unicode code point n. A value that is not a valid
Unicode scalar (negative, greater than 0x10FFFF, or a surrogate) is a
runtime fault. char(charcode(c)) == c for every one-character c.
Program arguments and standard input live in the os module
(section 15.9); nevla has no args or input builtin.
append
append(xs []T, v1 T, ..., vn T) []T
A fresh list: xs with the values appended, as in Go’s idiom
xs = append(xs, v). The first argument must be a list; every following
value must be assignable to its element type. Zero values yield a plain
copy. The original list is never modified; other names bound to it see
growth only through rebinding (chapter 11).
append on []byte observes the same contract: every call returns a fresh
buffer, and xs is never modified in place from the caller’s perspective.
An earlier reference-count-based in-place growth optimization was removed
(design 2026-07-14) after it broke exactly this contract: no refcount
threshold can distinguish xs = append(xs, v) (safe to reuse storage, since
xs immediately rebinds to the result) from ys := append(xs, v) (xs
must stay unchanged) without move semantics or escape analysis, which the
reference implementation has neither of. []byte append now always copies,
exactly like every other []T.
clone
clone(x []T) []T
clone(x map[K]V) map[K]V
A one-level copy of a list or map (chapter 11): the container is new, its
elements copy by their kinds, exactly Go’s slices.Clone/maps.Clone.
Applying clone to a value type is a compile-time error; value types
already copy. clone on []byte copies the underlying buffer.
Methods on builtin types
All receivers are unchanged; results are new values.
String methods
Receiver str. Positions and counts are in characters.
split(sep str) []str— split on the separator.trim() str— strip leading and trailing white space.upper() str,lower() str— case conversion.contains(sub str) bool,starts_with(prefix str) bool,ends_with(suffix str) bool— substring, prefix, and suffix tests.replace(from str, to str) str— replace all occurrences.find(sub str) int?— character index of the first occurrence,noneif absent.fields() []str— split on runs of white space; no empty fields.lines() []str— split on line feeds; a trailing line feed adds no empty line.trim_prefix(p str) str,trim_suffix(p str) str— remove a leading or trailingpif present, else unchanged.chars() []str— the characters as one-character strings.repeat(n int) str— the string tiledntimes; negativenfaults, as does a result exceeding the implementation’s size limit (2^30 bytes in the reference implementation).
fn main() {
s := " the nevla book "
t := s.trim()
print(t.to_upper()) // THE NEVLA BOOK
print(t.split(" ").join("-")) // the-nevla-book
print(t.replace("book", "spec")) // the nevla spec
i := t.index("nevla")
if i != none {
print(i) // 4
}
print("na".repeat(2) + " batman") // nana batman
print(len("héllo")) // 5: characters, not bytes
}
List methods
Receiver []T.
map(f fn(T) U) []U— applyfto each element.filter(f fn(T) bool) []T— keep elements wherefis true.each(f fn(T))— callfon each element; no result.sum() T—Tmust beintorfloat; the sum of the elements. Integer overflow faults (chapter 12). Summing an empty[]intyields 0; the result of summing an empty[]floatis unspecified in v1 (the reference implementation yields a value that faults on later float use).sorted() []T—Tmust beint,byte,float, orstr; a fresh ascending list.sorted_by(before fn(T, T) bool) []T— a sorted copy per the comparator; the sort is stable.contains(v T) bool— structural membership (section 11.2).join(sep str) str—Tmust bestr; concatenation with the separator.
[]byte (T = byte) has every method above except sum and join,
which byte does not satisfy (byte is not int/float, and is not
str); sorted, sorted_by, contains, filter, each, and map
all apply, per the general rules above.
fn main() {
xs := [3, 1, 4, 1, 5]
print(xs.sorted()) // [1, 1, 3, 4, 5]
print(xs.map(fn(x) { x * 10 }).sum()) // 140
print(xs.filter(fn(x) { x > 2 })) // [3, 4, 5]
print(xs.contains(4)) // true
print(xs.sorted_by(fn(a, b) { a > b })) // [5, 4, 3, 1, 1]
}
Map methods
Receiver map[K]V. Iteration order is insertion order (section 5.3).
keys() []K— the keys, in insertion order.values() []V— the values, in insertion order.has(k K) bool— key presence.delete(k K)— removeskin place, Go’s delete; the remaining order is preserved.
keys() on a map[byte]V and values() on a map[K]byte are
compile-time errors: the result would need to be a compact []byte, but
the two methods build their result generically across every K/V,
independently of byteness, and have no compact []byte repack. Iterate
with a for k, v := range m instead (section 8.7), which binds the byte
key or value directly with no repack in the way.
fn main() {
m := map[str]int{"b": 2, "a": 1}
print(m.keys()) // [b, a]: insertion order, not sorted
print(m.has("a")) // true
m.delete("b")
print(m.values()) // [1]
}