Skip to content
kata / a language workbench

Syntax and keywords

KataScript source is UTF-8 text, conventionally saved as .ks. Names contain ASCII letters, digits, and underscores, with a letter or underscore first. Names are case-sensitive: self and Self have different meanings.

# starts a comment that continues to the end of the line. Whitespace separates tokens; indentation does not create scope. Semicolons are optional statement separators and do not suppress a final expression’s value. When a new statement begins with parentheses or another postfix form, a semicolon can make the boundary explicit.

# Two bindings, followed by a call.
let width = 6; let height = 7
print(width * height)

let name = expression creates a binding in the current scope. name = expression reassigns an existing binding. A later let with the same name shadows the earlier binding. let name: Type = expression checks the initializer at runtime; it does not retain a constraint on later reassignment.

Tuple patterns destructure a value: let (name, score) = ("Ada", 42). Use _ to discard a position. Patterns can nest, but a binding name may occur only once within one pattern. There is no const declaration.

if condition { ... } elif condition { ... } else { ... } selects a branch. The selected branch’s final expression supplies its value; without a matching branch, the result is nil. See truthiness.

with { ... } introduces a standalone scope. with name = expression, ... { ... } also introduces local bindings. Its final expression supplies the block’s value. Bare braces are not a standalone expression block.

let area = with width = 6, height = 7 { width * height }
let label = if area > 40 { "large" } else { "small" }
print(label)

while condition { ... } repeats while its condition is truthy. for pattern in expression { ... } traverses an iterable, binding each item to the pattern. in belongs to for; it is not a membership operator. Use a collection’s lookup methods to test membership.

bail exits the nearest loop. cont skips the rest of the current iteration. ret expression exits the enclosing function, including from inside a loop.

for n in [1, 2, 3, 4] {
if n == 2 { cont }
if n == 4 { bail }
print(n)
}

Loops use the iteration protocols. Built-in range syntax and a range() function are not available.

match expression { pattern -> expression, ... } chooses the first matching arm. An arm can also contain a braced statement body. Patterns include literals, tuples, enum variants, bindings, and the wildcard _. Negative numeric literals and nested patterns are supported.

let item = Opt[Int].Val(42)
match item {
Val(n) -> print(n),
Non() -> print("absent"),
}

Construct a unit variant as Opt[Int].Non; match it with Non(). A bare name in a pattern binds the value, so Non alone is not a unit-variant test. Match guards and exhaustive-match checking are not implemented. See the guide for a walkthrough.

import dsa binds a module value. import mem.{Ptr, Buf} binds selected exports directly. Imports resolve the embedded standard library, not arbitrary local files. See modules.

unsafe { ... } permits raw memory intrinsics inside its body. It is intended for low-level standard-library work; it does not add an ownership model or make an operation valid by itself. See memory facilities.

These are the reserved words in the interpreter’s Token enum. Builtin names such as print, Int, and Arr are identifiers rather than keywords.

WordsMeaning
true, false, nilLiteral values
letCreate a binding
if, elif, else, withSelect a branch or introduce a scope
while, for, in, bail, contIterate and control a loop
matchSelect by pattern
func, retDefine and return from functions
kind, enum, typeDefine a product, sum, or interface
impl, self, SelfDefine methods
asDeclare conformance or take an interface view
import, unsafeImport library names or permit memory intrinsics