Skip to content

Search

Documentation

Balaur Script reference

Every construct of Balaur Script: types, values, operators, strings, lists, maps, optionals, structs, classes, enums and match, functions, any and modules.

Sur cette page

What each construct does, and what it refuses. The examples are programs from the compiler's test corpus (crates/balaur_bs/tests/programs/), which runs every one on wasmi, wasmtime, native code and the browser's engine and holds them to the same answer. Scripting covers how a script meets a node.

A file

A .bs file holds items in any order: fn, struct, class, enum, impl, const, extern fn and use. pub makes an item reachable from outside the file. // starts a comment.

const NAME = "ball";
pub fn label(hp: int) -> str { "{NAME}: hp {hp}" }

Types

Type Holds
int a 64-bit signed integer
float a 64-bit float
bool true or false
str UTF-8 text, counted by reference
Node a node of the scene, or none
[T] a list, counted by reference
{str: V}, {int: V} a map, kept in the order keys were first set
T? a T, or nil
any a value of any kind, looked at when the script runs
fn(A, B) -> R a function, named elsewhere
a struct a value, copied field by field
a class one object every holder shares
an enum one of its variants, a value

Vec2, Vec3 and Color are structs the engine declares, and a list of numbers stands where one goes: let p: Vec3 = [1.0, 2.0, 3.0];.

Variables and constants

let declares a variable, typed by its value or by what it names: let n = 3;, let x: float = 3;. A variable can be assigned again, and +=, -=, *=, /= and %= combine. A let inside a block ends with the block. A const is fixed for the file, and a module's reads as util::NAME.

Numbers

  • +, - and * on ints wrap past the ends of 64 bits.
  • / on ints truncates toward zero. Dividing by zero traps, and so does the smallest int divided by -1.
  • % takes the sign of the left side. % by zero traps.
  • An int meets a float as a float: n / 2.0 is a float, and let x: float = 3 holds 3.0.
  • as converts the rest: int as float, bool as int, and float as int, which truncates; a float past an int's range gives the nearest end, and NaN gives 0.
  • Floats follow IEEE 754, the same bits on every engine. A NaN the host sees is always the same NaN.

Strings

const NAME = "ball";
pub fn label(hp: int, speed: float, alive: bool) -> str {
    "{NAME}: hp {hp}, speed {speed}, alive {alive}, {{braces}}"
}
// label(7, 2.0, true) is "ball: hp 7, speed 2.0, alive true, {braces}"
  • "{x}" writes a value into the text, an any as its text(); {{ and }} write braces. \n, \t, \", \\ and \u{e9} are escapes.
  • + joins two strings and == compares them.
  • len counts bytes. slice(a, b) takes byte offsets; an offset inside a character moves back onto it, and one past the end stops at the end.
  • contains, starts_with, ends_with, find (or -1), replace, split, split_once, chars, trim, to_upper, to_lower, parse_int and parse_float (0 when the text is no number).

Lists

pub fn shuffle() -> [int] {
    let xs = [1, 2, 3];
    xs.insert(0, 0);
    xs.insert(4, 4);
    xs.insert(2, 9);
    let gone = xs.remove(2);
    xs.push(gone);
    xs
}
// shuffle() is [0, 1, 2, 3, 4, 9]
  • [a, b], and [value; count] for count copies.
  • push, pop, insert, remove, extend, sort, len, and at or xs[i] to read. An index past either end traps.
  • A list is shared, not copied: let b = a; b.push(1); changes a too.
  • for x in xs walks it in order.

Maps

pub fn ages() -> {str: int} {
    let ages: {str: int} = {"ada": 36, "alan": 41};
    ages["grace"] = 85;
    ages["ada"] += 1;
    ages
}
  • A key is a str or an int. A value is never nil: a missing key is.
  • m[k] reads a V?; m[k] = v sets. m[k] += 1 on a missing key traps.
  • has, remove, len, keys and values, in the order keys were set; a key removed and set again goes to the end.
  • A {str: V} goes into an any as a map and comes back out of one.
  • A literal is typed by where it goes: let m = {"a": 1}; does not compile.

