# Basics

This page is the "Basics" 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/) > Basics

## Statements

Every statement ends with a **period** (`.`).

```
Print "Hello, World!".
```

## Case Sensitivity

Keywords are **case-insensitive**. These are equivalent:

- `Print`, `print`, `PRINT`
- `If`, `if`, `IF`

## Comments

Comments use **parentheses** `( )` — just like parenthetical remarks in natural language writing.

```
(This is a comment)
print "Hello".

print "World". (end of line comment)

a number (the counter) called x is 5.

(Multi-line comments
work naturally across
several lines)

(Nested (parentheses (are supported)) too)
```

Comments can appear:

- On their own line
- At the end of a statement
- In the middle of a statement (between tokens)
- Spanning multiple lines

## Paragraph Breaks (Blank Lines)

Blank lines (paragraph breaks) organize code into logical sections. They are optional and have no effect on program execution *between two fully-terminated top-level constructs* — for example, between two function definitions or between two complete statements at the top level.

Inside an open clause they are **not** cosmetic: a blank line force-closes every clause that is still open, including an enclosing function definition. Use a blank line to end a construct deliberately (after a `while`, `for each`, `repeat`, `on error`, or nested `if` body), not to add visual spacing in the middle of a body.

```
print "Section 1".

print "Section 2".
```

