Skip to content

Control‐Flow

Humbat edited this page Oct 11, 2026 · 4 revisions

Control Flow & Switch Statements

Tera offers clean inline arrow syntax for rapid single-line statements alongside traditional block syntax for multi-line logic.


1. Conditional Statements (if/else)

You can write conditional branches using either a concise inline form or a traditional block form:

Inline Form

if: x > 5 => print("Greater!") else: print("Smaller!");

Block Form

if (x > 5) {
    print("Greater!");
} else {
    print("Smaller!");
}

2. Loops (while)Similarly, while loops support both inline and block structures:

Inline Form:

while: x > 0 => x = x - 1;

Block Form:

while (x > 0) {
    x = x - 1;
}

3. Loops (For):

Tera has two loop forms: a Tera-style for name: <iterable> that walks a value, and a C-style for (init; condition; update) with explicit clauses. Both accept a block body { ... } or an inline body => statement.

Form Syntax Walks
Tera-style for name: <iterable> { ... } any arr[T] — array literals, variables, and ranges such as 0..9
C-style for (init; condition; update) { ... } whatever the clauses say

Every example below was executed with node dist/bin.js <file>; the output shown is the real output, and every error string is copied verbatim.

Tera-style for

The iterable must be an array, and the loop variable is bound to the element type: iterating an arr[int] gives an int, arr[arr[int]] gives an arr[int], and so on.

// 1. over an inclusive range
for i: 0..4 {
  print("i =", i)
}

// 2. over an array literal
for fruit: ["apple", "banana", "cherry"] {
  print("fruit:", fruit)
}

// 3. over a range built from variables
let n: 3 => int
let upto: 0..n => arr[int]
for k: upto {
  print("k =", k)
}

// 4. nested loops over an array of arrays
let grid: [[1, 2], [3, 4]] => arr[arr[int]]
for row: grid {
  for cell: row {
    print("cell:", cell)
  }
}

Output:

i = 0
i = 1
i = 2
i = 3
i = 4
fruit: apple
fruit: banana
fruit: cherry
k = 0
k = 1
k = 2
k = 3
cell: 1
cell: 2
cell: 3
cell: 4

Ranges

start..end is inclusive and evaluates to a real arr[int], so it can be bound to a variable and reused. 3..3 yields [3], while a "backwards" range such as 5..1 yields nothing at all (see limitations).

for i: 3..3 {
  print("single:", i)
}

for i: 5..1 {
  print("should not print:", i)
}
print("descending range produced nothing")

for x: [] {
  print("should not print:", x)
}
print("empty array produced nothing")

Output:

single: 3
descending range produced nothing
empty array produced nothing

C-style for

// standard C-style loop: declaration, condition, update
for (let i: 0 => int; i < 3; i++) {
  print("i =", i)
}

// the init clause can assign to a variable declared outside the loop
let j: 10 => int
for (j = 0; j < 2; j = j + 1) {
  print("j =", j)
}

// every clause is optional; omit them all for an open-ended loop
let count: 0 => int
for (;;) {
  if (count == 3) {
    break
  }
  print("count =", count)
  count = count + 1
}

// `continue` still runs the update clause
for (let t: 0 => int; t < 5; t++) {
  if (t % 2 == 0) {
    continue
  }
  print("odd t:", t)
}

Output:

i = 0
i = 1
i = 2
j = 0
j = 1
count = 0
count = 1
count = 2
odd t: 1
odd t: 3

Clause rules:

Clause Optional? Accepted forms
init yes let name: <expr> => <type>, or any expression (e.g. an assignment)
condition yes any expression of type bool; omitting it means "always true"
update yes any expression — i++, i = i + 1, … — run after every iteration