Optionals

A T? is a T or nil. It is used through its methods: is_some, is_none, unwrap, which traps on nil, and or(fallback). x == nil compares. A value that is always there has none of them, and an optional cannot itself be optional.

pub fn names(k: int) -> str {
    let s: str? = if k > 0 { "pos {k}" } else { nil };
    s.or("none")
}

Structs and classes

A struct is a value: assigning or passing one copies its fields. A class is one object on the heap, shared by everyone holding it. A class holds itself through C? or a list; a struct only through a list.

pub struct Label { kind: str = "label", size: int = 3, text: str, tip: str? }
pub fn plain() -> str {
    let label = Label { text: "hi" };
    "{label.kind} {label.size} {label.text} {label.tip.is_none()}"
}
// plain() is "label 3 hi true"

A literal names what differs from the declared defaults. A field left out takes its default, or nil when it is optional; any other field must be named. impl Type { .. } gives a type its methods, with self writable.

Enums and match

An enum's variants carry nothing, values by position, or named fields. match takes the value apart.

enum Shape { Circle(float), Rect { w: float, h: float }, Empty }

fn area(s: Shape) -> float {
    match s {
        Shape::Circle(r) => r * r * 3.0,
        Shape::Rect { w, h } => w * h,
        Shape::Empty => 0.0,
    }
}
  • A variant is made by its path: Shape::Circle(2.0), Shape::Rect { w: 1.0, h: 2.0 }, Shape::Empty.
  • An arm names a variant and binds its values: by position, by field name, h: tall to rename, and _ to bind nothing.
  • _ takes any value; a bare name such as other takes any value and binds it.
  • A match names every variant once, or ends with an arm that takes any value. A missed variant, one named twice, or an arm after a catch-all does not compile.
  • The subject is worked out once. An arm may return, break or continue, and in an async fn an arm of a match statement may await.
  • An enum is a value, like a struct, so one cannot hold itself.
  • A module's enum is reached by its path: shapes::Shape::Circle(1.0).

Functions

fn double(x: int) -> int { x * 2 }
fn apply(f: fn(int) -> int, x: int) -> int { f(x) }
pub fn twice(k: int) -> int { apply(double, k) }
  • The last expression of a body, with no ;, is its value; return leaves early.
  • A function is a value: let f = double;, stored in a field, a list or an any. There are no closures, so a function value names a function.
  • fn grow(w: &mut Wave) writes back to the caller's own variable or field when it returns. The argument must be a variable or a field, and one place is never passed to two &mut parameters of one call.

Control flow

  • if is a value when both branches give one: let sign = if clockwise { 1.0 } else { -1.0 };.
  • while cond { .. }, loop { .. }, for i in a..b { .. } and for x in xs { .. }, with break and continue.
  • && and || stop at the first side that decides.

any

An any holds what has no one type: an HTTP reply, a save, a host's payload. Ints, floats, bools, strings, nodes, lists, structs and optionals box into one where one is wanted, and an any becomes the typed value a place asks for: let n: int = a["n"];.

kind, is_nil, as_int, as_float, as_bool, as_str, as_node, len, get, at, has, keys, text, with, set, push, put, insert, remove, equals and sorted. a.name reads a.get("name"). map().with("k", v) builds a map. A map or a list in an any changes in place for every holder.

Modules

use util; links util.bs from beside the file or a folder above it, and its pub items read as util::name. use a::b; is a/b.bs. Files may use each other, and the file that uses them.

Async

async fn may await a host call or another script async fn, and the host resumes it on a later frame. An async fn takes no &mut parameter: its caller runs on while it waits.

Errors

A compile error names its line and column: 6:7: cannot combine int and bool. A file that reads gives one error for each function that does not compile. A trap at run time (an index out of range, unwrap on nil, a division by zero, a missing map key under +=) ends the call, and the next call runs.

Not in the language

Generics, traits, closures, T?? and any?.