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#
| Specifier | Description | Example | Output |
|---|---|---|---|
{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 width may be zero - {var:0} and {var:00} pad nothing, the same no-op as any width too small to add characters. 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 in the table, and one the value's type can answer. A clause after the colon that is none of them is a compile error naming the valid forms. A width asks nothing of a type: every rendering is some number of characters long, so {var:N} is accepted whatever var is, and a number, a float, a text and a buffer are each padded out to N characters. 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. Anumber, afloatand abooleanhave one; atextor abufferdoes not.{var:x},{var:X},{var:b}and{var:o}write a whole number in another base. Anumberand abooleanare whole numbers. Afloatis 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 atextor abuffer.
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, on a number and a float alike. So {n:8.2} on a number called n is 255. prints 255.00, and {n:08.2} prints 00255.00. (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. A text variable reassigned from a format string releases the string it no longer holds.
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#
| Escape | Description |
|---|---|
{{ | Literal { |
}} | Literal } |
\n | Newline |
\t | Tab |
\\ | 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 "FizzBuzz" but if <cond2> print "Fizz" but if <cond3> print "Buzz".
Rules:
- First matching condition wins
- Chain with
but iforand if - Default value prints if no conditions match