Verified examples#
> Every program on this site, compiled and executed by a real V3 binary.
45/45
documentation examples compileVerified with freak 0.14.2 (Maverick) on 2026-09-12T16:12:25Z. Every code block on this site marked compiles was built by that compiler and, where it produces output, executed.
Each card below shows the exact source file under examples/, a badge for whether it compiled and ran, and the program's captured output. Reproduce the whole set with:
shell
python tools/verify.py --freak /path/to/freak
python tools/build_docs.pyThe harness copies each example into a clean temporary directory, runs freak build with the default LLVM backend, executes the resulting binary, and records stdout verbatim. A failure is recorded with its diagnostics rather than hidden.
Basics#
-- The smallest FREAK program.
-- `say` is always available; no import is needed.
task main() -> void {
say "Hello from FREAK"
}
Program output
Hello from FREAK
-- Bindings are declared with `pilot`.
-- `fixed pilot` marks an immutable binding.
-- `pilot mut` opts a binding into reassignment.
task main() -> void {
pilot answer: int = 42
pilot ratio: num = 3.5
pilot callsign: word = "Shirogane"
pilot ready: bool = true
fixed pilot MAX_SORTIES: int = 12
pilot mut score: int = 0
score = score + 10
score += 5
say word_from_int(answer)
say format_num(ratio)
say callsign
say word_from_bool(ready)
say word_from_int(MAX_SORTIES)
say word_from_int(score)
}
Program output
42
3.5
Shirogane
true
12
15
-- The type annotation is optional. V3 infers from the initializer.
-- When you do annotate, the annotation is a single identifier:
-- `int`, `num`, `word`, `bool`, or the name of a shape.
task main() -> void {
pilot a = 7 -- int
pilot b = 2.5 -- num
pilot c = "text" -- word
pilot d = false -- bool
say word_from_int(a)
say format_num(b)
say c
say word_from_bool(d)
}
Program output
7
2.5
text
false
-- V3 matches keywords CASE-INSENSITIVELY. `pilot`, `Pilot` and `PILOT`
-- are all the same keyword token, so none of them can be used as a name.
--
-- This bites most often on: Pilot, Result, Max, Route, Check, Move,
-- Copy, Some, Ok, Err, Got, Use, In, As, Each, Times, Done, Max.
--
-- Pick a synonym instead. These all compile:
shape Aviator {
tag: word
}
task outcome(value: int) -> word {
if value > 0 { give back "positive" }
give back "non-positive"
}
task main() -> void {
pilot a: Aviator = Aviator { tag: "V-1" }
pilot verdict: word = outcome(3)
pilot upper_bound: int = 100
say a.tag
say verdict
say word_from_int(upper_bound)
}
Program output
V-1
positive
100
-- A file does not need a `main`. Top-level statements run in order,
-- and top-level bindings are visible to every task in the file.
pilot squadron: word = "Valkyries"
pilot strength: int = 4
task roster() -> word {
give back "{squadron} x{strength}"
}
say "booting"
say roster()
strength = 5
say roster()
Program output
booting
Valkyries x4
Valkyries x5
Tasks#
-- `task` declares a function. `give back` returns a value.
-- A block-bodied task that returns a non-void type must use `give back`
-- on every path; there is no implicit tail return.
task add(a: int, b: int) -> int {
give back a + b
}
task shout(message: word) -> void {
say message.to_upper()
}
task classify(n: int) -> word {
if n < 0 { give back "negative" }
if n == 0 { give back "zero" }
give back "positive"
}
task main() -> void {
say word_from_int(add(20, 22))
shout("stand by")
say classify(0 - 3)
say classify(0)
say classify(9)
}
Program output
42
STAND BY
negative
zero
positive
-- Tasks may call themselves, and may call tasks declared later in the
-- file: every task is indexed before any body is checked.
task factorial(n: int) -> int {
if n <= 1 { give back 1 }
give back n * factorial(n - 1)
}
task fib(n: int) -> int {
if n < 2 { give back n }
give back fib(n - 1) + fib(n - 2)
}
task main() -> void {
say word_from_int(factorial(6))
say word_from_int(fib(12))
}
Program output
720
144
-- `|>` feeds the left value in as the FIRST argument of the call on the
-- right, so `x |> f(y)` is exactly `f(x, y)`.
task double(n: int) -> int { give back n * 2 }
task offset(n: int, by: int) -> int { give back n + by }
task main() -> void {
say word_from_int(5 |> double())
say word_from_int(5 |> offset(3))
say word_from_int(5 |> double() |> offset(1))
}
Program output
10
8
11
Control flow#
-- `if` / `else if` / `else`. Conditions take no parentheses.
task describe(power: int) -> word {
if power > 9000 {
give back "over nine thousand"
} else if power > 5000 {
give back "high"
} else if power > 1000 {
give back "moderate"
} else {
give back "low"
}
}
task main() -> void {
say describe(9001)
say describe(6000)
say describe(2000)
say describe(10)
}
Program output
over nine thousand
high
moderate
low
-- `when` matches a subject against literal arms. `_` is the catch-all.
-- Each arm takes one statement; use a block to run several.
task main() -> void {
pilot code: int = 2
when code {
1 -> say "launch"
2 -> {
say "hold"
say "await orders"
}
3 -> say "abort"
_ -> say "unknown"
}
pilot phase: word = "sortie"
when phase {
"briefing" -> say "in the briefing room"
"sortie" -> say "in the air"
_ -> say "elsewhere"
}
}
Program output
hold
await orders
in the air
-- V3 has three loop forms: a counted loop, a condition loop,
-- and the bounded `training arc`.
task main() -> void {
-- repeat N times
pilot mut total: int = 0
repeat 5 times {
total += 2
}
say word_from_int(total)
-- repeat until CONDITION (tested before each pass)
pilot mut countdown: int = 3
repeat until countdown == 0 {
say word_from_int(countdown)
countdown -= 1
}
-- break and continue work in every loop form
pilot mut seen: int = 0
repeat 10 times {
seen += 1
if seen == 3 { continue }
if seen == 6 { break }
}
say word_from_int(seen)
}
Program output
10
3
2
1
6
-- `training arc` is a loop with a mandatory session cap.
-- It stops when the condition becomes true OR when the cap is hit,
-- so it cannot spin forever.
task main() -> void {
pilot mut power: int = 0
training arc until power >= 100 max 8 sessions {
power += 20
}
say word_from_int(power)
-- The cap wins when the condition is never reachable.
pilot mut stuck: int = 0
training arc until stuck > 1000 max 4 sessions {
stuck += 1
}
say word_from_int(stuck)
}
Program output
100
4
-- A slightly larger program: integer square root by bisection, driven by
-- a `training arc` so the loop is provably bounded.
shape Bounds {
lo: int
hi: int
}
task isqrt(target: int) -> int {
if target < 2 { give back target }
pilot mut b: Bounds = Bounds { lo: 1, hi: target }
training arc until b.lo >= b.hi max 64 sessions {
pilot mid: int = (b.lo + b.hi + 1) / 2
if mid * mid > target {
b.hi = mid - 1
} else {
b.lo = mid
}
}
give back b.lo
}
task main() -> void {
say word_from_int(isqrt(0))
say word_from_int(isqrt(1))
say word_from_int(isqrt(15))
say word_from_int(isqrt(16))
say word_from_int(isqrt(17))
say word_from_int(isqrt(1000000))
}
Program output
0
1
3
4
4
1000
Operators#
-- Arithmetic, comparison and logic.
task main() -> void {
pilot a: int = 17
pilot b: int = 5
say word_from_int(a + b)
say word_from_int(a - b)
say word_from_int(a * b)
say word_from_int(a / b)
say word_from_int(a % b)
say word_from_bool(a > b)
say word_from_bool(a == 17)
say word_from_bool(a != b)
say word_from_bool(a <= 17)
say word_from_bool(a > b and b > 0)
say word_from_bool(a < b or b > 0)
say word_from_bool(not false)
-- Unary minus applies to an expression. A negative *literal*
-- is written as a subtraction: `0 - 5`.
pilot neg: int = 0 - 5
say word_from_int(-neg)
}
Program output
22
12
85
3
2
true
true
true
true
true
true
true
5
-- The anime operators that V3 actually implements.
-- The three unary forms are PREFIX and accept `int` only.
task main() -> void {
pilot base: int = 6
say word_from_int(FINAL FORM base) -- base * base
say word_from_int(PLUS ULTRA base) -- base * 2
say word_from_int(TSUNDERE base) -- 0 - base
-- NAKAMA is infix, at the same precedence as + and -.
say word_from_int(3 NAKAMA 4)
}
Program output
36
12
-6
8
Words#
-- `word` is FREAK's string type. These methods are compiler builtins,
-- available with no import.
task main() -> void {
pilot s: word = " Muv-Luv Alternative "
say s.trim()
say word_from_int(s.length())
say s.trim().to_upper()
say s.trim().to_lower()
say word_from_bool(s.contains("Luv"))
say word_from_bool(s.trim().starts_with("Muv"))
say word_from_bool(s.trim().ends_with("ive"))
say s.trim().replace("Luv", "Love")
say s.trim().substring(0, 3)
say s.trim().char_at(0)
-- Concatenation with `+`, or with the builtin.
say "Muv" + "-" + "Luv"
say word_concat("XM", "3")
-- Numeric conversion in both directions.
say word_from_int("108".to_int())
say format_num("2.5".to_num())
say char_to_word(70)
}
Program output
Muv-Luv Alternative
23
MUV-LUV ALTERNATIVE
muv-luv alternative
true
true
true
Muv-Love Alternative
Muv
M
Muv-Luv
XM3
108
2.5
F
-- `{path}` inside a double-quoted word interpolates a binding.
-- A path is an identifier plus zero or more `.field` hops, and must
-- resolve to word, int, num or bool. Anything that is not a valid path
-- stays literal text.
-- (`Aviator`, not `Pilot`: V3 keywords are case-insensitive, so `Pilot`
-- is the `pilot` keyword and cannot name a shape.)
shape Aviator {
name: word
score: int
}
impl Aviator {
task summary(self) -> word {
give back "{self.name} scored {self.score}"
}
}
task main() -> void {
pilot callsign: word = "Valkyrie"
pilot sorties: int = 12
pilot rate: num = 0.75
pilot active: bool = true
say "callsign={callsign} sorties={sorties}"
say "rate={rate} active={active}"
pilot p: Aviator = Aviator { name: "Meiya", score: 98 }
say "shape: {p.name} / {p.score}"
say p.summary()
-- Not a path, so it is printed literally:
say "literal {1 + 2} braces"
}
Program output
callsign=Valkyrie sorties=12
rate=0.75 active=true
shape: Meiya / 98
Meiya scored 98
literal {1 + 2} braces
-- `word_builder::*` accumulates a word without the quadratic cost of
-- repeated `+` concatenation. The builder is an `int` handle.
task main() -> void {
pilot b: int = word_builder::new()
word_builder::append(b, "Mission: ")
word_builder::append(b, "Valkyries")
word_builder::append_char(b, 32) -- a space, by codepoint
word_builder::append_int(b, 4)
say word_from_int(word_builder::length(b))
-- `finish` consumes the builder and returns the accumulated word.
say word_builder::finish(b)
-- with_capacity pre-allocates; clear reuses; discard frees unused.
pilot c: int = word_builder::with_capacity(64)
word_builder::append(c, "scratch")
word_builder::clear(c)
word_builder::append(c, "reused")
say word_builder::finish(c)
}
Program output
20
Mission: Valkyries 4
reused
Shapes#
-- `shape` declares a record type. Each field is `name: type`,
-- where the type is a single identifier.
shape Vector2 {
x: int
y: int
}
shape Squad {
callsign: word
members: int
ready: bool
}
task main() -> void {
pilot v: Vector2 = Vector2 { x: 3, y: 4 }
say word_from_int(v.x)
say word_from_int(v.y)
-- Fields are assignable.
v.x = 10
say word_from_int(v.x)
pilot s: Squad = Squad { callsign: "Valkyries", members: 4, ready: true }
say s.callsign
say word_from_int(s.members)
say word_from_bool(s.ready)
}
Program output
3
4
10
Valkyries
4
true
-- `impl Shape { ... }` attaches methods.
-- A method whose first parameter is `self` is an instance method,
-- called as `value.method(args)`.
-- A method without `self` is an associated method,
-- called as `Shape::method(args)`.
shape Rect {
w: int
h: int
}
impl Rect {
task square(side: int) -> Rect {
give back Rect { w: side, h: side }
}
task area(self) -> int {
give back self.w * self.h
}
task perimeter(self) -> int {
give back 2 * (self.w + self.h)
}
task scaled(self, factor: int) -> Rect {
give back Rect { w: self.w * factor, h: self.h * factor }
}
}
task main() -> void {
pilot r: Rect = Rect { w: 3, h: 5 }
say word_from_int(r.area())
say word_from_int(r.perimeter())
pilot big: Rect = r.scaled(3)
say word_from_int(big.area())
pilot sq: Rect = Rect::square(4)
say word_from_int(sq.area())
}
Program output
15
16
135
16
-- Shapes may hold other shapes. Field access chains through them,
-- including inside string interpolation.
shape Engine {
thrust: int
}
shape Tsf {
name: word
engine: Engine
}
impl Tsf {
task report(self) -> word {
give back "{self.name} thrust={self.engine.thrust}"
}
}
task main() -> void {
pilot unit: Tsf = Tsf {
name: "Takemikazuchi",
engine: Engine { thrust: 880 }
}
say unit.name
say word_from_int(unit.engine.thrust)
say unit.report()
}
Program output
Takemikazuchi
880
Takemikazuchi thrust=880
-- `impl Doctrine for Shape` registers an operator implementation.
-- V3 does not accept a `doctrine` *declaration*, but it does accept
-- this impl form for the built-in operator doctrines, and the method
-- is callable by name.
shape Vec2 {
x: int
y: int
}
impl Add for Vec2 {
task add(self, other: Vec2) -> Vec2 {
give back Vec2 { x: self.x + other.x, y: self.y + other.y }
}
}
impl Vec2 {
task show(self) -> word {
give back "({self.x}, {self.y})"
}
}
task main() -> void {
pilot a: Vec2 = Vec2 { x: 1, y: 2 }
pilot b: Vec2 = Vec2 { x: 10, y: 20 }
pilot c: Vec2 = a.add(b)
say c.show()
}
Program output
(11, 22)
Lists & arrays#
-- `List<T>` is V3's typed sequence. The element type must be a scalar
-- (`int`, `num`, `word`, `bool`) or the name of a shape. Nesting is not
-- allowed: there is no `List<List<int>>`.
shape Contact {
tag: word
}
-- A List may be a parameter and a return type. It may NOT be a shape
-- field: that reports "V3 owned shape fields do not yet support List
-- values".
task count_all(items: List<word>) -> int {
give back items.length()
}
task first_three() -> List<int> {
give back [1, 2, 3]
}
task main() -> void {
-- An array literal infers List<T> from its elements.
pilot names: List<word> = ["Takeru", "Meiya", "Sumika"]
say word_from_int(names.length())
say names[1]
-- Indexed assignment requires `pilot mut`.
pilot mut scores: List<int> = [10, 20, 30]
scores[0] = 99
say word_from_int(scores[0])
-- Every scalar element type works.
pilot ratios: List<num> = [1.5, 2.5]
say format_num(ratios[1])
pilot flags: List<bool> = [true, false]
say word_from_bool(flags[0])
-- Shapes too, and field access chains off the element.
pilot contacts: List<Contact> = [Contact { tag: "BETA-1" }]
say contacts[0].tag
-- List::filled(value, count) builds a pre-populated list.
pilot zeros: List<int> = List::filled(0, 4)
say word_from_int(zeros.length())
-- Passed to a task, and returned from one.
say word_from_int(count_all(names))
say word_from_int(first_three()[2])
-- Lists grow as of v0.14.2. Mutating methods need `pilot mut`.
pilot mut queue: List<int> = List::new()
queue.push(10)
queue.push(20)
queue.push(30)
say word_from_int(queue.length())
say word_from_int(queue.pop()) -- 30, removed and returned
say word_from_int(queue.length())
say word_from_int(queue.capacity()) -- starts at 8, doubles as needed
queue.reserve(64)
say word_from_int(queue.capacity())
queue.clear()
say word_from_int(queue.length())
-- Iterate with an index; there is no `for each`.
pilot mut i: int = 0
repeat names.length() times {
say names[i]
i += 1
}
}
Program output
3
Meiya
99
2.5
true
BETA-1
4
3
3
3
30
2
8
64
0
Takeru
Meiya
Sumika
-- V3 arrays are runtime handles of `word` elements, addressed by an
-- `int` handle. Store other types by converting them to `word`.
task main() -> void {
pilot xs: int = array_new()
array_push(xs, "alpha")
array_push(xs, "bravo")
array_push(xs, "charlie")
say word_from_int(array_len(xs))
say array_get(xs, 0)
say array_get(xs, 2)
array_set(xs, 1, "BRAVO")
say array_get(xs, 1)
-- Iterate with an index and a counted loop.
pilot mut i: int = 0
repeat array_len(xs) times {
say array_get(xs, i)
i += 1
}
array_release(xs)
}
Program output
3
alpha
charlie
BRAVO
alpha
BRAVO
charlie
-- An array literal infers `List<T>` from its elements. Numbers stay
-- numbers -- there is no need to convert them to `word` any more.
--
-- The older untyped `array_*` handle still exists alongside it; see
-- arrays.html for when each one applies.
task main() -> void {
pilot names: List<word> = ["Takeru", "Meiya", "Sumika"]
say word_from_int(names.length())
say names[1]
pilot numbers: List<int> = [3, 1, 2]
say word_from_int(numbers.length())
say word_from_int(numbers[0])
-- The array_* builtins accept a List<word> as well as a raw handle.
say word_from_int(array_len(names))
say array_get(names, 2)
-- std/algorithm.fk tasks are declared with `handle: int`, so they take
-- only the legacy handle. Build one with array_new() to use them.
pilot h: int = array_new()
array_push(h, word_from_int(3))
array_push(h, word_from_int(1))
array_push(h, word_from_int(2))
array_sort_int(h)
say array_join(h, ",")
say word_from_int(array_sum_int(h))
say word_from_int(array_max_int(h))
say word_from_int(array_binary_search_int(h, 3))
array_reverse(h)
say array_join(h, "|")
say word_from_bool(array_contains(h, "2"))
array_release(h)
}
Program output
3
Meiya
3
3
3
Sumika
1,2,3
6
3
2
3|2|1
true
Standard library#
-- std/string.fk ships as plain FREAK source and is always linked in by
-- `freak build` / `freak run`. Its tasks are global; there is no import.
task main() -> void {
say string_repeat("ha", 3)
say string_reverse("FREAK")
say word_from_int(string_count("banana", "an"))
say string_pad_left("7", 4, "0")
say string_pad_right("7", 4, ".")
say word_from_int(string_index_of("alternative", "native"))
say word_from_bool(is_digit("5"))
say word_from_bool(is_alpha("x"))
say word_from_bool(is_whitespace(" "))
}
Program output
hahaha
KAERF
2
0007
7...
5
true
true
true
-- Two numeric surfaces exist side by side:
-- `math::*` are compiler builtins over `num` (floating point).
-- `std_*` come from std/math.fk and work on `int`.
task main() -> void {
-- builtins, num
say format_num(math::sqrt(144.0))
say format_num(math::pow(2.0, 10.0))
say format_num(math::floor(3.9))
say format_num(math::ceil(3.1))
say format_num(math::sin(0.0))
say format_num(math::cos(0.0))
-- std/math.fk, int
say word_from_int(std_abs(0 - 9))
say word_from_int(std_max(3, 8))
say word_from_int(std_min(3, 8))
say word_from_int(std_clamp(42, 0, 10))
say word_from_int(std_pow(2, 8))
say word_from_int(std_gcd(84, 30))
say word_from_int(std_lcm(4, 6))
say word_from_int(std_factorial(6))
say word_from_int(std_fibonacci(12))
say word_from_bool(std_is_even(4))
say word_from_int(std_sign(0 - 3))
}
Program output
12
1024
3
4
0
1
9
8
3
10
256
6
12
720
144
true
-1
-- std/convert.fk: base conversion and safe parsing.
task main() -> void {
say int_to_hex(255)
say int_to_bin(10)
say int_to_oct(64)
say word_from_int(char_to_digit("7"))
say word_from_int(word_to_int_safe("not a number"))
say word_from_int(word_to_int_safe("123"))
say bool_to_word(true)
}
Program output
ff
1010
100
7
0
123
true
-- std/json.fk is a pure-FREAK parser. Values are `int` handles.
task main() -> void {
json_init()
pilot doc: int = json_parse("{\"unit\":\"Valkyries\",\"members\":4,\"ready\":true}")
say json_get_type(doc)
say word_from_int(json_obj_len(doc))
say word_from_bool(json_obj_has(doc, "unit"))
say json_get_str(json_obj_get(doc, "unit"))
say word_from_int(json_get_int(json_obj_get(doc, "members")))
say word_from_bool(json_get_bool(json_obj_get(doc, "ready")))
pilot list: int = json_parse("[10, 20, 30]")
say word_from_int(json_arr_len(list))
say word_from_int(json_get_int(json_arr_get(list, 1)))
}
Program output
o
3
true
Valkyries
4
true
3
20
-- std/version.fk implements semver parsing and constraint matching.
task main() -> void {
pilot parsed: word = ver_parse("2.14.1-rc.1+build7")
say word_from_int(ver_major(parsed))
say word_from_int(ver_minor(parsed))
say word_from_int(ver_patch(parsed))
say ver_pre(parsed)
say ver_build(parsed)
say ver_to_string(parsed)
say word_from_bool(ver_lt("1.2.3", "1.10.0"))
say word_from_bool(ver_gt("2.0.0", "1.9.9"))
say word_from_bool(ver_eq("1.0.0", "1.0.0"))
say word_from_bool(ver_satisfies("1.4.2", "^1.4"))
say word_from_bool(ver_satisfies("2.0.0", "^1.4"))
}
Program output
2
14
1
rc.1
build7
2.14.1-rc.1+build7
false
true
true
true
false
Console#
-- Coloured console output. There is no colour library: you build the
-- ANSI escape sequences yourself as `word` values and concatenate them.
--
-- `\x1b` is the ESC byte. The V3 lexer preserves `\xNN` escapes all the
-- way through to the backend, so they reach the terminal intact.
pilot C_RESET = "\x1b[0m"
pilot C_BOLD = "\x1b[1m"
pilot C_DIM = "\x1b[2m"
pilot C_RED = "\x1b[31m"
pilot C_GREEN = "\x1b[32m"
pilot C_CYAN = "\x1b[36m"
pilot C_BRED = "\x1b[1;31m"
-- 24-bit truecolour: ESC [ 38;2;R;G;B m
pilot C_PINK = "\x1b[38;2;255;100;200m"
task rgb(r: int, g: int, b: int) -> word {
give back "\x1b[38;2;" + word_from_int(r) + ";" + word_from_int(g) + ";" + word_from_int(b) + "m"
}
task main() -> void {
say C_BRED + "error" + C_RESET + ": engine failure"
say C_GREEN + "ok" + C_RESET + ": all systems nominal"
say C_DIM + "hint: check the coolant line" + C_RESET
say C_BOLD + C_CYAN + "XM3" + C_RESET + " reflex link online"
say C_PINK + "freak" + C_RESET + " 0.14.1"
say rgb(255, 180, 0) + "amber warning" + C_RESET
}
Program output (ANSI rendered)
error: engine failure
ok: all systems nominal
hint: check the coolant line
XM3 reflex link online
freak 0.14.1
amber warning
-- A tour of the SGR (Select Graphic Rendition) code space, to confirm
-- every family survives the V3 lexer, both backends and the runtime.
--
-- Form: ESC [ params m where ESC is \x1b
pilot RESET = "\x1b[0m"
task sgr(code: word) -> word {
give back "\x1b[" + code + "m"
}
task swatch(label: word, code: word) -> void {
say sgr(code) + label + RESET
}
task main() -> void {
-- Attributes
swatch("bold", "1")
swatch("dim", "2")
swatch("italic", "3")
swatch("underline", "4")
swatch("blink", "5")
swatch("reverse", "7")
swatch("hidden", "8")
swatch("strike", "9")
-- Standard foreground 30-37
swatch("black", "30")
swatch("red", "31")
swatch("green", "32")
swatch("yellow", "33")
swatch("blue", "34")
swatch("magenta", "35")
swatch("cyan", "36")
swatch("white", "37")
-- Bright foreground 90-97
swatch("bright red", "91")
swatch("bright green", "92")
swatch("bright cyan", "96")
-- Background 40-47 and bright 100-107
swatch("on red", "41")
swatch("on blue", "44")
swatch("on bright yellow", "103")
-- Combined parameters in one sequence
swatch("bold + underline + red", "1;4;31")
swatch("white on magenta", "37;45")
-- 256-colour: 38;5;N foreground, 48;5;N background
swatch("256 fg 208", "38;5;208")
swatch("256 bg 22", "48;5;22")
-- 24-bit truecolour: 38;2;R;G;B and 48;2;R;G;B
swatch("rgb fg", "38;2;255;100;200")
swatch("rgb bg", "48;2;20;20;90")
-- Selective reset: 22 clears bold/dim, 24 clears underline,
-- 39 resets foreground, 49 resets background.
say sgr("1") + "bold " + sgr("22") + "normal again" + RESET
say sgr("31") + "red " + sgr("39") + "default again" + RESET
}
Program output (ANSI rendered)
bold
dim
italic
underline
blink
reverse
strike
black
red
green
yellow
blue
magenta
cyan
white
bright red
bright green
bright cyan
on red
on blue
on bright yellow
bold + underline + red
white on magenta
256 fg 208
256 bg 22
rgb fg
rgb bg
bold normal again
red default again
-- Printing on the same line.
--
-- `say` ALWAYS appends a newline -- there is no print-without-newline
-- builtin in V3. These are the three techniques that work instead.
task main() -> void {
-- ---------------------------------------------------------------
-- 1. Build the line, then say it once.
--
-- The usual answer. Nothing is written until the say, so the pieces
-- land on one line.
-- ---------------------------------------------------------------
pilot mut row: word = "loading:"
pilot mut i: int = 0
repeat 5 times {
row += " " + word_from_int(i)
i += 1
}
say row
-- For a lot of pieces, word_builder avoids re-copying the word on
-- every concatenation.
pilot b: int = word_builder::new()
word_builder::append(b, "squadron:")
pilot mut n: int = 1
repeat 3 times {
word_builder::append(b, " V-")
word_builder::append_int(b, n)
n += 1
}
say word_builder::finish(b)
-- ---------------------------------------------------------------
-- 2. Carriage return inside ONE say, to overwrite within the line.
--
-- "\r" returns the cursor to column 0. Everything after it paints
-- over what came before, on the same physical line.
-- ---------------------------------------------------------------
say "calculating...\r\x1b[Kdone "
-- ---------------------------------------------------------------
-- 3. Cursor-up, to overwrite a line a PREVIOUS say already ended.
--
-- "\x1b[1A" moves up one row, "\r" returns to column 0, "\x1b[K"
-- erases to end of line. The say then redraws that row and its own
-- newline puts the cursor back where it started.
--
-- In a terminal these four lines display as one line counting up.
-- Captured to a file they stay separate, because the escapes are
-- cursor movement rather than text.
-- ---------------------------------------------------------------
say "progress 0%"
say "\x1b[1A\r\x1b[Kprogress 33%"
say "\x1b[1A\r\x1b[Kprogress 66%"
say "\x1b[1A\r\x1b[Kprogress 100%"
say "finished"
}
Program output (ANSI rendered)
loading: 0 1 2 3 4
squadron: V-1 V-2 V-3
done
progress 100%
finished
System#
-- `fs::*` are compiler builtins. Paths are plain `word` values.
task main() -> void {
fs::write("sortie.log", "launch\n")
fs::append("sortie.log", "engage\n")
if fs::exists("sortie.log") {
say fs::read("sortie.log").trim()
}
fs::make_dir("hangar_out")
say word_from_bool(fs::exists("hangar_out"))
-- `fs::delete` is file-only in V3, and reports true when the file is
-- gone -- whether this call removed it or it was already absent.
say word_from_bool(fs::delete("sortie.log"))
say word_from_bool(fs::exists("sortie.log"))
}
Program output
launch
engage
true
true
false
-- `ByteBuffer` is a real builtin type with methods, unlike the `int`
-- handles used elsewhere. It is a seekable binary read/write cursor.
task main() -> void {
pilot buf: ByteBuffer = ByteBuffer::new()
buf.write_word("hi")
buf.write_int(7)
buf.write_byte(255)
say word_from_int(buf.length())
say word_from_int(buf.position())
-- Rewind and read the same values back.
buf.seek(0)
say buf.read_word(2)
say word_from_int(buf.read_int())
say word_from_int(buf.read_byte())
say word_from_int(buf.remaining())
-- status() reports the last failure; 0 means clean.
say word_from_int(buf.status())
buf.release()
}
Program output
11
0
hi
7
255
0
0
-- Process and clock builtins.
-- Note: `process::args()` is deliberately rejected by V3. Use the
-- indexed pair `process::args_count()` / `process::arg(i)` instead.
task main() -> void {
pilot argc: int = process::args_count()
say word_from_int(argc)
pilot mut i: int = 0
repeat argc times {
say "arg {i}: " + process::arg(i)
i += 1
}
pilot started: int = time::now_ms()
pilot mut spin: int = 0
repeat 1000 times {
spin += 1
}
pilot elapsed: int = time::now_ms() - started
say word_from_bool(elapsed >= 0)
say word_from_bool(process::env("PATH").length() > 0)
}
Program output
3
arg 0: C:\Users\cozor\AppData\Local\Temp\fkdocs_e8et7pyk\process_time.exe
arg 1: alpha
arg 2: bravo
true
true
-- V3's `extern` is a single flat declaration -- no `extern [C] { ... }`
-- block, no calling convention, no `link=`. Parameter and return types
-- are single identifiers.
--
-- The declared symbol must exist at link time and must not start with
-- the reserved `__freak_` prefix.
extern task abs(v: int) -> int
task main() -> void {
say word_from_int(abs(0 - 41))
say word_from_int(abs(41))
}
Program output
41
41
Anime layer#
-- `eventually { ... }` marks a cleanup block.
--
-- IMPORTANT V3 caveat: V3 emits the block INLINE at the point it is
-- written. It is not deferred to the end of scope and it does not run
-- on `give back`, `break` or `panic`. Place it where you want it to run.
task main() -> void {
say "engaging"
eventually {
say "cleanup"
}
say "disengaged"
}
Program output
engaging
cleanup
disengaged
-- V3 parses a bare `@name` annotation and then ignores it.
-- It carries no semantics, and it must NOT take arguments:
-- `@rival(meiya)` does not parse as an annotation in V3.
@protagonist
task hero() -> word {
give back "Shirogane Takeru"
}
@deprecated
task legacy() -> word {
give back "old path"
}
@season_finale
task main() -> void {
say hero()
say legacy()
}
Program output
Shirogane Takeru
old path
Tutorials#
-- Tutorial 1, finished program.
--
-- A greeter that takes a name, decides how formal to be, and reports.
-- Everything here is core V3: bindings, a shape, a method, a task,
-- interpolation, a conditional and a counted loop.
shape Aviator {
name: word
sorties: int
}
impl Aviator {
-- An associated method: called as Aviator::recruit(...).
task recruit(name: word) -> Aviator {
give back Aviator { name: name, sorties: 0 }
}
-- An instance method: `self` is the receiver.
task rank(self) -> word {
if self.sorties >= 50 { give back "veteran" }
if self.sorties >= 10 { give back "regular" }
give back "rookie"
}
task greeting(self) -> word {
-- Interpolation substitutes PATHS only, never calls. Writing
-- "{self.rank()}" would print those characters literally, so the
-- call result goes into a binding first.
pilot rank: word = self.rank()
give back "{self.name} - {self.sorties} sorties, rank {rank}"
}
}
task main() -> void {
pilot mut takeru: Aviator = Aviator::recruit("Shirogane Takeru")
-- Fly a few missions.
repeat 12 times {
takeru.sorties += 1
}
say takeru.greeting()
pilot rank: word = takeru.rank()
say "logged as: {rank}"
if takeru.sorties > 10 {
say "cleared for the next operation"
} else {
say "needs more flight hours"
}
}
Program output
Shirogane Takeru - 12 sorties, rank regular
logged as: regular
cleared for the next operation
-- Tutorial 2, finished program.
--
-- A small command-line tool: read a file, count its lines, words and
-- characters, then print a coloured report. Falls back to a sample file
-- it writes itself when given no argument, so it always has input.
pilot mut C_RESET = "\x1b[0m"
pilot mut C_BOLD = "\x1b[1m"
pilot mut C_DIM = "\x1b[2m"
pilot mut C_RED = "\x1b[1;31m"
pilot mut C_GREEN = "\x1b[32m"
pilot mut C_CYAN = "\x1b[36m"
-- Honour the NO_COLOR convention. Blanking the constants keeps every
-- call site working unchanged.
task configure_output() -> void {
if process::env("NO_COLOR") != "" {
C_RESET = ""
C_BOLD = ""
C_DIM = ""
C_RED = ""
C_GREEN = ""
C_CYAN = ""
}
}
task sample_path() -> word {
pilot path: word = "tut_sample.txt"
fs::write(path, "the beta do not negotiate\nhumanity answers with steel\nand with pilots\n")
give back path
}
-- Which file are we working on? Argument 0 is the executable itself.
task target_path() -> word {
if process::args_count() > 1 {
give back process::arg(1)
}
give back sample_path()
}
task count_words(text: word) -> int {
pilot mut total: int = 0
pilot mut in_word: bool = false
pilot mut i: int = 0
repeat text.length() times {
pilot c: word = text.char_at(i)
pilot blank: bool = c == " " or c == "\n" or c == "\t" or c == "\r"
if blank {
in_word = false
} else {
if not in_word { total += 1 }
in_word = true
}
i += 1
}
give back total
}
task count_lines(text: word) -> int {
if text.length() == 0 { give back 0 }
pilot mut total: int = 1
pilot mut i: int = 0
repeat text.length() times {
if text.char_at(i) == "\n" { total += 1 }
i += 1
}
-- A trailing newline does not start a new line.
if text.char_at(text.length() - 1) == "\n" { total -= 1 }
give back total
}
task row(label: word, value: int) -> void {
say " " + C_DIM + label + C_RESET + " " + C_BOLD + word_from_int(value) + C_RESET
}
task main() -> void {
configure_output()
pilot path: word = target_path()
if not fs::exists(path) {
say C_RED + "error" + C_RESET + ": no such file: " + path
process::exit(1)
}
pilot text: word = fs::read(path)
say C_CYAN + C_BOLD + "report" + C_RESET + C_DIM + " for " + path + C_RESET
row("lines ", count_lines(text))
row("words ", count_words(text))
row("characters", text.length())
if count_words(text) > 5 {
say " " + C_GREEN + "ok" + C_RESET + " enough material to work with"
}
fs::delete("tut_sample.txt")
}
Program output (ANSI rendered)
report for tut_sample.txt
lines 3
words 12
characters 70
ok enough material to work with
-- Tutorial 3, finished program.
--
-- Model a squadron with shapes, keep the members in a List, and compute
-- a summary. Shows the two collection surfaces working side by side and
-- the one restriction that catches people: a shape cannot hold a List.
shape Airframe {
model: word
thrust: int
}
shape Member {
callsign: word
sorties: int
frame: Airframe
}
impl Member {
task enlist(callsign: word, model: word, thrust: int) -> Member {
give back Member {
callsign: callsign,
sorties: 0,
frame: Airframe { model: model, thrust: thrust }
}
}
task veteran(self) -> bool {
give back self.sorties >= 20
}
task line(self) -> word {
give back "{self.callsign} {self.frame.model} thrust {self.frame.thrust} sorties {self.sorties}"
}
}
-- A List may be a parameter and a return type. It may NOT be a field on
-- a shape: that reports
-- "V3 owned shape fields do not yet support List values"
task total_sorties(counts: List<int>) -> int {
pilot mut sum: int = 0
pilot mut i: int = 0
repeat counts.length() times {
sum += counts[i]
i += 1
}
give back sum
}
task main() -> void {
pilot mut ichi: Member = Member::enlist("Valkyrie-1", "Takemikazuchi", 880)
pilot mut ni: Member = Member::enlist("Valkyrie-2", "Shiranui", 640)
pilot mut san: Member = Member::enlist("Valkyrie-3", "Gekishin", 520)
repeat 24 times { ichi.sorties += 1 }
repeat 8 times { ni.sorties += 1 }
repeat 31 times { san.sorties += 1 }
say ichi.line()
say ni.line()
say san.line()
-- A typed List holds the numbers we want to aggregate.
pilot counts: List<int> = [ichi.sorties, ni.sorties, san.sorties]
say "total sorties: " + word_from_int(total_sorties(counts))
say "squadron size: " + word_from_int(counts.length())
-- Names in a List<word>, indexed like any other list.
pilot names: List<word> = [ichi.callsign, ni.callsign, san.callsign]
pilot mut i: int = 0
repeat names.length() times {
say " member " + word_from_int(i) + ": " + names[i]
i += 1
}
-- Counting veterans needs the shapes themselves, so loop over them
-- directly rather than trying to put Members in a List.
pilot mut veterans: int = 0
if ichi.veteran() { veterans += 1 }
if ni.veteran() { veterans += 1 }
if san.veteran() { veterans += 1 }
say "veterans: " + word_from_int(veterans)
-- The legacy handle is still the right tool when you need the
-- std/algorithm.fk helpers, which are declared with `handle: int`.
pilot ranking: int = array_new()
array_push(ranking, word_from_int(ichi.sorties))
array_push(ranking, word_from_int(ni.sorties))
array_push(ranking, word_from_int(san.sorties))
array_sort_int(ranking)
say "sorted sorties: " + array_join(ranking, ", ")
say "busiest pilot flew " + word_from_int(array_max_int(ranking))
array_release(ranking)
}
Program output
Valkyrie-1 Takemikazuchi thrust 880 sorties 24
Valkyrie-2 Shiranui thrust 640 sorties 8
Valkyrie-3 Gekishin thrust 520 sorties 31
total sorties: 63
squadron size: 3
member 0: Valkyrie-1
member 1: Valkyrie-2
member 2: Valkyrie-3
veterans: 2
sorted sorties: 8, 24, 31
busiest pilot flew 31
Project structure#
-- Reusing code across files in V3.
--
-- The compiler takes exactly one source file, and Hangar packages are
-- never linked into it, so a "library" is just source you concatenate
-- ahead of your program:
--
-- cat lib/text.fk src/app.fk > build/combined.fk
-- freak build build/combined.fk
--
-- This file is what that produces. Everything lands in one flat global
-- namespace, so a library must prefix its names to avoid collisions --
-- with your code, and with the std/ tasks the build already links.
-- ===================================================================
-- lib/text.fk -- the "package"
-- ===================================================================
shape TextStats {
words: int
longest: word
}
task text_is_blank(c: word) -> bool {
give back c == " " or c == "\n" or c == "\t" or c == "\r"
}
task text_count_words(source: word) -> int {
pilot mut total: int = 0
pilot mut in_word: bool = false
pilot mut i: int = 0
repeat source.length() times {
if text_is_blank(source.char_at(i)) {
in_word = false
} else {
if not in_word { total += 1 }
in_word = true
}
i += 1
}
give back total
}
task text_longest_word(source: word) -> word {
pilot mut best: word = ""
pilot mut current: word = ""
pilot mut i: int = 0
repeat source.length() times {
pilot c: word = source.char_at(i)
if text_is_blank(c) {
if current.length() > best.length() { best = current }
current = ""
} else {
current = current + c
}
i += 1
}
if current.length() > best.length() { best = current }
give back best
}
task text_analyse(source: word) -> TextStats {
give back TextStats {
words: text_count_words(source),
longest: text_longest_word(source)
}
}
-- ===================================================================
-- src/app.fk -- your program
-- ===================================================================
task main() -> void {
pilot report: TextStats = text_analyse("the beta will not negotiate with humanity")
say word_from_int(report.words)
say report.longest
-- `use` lines are stripped before parsing, so leaving one in a
-- concatenated build is harmless -- and equally, it imports nothing.
}
Program output
7
negotiate
Complete programs#
-- FizzBuzz, written the way V3 wants it: a counted loop, a mutable
-- counter, and `when` over a computed tag.
task tag(n: int) -> word {
if n % 15 == 0 { give back "FizzBuzz" }
if n % 3 == 0 { give back "Fizz" }
if n % 5 == 0 { give back "Buzz" }
give back word_from_int(n)
}
task main() -> void {
pilot mut n: int = 1
repeat 15 times {
say tag(n)
n += 1
}
}
Program output
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
-- A complete small program: split a sentence into words, count them,
-- and report the longest one. Uses shapes, arrays, loops and std tasks.
shape Report {
total: int
longest: word
}
impl Report {
task show(self) -> word {
give back "total={self.total} longest={self.longest}"
}
}
task split_words(text: word, out: int) -> void {
pilot mut current: word = ""
pilot mut i: int = 0
repeat text.length() times {
pilot c: word = text.char_at(i)
if c == " " {
if current.length() > 0 {
array_push(out, current)
current = ""
}
} else {
current = current + c
}
i += 1
}
if current.length() > 0 {
array_push(out, current)
}
}
task analyse(text: word) -> Report {
pilot words: int = array_new()
split_words(text, words)
pilot mut longest: word = ""
pilot mut i: int = 0
repeat array_len(words) times {
pilot w: word = array_get(words, i)
if w.length() > longest.length() {
longest = w
}
i += 1
}
-- `result` is a reserved word in V3, so the binding is `summary`.
pilot summary: Report = Report { total: array_len(words), longest: longest }
array_release(words)
give back summary
}
task main() -> void {
pilot r: Report = analyse("the beta will not negotiate with humanity")
say r.show()
say word_from_int(r.total)
say r.longest.to_upper()
}
Program output
total=7 longest=negotiate
7
NEGOTIATE