From 9438874dad85ea7caaf14c5f82fdfdcb746452a3 Mon Sep 17 00:00:00 2001 From: invisageable Date: Mon, 15 Jun 2026 22:31:11 +0200 Subject: [PATCH 1/6] feat(zo): remove how-to-programs their mostly exist in initiation --- crates/compiler/zo-how-to/000_prologue.zo | 77 ------- crates/compiler/zo-how-to/001_hello.zo | 29 --- crates/compiler/zo-how-to/002_primitives.zo | 96 --------- .../zo-how-to/003_variables_and_shadowing.zo | 71 ------- .../compiler/zo-how-to/004_interpolation.zo | 87 -------- crates/compiler/zo-how-to/005_strings.zo | 73 ------- crates/compiler/zo-how-to/006_arrays.zo | 192 ------------------ crates/compiler/zo-how-to/008_tuples.zo | 65 ------ crates/compiler/zo-how-to/011_binops.zo | 74 ------- crates/compiler/zo-how-to/012_branching.zo | 59 ------ crates/compiler/zo-how-to/013_matching.zo | 53 ----- crates/compiler/zo-how-to/014_loops.zo | 128 ------------ crates/compiler/zo-how-to/021_enums.zo | 63 ------ crates/compiler/zo-how-to/022_structs.zo | 59 ------ crates/compiler/zo-how-to/023_type_alias.zo | 47 ----- crates/compiler/zo-how-to/024_apply.zo | 64 ------ crates/compiler/zo-how-to/036_functions.zo | 94 --------- crates/compiler/zo-how-to/037_closures.zo | 91 --------- crates/compiler/zo-how-to/040_inference.zo | 76 ------- .../041_generics_and_monomorphization.zo | 58 ------ .../zo-how-to/042_generics_structs.zo | 41 ---- .../compiler/zo-how-to/043_generics_multi.zo | 44 ---- .../compiler/zo-how-to/044_generics_apply.zo | 46 ----- .../zo-how-to/045_generics_type_alias.zo | 43 ---- crates/compiler/zo-how-to/046_generic_enum.zo | 20 -- .../compiler/zo-how-to/047_result_option.zo | 33 --- crates/compiler/zo-how-to/048_io_all.zo | 49 ----- crates/compiler/zo-how-to/048_io_read_file.zo | 20 -- crates/compiler/zo-how-to/061_zsx_hello.zo | 62 ------ .../zo-how-to/062_zsx_dynamic_attributes.zo | 14 -- crates/compiler/zo-how-to/063_zsx_styling.zo | 20 -- .../zo-how-to/064_zsx_html_directive.zo | 57 ------ .../zo-how-to/065_zsx_nested_component.zo | 18 -- crates/compiler/zo-how-to/066_zsx_state.zo | 23 --- .../zo-how-to/067_zsx_state_counter.zo | 21 -- crates/compiler/zo-how-to/068_zsx_events.zo | 26 --- .../zo-how-to/069_zsx_reactive_attributes.zo | 55 ----- crates/compiler/zo-how-to/word_count.zo | 64 ++++++ 38 files changed, 64 insertions(+), 2148 deletions(-) delete mode 100644 crates/compiler/zo-how-to/000_prologue.zo delete mode 100644 crates/compiler/zo-how-to/001_hello.zo delete mode 100644 crates/compiler/zo-how-to/002_primitives.zo delete mode 100644 crates/compiler/zo-how-to/003_variables_and_shadowing.zo delete mode 100644 crates/compiler/zo-how-to/004_interpolation.zo delete mode 100644 crates/compiler/zo-how-to/005_strings.zo delete mode 100644 crates/compiler/zo-how-to/006_arrays.zo delete mode 100644 crates/compiler/zo-how-to/008_tuples.zo delete mode 100644 crates/compiler/zo-how-to/011_binops.zo delete mode 100644 crates/compiler/zo-how-to/012_branching.zo delete mode 100644 crates/compiler/zo-how-to/013_matching.zo delete mode 100644 crates/compiler/zo-how-to/014_loops.zo delete mode 100644 crates/compiler/zo-how-to/021_enums.zo delete mode 100644 crates/compiler/zo-how-to/022_structs.zo delete mode 100644 crates/compiler/zo-how-to/023_type_alias.zo delete mode 100644 crates/compiler/zo-how-to/024_apply.zo delete mode 100644 crates/compiler/zo-how-to/036_functions.zo delete mode 100644 crates/compiler/zo-how-to/037_closures.zo delete mode 100644 crates/compiler/zo-how-to/040_inference.zo delete mode 100644 crates/compiler/zo-how-to/041_generics_and_monomorphization.zo delete mode 100644 crates/compiler/zo-how-to/042_generics_structs.zo delete mode 100644 crates/compiler/zo-how-to/043_generics_multi.zo delete mode 100644 crates/compiler/zo-how-to/044_generics_apply.zo delete mode 100644 crates/compiler/zo-how-to/045_generics_type_alias.zo delete mode 100644 crates/compiler/zo-how-to/046_generic_enum.zo delete mode 100644 crates/compiler/zo-how-to/047_result_option.zo delete mode 100644 crates/compiler/zo-how-to/048_io_all.zo delete mode 100644 crates/compiler/zo-how-to/048_io_read_file.zo delete mode 100644 crates/compiler/zo-how-to/061_zsx_hello.zo delete mode 100644 crates/compiler/zo-how-to/062_zsx_dynamic_attributes.zo delete mode 100644 crates/compiler/zo-how-to/063_zsx_styling.zo delete mode 100644 crates/compiler/zo-how-to/064_zsx_html_directive.zo delete mode 100644 crates/compiler/zo-how-to/065_zsx_nested_component.zo delete mode 100644 crates/compiler/zo-how-to/066_zsx_state.zo delete mode 100644 crates/compiler/zo-how-to/067_zsx_state_counter.zo delete mode 100644 crates/compiler/zo-how-to/068_zsx_events.zo delete mode 100644 crates/compiler/zo-how-to/069_zsx_reactive_attributes.zo create mode 100644 crates/compiler/zo-how-to/word_count.zo diff --git a/crates/compiler/zo-how-to/000_prologue.zo b/crates/compiler/zo-how-to/000_prologue.zo deleted file mode 100644 index ce9cfdce..00000000 --- a/crates/compiler/zo-how-to/000_prologue.zo +++ /dev/null @@ -1,77 +0,0 @@ --! # how to zo: 000_prologue.zo --! --! @cmd: `zo run 000_prologue.zo` --! --! Welcome. You are here because you believe software should be better. --! --! For too long, we’ve accepted a "Great Divide." On one side, the Web: --! easy to build, but buried under layers of bloat and "runtime --! surprises." On the other, Native: powerful and fast, but rigid and --! painful to craft. Low-level UI design can be more accessible. --! --! zo (pronounced /zuː/) is more than a language. It is a compiler of a --! compiler inside another giant compiler that is itself inside a --! gigantic compiler. --! --! We are here to enhance the developer experience, making it seamless --! to build software that reflects **your** creativity. We focus on the --! details that matter, opening new dimensions where transforming --! your thoughts into programs is not just easy, but enjoyable. --! --! This folder is the gateway to your workstation. We are giving you --! the keys. You will never have to work blind again. --! --! Whether you are building a high-performance GPU tool or a lightweight web --! app, you should use the same brain, the same logic, and the same elegant --! syntax (zsx). --! --! ## the vision. --! --! - End the Uniformity: "we are against the abundant software uniformity --! of today. zo exists to push the boundaries of innovation to the limit.* --! - Total Information: *from design to delivery, zo provides every bit of --! information you need about your program. no hidden ayers. no black boxes.* --! - The Next Generation: *we aren't just creating tools; we are redefining user --! interfaces to create high-performance software on a global scale.* --! --! ## your roadmap. --! --! The Basics (001-010) — *hello world, types, and variables.* --! Logic & Control (011-020) — *branching, matching, and loops.* --! Architecture (021-035) — *structs, Enums, and apply blocks.* --! The Type System (036-050) — *functions, closures, and inference.* --! Systems (051-060) — *modules and FFI (calling C/JS).* --! Built-in UI (061-080) — *zsx templates and Cross-platform apps.* --! --! Every file in this guide is a valid zo program. Run it, break it, --! and see the instructions for yourself. Run this one now: --! --! ```bash --! zo run 000_prologue.zo --! ``` --! --! JOiN THE DEVOLUTiON. - --- This is a "line comment." The compiler ignores it. --- Everything after `--` is just for humans and AI now. - --* - This is a "block comment." - It can span multiple lines. - It's great for long-winded explanations like this one. -*- - --- And `-!` is a "doc comment." It documents the code that follows it. --- You saw it at the top of this file! - --- `fun` declares a function. --- `main` is the entry point — the first piece of code that runs. -fun main() { - -- This program does nothing... yet. - -- See you in `001_hello.zo`! -} - --! ## the capstone. --! --! - `fun` is the keyword for function declaration. --! diff --git a/crates/compiler/zo-how-to/001_hello.zo b/crates/compiler/zo-how-to/001_hello.zo deleted file mode 100644 index 575216ac..00000000 --- a/crates/compiler/zo-how-to/001_hello.zo +++ /dev/null @@ -1,29 +0,0 @@ --! how to hello: 001_hello.zo. --! --! @cmd: `zo run 001_hello.zo` --! --! Let's start with a simple program. In this lesson, we learn how to print --! `hello, hacker hero`. Unlike most programming language, zo uses `showln` --! instead. - -fun main() { - -- There are several builtin functions in zo such as `show`, `eshow`, - -- `eshowln` for printing. - -- - -- In our case, we call `showln`. This tells the compiler to display, - -- the value passed as argument with a newline at the end. - showln("hello, hacker hero"); -} - --! ## the capstone. --! --! - every zo programs must have a `main` function. --! - `showln` comes from `io` and must be used to print. --! --! ## try this. --! --! - Change `hello` by whatever you want. --! - Add more `showln` calls. - --- EXPECTED OUTPUT: --- hello, hacker hero diff --git a/crates/compiler/zo-how-to/002_primitives.zo b/crates/compiler/zo-how-to/002_primitives.zo deleted file mode 100644 index 5405b3ae..00000000 --- a/crates/compiler/zo-how-to/002_primitives.zo +++ /dev/null @@ -1,96 +0,0 @@ --! # how to primitives: 002_primitives.zo --! --! @cmd: `zo run 002_primitives.zo` --! --! Programming is just moving data around. In zo, data comes in a few basic --! flavors called "primitives". --! --! You don't need to memorize all of these right now. Just know they exist. The --! most important thing: zo is smart. It "infers" types so you don't have to be --! a keyboard typist just to declare a number. - -fun main() { - -- A bare number like `42` is an `int` — a signed 32-bit integer. - showln(42); - - -- zo supports big numbers natively — `600851475143` just works. - showln(600851475143); - - -- You can also write integers in different bases: - showln(0b11110000); -- binary (prefix `0b`). - showln(0o77); -- octal (prefix `0o`). - showln(0xff); -- hexadecimal notation. - - -- zo has parse modifiers — a shorthand to mark the base inline: - showln(b#30); -- binary notation. - showln(o#75); -- octal notation. - showln(x#76); -- hexadecimal notation. - - -- `float` adding `.0` tells zo it's a float (64-bit double). - showln(14.0); - showln(3.14159); - - -- Scientific e-notation is supported: `1.0e10`, `2.5e-3`: - showln(1.0e10); - showln(2.5e-3); - - -- `str` and `char` are simple, double quotes for text ("str"), single - -- quotes for one letter ('char'). - -- - -- Note: strings live in the binary's read-only data section — no heap - -- allocation for literals. - showln("JOiN THE DEVOLUTiON."); - showln('z'); - - -- `bytes` are written with backticks. Gives the raw byte value (no Unicode). - showln(`z`); - - -- booleans are basic, only 'true' and 'false'. No "truthy" or "falsy" logic - -- here!. - showln(true); - showln(false); -} - --- missing: --- --- - using `_` as a separator for clarity — 0b1010_0010_0101_1111, 16_777_216, etc. - - --! ## the integer family. --! --! signed: s8, s16, s32 (int), s64 --! unsigned: u8, u16, u32 (uint), u64 --! --! ## the float family (La FF). --! --! f32: 32-bit (for GPUs/Games) --! f64: 64-bit (float/default) --! --! ## the capstone. --! --! - ... --! --! ## try this. --! --! - Try a really big number: `showln(99999999999999);` --! - Mix bases: what does `0b1010 + 0xA` give you? --! - Write `showln(0.1 + 0.2);` — welcome to floating point. - --- EXPECTED OUTPUT: --- 42 --- 600851475143 --- 240 --- 63 --- 255 --- 30 --- 75 --- 76 --- 14.0 --- 3.141589 (float precision loss in ftoa) --- 10000000000.0 --- 0.2500 --- JOiN THE DEVOLUTiON. --- z --- 122 --- true --- false diff --git a/crates/compiler/zo-how-to/003_variables_and_shadowing.zo b/crates/compiler/zo-how-to/003_variables_and_shadowing.zo deleted file mode 100644 index 217a42a7..00000000 --- a/crates/compiler/zo-how-to/003_variables_and_shadowing.zo +++ /dev/null @@ -1,71 +0,0 @@ --! # how to variables: 003_variables_and_shadowing.zo --! --! @cmd: `zo run 003_variables_and_shadowing.zo` --! --! Data needs a name. In zo, you have three ways to name a value: --! `val` for constants, `imu` for immutable locals, and `mut` when things change. --! --! Pick the least powerful one that works — that's the zo way. - --- `val` is a compile-time constant. It cannot be inferred. --- --- The compiler inlines it everywhere. --- No stack slot, no runtime cost. Use it for things that never change. -val VERSION: str = "1.0.0"; -val MAX_HEALTH: int = 100; - -fun main() { - -- `imu` declares an immutable local. Once set, it cannot change. - -- Use this for 90% of your variables. It makes code easier to read. - imu name: str = "johndoe"; - showln(name); - - -- `mut` declares a mutable local. The value can be reassigned. - -- Reach for `mut` only when something genuinely changes over time. - mut health: int = 22; - showln(health); - - -- We can reassign a `mut` variable like this: - health = 50; - showln(health); - - -- Shadowing: You can re-declare a name in the same scope. - -- This creates a NEW variable — the original is untouched. - imu x: int = 1; - imu x: int = 2; - imu x: int = x + 3; -- Uses the '2' to create a new '5' - showln(x); - - -- Constants work anywhere — even inside functions. - showln(VERSION); -} - --! ## when to use what? --! --! `val` — Global constant. Known before the program even runs. --! `imu` — Immutable local. Set once, read many. (The Default). --! `mut` — Mutable local. Changes over time. --! --! Rule of thumb: `val` > `imu` > `mut`. Use the strictest one that works. --! --! ## a note on types. --! --! In this lesson, we wrote every type explicitly (like `: int`). --! In practice, you'll rarely need to. zo can almost always figure it out. --! We'll stay explicit for now to help you learn, but soon we'll let the --! compiler do the heavy lifting in `040_inference.zo`. --! --! ## try this. --! --! - Add `imu level: int = 1;` and print it. --! - Try changing `name` after declaring it — see the compiler error? --! - Try creating a `val` inside `main` and a `val` outside. Both work! - --- EXPECTED OUTPUT: --- johndoe --- 22 --- 50 --- 5 --- 1.0.0 - - diff --git a/crates/compiler/zo-how-to/004_interpolation.zo b/crates/compiler/zo-how-to/004_interpolation.zo deleted file mode 100644 index dee48b10..00000000 --- a/crates/compiler/zo-how-to/004_interpolation.zo +++ /dev/null @@ -1,87 +0,0 @@ --! # how to interpolation: 004_interpolation.zo --! --! @cmd: `zo run 004_interpolation.zo` --! --! Numbers are useless if you can't show them. In zo, you drop values --! directly into strings with `{curly braces}`. --! --! No complex format functions. No clunky string concatenation. --! Just write it, and the compiler handles the rest at compile-time. - -fun main() { - -- Simply put `{variable}` inside a string. - imu name: str = "johndoe"; - imu hp: int = 100; - imu attack: int = 15; - - showln("hero: {name}"); - showln("hp: {hp}"); - - -- Arithmetic works how you'd expect: `+`, `-`, `*`, `/`, `%`. - -- We compute the power level first, then print it. - imu power: int = hp + attack; - showln("power level: {power}"); - - -- You can interpolate as many values as you want in one go. - imu x: int = 10; - imu y: int = 12; - showln("position: {x}, {y}"); - - -- When a hero takes damage, we need a value that *changes*. - -- That's where `mut` comes in. - mut current_hp: int = 100; - imu damage: int = 25; - showln("hp before hit: {current_hp}"); - - -- Regular reassignment: - current_hp = current_hp - damage; - showln("hp after hit: {current_hp}"); - - -- Compound assignment: `-=`, `+=`, `*=`, `/=` also work. - -- These are just shorter ways to write `x = x - y`. - current_hp -= 10; - showln("hp after poison: {current_hp}"); - - current_hp += 30; -- A quick heal! - showln("hp after heal: {current_hp}"); -} - --! ## how it works under the hood. --! --! `showln("{name}")` is NOT a slow runtime "format" call. --! The zo compiler breaks it down (desugars it) into separate calls: --! --! ``` --! showln("hero: {name}") --! → show("hero: ") --! → showln(name) --! ``` --! --! This means: --! --! 1. Zero extra memory allocations. --! 2. Zero runtime parsing of strings. --! 3. Zero surprises. It's as fast as writing the calls manually. --! --! ## try this. --! --! - Add a `crit_multiplier: int = 2;` and print `attack * crit_multiplier`. --! - Use `%` (modulo) to see what's left: `showln(10 % 3);`. --! - Try to interpolate a variable that hasn't been declared yet. --! --! ## the capstone. --! --! - only variable can be interpolate. --! - interpolation can supported any types. --! --! Watch how the compiler protects you! - --- EXPECTED OUTPUT: --- hero: johndoe --- hp: 100 --- power level: 115 --- position: 10, 12 --- hp before hit: 100 --- hp after hit: 75 --- hp after poison: 65 --- hp after heal: 95 diff --git a/crates/compiler/zo-how-to/005_strings.zo b/crates/compiler/zo-how-to/005_strings.zo deleted file mode 100644 index 04c5e391..00000000 --- a/crates/compiler/zo-how-to/005_strings.zo +++ /dev/null @@ -1,73 +0,0 @@ --! # how to strings: 007_strings.zo --! --! @cmd: `zo run 007_strings.zo` --! --! Strings are sequences of bytes. In zo, string literals live in the --! binary's read-only data section — no heap allocation. You already --! know `showln("hello")`. Now let's combine and work with them. - -fun main() { - -- string literals are double-quoted. - imu greeting: str = "hello"; - imu name: str = "johndoe"; - - -- `++` concatenates two strings at runtime. - -- the result is a new string on the stack. - imu full: str = greeting ++ ", " ++ name ++ "!"; - showln(full); - - -- the originals are unchanged — strings are immutable. - showln(greeting); - showln(name); - - -- you can also concat literals directly. - -- the compiler folds them at compile-time — zero cost. - imu title: str = "the " ++ "devolution"; - showln(title); - - -- indexing: `s[i]` returns the char at byte position i. - -- this is O(1) — a single byte load, zero allocation. - imu first: char = greeting[0]; - imu last: char = greeting[4]; - - showln(first); - showln(last); - - -- chars compare naturally. - check@eq(greeting[0], 'h'); - check@eq(greeting[4], 'o'); - - -- recall from lesson 004: interpolation embeds values - -- into strings. `{variable}` is resolved at compile-time. - imu hp: int = 100; - showln("hp: {hp}"); -} - --! ## string operations. --! --! `++` — concatenation (runtime or compile-time). --! `{var}` — interpolation inside `showln` (compile-time). --! `s[i]` — char at byte index i (O(1), bounds-checked). --! --! Strings in zo are length-prefixed: `[len:u64][bytes][null]`. --! no scanning for terminators — the length is always known. --! --! ## the capstone. --! --! - ... --! --! ## try this. --! --! - Concat three variables into a sentence. --! - Try `"hello" ++ 42` — zo stops you (type mismatch). --! - Build a multi-line message with `++` and `"\n"`. --! - Index into a string and compare with a char literal. - --- EXPECTED OUTPUT: --- hello, johndoe! --- hello --- johndoe --- the devolution --- h --- o --- hp: 100 diff --git a/crates/compiler/zo-how-to/006_arrays.zo b/crates/compiler/zo-how-to/006_arrays.zo deleted file mode 100644 index d51119b4..00000000 --- a/crates/compiler/zo-how-to/006_arrays.zo +++ /dev/null @@ -1,192 +0,0 @@ --! # how to arrays: 005_arrays.zo --! --! @cmd: `zo run 005_arrays.zo` --! --! An array is a fixed-size list of values, all the same type. zo supports --! dynamic arrays (`[]int`), static arrays (`[3]int`), and N-dimensional --! arrays (`[4][4]float`, `[2][2][2]int`). No heap. No growing. Just flat --! blocks of memory on the stack. - -fun main() { - -- declare an array with `[]type` and fill it with `[a, b, c]`. - -- zo infers the length from the literal. - imu loot: []int = [10, 25, 50, 5, 100]; -- // A dynamic of array of integers. - - -- access elements with `arr[index]`. indices start at 0. - imu first: int = loot[0]; - imu last: int = loot[4]; - - showln("first chest: {first} gold"); - showln("last chest: {last} gold"); - - -- you can index with expressions too. - imu mid: int = loot[2]; - showln("middle chest: {mid} gold"); - - -- arrays + loops = power. iterate with `for` and a range. - mut total: int = 0; - - for i := 0..5 { - total = total + loot[i]; - } - - showln("total gold: {total}"); - - -- array math — pull values out, combine them. - imu dmg: []int = [5, 12, 8, 20, 3]; - imu combo: int = dmg[0] + dmg[1]; - showln("combo damage: {combo}"); - - -- verify with check@eq. - check@eq(loot[0], 10); - check@eq(dmg[2] * dmg[4], 24); - check@eq(combo, 17); - - -- mutate array elements with `arr[i] = value`. - -- the array must be declared `mut`. - mut hp: []int = [100, 80, 60]; - - hp[0] = hp[0] - 25; - hp[1] = hp[1] - 10; - hp[2] = hp[2] - 40; - - imu warrior: int = hp[0]; - imu mage: int = hp[1]; - imu rogue: int = hp[2]; - - showln("warrior: {warrior} hp"); - showln("mage: {mage} hp"); - showln("rogue: {rogue} hp"); - - check@eq(hp[0], 75); - check@eq(hp[1], 70); - check@eq(hp[2], 20); - - -- static arrays — size is part of the type. - imu slots: [3]int = [1, 2, 3]; - imu names: [2]str = ["warrior", "mage"]; - - imu s0: int = slots[0]; - imu s2: int = slots[2]; - imu n0: str = names[0]; - imu n1: str = names[1]; - - check@eq(s0, 1); - check@eq(s2, 3); - check@eq(n0, "warrior"); - check@eq(n1, "mage"); - - showln("slot 0: {s0}"); - showln("slot 2: {s2}"); - - -- 2D arrays — grids, matrices, tile maps. - imu grid: [2][3]int = [[1, 2, 3], [4, 5, 6]]; - - imu g00: int = grid[0][0]; - imu g02: int = grid[0][2]; - imu g11: int = grid[1][1]; - imu g12: int = grid[1][2]; - - check@eq(g00, 1); - check@eq(g02, 3); - check@eq(g11, 5); - check@eq(g12, 6); - - showln("grid[0][0]: {g00}"); - showln("grid[1][2]: {g12}"); - - -- 3D arrays — voxels, spatial data, RGB volumes. - imu cube: [2][2][2]int = [ - [[1, 2], [3, 4]], - [[5, 6], [7, 8]] - ]; - - imu c000: int = cube[0][0][0]; - imu c011: int = cube[0][1][1]; - imu c101: int = cube[1][0][1]; - imu c111: int = cube[1][1][1]; - - check@eq(c000, 1); - check@eq(c011, 4); - check@eq(c101, 6); - check@eq(c111, 8); - - showln("cube[0][0][0]: {c000}"); - showln("cube[1][1][1]: {c111}"); - - -- arrays work with all types. - imu flags: [3]bool = [true, false, true]; - imu temps: [3]float = [36.5, 37.0, 38.2]; - imu labels: [2]str = ["fire", "ice"]; - - imu f0: bool = flags[0]; - imu f1: bool = flags[1]; - imu t1: float = temps[1]; - imu l0: str = labels[0]; - imu l1: str = labels[1]; - - check@eq(f0, true); - check@eq(f1, false); - check@eq(l0, "fire"); - check@eq(l1, "ice"); - - showln("flag 0: {f0}"); - showln("temp 1: {t1}"); - showln("label 0: {l0}"); -} - --! ## array types. --! --! ``` --! -- dynamic arrays (size inferred from literal). --! []int — dynamic array of integers. --! []float — dynamic array of floats. --! []bool — dynamic array of booleans. --! []str — dynamic array of strings. --! --! -- static arrays (size is part of the type). --! [3]int — static array of 3 integers. --! [12]str — static array of 12 strings. --! --! -- N-dimensional arrays. --! [4][4]float — 2D static array (4x4 matrix). --! [4][4][4]float — 3D static array (4x4x4 cube). --! ``` --! --! Arrays support all primitive types: `int`, `float`, `bool`, `str`. --! The length is fixed at creation. no push, no pop, no resize. --! --! ## the capstone. --! --! - Arrays store same-type values only. --! - `[]int` — **dynamic array, size inferred.** --! - `[3]int` — **static array, size in the type.** --! - `[2][3]int` — **2D array (2 rows, 3 columns).** --! - `[2][2][2]int` — **3D array (2x2x2 cube).** --! - Indexing is chained: `grid[row][col]`, `cube[x][y][z]`. --! --! ## try this. --! --! - Create a `[]float` of temperatures and find the average with a loop. --! - Use `check@eq(arr[2] * arr[3], 24)` to verify array math. --! - What happens if you access `loot[5]`? (out of bounds — try it.) --! - Create a `[3][3]float` identity matrix and verify the diagonal. - --- EXPECTED OUTPUT: --- first chest: 10 gold --- last chest: 100 gold --- middle chest: 50 gold --- total gold: 190 --- combo damage: 17 --- warrior: 75 hp --- mage: 70 hp --- rogue: 20 hp --- slot 0: 1 --- slot 2: 3 --- grid[0][0]: 1 --- grid[1][2]: 6 --- cube[0][0][0]: 1 --- cube[1][1][1]: 8 --- flag 0: true --- temp 1: 37.0 --- label 0: fire diff --git a/crates/compiler/zo-how-to/008_tuples.zo b/crates/compiler/zo-how-to/008_tuples.zo deleted file mode 100644 index 92b939f4..00000000 --- a/crates/compiler/zo-how-to/008_tuples.zo +++ /dev/null @@ -1,65 +0,0 @@ --! # how to tuples: 006_tuples.zo --! --! @cmd: `zo run 006_tuples.zo` --! --! Sometimes you need to group data, but you don't want the overhead of --! defining a whole `struct`. --! --! Tuples group different types together in a fixed "shape." Unlike arrays --! (where every item must be the same type), tuples are happy to mix and --! match. No names — just positions. - -fun main() { - -- create a tuple with parentheses. access fields with `.0`, `.1`. - -- perfect for quick coordinate pairs. - imu pos: (int, int) = (3, 7); - imu px: int = pos.0; - imu py: int = pos.1; - showln("hero is at ({px}, {py})"); - - -- tuples can hold totally different types. - -- here we group a name (str), health (int), and attack (int). - imu hero: (str, int, int) = ("johndoe", 100, 15); - imu name: str = hero.0; - imu hp: int = hero.1; - imu atk: int = hero.2; - showln("name: {name}"); - showln("hp: {hp}, atk: {atk}"); - - -- tuples are useful for "status + value" patterns. - imu dmg: (int, bool) = (25, true); - imu amount: int = dmg.0; - showln("damage: {amount}"); - - -- tuples work great with functions that return multiple values. - -- we'll see that in lesson `036_functions.zo`! -} - --! ## tuple access. --! --! `.0` — first element. --! `.1` — second element. --! `.2` — third element, and so on. --! --! Tuples are laid out flat in memory. They are just anonymous data --! packets on the stack. no pointers, no hidden heap costs. --! --! If you need names, use a struct. If you need a collection, use an array. --! --! ## the capstone. --! --! - Tuple must be declared with `()`. --! - Access tuple is done with `tup.0`. --! --! ## try this. --! --! - Create a `(int, int, int)` tuple for RGB color values. --! - Nest a tuple inside another: --! `imu nested: ((int, int), (int, int)) = ((1, 2), (3, 4));` --! - Access the nested value: `imu v: int = nested.0.1;` - --- EXPECTED OUTPUT: --- hero is at (3, 7) --- name: johndoe --- hp: 100, atk: 15 --- damage: 25 diff --git a/crates/compiler/zo-how-to/011_binops.zo b/crates/compiler/zo-how-to/011_binops.zo deleted file mode 100644 index 85d8d7a7..00000000 --- a/crates/compiler/zo-how-to/011_binops.zo +++ /dev/null @@ -1,74 +0,0 @@ --! # how to binops: 011_binops.zo --! --! @cmd: `zo run 011_binops.zo` --! --! Operators are the verbs of your program. They take two values and --! produce a new one. In zo, operators are predictable — no hidden --! type conversions, no magic rounding. Just pure data transformation. - -fun main() { - -- arithmetic: `+`, `-`, `*`, `/`, `%`. - imu a: int = 10; - imu b: int = 3; - - imu sum: int = a + b; - imu diff: int = a - b; - imu prod: int = a * b; - imu div: int = a / b; - imu mod: int = a % b; - - showln("sum: {sum}"); - showln("diff: {diff}"); - showln("prod: {prod}"); - showln("div: {div}"); - showln("mod: {mod}"); - - -- comparison operators always produce a `bool`. - imu eq: bool = 10 == 20; - imu neq: bool = 10 != 20; - imu gt: bool = 10 > 20; - imu lte: bool = 20 <= 20; - - showln("equal: {eq}"); - showln("not equal: {neq}"); - showln("greater: {gt}"); - showln("less or equal: {lte}"); - - -- bitwise operators work on individual bits. - imu flags: int = 0b1010 & 0b1100; - imu shifted: int = 1 << 3; - - showln("bitwise and: {flags}"); - showln("bit shift: {shifted}"); -} - --! ## operator table. --! --! ``` --! arithmetic: `+` `-` `*` `/` `%` --! comparison: `==` `!=` `<` `>` `<=` `>=` --! logical: `&&` `||` `!` --! bitwise: `&` `|` `^` `<<` `>>` --! ``` --! --! These map directly to CPU instructions. They are the fastest --! operations your computer can perform. --! --! ## try this. --! --! - Try `10.0 / 3.0` — float division keeps decimals. --! - Try bitwise OR: `imu v: int = 10 | 12;` --! - Try XOR: `imu v: int = 0b1010 ^ 0b1100;` - --- EXPECTED OUTPUT: --- sum: 13 --- diff: 7 --- prod: 30 --- div: 3 --- mod: 1 --- equal: false --- not equal: true --- greater: false --- less or equal: true --- bitwise and: 8 --- bit shift: 8 diff --git a/crates/compiler/zo-how-to/012_branching.zo b/crates/compiler/zo-how-to/012_branching.zo deleted file mode 100644 index cfe6c6dc..00000000 --- a/crates/compiler/zo-how-to/012_branching.zo +++ /dev/null @@ -1,59 +0,0 @@ --! # how to branching: 012_branching.zo --! --! @cmd: `zo run 012_branching.zo` --! --! A program that always does the same thing is just a script. To build --! something real, your code needs to make decisions based on data. --! --! In zo, we use `if` for branching and `when` for quick expressions. --! No parentheses required around conditions — keep it clean. - -fun main() { - -- The basic branch: `if` followed by a block `{}`. - imu courage: int = 7; - - if courage >= 5 { - showln("you enter the dark of the devolution."); - showln("a faint glow ahead..."); - } else { - showln("you take the forest path."); - showln("birds sing overhead."); - } - - -- Chain decisions with `else if`. - -- The compiler checks in order, stops at the first true one. - imu hp: int = 40; - - if hp > 75 { - showln("you feel strong."); - } else if hp > 25 { - showln("you've seen better days."); - } else { - showln("barely standing..."); - } - - -- `when` is a ternary expression: it evaluates to a value. - -- `when cond ? a : b` — true gives `a`, false gives `b`. - imu status: str = when hp > 50 ? "healthy" : "wounded"; - showln("status: {status}"); -} - --! ## the expression difference. --! --! `if` is a statement — it does something. --! `when` is an expression — it represents a value. --! --! That's why you can assign `when` to a variable: --! `imu x: str = when 1 > 0 ? "yes" : "no";` --! --! ## try this. --! --! - Add a third `else if` branch for `hp > 50`. --! - Use `when` to set a damage multiplier: `1` if `hp > 10`, else `2`. --! - Nest an `if` inside another `if` — check courage AND hp. - --- EXPECTED OUTPUT: --- you enter the dark of the devolution. --- a faint glow ahead... --- you've seen better days. --- status: wounded \ No newline at end of file diff --git a/crates/compiler/zo-how-to/013_matching.zo b/crates/compiler/zo-how-to/013_matching.zo deleted file mode 100644 index 46c49182..00000000 --- a/crates/compiler/zo-how-to/013_matching.zo +++ /dev/null @@ -1,53 +0,0 @@ --! # how to match: 013_matching.zo --! --! @cmd: `zo run 013_matching.zo` --! --! `match` dispatches on a value by comparing it to a series --! of patterns. Supports literal patterns (int), the wildcard --! `_`, and enum variant patterns with payload binding. - -enum Loot { - Gold(int), - Potion(int), - Nothing, -} - -fun main() { - -- literal int patterns + wildcard. - imu a: int = 0; - imu b: int = 3; - imu c: int = 99; - - match a { - 0 => showln("sunday"), - 3 => showln("wednesday"), - _ => showln("weekend"), - } - - match b { - 0 => showln("sunday"), - 3 => showln("wednesday"), - _ => showln("weekend"), - } - - match c { - 0 => showln("sunday"), - 3 => showln("wednesday"), - _ => showln("weekend"), - } - - -- enum variant patterns with payload binding. - imu reward: Loot = Loot::Gold(50); - - match reward { - Loot::Gold(n) => showln(n), - Loot::Potion(n) => showln(n), - Loot::Nothing => showln(0), - } -} - --- EXPECTED OUTPUT: --- sunday --- wednesday --- weekend --- 50 diff --git a/crates/compiler/zo-how-to/014_loops.zo b/crates/compiler/zo-how-to/014_loops.zo deleted file mode 100644 index fb270f4a..00000000 --- a/crates/compiler/zo-how-to/014_loops.zo +++ /dev/null @@ -1,128 +0,0 @@ --! # how to loops: 014_loops.zo --! --! @cmd: `zo run 014_loops.zo` --! --! Loops let your code repeat. zo has two kinds: `while` for --! condition-based looping, and `for` for counted iteration over --! ranges. No parentheses, no ceremony. - -fun main() { - -- `while condition { body }` repeats until condition is false. - -- A simple combat loop: hero attacks until the monster falls. - mut monster_hp: int = 50; - imu hero_atk: int = 18; - mut turn: int = 1; - - while monster_hp > 0 { - monster_hp -= hero_atk; - - -- `break` exits the loop early. - if monster_hp <= 0 { - showln("turn {turn}: monster falls!"); - } - - turn += 1; - } - - -- nested while loops: multiplication table. - mut row2: int = 1; - - while row2 <= 3 { - mut col2: int = 1; - - while col2 <= 3 { - imu product: int = row2 * col2; - - showln("{row2} x {col2} = {product}"); - - col2 += 1; - } - - row2 += 1; - } - - -- `for` loops iterate over a range. - -- `0..5` produces 0, 1, 2, 3, 4. upper bound is exclusive. - for i := 0..5 { - showln("chest {i}"); - } - - -- combine loops with arrays for real power. - imu loot: []int = [10, 25, 50]; - mut total: int = 0; - - for i := 0..3 { - total = total + loot[i]; - } - - showln("total gold: {total}"); - - -- nested for loops: traverse a 2D grid. - -- think of a 3x3 dungeon map. - for row := 0..3 { - for col := 0..3 { - showln("room ({row}, {col})"); - } - } - - -- nested for + array: sum a 2D grid. - imu grid: [][]int = [[1, 2, 3], [4, 5, 6]]; - mut grid_sum: int = 0; - - for r := 0..2 { - for c := 0..3 { - imu v: int = grid[r][c]; - - grid_sum = grid_sum + v; - } - } - - showln("grid sum: {grid_sum}"); -} - --! ## loop types. --! --! `while cond { ... }` — repeat while condition is true. --! `for i := 0..n { ... }` — counted loop, i goes from 0 to n-1. --! `loop { ... }` — infinite loop (use `break` to exit). --! --! `break` exits the loop. `continue` skips to the next iteration. --! nested loops work: put one loop inside another to traverse --! 2D grids, 3D arrays, or any multi-dimensional structure. --! --! ## try this. --! --! - add a hero hp that decreases each turn. print "you died" --! if it hits zero before the monster. --! - use `for` to sum an array of damage values. --! - try `loop { break; }` — the infinite loop that exits --! immediately. --! - traverse a 4D array with four nested for loops. - --- EXPECTED OUTPUT: --- turn 3: monster falls! --- 1 x 1 = 1 --- 1 x 2 = 2 --- 1 x 3 = 3 --- 2 x 1 = 2 --- 2 x 2 = 4 --- 2 x 3 = 6 --- 3 x 1 = 3 --- 3 x 2 = 6 --- 3 x 3 = 9 --- chest 0 --- chest 1 --- chest 2 --- chest 3 --- chest 4 --- total gold: 85 --- room (0, 0) --- room (0, 1) --- room (0, 2) --- room (1, 0) --- room (1, 1) --- room (1, 2) --- room (2, 0) --- room (2, 1) --- room (2, 2) --- grid sum: 21 diff --git a/crates/compiler/zo-how-to/021_enums.zo b/crates/compiler/zo-how-to/021_enums.zo deleted file mode 100644 index a89c5f1e..00000000 --- a/crates/compiler/zo-how-to/021_enums.zo +++ /dev/null @@ -1,63 +0,0 @@ --! # how to enums: 021_enums.zo --! --! @cmd: `zo run 021_enums.zo` --! --! An enum is a type that can be one of several variants. Think of it --! as a "choice" — the data is always exactly one of the options. --! No nulls, no ambiguity. One type, many shapes. - -enum Monster { - Goblin, - Skeleton, - Dragon, -} - -enum Loot { - Gold(int), - Potion(int), - Nothing, -} - -fun main() { - -- create enum values with `EnumName::Variant`. - -- unit variants (no data) are just tags — fast and small. - imu enemy: Monster = Monster::Goblin; - imu boss: Monster = Monster::Dragon; - - -- each variant gets a discriminant: 0, 1, 2, ... - -- so Goblin = 0, Skeleton = 1, Dragon = 2. - showln(enemy); - showln(boss); - - -- tuple variants carry payload data. - showln(Loot::Gold(50)); - showln(Loot::Potion(25)); - showln(Loot::Nothing); -} - --! ## enum layout. --! --! every enum value is a pointer to `[tag, f0, f1, ...]` on --! the stack. unit variants allocate a single slot holding --! just the discriminant; tuple variants add one slot per --! payload field. uniform representation keeps equality, --! printing, and (soon) pattern matching straightforward — --! every load of an enum-typed local gives you a pointer to --! dereference, regardless of the variant's shape. --! --! `Goblin` — tag = 0 --! `Skeleton` — tag = 1 --! `Dragon` — tag = 2 --! --! ## try this. --! --! - Create a `Direction` enum: `North`, `South`, `East`, `West`. --! - Create a `Weapon` enum: `Sword`, `Bow`, `Staff`. --! - Compare two enum values: `if enemy == boss { ... }` - --- EXPECTED OUTPUT: --- Monster::Goblin --- Monster::Dragon --- Loot::Gold(50) --- Loot::Potion(25) --- Loot::Nothing diff --git a/crates/compiler/zo-how-to/022_structs.zo b/crates/compiler/zo-how-to/022_structs.zo deleted file mode 100644 index 0290b735..00000000 --- a/crates/compiler/zo-how-to/022_structs.zo +++ /dev/null @@ -1,59 +0,0 @@ --! # how to structs: 022_structs.zo --! --! @cmd: `zo run 022_structs.zo` --! --! A struct groups named fields into one type. It's how you model --! real things — a hero, an item, a position. Access fields with `.`. - -struct Item { - -- fields are `name: type` pairs, separated by commas. - name: str, - power: int, -} - -struct Hero { - name: str, - hp: int, - attack: int, -} - -fun main() { - -- create a struct with `Name { field = value }`. - imu sword: Item = Item { name = "iron sword", power = 12 }; - imu sword_name: str = sword.name; - imu sword_power: int = sword.power; - showln("equipped: {sword_name}"); - showln("power: {sword_power}"); - - -- build a hero. - imu hero: Hero = Hero { name = "arya", hp = 100, attack = 15 }; - imu hero_name: str = hero.name; - imu hero_hp: int = hero.hp; - showln("hero: {hero_name}"); - showln("hp: {hero_hp}"); - - -- use fields directly for logic. - imu alive: bool = hero.hp > 0; - showln("alive: {alive}"); -} - --! ## the capstone. --! --! Structs are flat data on the stack. each field occupies a slot. --! No hidden pointers, No heap allocation for the struct itself. --! --! Want to attach methods to a struct? that's `apply` — covered in lesson --! 024_apply.zo. --! --! ## try this. --! --! - Add a `def: int` field to `Hero`. --! - Create a `Potion` struct with `name: str` and `heal: int`. --! - Compare two heroes by their `hp` field. - --- EXPECTED OUTPUT: --- equipped: iron sword --- power: 12 --- hero: arya --- hp: 100 --- alive: true diff --git a/crates/compiler/zo-how-to/023_type_alias.zo b/crates/compiler/zo-how-to/023_type_alias.zo deleted file mode 100644 index b46a0c64..00000000 --- a/crates/compiler/zo-how-to/023_type_alias.zo +++ /dev/null @@ -1,47 +0,0 @@ --! # how to type aliases: 023_type_alias.zo --! --! @cmd: `zo run 023_type_alias.zo` --! --! Types can get verbose. A type alias gives a complex type a --! simple name. It doesn't create a new type — it's just a --! shorthand. The compiler treats it as the original. - --- `type Name = ExistingType;` creates an alias. -type Health = int; -type Name = str; -type Position = (int, int); - -fun main() { - -- use aliases exactly like the original type. - imu hp: Health = 100; - imu hero: Name = "arya"; - showln(hp); - showln(hero); - - -- tuple aliases make complex types readable. - imu pos: Position = (3, 7); - imu x: int = pos.0; - imu y: int = pos.1; - showln("x: {x}, y: {y}"); -} - --! ## the capstone. --! --! aliases shine when a type appears many times: --! --! `type Color = (int, int, int);` --! `type Grid = []int;` --! --! The reader sees `Color` and knows what it means. The compiler --! sees `(int, int, int)` and does its job. --! --! ## try this. --! --! - Create `type Damage = int;` and use it in a function. --! - Alias a tuple: `type Color = (int, int, int);` --! - Try using a `Position` where an `(int, int)` is expected. - --- EXPECTED OUTPUT: --- 100 --- arya --- x: 3, y: 7 diff --git a/crates/compiler/zo-how-to/024_apply.zo b/crates/compiler/zo-how-to/024_apply.zo deleted file mode 100644 index 2339fb7b..00000000 --- a/crates/compiler/zo-how-to/024_apply.zo +++ /dev/null @@ -1,64 +0,0 @@ --! # how to apply: 024_apply.zo --! --! @cmd: `zo run 024_apply.zo` --! --! In lesson 022, we built structs. Now we attach behavior to them. --! `apply` connects methods to a type — data and logic, together --! but separate. - -struct Hero { - name: str, - hp: int, - atk: int, -} - -apply Hero { - -- instance methods take `self` as the first parameter. - -- `self` is the struct this method is called on. - fun is_alive(self) -> bool { - return self.hp > 0; - } - - fun power(self) -> int { - return self.hp + self.atk; - } -} - -fun main() { - imu hero: Hero = Hero { name = "arya", hp = 100, atk = 15 }; - - -- call methods with dot notation: `instance.method()`. - imu alive: bool = hero.is_alive(); - imu pow: int = hero.power(); - - imu hero_name: str = hero.name; - showln("hero: {hero_name}"); - showln("alive: {alive}"); - showln("power: {pow}"); - - -- methods are just functions scoped to a type. - -- `hero.is_alive()` is sugar for `Hero::is_alive(hero)`. - imu dead_hero: Hero = Hero { name = "ghost", hp = 0, atk = 0 }; - imu dead: bool = dead_hero.is_alive(); - showln("ghost alive: {dead}"); -} - --! ## two kinds of methods. --! --! Instance — takes `self`, called with `.` --! - `hero.is_alive()` acts on an existing value. --! --! Static — no `self`, called with `::` (coming soon) --! - `Hero::new(...)` creates a new value. --! --! ## try this. --! --! - Add a `describe(self)` method that prints the hero's name. --! - Create a `Weapon` struct with `apply` and a `damage(self)` method. --! - Try calling a method on a different struct — see the error. - --- EXPECTED OUTPUT: --- hero: arya --- alive: true --- power: 115 --- ghost alive: false diff --git a/crates/compiler/zo-how-to/036_functions.zo b/crates/compiler/zo-how-to/036_functions.zo deleted file mode 100644 index cd907466..00000000 --- a/crates/compiler/zo-how-to/036_functions.zo +++ /dev/null @@ -1,94 +0,0 @@ --! # how to functions: 036_functions.zo --! --! @cmd: `zo run 036_functions.zo` --! --! Functions are the building blocks of every program. They take --! input, do work, and return output. In zo, you declare them with --! `fun`, list parameters with types, and optionally return a value. - --- `fun name(params) -> ReturnType { body }` -fun add(a: int, b: int) -> int { - return a + b; -} - -fun double(x: int) -> int { - return x * 2; -} - -fun is_positive(x: int) -> bool { - return x > 0; -} - --- early return: abs returns x if positive, -x if negative. -fun absolute(x: int) -> int { - if x >= 0 { - return x; - } - - return -x; -} - --- function calling another function. -fun distance(a: int, b: int) -> int { - return absolute(a - b); -} - -fun main() { - -- call a function by name with arguments. - imu sum: int = add(10, 20); - showln(sum); - - -- chain calls: the output of one feeds another. - imu d: int = double(add(3, 4)); - showln(d); - - -- functions that return bool. - imu pos: bool = is_positive(42); - imu neg: bool = is_positive(0); - showln(pos); - showln(neg); - - -- use function results in expressions. - imu total: int = add(sum, d); - showln(total); - - -- early return with branching. - imu abs1: int = absolute(-7); - showln(abs1); - imu abs2: int = absolute(3); - showln(abs2); - - -- function calling function. - imu dist: int = distance(3, 10); - showln(dist); -} - --! ## function anatomy. --! --! ```zo --! fun name(param: type, ...) -> ReturnType { --! body --! } --! ``` --! --! `return expr;` exits the function with a value. functions --! without `->` return unit (nothing). --! --! functions can call other functions. early return with `if` --! lets you handle special cases at the top. --! --! ## try this. --! --! - write `fun max(a: int, b: int) -> int` using `if`. --! - write `fun clamp(x: int, lo: int, hi: int) -> int`. --! - write a recursive `fun factorial(n: int) -> int`. - --- EXPECTED OUTPUT: --- 30 --- 14 --- true --- false --- 44 --- 7 --- 3 --- 7 diff --git a/crates/compiler/zo-how-to/037_closures.zo b/crates/compiler/zo-how-to/037_closures.zo deleted file mode 100644 index 71136835..00000000 --- a/crates/compiler/zo-how-to/037_closures.zo +++ /dev/null @@ -1,91 +0,0 @@ --! # how to closures: 037_closures.zo --! --! @cmd: `zo run 037_closures.zo` --! --! A closure is a function without a name, stored as a value. --! Different weapons have different damage formulas — you could --! write a function for each, or store the formula itself. - --- higher-order function: takes a closure and a value. -fun transform(f: Fn(int) -> int, x: int) -> int { - return f(x); -} - -fun main() { - -- `fn(params) -> type => expr` creates an inline closure. - imu double := fn(x: int) -> int => x * 2; - imu result: int = double(21); - showln(result); - - -- closures with multiple params. - imu add := fn(a: int, b: int) -> int => a + b; - imu sum: int = add(10, 20); - showln(sum); - - -- closures can capture outer variables (by copy). - imu bonus: int = 10; - imu with_bonus := fn(x: int) -> int => x + bonus; - imu boosted: int = with_bonus(5); - showln(boosted); - - -- block closures: multiple statements. - imu compute := fn(x: int) -> int { - imu doubled: int = x * 2; - imu shifted: int = doubled + 10; - return shifted; - }; - - showln(compute(5)); - - -- explicit Fn type annotation. - imu negate: Fn(int) -> int = fn(x: int) -> int => -x; - showln(negate(7)); - - -- closure calling another closure. - imu triple := fn(x: int) -> int => x * 3; - imu triple_then_add := fn(x: int) -> int => triple(x) + 1; - showln(triple_then_add(10)); - - -- pass a closure to a function. - imu square := fn(x: int) -> int => x * x; - imu r: int = transform(square, 6); - showln(r); - - -- closures in a loop. - imu inc := fn(x: int) -> int => x + 1; - mut counter: int = 0; - - for i := 0..5 { - counter = inc(counter); - } - - showln(counter); -} - --! ## closure syntax. --! --! inline: `fn(x: int) -> int => x * 2` --! block: `fn(x: int) -> int { return x * 2; }` --! --! closures are values — store them, pass them, call them. --! `Fn(int) -> int` is the type signature for a closure. --! --! closures capture outer variables by copy. mutations to the --! original variable after closure creation don't affect the --! captured value. --! --! ## try this. --! --! - create a `heal` closure: `fn(hp: int) -> int => hp + 30`. --! - store different damage formulas and pick one at runtime. --! - write a `compose` function that chains two closures. - --- EXPECTED OUTPUT: --- 42 --- 30 --- 15 --- 20 --- -7 --- 31 --- 36 --- 5 diff --git a/crates/compiler/zo-how-to/040_inference.zo b/crates/compiler/zo-how-to/040_inference.zo deleted file mode 100644 index b5b0f0e2..00000000 --- a/crates/compiler/zo-how-to/040_inference.zo +++ /dev/null @@ -1,76 +0,0 @@ --! # how to inference: 040_inference.zo --! --! @cmd: `zo run 040_inference.zo` --! --! Let's be honest — you really thought zo wouldn't give you the --! joy of type inference?! No way! --! --! Until now, we wrote every type explicitly: `imu x: int = 42;`. --! But zo can figure types out on its own. The `:=` operator tells --! the compiler: "look at the value and deduce the type." --! --! This is not dynamic typing. Once zo decides `x` is an `int`, --! it stays an `int` forever. It's just less typing for you. - -fun add(a: int, b: int) -> int { - return a + b; -} - -fun main() { - -- `:=` infers the type from the right-hand side. - imu x := 10; -- zo sees 10, deduces int. - imu y := 3.14; -- zo sees 3.14, deduces float. - imu name := "arya"; -- zo sees "arya", deduces str. - imu alive := true; -- zo sees true, deduces bool. - - showln(x); - showln(y); - showln(name); - showln(alive); - - -- inference flows through expressions. - imu sum := x + 20; -- int + int = int. - showln(sum); - - -- inference works with tuples. - imu pair := (x, alive); -- zo deduces (int, bool). - imu a: int = pair.0; - imu b: bool = pair.1; - showln(a); - showln(b); - - -- inference works with function calls. - imu result := add(10, 20); -- add returns int, so result is int. - showln(result); -} - --! ## when to use `:=` vs `: type =`. --! --! `:=` — let zo figure it out. use when the type is obvious. --! `: type =` — write the type yourself. use for documentation or when the --! compiler needs a hint. --! --! Rule of thumb: if the value makes the type obvious, use `:=`. --! If the reader would have to think, write the type. --! --! ## how it works. --! --! zo uses Hindley-Milner type inference. it looks at constants --! (`42`, `"hi"`), operators (`+`, `>`), and function signatures --! to deduce every type at compile time. zero runtime cost. --! --! ## try this. --! --! - Try `imu x := 10; x = "hello";` — zo stops you. --! - Use `:=` with an array: `imu nums := [1, 2, 3];` --! - Try `mut count := 0; count += 1;` — inference + mutation. - --- EXPECTED OUTPUT: --- 10 --- 3.140000 --- arya --- true --- 30 --- 10 --- true --- 30 diff --git a/crates/compiler/zo-how-to/041_generics_and_monomorphization.zo b/crates/compiler/zo-how-to/041_generics_and_monomorphization.zo deleted file mode 100644 index c28ebbd5..00000000 --- a/crates/compiler/zo-how-to/041_generics_and_monomorphization.zo +++ /dev/null @@ -1,58 +0,0 @@ --! # how to generics: 041_generics.zo --! --! @cmd: `zo run 041_generics.zo` --! --! Sometimes you want a function to work for any type. Instead of --! writing `add_int`, `add_float`, etc., you use a placeholder. --! In zo, generic params are prefixed with `$`. - --- `$T` stands for "any type." when you call this function, --- zo replaces `$T` with the actual type you use. -fun identity<$T>(x: $T) -> $T { x } - -fun double<$T>(x: $T, y: $T) -> $T { - return x + y; -} - -fun main() { - -- zo infers `$T = int` from the argument. - imu a: int = identity(42); - imu b: int = identity(99); - showln(a); - showln(b); - - -- same function, different types — no rewrite needed. - imu c: str = identity("hello"); - showln(c); - - -- generics work with multiple params of the same type. - imu sum: int = double(10, 20); - showln(sum); - - -- TODO: explicit type params at call site (turbofish). - -- this syntax is a work in progress: - -- imu d: int = identity(7); -} - --! ## how it works: monomorphization. --! --! when zo sees `identity(42)` and `identity("hello")`, it generates --! two separate functions in the binary: --! --! `identity_int` — fast, optimized for integers. --! `identity_str` — fast, optimized for strings. --! --! generic code with zero runtime cost. just as fast as writing --! the specialized functions by hand. --! --! ## try this. --! --! - Write `fun max<$T>(a: $T, b: $T) -> $T` using `when`. --! - Call `identity` with a `bool` — what type does zo pick? --! - Try passing an `int` where a `str` is expected — see the error. - --- EXPECTED OUTPUT: --- 42 --- 99 --- hello --- 30 diff --git a/crates/compiler/zo-how-to/042_generics_structs.zo b/crates/compiler/zo-how-to/042_generics_structs.zo deleted file mode 100644 index f75742a2..00000000 --- a/crates/compiler/zo-how-to/042_generics_structs.zo +++ /dev/null @@ -1,41 +0,0 @@ --! # how to generic structs: 042_generics_structs.zo --! --! @cmd: `zo run 042_generics_structs.zo` --! --! In lesson 041 we made generic functions. The same idea applies to --! structs — define the shape once, fill it with any type. - -struct Pair<$T> { - first: $T, - second: $T, -} - -fun main() { - -- zo infers `$T = int` from the field values. - imu pair := Pair { first = 10, second = 20 }; - showln(pair.first); - showln(pair.second); - - -- use the result in expressions. - imu sum: int = pair.first + pair.second; - showln(sum); -} - --! ## what happens under the hood. --! --! zo generates a separate struct for each type you use: --! --! `Pair_int` with two `int` fields. --! --! same monomorphization as generic functions — zero cost. --! --! ## try this. --! --! - Create a `Triple<$T>` with three fields. --! - Make a `Pair` of booleans. --! - Try mixing types in one `Pair` — zo stops you. - --- EXPECTED OUTPUT: --- 10 --- 20 --- 30 diff --git a/crates/compiler/zo-how-to/043_generics_multi.zo b/crates/compiler/zo-how-to/043_generics_multi.zo deleted file mode 100644 index af906ec0..00000000 --- a/crates/compiler/zo-how-to/043_generics_multi.zo +++ /dev/null @@ -1,44 +0,0 @@ --! # how to multi-param generics: 043_generics_multi.zo --! --! @cmd: `zo run 043_generics_multi.zo` --! --! One type param is great. Two is powerful. With multiple generic --! params, a single function handles any combination of types. - --- `$A` and `$B` can be different types. -fun first<$A, $B>(a: $A, b: $B) -> $A { a } -fun second<$A, $B>(a: $A, b: $B) -> $B { b } - -fun main() { - -- `first` keeps the first arg, ignores the second. - imu a: int = first(42, "hello"); - showln(a); - - -- `second` keeps the second arg, ignores the first. - imu b: int = second("world", 99); - showln(b); - - -- zo infers both `$A` and `$B` independently. - imu c: str = first("zo", 0); - showln(c); -} - --! ## naming convention. --! --! `$T` — one type param (the common case). --! `$A, $B` — two different type params. --! `$K, $V` — key/value (for maps, later). --! --! the `$` prefix is required. it tells zo "this is a type --! parameter, not a concrete type." --! --! ## try this. --! --! - Write `fun swap<$A, $B>(a: $A, b: $B) -> ($B, $A)`. --! - Try three params: `fun triple<$A, $B, $C>(...)`. --! - What if `$A` and `$B` happen to be the same type? - --- EXPECTED OUTPUT: --- 42 --- 99 --- zo diff --git a/crates/compiler/zo-how-to/044_generics_apply.zo b/crates/compiler/zo-how-to/044_generics_apply.zo deleted file mode 100644 index 796dcdc1..00000000 --- a/crates/compiler/zo-how-to/044_generics_apply.zo +++ /dev/null @@ -1,46 +0,0 @@ --! # how to generic apply: 044_generics_apply.zo --! --! @cmd: `zo run 044_generics_apply.zo` --! --! Generic structs + apply = reusable data with reusable behavior. --! Define the struct once, attach methods once, use with any type. - -struct Box<$T> { - value: $T, -} - -apply Box<$T> { - fun unwrap(self) -> $T { - return self.value; - } -} - -fun main() { - imu a := Box { value = 42 }; - imu va: int = a.unwrap(); - showln(va); - - imu b := Box { value = 99 }; - imu vb: int = b.unwrap(); - showln(vb); -} - --! ## the pattern. --! --! ```zo --! struct Name<$T> { ... } --! apply Name<$T> { fun method(self) -> $T { ... } } --! ``` --! --! `self` inside `apply` is the concrete instance. zo --! monomorphizes the entire apply block for each type used. --! --! ## try this. --! --! - Add a second method `fun double(self) -> $T`. --! - Create `Pair<$T>` with `fun sum(self) -> $T`. --! - Chain methods: `Box { value = 5 }.unwrap()`. - --- EXPECTED OUTPUT: --- 42 --- 99 diff --git a/crates/compiler/zo-how-to/045_generics_type_alias.zo b/crates/compiler/zo-how-to/045_generics_type_alias.zo deleted file mode 100644 index 8c150885..00000000 --- a/crates/compiler/zo-how-to/045_generics_type_alias.zo +++ /dev/null @@ -1,43 +0,0 @@ --! # how to generic type aliases: 045_generics_type_alias.zo --! --! @cmd: `zo run 045_generics_type_alias.zo` --! --! Type aliases (lesson 023) can also be generic. Give a complex --! generic type a short name — the compiler expands it for you. - -type Id<$T> = $T; -type Pair<$T> = ($T, $T); -type Grid<$T> = []$T; - -fun main() { - imu x: Id = 42; - showln(x); - - imu pos: Pair = (3, 7); - imu px: int = pos.0; - imu py: int = pos.1; - showln("position: ({px}, {py})"); - - imu row: Grid = [10, 20, 30]; - imu first: int = row[0]; - showln("first: {first}"); -} - --! ## the idea. --! --! `type Id<$T> = $T;` — identity alias. --! `type Pair<$T> = ($T, $T);` — pair of same type. --! `type Grid<$T> = []$T;` — array alias. --! --! same monomorphization — zo expands the alias at compile time. --! --! ## try this. --! --! - create `type Color = (int, int, int);` and use it. --! - alias a struct: `type PlayerList<$T> = $T[];` --! - nest aliases: `type Matrix<$T> = Grid<$T>[];` - --- EXPECTED OUTPUT: --- 42 --- position: (3, 7) --- first: 10 diff --git a/crates/compiler/zo-how-to/046_generic_enum.zo b/crates/compiler/zo-how-to/046_generic_enum.zo deleted file mode 100644 index 3e2b6b42..00000000 --- a/crates/compiler/zo-how-to/046_generic_enum.zo +++ /dev/null @@ -1,20 +0,0 @@ --! # how to generic enum match: 032_match_generic_enum.zo --! --! @cmd: `zo run 032_match_generic_enum.zo` - -enum Option<$T> { - Some($T), - None, -} - -fun main() { - imu some: Option = Option::Some(42); - - match some { - Option::Some(v) => showln(v), - Option::None => showln(0), - } -} - --- EXPECTED OUTPUT: --- 42 diff --git a/crates/compiler/zo-how-to/047_result_option.zo b/crates/compiler/zo-how-to/047_result_option.zo deleted file mode 100644 index 2e3d41c2..00000000 --- a/crates/compiler/zo-how-to/047_result_option.zo +++ /dev/null @@ -1,33 +0,0 @@ --! # how to result and option: 047_result_option.zo --! --! @cmd: `zo run 047_result_option.zo` --! --! `Option` and `Result` are auto-imported from the preload --! module. No `load` statement needed. - -fun main() { - imu a: Option = Option::Some(42); - imu b: Option = Option::None; - - match a { - Option::Some(v) => showln(v), - Option::None => showln(0), - } - - match b { - Option::Some(v) => showln(v), - Option::None => showln(0), - } - - imu ok: Result = Result::Pass(99); - - match ok { - Result::Pass(v) => showln(v), - Result::Fail(e) => showln(e), - } -} - --- EXPECTED OUTPUT: --- 42 --- 0 --- 99 diff --git a/crates/compiler/zo-how-to/048_io_all.zo b/crates/compiler/zo-how-to/048_io_all.zo deleted file mode 100644 index 98666f56..00000000 --- a/crates/compiler/zo-how-to/048_io_all.zo +++ /dev/null @@ -1,49 +0,0 @@ --! # how to io: 048_io.zo --! --! @cmd: `zo run 048_io.zo` --! --! File I/O functions are auto-imported from preload. --! All file operations return `Result` — match to --! handle errors. - -fun main() { - imu path := "/tmp/zo-048-io-test.txt"; - - imu written := write_file(path, "hello zo"); - - match written { - Result::Pass(count) => showln(count), - Result::Fail(error) => eshowln(error), - } - - imu found := exists(path); - showln(found); - - imu content := read_file(path); - - match content { - Result::Pass(text) => showln(text), - Result::Fail(error) => eshowln(error), - } - - imu appended := append_file(path, " rocks"); - - match appended { - Result::Pass(count) => showln(count), - Result::Fail(error) => eshowln(error), - } - - imu full := read_file(path); - - match full { - Result::Pass(text) => showln(text), - Result::Fail(error) => eshowln(error), - } -} - --- EXPECTED OUTPUT: --- 8 --- true --- hello zo --- 6 --- hello zo rocks diff --git a/crates/compiler/zo-how-to/048_io_read_file.zo b/crates/compiler/zo-how-to/048_io_read_file.zo deleted file mode 100644 index 79f6c1d5..00000000 --- a/crates/compiler/zo-how-to/048_io_read_file.zo +++ /dev/null @@ -1,20 +0,0 @@ --! # how to io: 048_io.zo --! --! @cmd: `zo run 048_io.zo` --! --! File I/O functions are auto-imported from preload. --! All file operations return `Result` — match to --! handle errors. - -fun main() { - imu content := read_file("samples/join-the-devolution.txt"); - - match content { - Result::Pass(text) => showln(text), - Result::Fail(error) => eshowln(error), - } -} - --- EXPECTED OUTPUT: --- JOiN THE DEVOLUTiON - diff --git a/crates/compiler/zo-how-to/061_zsx_hello.zo b/crates/compiler/zo-how-to/061_zsx_hello.zo deleted file mode 100644 index 6c019507..00000000 --- a/crates/compiler/zo-how-to/061_zsx_hello.zo +++ /dev/null @@ -1,62 +0,0 @@ --! # how to zsx: 061_zsx_hello.zo --! --! @cmd: `zo run 061_zsx_hello.zo` --! --! Writing native apps used to be hard for web developers. Different --! tools, different rules, not the same paradigm, and a lot of pain. --! For example, you don't style using CSS in the native world. WTF --! are you doing!? do not bring CSS into the native side! Sorry my --! friend... --! --! **zsx — zo Syntax Extension** — changes that. It brings the --! ergonomy of web templates to the power of native systems. One --! syntax, two worlds. - -fun main() { - imu name: str = "johndoe"; - - -- This is zsx. it's not a string — it's a first-class UI element. - -- It works on `native` and `web`. - -- - -- `::=` declares a template. its type is ``. - -- Embed variables with `{name}` — evaluated at compile-time. - -- No slow runtime lookups! - imu hello: ::=

