01 / hello.zi
Hello, World!
The smallest complete program.
main :: () {
print("Hello, World!\n");
}ziran run hello.zi
# Hello, World!02 / greet.zi
Print values
Each % in a print format takes the next argument, and %% prints a percent sign.
main :: () {
name := "Ziran";
year: s32 = 2026;
print("Hello, %! It is %.\n", name, year);
print("% + % = %\n", 40, 2, 40 + 2);
print("pi is about %, and 100%% sure\n", 3.14159);
}ziran run greet.zi
# Hello, Ziran! It is 2026.
# 40 + 2 = 42
# pi is about 3.14159, and 100% sure03 / values.zi
Values
Integers, floats, booleans, and text.
main :: () {
print("text: %\n", "ziran");
print("1 + 1 = %\n", 1 + 1);
print("7.0 / 3.0 = %\n", 7.0 / 3.0);
print("true and false: %\n", true && false);
print("true or false: %\n", true || false);
print("not true: %\n", !true);
}ziran run values.zi
# text: ziran
# 1 + 1 = 2
# 7.0 / 3.0 = 2.3333333333333335
# true and false: false
# true or false: true
# not true: false04 / variables.zi
Variables and constants
A declared type, an inferred type, a default zero value, and a compile-time constant.
LIMIT :: 100;
main :: () {
greeting: string = "initial";
print("%\n", greeting);
first: s32 = 1;
second: s32 = 2;
print("% %\n", first, second);
ready := true;
print("%\n", ready);
count: s32;
print("% (a variable starts at zero)\n", count);
fruit := "apple";
print("% and the limit is %\n", fruit, LIMIT);
}ziran run variables.zi
# initial
# 1 2
# true
# 0 (a variable starts at zero)
# apple and the limit is 10005 / functions.zi
Functions
Plain calls, default and named arguments, and recursion.
Add :: (a: s32, b: s32) -> s32 {
return a + b;
}
Greet :: (name: string, greeting: string = "Hello") {
print("%, %!\n", greeting, name);
}
Factorial :: (n: s64) -> s64 {
if n <= 1 { return 1; }
return n * Factorial(n - 1);
}
Fibonacci :: (n: s32) -> s32 {
if n < 2 { return n; }
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
main :: () {
print("1 + 2 = %\n", Add(1, 2));
Greet("Ziran");
Greet("world", greeting = "Goodbye");
print("10! = %\n", Factorial(10));
print("fibonacci(20) = %\n", Fibonacci(20));
}ziran run functions.zi
# 1 + 2 = 3
# Hello, Ziran!
# Goodbye, world!
# 10! = 3628800
# fibonacci(20) = 676506 / conditions.zi
Conditions
If and else chains, and an if-case that picks a branch by value.
Describe :: (number: s32) -> string {
if number < 0 {
return "negative";
} else if number == 0 {
return "zero";
}
return "positive";
}
DayKind :: (day: s32) -> string {
if day == {
case 6; return "weekend";
case 7; return "weekend";
}
return "weekday";
}
main :: () {
print("-4 is %\n", Describe(-4));
print("0 is %\n", Describe(0));
print("9 is %\n", Describe(9));
if 7 % 2 == 0 {
print("7 is even\n");
} else {
print("7 is odd\n");
}
print("day 3 is a %\n", DayKind(3));
print("day 6 is a %\n", DayKind(6));
}ziran run conditions.zi
# -4 is negative
# 0 is zero
# 9 is positive
# 7 is odd
# day 3 is a weekday
# day 6 is a weekend07 / loops.zi
Loops
While, ranges, collections, break, and continue.
main :: () {
index: s32 = 1;
while index <= 3 {
print("while %\n", index);
index += 1;
}
for step: 0..2 {
print("range %\n", step);
}
fruits := string.["apple", "banana", "cherry"];
for fruit, position: fruits {
print("% is at %\n", fruit, position);
}
for number: 0..9 {
if number % 2 == 0 { continue; }
if number > 7 { break; }
print("odd %\n", number);
}
}ziran run loops.zi
# while 1
# while 2
# while 3
# range 0
# range 1
# range 2
# apple is at 0
# banana is at 1
# cherry is at 2
# odd 1
# odd 3
# odd 5
# odd 708 / arrays.zi
Arrays and slices
A fixed array, a view into part of it, and a sum.
Sum :: (numbers: []s32) -> s32 {
total: s32 = 0;
for numbers { total += it; }
return total;
}
main :: () {
empty: [5]s32;
print("empty: % % % % %\n", empty[0], empty[1], empty[2], empty[3], empty[4]);
numbers := s32.[1, 2, 3, 4, 5];
numbers[4] = 100;
print("set: %, count: %\n", numbers[4], numbers.count);
middle := numbers[1:4];
print("middle has % items starting with %\n", middle.count, middle[0]);
print("sum of all: %\n", Sum(numbers[:]));
print("sum of middle: %\n", Sum(middle));
}ziran run arrays.zi
# empty: 0 0 0 0 0
# set: 100, count: 5
# middle has 3 items starting with 2
# sum of all: 110
# sum of middle: 909 / structs.zi
Structs
Records with fields, passed and returned by value.
Point :: struct {
x: s32;
y: s32;
}
Rectangle :: struct {
origin: Point;
width: s32;
height: s32;
}
Area :: (shape: Rectangle) -> s32 {
return shape.width * shape.height;
}
Grow :: (shape: Rectangle, amount: s32) -> Rectangle {
grown := shape;
grown.width += amount;
grown.height += amount;
return grown;
}
main :: () {
corner := Point.{x = 3, y = 4};
print("point: (%, %)\n", corner.x, corner.y);
box := Rectangle.{origin = corner, width = 10, height = 5};
print("area: %\n", Area(box));
box = Grow(box, 2);
print("grown: %x% with area %\n", box.width, box.height, Area(box));
copy := box;
copy.width = 1;
print("the copy is % wide, the original still %\n", copy.width, box.width);
}ziran run structs.zi
# point: (3, 4)
# area: 50
# grown: 12x7 with area 84
# the copy is 1 wide, the original still 1210 / enums.zi
Enums
Named values and a switch over them.
Shape :: enum {
CIRCLE;
SQUARE;
TRIANGLE;
}
Corners :: (shape: Shape) -> s32 {
if shape == {
case .CIRCLE; return 0;
case .SQUARE; return 4;
case .TRIANGLE; return 3;
}
return -1;
}
main :: () {
print("a circle has % corners\n", Corners(.CIRCLE));
print("a square has % corners\n", Corners(.SQUARE));
print("a triangle has % corners\n", Corners(.TRIANGLE));
}ziran run enums.zi
# a circle has 0 corners
# a square has 4 corners
# a triangle has 3 corners11 / text_basics.zi
Strings
Length, bytes, slices, and comparison.
CountByte :: (text: string, wanted: u8) -> s32 {
found: s32 = 0;
for byte: text {
if byte == wanted { found += 1; }
}
return found;
}
main :: () {
word := "banana";
print("% has % bytes\n", word, word.count);
print("the first byte is %\n", word[0]);
print("it contains % a's\n", CountByte(word, #char "a"));
print("its middle is %\n", word[1:5]);
print("equal to \"banana\": %\n", word == "banana");
print("equal to \"apple\": %\n", word == "apple");
}ziran run text_basics.zi
# banana has 6 bytes
# the first byte is 98
# it contains 3 a's
# its middle is anan
# equal to "banana": true
# equal to "apple": false12 / defer.zi
Defer
A deferred statement runs when its scope ends, last deferred first.
Work :: (name: string) {
print("open %\n", name);
defer print("close %\n", name);
print("use %\n", name);
}
main :: () {
defer print("done\n");
Work("file");
{
defer print("leave the block\n");
print("inside the block\n");
}
}ziran run defer.zi
# open file
# use file
# close file
# inside the block
# leave the block
# done13 / fizzbuzz.zi
FizzBuzz
Multiples of 3 print Fizz, of 5 Buzz, and of both FizzBuzz.
main :: () {
for number: 1..20 {
if number % 15 == 0 {
print("FizzBuzz\n");
} else if number % 3 == 0 {
print("Fizz\n");
} else if number % 5 == 0 {
print("Buzz\n");
} else {
print("%\n", number);
}
}
}ziran run fizzbuzz.zi
# 1
# 2
# Fizz
# 4
# Buzz
# Fizz
# 7
# 8
# Fizz
# Buzz
# 11
# Fizz
# 13
# 14
# FizzBuzz
# 16
# 17
# Fizz
# 19
# Buzz14 / primes.zi
The Sieve of Eratosthenes
Cross out every multiple of each prime.
LIMIT :: 50;
main :: () {
composite: [LIMIT + 1]bool;
for number: 2..LIMIT {
if composite[number] { continue; }
print("% ", number);
multiple := number * number;
while multiple <= LIMIT {
composite[multiple] = true;
multiple += number;
}
}
print("\n");
}ziran run primes.zi
# 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 15 / score.zi
Model a score
Define a record, pass it to a procedure, and return the answer.
Score :: struct {
value: s32;
bonus: s32;
}
Total :: (score: Score) -> s32 {
return score.value + score.bonus;
}
Answer :: () -> s32 {
score: Score = Score.{value = 40, bonus = 2};
return Total(score);
}ziran run --entry score:Answer score.zi
# 4216 / text_demo.zi
Use a standard module
Import the text module and test a prefix with ASCII case folding.
#import "text"
Answer :: () -> s32 {
if StartsWithFoldASCII("Ziran", "zi") {
return 1;
}
return 0;
}ziran run --entry text_demo:Answer text_demo.zi
# 117 / Native output
Explore all four outputs
Compare C, Go, portable ZX and direct WebAssembly in the target matrix. Inspect the checked IR, download actual generated files and compare measured timings for the same source.
Generate native code
Every program above prints the same output on the portable runner and as C and Go; tests/examples.sh checks it. For Go, --exe with an entry writes a runnable program. Go is needed only to run that output.
build/bin/ziran build --target=go --pkg main --exe --entry fizzbuzz:main --root . -o out/go site/examples/fizzbuzz.zi
(cd out/go && go run .)
C output mirrors the source path, here out/c/site/examples/fizzbuzz.c and its header, which declares fizzbuzz_main; call it from your program, or put #program_export before main to make the output a complete program.
build/bin/ziran build --target=c --root . -o out/c site/examples/fizzbuzz.ziNative and portable support can differ by feature. See the current status before choosing a target.