Control Flow
Conditionals, loops, switch, exceptions, and optional chaining — all with familiar syntax.
if / else
Parentheses around the condition are required. Braces around each branch are required:
let score = 85
if (score >= 90) {
out "A"
} else if (score >= 75) {
out "B"
} else if (score >= 60) {
out "C"
} else {
out "F"
}while
let i = 0
while (i < 5) {
out i
i++
}
// → 0, 1, 2, 3, 4for
C-style for loop. The variable is scoped to the loop — it doesn't exist after the closing brace:
for (let i = 0; i < 5; i++) {
out i
}
// → 0, 1, 2, 3, 4
// Sum of an array by index
let nums = [10, 20, 30, 40, 50]
let sum = 0
for (let i = 0; i < nums.length; i++) {
sum += nums[i]
}
out sum // → 150for-in
Iterate directly over every element in an array, or every character in a string:
let fruits = ["apple", "banana", "cherry"]
for (let fruit in fruits) {
out fruit
}
// → apple, banana, cherry
// Over a string — visits each character
let result = ""
for (let c in "abc") {
result = result + c + "-"
}
out result // → a-b-c-for-in is a copy. Mutating it doesn't change the original array. Use an index loop if you need to modify elements.do-while
Runs the body at least once, even if the condition is false from the start:
let i = 0
do {
out i
i++
} while (i < 3)
// → 0, 1, 2
// Runs once even when condition is false
let x = 100
do {
out "ran once"
} while (x < 0) // → ran oncebreak and continue
// break — exit the loop early
for (let i = 0; i < 10; i++) {
if (i == 5) { break }
out i
}
// → 0, 1, 2, 3, 4
// continue — skip to the next iteration
for (let i = 0; i < 5; i++) {
if (i == 2) { continue }
out i
}
// → 0, 1, 3, 4Labeled loops
Break or continue an outer loop from inside a nested one:
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (j == 1) { continue outer } // skip inner, go to next i
out "{i},{j}"
}
}
// → 0,0 1,0 2,0switch
Match a value against cases. No fall-through — only the matched case runs. One case can match multiple values:
let day = 3
switch (day) {
case 1: { out "Monday" }
case 2: { out "Tuesday" }
case 3: { out "Wednesday" }
default: { out "Other" }
}
// → Wednesday
// Multiple values per case
switch (day) {
case 1, 2, 3, 4, 5: { out "Weekday" }
case 6, 7: { out "Weekend" }
}Exceptions
Use throw to raise an error, try/catch to handle it, and finally for cleanup that always runs:
fn int divide(int a, int b) {
if (b == 0) { throw "Cannot divide by zero" }
return a / b
}
try {
let result = divide(10, 0)
out result
} catch (e) {
out "Error: {e}" // → Error: Cannot divide by zero
} finally {
out "Done." // always runs
}You can throw any value — strings, numbers, objects:
throw 404
throw { code: 500, message: "Internal error" }catch and finally are both optional. Use just finally when you only need cleanup:
try {
riskyOperation()
} finally {
cleanup() // runs even if riskyOperation throws
}Optional chaining
Use ?. to call a method or access a field only when the value is not null. If it is null, the whole expression returns null instead of crashing:
let s = null let upper = s?.toUpperCase() // s is null → upper = null, no error // Combine with ?? for a safe fallback let result = s?.toUpperCase() ?? "no value" out result // → no value // Chain multiple ?. let value = a?.getNext()?.getValue() ?? 0