hello, {name}!

; - - -- We print it to see the structure (or render it to a window/browser). - showln(hello); - -- Template can also been checked. The compiler understand how to treats them. - check@eq(hello, "

hello, johndoe!

"); - - -- `#render` is the directive that activates the template engine. - -- on native, it renders to a GPU-accelerated window. - -- on web, it compiles to optimized JavaScript. - #render hello; -} - --! ## what is zsx? --! --! inspired by the abandoned E4X project, zsx treats UI as a --! first-class citizen. it isn't "embedded HTML" — it's part of --! the zo language itself. --! --! ## universal rendering. --! --! - **native:** compiles to high-performance GPU calls (via egui). --! - **web:** compiles to highly optimized, thin JavaScript. --! --! ## the elm promise: no runtime errors. --! --! most UI frameworks fail at runtime because of `undefined` values. --! in zo, the compiler checks your zsx at build-time. if the data --! doesn't exist or the type is wrong, it won't compile. total --! safety, zero crashes. --! --! ## try this. --! --! - Change `name` to your own name and run it. --! - Try `{name}` with a variable that doesn't exist — see the error. --! - Add a second element: `imu sub: ::=

welcome back

;` - --- NOTE: in a real app, you define these as 'components', --- which we cover in 065_components.zo. diff --git a/crates/compiler/zo-how-to/062_zsx_dynamic_attributes.zo b/crates/compiler/zo-how-to/062_zsx_dynamic_attributes.zo deleted file mode 100644 index 5fbd774f..00000000 --- a/crates/compiler/zo-how-to/062_zsx_dynamic_attributes.zo +++ /dev/null @@ -1,14 +0,0 @@ --- How the "builtin styling" works. Is it scoped CSS? we should check the plan. - -fun main() { - imu src: str = "../zo-notes/public/assets/roll-safe-meme-1.jpg"; - imu name: str = "Kayode Ewumi"; - -- interpolation can be use as attribute or inside a string. - -- - -- Note: `{src}` this is the shorthand of: `src={src}`. - imu image: ::= a picture of '{name}'; - - check@eq(image, "\"a"); - - #render image; -} diff --git a/crates/compiler/zo-how-to/063_zsx_styling.zo b/crates/compiler/zo-how-to/063_zsx_styling.zo deleted file mode 100644 index 4dcbc891..00000000 --- a/crates/compiler/zo-how-to/063_zsx_styling.zo +++ /dev/null @@ -1,20 +0,0 @@ --- How the "builtin styling" works. Is it scoped CSS? we should check the plan. --- global, scoped. - --- this is scoped. -$: { - -- custom css rules looks like `tailwind`. - -- `w`: `width`, `h`: `height`, `bc`, `background-color`, etc. - p { - c: blue; - fw: 800; - fs: 2em; - } -} - -fun main() { - -- css rules for `p` will be applied to this tag element. - imu view: ::=

JOiN THE DEVOLUTiON

; - - #render view; -} diff --git a/crates/compiler/zo-how-to/064_zsx_html_directive.zo b/crates/compiler/zo-how-to/064_zsx_html_directive.zo deleted file mode 100644 index e1a6e4d1..00000000 --- a/crates/compiler/zo-how-to/064_zsx_html_directive.zo +++ /dev/null @@ -1,57 +0,0 @@ --! # how to zsx: `{#html expr}` — splice raw HTML into a template --! --! Ordinarily, strings embedded in templates are inserted as --! plain text — characters like `<` and `>` have no special --! meaning. But sometimes you want to render HTML directly into --! a component: a pre-rendered markdown blob, a documentation --! page imported at build time, a snippet fetched from a CMS. --! --! In zo, the special `{#html expr}` directive does exactly --! this. Equivalent to Svelte's `{@html expr}`, Vue's --! `v-html`, and React's `dangerouslySetInnerHTML`. - -fun main() { - -- Source string — a regular zo `str` literal that happens - -- to contain HTML markup. `imu` (immutable) is required — - -- MVP `#html` does not support dynamic sources. - imu strong: str = "here's some html!!!"; - - -- `{#html strong}` splices the string's structure into the - -- enclosing `

`. At compile time, the executor runs the - -- string through zo's template parser and extracts the - -- resulting commands (`Element { strong }`, text node, - -- `EndElement`) directly into the `

`'s command stream. - imu paragraph: ::=

{#html strong}

; - - -- `showln` pretty-prints the template as HTML-like text so - -- we can see the structure at runtime. Expect: - --

here's some html!!!

- showln(paragraph); - - -- Programmatic assertion — the test runner uses this to - -- verify the splice landed correctly in the command stream. - check@eq(paragraph, "

here's some html!!!

"); - - -- `#render` renders to native egui / web DOM as usual. - #render paragraph; -} - --! ## caveats --! --! **XSS:** `#html` is an explicit opt-in to unescaped HTML. --! Anything in the source string ends up in the final DOM --! verbatim — including `