**Note:** A function definition is closed by a **blank line (paragraph break)** — this is *required*, not a style convention. A period closes only the innermost open clause (rule 1 below), so the period ending a body statement does **not** close the function. Without a blank line after the body, every statement following the signature is absorbed into the function body; since the function is typically not called from within itself, the program then silently does nothing (exit 0, no output). A following `To` or `Library` does begin a new top-level construct and so ends the body, but any other statement is absorbed. The compiler warns when a function definition is still open at end of file. See [The termination rule](https://vox-lang.dev/docs/basics/#the-termination-rule) below.

## Sentence Consumption

Action-consuming constructs (loops, conditionals, error handlers) consume the **entire sentence** they appear in. Multiple actions within that sentence are separated by **commas**.

```
(Single action)
While x is less than 10, increment x.

(Multiple comma-separated actions in one sentence)
While x is less than 10, print x, increment x.

(For loops work the same way)
For each number from 1 to 10, print the number, print " ".

(Error handlers too)
On error print "Something went wrong", exit 1.

(If/else with multiple actions)
If x is greater than 10 then, print "big", set y to 1. Otherwise, print "small", set y to 0.
```

**Key Rules:**

- **Period** (`.`) ends the entire construct, including all its actions
- **Comma** (`,`) separates multiple actions within the same construct
- Only **function definitions** can span multiple sentences (using paragraph breaks)

**Sentence ownership (nested constructs):**

- A nested construct (especially `if ... then`) owns its **own trailing period**.
- Outer constructs (`while`, `for each`, `repeat`) do **not** steal that inner period.
- After an inner `if` ends, the outer sentence may continue with more actions.

```
While content is not empty,
    if number_lines then,
        print "{line}: " without newline.
    write content to output,
    read line from source into content.
```

In the example above, the period after the inner `if` closes only that `if`. The `while` body continues with `write` and `read`.

## The termination rule

Two rules govern where a construct's body ends, and together they explain everything above precisely:

1. **A period closes the most recently opened clause** — the innermost one currently open (`if`, `on error`, `for`, `while`, `repeat`), and only that one. This is why the nested `if` example above works: its period closes the `if`, not the `while`. One period closes one level; to close more than one, write more than one — see [Closing more than one level](https://vox-lang.dev/docs/basics/#closing-more-than-one-level).
2. **A blank line (paragraph break) force-closes every open clause at once** — including an enclosing function definition. Think of nested HTML `<div>`s: a paragraph boundary closes all of them together, the same way you would never continue a single sentence across a paragraph break in English.

```
(A blank line closes everything still open, not just the nearest thing)
a number called retries is 0.
While retries is less than 3,
    if retries is equal to 1 then, print "retrying".
    the retries is retries add 1.

Print "done".
```

This prints `retrying` once (when `retries` is 1) then `done` once, after the loop runs its full three iterations — the blank line closes the `while` (rule 2) even though the `if`'s own period already closed the `if` (rule 1); there is nothing special about the `if` being the loop's last action, the blank line would close the loop the same way after any kind of action.

**This applies uniformly** — `while`, `for each`, `repeat`, and `on error` all terminate their body on a blank line, regardless of what the last body statement was (an ordinary statement, an `if`/`on error`, or another nested loop).

**Caution:** because rule 1 means a nested construct's own period doesn't close its parent, a blank line placed purely for visual readability *inside* a loop body — after a nested `if` or a nested loop, before more of the same loop's actions — will close that loop early, not just add whitespace:

```
(This blank line is NOT cosmetic - it ends the outer while)
a number called round is 0.
While round is less than 2,
    the round is round add 1,
    For each item in batch,
        print item.

    print "batch done".
```

This prints `1 2 1 2 batch done` — not `1 2 batch done 1 2 batch done` as the indentation suggests. `print "batch done".` runs once, after the loop, not once per batch, because the blank line closed the `while` right after the nested `for each` closed itself.

**This can hang your program with no error message, if the ejected statement happens to be the loop's own increment:**

```
(DON'T DO THIS - infinite loop, no diagnostic, the blank line ejects the increment)
a number called counter is 1.
While counter is less than or equal to 2,
    For each k from 1 to 2,
        print "inner {k}".

    increment counter.
Print "end".
```

`increment counter.` is ejected from the `while` body by the same blank line, so `counter` never changes and the loop never becomes false — it hangs forever, printing `inner 1` / `inner 2` on repeat, and `Print "end".` never runs. There is no error, no warning, and nothing in the output points at the blank line as the cause. If a loop that should terminate hangs instead, check for a blank line inside its body first.

A blank line placed **after a comma** (mid-sentence, more actions still to come) is the one exception — it is still just visual spacing there, since the sentence is explicitly still open:

```
(Safe: this blank line follows a comma, so it stays cosmetic)
While retries is less than 3,
    print "attempt {retries}",

    increment retries.
```

## Closing more than one level

Rule 1 closes exactly one level, and rule 2 closes all of them. When you are nested several levels deep and want to come back up **some** of the way, **periods stack: write one period per level you want to close.**

```
(Three nested ifs, so three periods to get all the way back out)
a number called n is 0.
If n is equal to 1 then,
    If n is equal to 1 then,
        If n is equal to 1 then, print "innermost"...
print "back at the top".
```

This prints `back at the top`. The three periods close the innermost `if`, then the middle one, then the outer one, so `print "back at the top".` runs at the top level. Written with one period or two, it would still be inside an `if` whose condition is false, and would print nothing at all — with no error.

Indentation is **not** what decides this. Vox ignores leading whitespace entirely, so a program can be minified without changing its meaning; the period count is the only thing that closes a clause.

### This is how you choose which `if` an `Otherwise` belongs to

An `Otherwise` (or `But if`) continues the innermost `if` that is still open. Closing that `if` first is therefore how you hand the `Otherwise` to an enclosing one. These two programs differ by a **single character** and behave differently:

```
(ONE period: the Otherwise belongs to the INNER if)
a number called m is 5.
If m is equal to 1 then,
    If m is equal to 2 then,
        print "inner then".
    Otherwise,
        print "outer else".

Print "done".
```

prints only `done`. The `Otherwise` continued the inner `if`, so the whole construct sits inside `If m is equal to 1`, which is false — nothing in it runs.

```
(TWO periods: the inner if is closed, so the Otherwise belongs to the OUTER one)
a number called m is 5.
If m is equal to 1 then,
    If m is equal to 2 then,
        print "inner then"..
    Otherwise,
        print "outer else".

Print "done".
```

prints `outer else` then `done`, which is what the indentation in both versions suggests — but only the second one actually says it.

An empty `Otherwise,.` closes an inner chain the same way and is easier to read than a run of periods, since it names the thing being closed instead of asking you to count:

```
(Same result as two periods, spelled out instead of counted)
a number called m is 5.
If m is equal to 1 then,
    If m is equal to 2 then,
        print "inner then".
    Otherwise,.
    Otherwise,
        print "outer else".

Print "done".
```

This also prints `outer else` then `done`. The first `Otherwise,.` takes the inner `if`'s else branch and does nothing with it, which closes that chain; the second one is then free to continue the outer `if`.

**Get the count wrong and nothing tells you.** Too few periods and the following statements are absorbed into a clause you thought you had left; too many and they escape one you meant to stay in. Either way the program still compiles and still runs. If a branch never seems to execute, or a loop that should finish hangs instead, count the periods between it and the construct it belongs to — and remember the hanging case is the same one described above under rule 2: the absorbed statement is the loop's own increment.

## Ranges

Ranges define a sequence of numbers from a start to an end value. They are **not** allocated as lists - they compile directly to efficient loop constructs with a counter, bounds check, and increment.

```
(Basic range in for-each loop)
For each number from 1 to 10, print the number.

(Range with variable bounds)
Set start to 1.
Set end to 5.
For each number from start to end, print the number.

(Range in loop expansion - see below)
print each number from 1 to 10.
```

**Key points:**

- Ranges are **inclusive** - `1 to 5` includes 1, 2, 3, 4, and 5
- Ranges compile to efficient assembly loops, not list allocations
- The loop variable (`the number`) is available inside the loop body

## Loop Expansion

The `each...from` syntax is a **universal loop expansion** that works with any action. It transforms a single action into a loop that executes for each item in a collection or range.

```
(Print each item from a list)
print each number from [1, 2, 3].

(Print each number from a range)
print each number from 1 to 15.

(Call a user function for each item)
process of each item from mylist, print "done".

(Open a file for each argument)
a buffer called content.
open a file for reading called source at each filename from arguments's all,
  read from source into content,
  print the content,
  close source.
```

**Syntax:** `<action> each <variable> from <collection>, <additional actions>`

The action executes once per item in the collection or range, with the loop variable bound to each item. Additional comma-separated actions execute inside the loop after the main action.

**Works with:**

- `print each X from Y` - print each item
- `function of each X from Y` - call function for each item
- `open ... at each X from Y` - open file for each path
- Any action that takes an argument

**Supported collections:**

- **Ranges:** `1 to 10`, `start to end` - numeric sequences
- **Lists:** `[1, 2, 3]`, any list variable
- `arguments's all` - all command-line arguments (argv[1..])

### Chained `each` clauses — a grid

More than one `each <variable> from <collection>` clause may appear in a single sentence, joined by `and`. The action then runs once per element of the **Cartesian product** of the collections, in **row-major order** — the leftmost clause is the outermost loop, exactly as if the clauses were nested `For each` loops written left to right:

```
'pair' of each x from [1, 2] and each y from [10, 20].
```

runs `'pair'` four times — `(1,10), (1,20), (2,10), (2,20)` — identical to:

```
For each x from [1, 2],
    For each y from [10, 20],
        'pair' of x and y.
```

There is **no limit** on the number of clauses. A fixed (non-`each`) argument may sit among them in any position, and is evaluated once per call:

```
'pair' of 5 and each y from [10, 20].       (fixed first, then expansion)
'pair' of each x from [1, 2] and 99.        (expansion first, then fixed)
```

An inner collection may use a variable bound by an outer clause, giving triangle iteration:

```
'pair' of each row from [1, 2, 3] and each col from 1 to row.
```

A range bound in an `each` clause takes a primary, not an expression — `each col from row add 1 to 4` is a parse error. Brace an arithmetic bound: `each col from {row add 1} to 4`.

See **Loop Expansion with Collections** below for the arity rule, the empty-collection rule, duplicate loop variables, and after-loop values.

## Conditional Branching with `but if`

The `but if` clause is a generic conditional branch over a base action. It is available in both `for each` loops and loop expansion (`<action> each ... from ...`).

```
(FizzBuzz example - print number, but override with word if divisible)
print each number from 1 to 15,
    but if the number modulo 6 is equal to 0 print "fizzbuzz",
    but if the number modulo 2 is equal to 0 print "fizz",
    but if the number modulo 3 is equal to 0 print "buzz".

(Simple even/odd labeling)
print each number from 1 to 10,
    but if the number modulo 2 is equal to 0 print "even".

(Append to a list with a conditional override)
append each number from 1 to 5 to out,
    but if the number modulo 2 is equal to 0 append 0.

(With for-each loop)
For each number from 1 to 15,
  print the number,
    but if divisible of the number and 3 is true print "divisible by 3".
```

**Syntax:** `<base action>, but if <condition> <alternative action>, but if <condition> <alternative action>, ... [otherwise <default alternative action>].`

**How it works:**

1. The default action is the base statement.
2. Each `but if` clause is checked in order.
3. If a condition is true, that alternative action runs instead of the default.
4. If no conditions match, the default action runs.
5. An optional trailing `otherwise` clause provides a final alternative.

**Key points:**

- Conditions are checked in order - first match wins
- Multiple `but if` clauses can be chained
- The alternative action can be any valid Vox statement
- `otherwise` provides a catch-all alternative
- Works with both ranges and collections
- The loop variable (`the number`) is available in conditions
- In an `append` branch, the `to <list/buffer>` target may be omitted and is inherited from the base append statement; retargeting to a different list/buffer is not allowed

## Inline Substitution with `treating`

The `treating X as Y` clause performs inline value substitution - like bash's `${var//X/Y}` but readable.

```
(Replace '-' with "/dev/stdin" for each filename)
open a file for reading called source at each filename from arguments's all treating "-" as "/dev/stdin",
  read from source into content,
  write content to output,
  close source.

(Print with default value)
print each name from names treating "" as "Anonymous".

(Call function with substitution)
process of each filename from files treating "-" as "/dev/stdin".

(Append with substitution - the clause goes with the `each` clause, before
 the `to <destination>`)
append each name from names treating "" as "Anonymous" to cleaned.
```

**Syntax:** `... each <var> from <collection> treating <match> as <replacement>, ...`

If the loop variable equals `<match>`, it's replaced with `<replacement>` for that iteration.

Equality is by type as well as by value: a `<match>` whose type differs from the element's never fires, and that element comes through unchanged — and where the compiler can prove the mismatch, it says so at compile time instead. Where the element, the `<match>` or the `<replacement>` is a `value`, the runtime tag it carries is what the comparison reads, and a substitution that fires hands the `<replacement>`'s own type out with it.
