Repository navigation
Control‐Flow
Tera offers clean inline arrow syntax for rapid single-line statements alongside traditional block syntax for multi-line logic.
You can write conditional branches using either a concise inline form or a traditional block form:
if: x > 5 => print("Greater!") else: print("Smaller!");if (x > 5) {
print("Greater!");
} else {
print("Smaller!");
}while: x > 0 => x = x - 1;while (x > 0) {
x = x - 1;
}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.
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
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
// 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 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.
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'.
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.
Stops the innermost loop, or — when used inside a switch — stops the switch only.
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.
// 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.
break // [Type Error] 'break' used outside of a loop or switch.
continue // [Type Error] 'continue' used outside of a loop.
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.
// 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 + "!");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;
}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;
}