The C-style form is disambiguated from the Tera-style form purely by the opening (.

Inline bodies and evaluation order

// inline bodies with `=>`
for i: 0..2 => print("inline i =", i)

// `0..n-1` binds as `0..(n - 1)` because `-` is tighter than `..`
let n: 4 => int
for i: 0..n-1 {
  print("last-index i =", i)
}

// the iterable is evaluated once, before the first iteration
fn makeRange() => arr[int] {
  print("computing the iterable")
  return 0..2
}
for v: makeRange() {
  print("v =", v)
}

// assigning to the loop variable does not change the iteration sequence
for i: 0..2 {
  print("loop i =", i)
  i = i + 100
  print("reassigned i =", i)
}

Output:

inline i = 0
inline i = 1
inline i = 2
last-index i = 0
last-index i = 1
last-index i = 2
last-index i = 3
computing the iterable
v = 0
v = 1
v = 2
loop i = 0
reassigned i = 100
loop i = 1
reassigned i = 101
loop i = 2
reassigned i = 102

Notice the last block: each iteration gets a fresh environment for the loop variable, so the write to i inside the body is discarded before the next iteration starts.

The loop variable is scoped to the loop

Both forms bind the loop variable in a child environment, so it is gone once the loop ends. A let in a C-style init clause is scoped the same way:

for i: 0..2 {
  print(i)
}
print(i)          // [Type Error] Undefined variable 'i'.
for (let i: 0 => int; i < 1; i++) {
  print("inside:", i)
}
print(i)          // [Type Error] Undefined variable 'i'.

Errors

Type errors (no line number yet):

Program Message
for x: 5 { } [Type Error] Cannot iterate over non-array type 'int'.
for c: "abc" { } [Type Error] Cannot iterate over non-array type 'string'.
for i: 0.."a" { } [Type Error] Range bounds must be of type 'int', got 'int' and 'string'.
for (let i: 0 => int; i; i++) { } [Type Error] For loop condition must be a boolean, got 'int'.
reading the loop variable after the loop [Type Error] Undefined variable 'i'.

Parse errors (these do carry a line number):

Program Message
for i 0..2 { } [Line 1] Expected ':' after loop variable. Got '0'
for i: 0..2 print(i) [Line 1] Expected '{' or '=>' before loop body. Got 'print'
for i: 0..5..2 { } [Line 1] Expected '{' or '=>' before loop body. Got '..'
for i: 0..(2) { } [Parser Error] Unexpected token '(' on line 1

The interpreter also guards non-array values at runtime ([Runtime Error] Cannot iterate over non-array value.), but the type checker rejects those programs first.

4. break and continue

break

Stops the innermost loop, or — when used inside a switch — stops the switch only.

continue

Skips the rest of the current iteration and jumps to the next one. In a C-style for (init; cond; update) clause the update step still runs before the next test.

Both statements are validated statically, so a misplaced one is a compile-time error, not a runtime surprise:

Situation Result
break outside any loop/switch [Type Error] 'break' used outside of a loop or switch.
continue outside any loop [Type Error] 'continue' used outside of a loop.
continue directly in a switch (no enclosing loop) [Type Error] 'continue' used outside of a loop.

break/continue written inside a function body are checked against that function's own loops — they never "escape" into the caller's loop.

Example: all four loop forms

// break in a while loop
let i: 0 => int
while (true) {
  if (i == 3) {
    break
  }
  print("i =", i)
  i++
}

// continue: skip even numbers
for n: 0..9 {
  if (n % 2 == 0) {
    continue
  }
  print("odd:", n)
}

// C-style for: continue still runs the update clause
for (let k: 0 => int; k < 5; k++) {
  if (k == 2) {
    continue
  }
  print("k =", k)
}

// break inside switch exits the switch, not the enclosing loop
for s: 0..2 {
  switch (s) {
    case 1:
      print("case one")
      break
    default:
      print("default:", s)
  }
  print("after switch, s =", s)
}

Output:

i = 0
i = 1
i = 2
odd: 1
odd: 3
odd: 5
odd: 7
odd: 9
k = 0
k = 1
k = 3
k = 4
default: 0
after switch, s = 0
case one
after switch, s = 1
default: 2
after switch, s = 2

Notice the last block: break ends the switch, then execution continues with after switch, s = ... in the enclosing loop.

Errors

break       // [Type Error] 'break' used outside of a loop or switch.
continue    // [Type Error] 'continue' used outside of a loop.

5. Built-in I/O (print & input)

Tera provides standalone built-in functions for handling console input and output streams:

  • print(...): Outputs values to the console.
  • input(prompt): Displays an optional prompt string to the user and captures the input.

Input Examples

// Direct assignment with input capture
let x: input("add here");
print(x);

// Re-assigning typed variables with user input
let name: "Guest" => string;
name = input("Enter your name here");
print("Hello, " + name + "!");

6. Switch Statements (switch)

Tera uses a traditional JavaScript-style switch statement for evaluating a single expression against multiple potential cases. The break keyword is used to exit the switch block and prevent fall-through execution:

let status: 200 => int;

switch (status) {
    case 200:
        print("OK!");
        break;
    case 404:
        print("Not Found!");
        break;
    case 500:
        print("Server Error!");
        break;
    default:
        print("Unknown status code");
        break;
}

7. Full Sample Program (main.tera)

Here is how everything comes together in a complete Tera program:

// Variable and array declarations
let threshold: 5 => int;
let numbers: [1, 2, 3, 6, 8] => arr[int];

// Function declaration
fn process(num: int) => int {
    if: num > threshold => print("Above threshold!") else: print("Below threshold!");
    return num * 2;
}

// Processing array elements
let i: 0 => int;

while (i < numbers.length) {
    let current: numbers[i];
    let result: process(current) => int;
    print(result);
    i = i + 1;
}