Reference

Input/Output

Generated from LANGUAGE.md at bf2cba0, 2026-08-22.

Print

Print "Hello, World!".
Print the x.
Print 'add numbers' of 3 and 5.

Print without newline:

Print "Loading: " without newline.
Print progress without newline.
Print "%".

Format Strings

Embed variables and expressions directly in strings using curly braces {}:

a text called name is "Alice".
a number called age is 25.
Print "Hello, {name}! You are {age} years old.".

Format Specifiers

SpecifierDescriptionExampleOutput
{var}Default formatting{name}Alice
{var:.N}N decimal places{pi:.2}3.14
{var:N}Pad to N characters{x:6} 42
{var:0N}Zero-pad to N chars{x:06}000042
{var:x}Hexadecimal (lowercase){n:x}0xff
{var:X}Hexadecimal (uppercase){n:X}0xFF
{var:b}Binary{n:b}101
{var:o}Octal{n:o}0o10
{var:04x}Padded hex{n:04x}0x00ff

The value inside {} must be a variable or expression, not a bare literal — {255:x} is rejected (255 is read as a variable name). The examples above assume a declared a number called n is 255. (set n to 5 or 8 for the binary and octal rows).

N is a count in both forms, and both render in full: {var:N} pads out to N characters and {var:.N} prints exactly N decimal places, correctly rounded (an exact tie goes to the even digit). Neither is capped — a very large N is simply a very large amount of output — but N has to be a count the compiler can hold, at most 9223372036854775807; past that it is a compile error naming the limit, not a width that quietly does nothing. A precision past the value's exact decimal expansion pads with zeros, since the expansion has ended and not because accuracy has run out: a float is a binary fraction, so it always has an exact finite expansion, and {pi:.50} prints all fifty places of it. A whole number has an exact expansion too — itself, then zeros — so {n:.2} on a number called n is 255. prints 255.00, and prints it exactly for every number Vox can hold.

A specifier has to be one the value's type can answer. A width asks nothing of a type: every rendering is some number of characters long, so {var:N} is accepted whatever var is (on a float or a text the value is rendered and the padding is not applied yet). The other two do ask something, and asking it of the wrong type is a compile error naming the way out, not a wrong answer:

  • {var:.N} counts places in a number's decimal expansion. A number, a float and a boolean have one; a text or a buffer does not.
  • {var:x}, {var:X}, {var:b} and {var:o} write a whole number in another base. A number and a boolean are whole numbers. A float is not — {ratio:b} is refused rather than quietly dropping the fraction, so write {ratio as a number:b} when that is what you meant — and neither is a text or a buffer.

Inside an expression hole the cast has nowhere to go — the braces a whole-expression cast needs are the hole's own — so work the value out into a number first and render that:

a float called ratio is 2.5.
a number called total is {ratio multiply 2.0} as a number.
Print "{total:x}".                (0x5)

A value, a list and a map render through their own routines, which ignore a specifier and print the value; a value's type is not known until it runs, so there is nothing to check.

The two compose: {var:8.2} asks for two decimal places, padded out to eight characters. The precision decides the digits and the width decides the padding, and each is honoured wherever there is something to honour it with — the same rule the width follows on its own. So {n:8.2} on a number called n is 255. prints 255.00, and {n:08.2} prints 00255.00; on a float the places are printed and the padding is not, because there is no float padder yet — exactly as a bare {f:8} prints the float unpadded. (A width composes with a radix too, which is the {var:04x} row above.)

Expressions in Format Strings

a number called x is 10.
a number called y is 3.
Print "Sum: {x add y}".
Print "Product: {x multiply y}".
Print "Arguments: {arguments's count}".

Format Strings as Values

Format strings are expressions, not just print arguments. Used as a value, a format string materializes into a fresh NUL-terminated string, so it works as a text initializer or assignment and survives being carried through lists (e.g. into an Execute argument list):

a buffer called word is 64 bytes in size.
copy hello to word.

a text called tok is "{word}".        (text from buffer contents)
a text called path is "/bin/{tok}".   (text from another text)

a list called cmdargs is [].
append tok to cmdargs.
Execute "/bin/echo" with arguments cmdargs.

Each evaluation allocates a new string; the source buffer can be cleared and reused without affecting texts already created from it.

Format Strings Everywhere

Every statement that takes a string value accepts a format string: write, buffer set/copy/append, filesystem paths (Create a directory called "{base}/{name}"), treating clauses, and function arguments. All sinks share one name resolver, so special names like {arguments's first} and {current time's hour}, format specifiers, and the 0x/0o hex/octal prefixes render identically whether the result is printed, written to a file, or built into a text or a buffer - a float's precision included: {ratio:.2} reads 2.50 in every one of them.

Declarations in Branches

A variable (or file handle) declared in EVERY branch of an if/otherwise chain definitely exists afterwards: it can be used after the branch and from inside functions, exactly like a top-level declaration. A name declared in only SOME branches remains scoped to its condition, and cross-condition use is still a compile error.

if 'output file' is empty then,
  Open a file for writing called output at 1.
Otherwise,
  Open a file for writing called output at 'output file'.

(output exists on every path - usable here and in functions)
write "hello\n" to output.

Escape Sequences

EscapeDescription
{{Literal {
}}Literal }
\nNewline
\tTab
\\Literal backslash

Example:

Print "Use {{braces}} for literal braces.".
Print "Tab:\there".
Print "Line1\nLine2".

Conditional Print

Print <default>, but if <condition> print <value>.

Chained conditions:

Print the number, but if <cond1> print "fizz buzz" but if <cond2> print "fizz" but if <cond3> print "buzz".

Rules:

  • First matching condition wins
  • Chain with but if or and if
  • Default value prints if no conditions match