# Input/Output

This page is the "Input/Output" section of the Voxlang language reference. It is generated from LANGUAGE.md in the Voxlang compiler's own repository, so it says what the specification says and nothing more.

A URL is the heading, lowercased, with punctuation dropped and spaces turned into hyphens. Each "##" section of the spec is a page under /docs/, and every heading inside it is a fragment on that page, so "File I/O" is /docs/file-io/ and "Reading a whole file" inside it is /docs/file-io/#reading-a-whole-file.

As data: https://vox-lang.dev/docs/index.json lists every section and every heading with its URL, https://vox-lang.dev/docs/search.json carries one entry per heading with its first sentence and keywords, and https://vox-lang.dev/docs/anchors.json maps every slug to the page it lives on. Search is also a JSON endpoint: https://vox-lang.dev/docs/search?q=<words> returns ranked results as JSON, no page load.

Source: LANGUAGE.md at bf2cba0, 2026-08-22. https://github.com/Vox-lang/vox/blob/bf2cba037976586c30d22a48e7b7246d347f54a1/LANGUAGE.md

[Reference](https://vox-lang.dev/docs/) > Input/Output

## 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 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

| 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

```vox fragment
Print <default>, but if <condition> print <value>.
```

**Chained conditions:**

```vox fragment
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
