01 / hello.zi

Hello, World!

The smallest complete program.

main :: () {
    print("Hello, World!\n");
}
ziran run hello.zi
# Hello, World!

Download hello.zi ↓

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% sure

Download greet.zi ↓

03 / 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: false

Download values.zi ↓

04 / 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 100

Download variables.zi ↓

05 / 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) = 6765

Download functions.zi ↓

06 / 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 weekend

Download conditions.zi ↓

07 / 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 7

Download loops.zi ↓

08 / 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: 9

Download arrays.zi ↓

09 / 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 12

Download structs.zi ↓

10 / 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 corners

Download enums.zi ↓

11 / 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": false

Download text_basics.zi ↓

12 / 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
# done

Download defer.zi ↓

13 / 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
# Buzz

Download fizzbuzz.zi ↓

14 / 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 

Download primes.zi ↓

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
# 42

Download score.zi ↓

16 / 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
# 1

Download text_demo.zi ↓

17 / 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.zi

Native and portable support can differ by feature. See the current status before choosing a target.