Foreword
Welcome to The Toka Programming Language Guide.
This book is a comprehensive guide to learning Toka — a modern systems programming language designed for safety, simplicity, and performance.
Toka is created by YiZhonghua. The official language repository is at github.com/tokalang/toka — visit it to explore the compiler, standard library, and more.
Who This Book Is For
- Beginners who want to learn their first systems programming language
- Experienced developers coming from C, Rust, Go, or other languages
- Language enthusiasts curious about the Hat Principle and PAL Checker
What You'll Learn
- Getting started — Install Toka and write your first program
- Language basics — Variables, types, functions, control flow
- The Hat Principle — Toka's unique approach to memory safety
- Advanced features — Generics, error handling, concurrency
- Standard library tours — Strings, collections, IO, networking, and more
- Hands-on projects — Build CLI tools, HTTP servers, and regex engines
How to Use This Book
If you're new to Toka, start from the beginning and work your way through. Each chapter builds on the previous one.
If you're an experienced developer, feel free to jump to specific topics using the sidebar navigation.
Language Version Alignment
This book is kept up-to-date with the latest Toka compiler development. The current version of this documentation is aligned with:
- Compiler Version:
v0.9.8 - Toka Commit:
aadfd52(Unify three-track string system, abolish while in favor of loop, refactor std with generational Slab allocator, and introduce forge parallel build engine) - Tag:
v0.9.8-03
A Note From the Author
Toka is a young but ambitious language. This guide was written alongside the language's development to help early adopters get started. As Toka evolves, this book will evolve with it.
[!NOTE] Since Toka is still under active development and rapid iteration, some descriptions or features in this book may occasionally have slight discrepancies with the actual behavior of the latest compiler. For absolute accuracy, please always refer to the official compiler and standard library source code as the ultimate source of truth.
Happy coding!
— lumicore-dev
Getting Started
Welcome to Toka! Toka is a modern systems programming language designed for memory safety, high performance, and intuitive development. It implements the Hat Principle to achieve compile-time memory safety without a garbage collector or manual lifetime annotations.
To help you get up and running as quickly as possible, this chapter is divided into three logical steps. We recommend following them in order:
🧭 Your Path to Toka
-
Installation Set up the Toka compiler and toolchain on your system. We provide a single-command quick installer for Linux, macOS, and Windows, as well as instructions for building from source.
-
Hello, Toka! Write, compile, and run your very first Toka program. You'll learn how to invoke the compiler directly and understand the anatomy of a basic Toka executable.
-
Project Structure Scale up from a single-file script to a structured multi-file package. Learn about Toka's built-in package manager, defining package metadata in
package.tk, adding dependencies, and structuring library projects.
Let's begin by installing Toka on your machine!
Installation
Getting Toka on your system takes just a few seconds.
Quick Install
The easiest way to install Toka is via the official install script:
curl -fsSL https://tokalang.dev/install.sh | bash
This script will:
- Detect your operating system and CPU architecture
- Download the latest stable release
- Install to
~/.toka/bin/ - Configure environment variables
Setting Up Environment
After installation, add these to your ~/.bashrc or ~/.zshrc:
export PATH="$HOME/.toka/bin:$PATH"
export TOKA_LIB="$HOME/.toka/lib"
Reload your shell:
source ~/.bashrc
Verify Installation
tokac --version
You should see output similar to:
toka version 0.9.7 (Built: May 21 2026)
Building from Source
For the latest development version:
git clone https://github.com/tokalang/toka.git
cd toka
make -C build -j8
Supported Platforms
| Platform | Status |
|---|---|
| Linux (x86_64) | ✅ Primary |
| macOS (Apple Silicon) | ✅ Supported |
| macOS (Intel) | ✅ Supported |
| Windows (x86_64) | ✅ Supported |
| Linux (ARM64) | ⚠️ Experimental |
Hello, Toka!
Let's write your first Toka program. Create a file called hello.tk:
import std/io::println
fn main() -> i32 {
println("Hello, Toka!")
return 0
}
Running the Program
Toka provides two ways to run your code:
Option 1: One-step run
toka run hello.tk
This compiles and executes your program in a single command.
Option 2: Build then run
tokac build hello.tk -o hello
./hello
This gives you a standalone binary that you can distribute.
Expected Output
Hello, Toka!
Understanding the Code
Let's break down what each part does:
import std/io::println— Brings theprintlnfunction from the standard library's IO module into scope.fn main() -> i32— Declares the entry point of your program. Every Toka executable needs amainfunction. The-> i32means it returns an integer (the exit code).println("Hello, Toka!")— Prints the string to standard output.return 0— Returns exit code 0, indicating success.
Quick One-Liner
For a quick test without creating a file, you can also use:
echo 'import std/io::println fn main() -> i32 { println("Hello, Toka World!") return 0 }' > hello.tk
toka run hello.tk
What's Next?
In the next section, we'll explore the standard project structure and how to organize larger Toka projects.
Project Structure
Understanding Toka's project structure helps you organize your code effectively.
Single-File Programs
For quick prototyping or simple scripts, a single .tk file is perfectly sufficient. As we saw in the Hello, Toka! section, you can write all your code in one file (like hello.tk) and execute it instantly with:
toka run hello.tk
Multi-File Projects
For larger projects, initialize a project using toka init. This creates a package.tk file:
// package.tk
pub const PACKAGE = (
name = "my_project",
version = "0.1.0",
dependencies = ()
)
Directory layout:
my-project/
├── package.tk # Project configuration and dependencies
├── src/
│ ├── main.tk # Entry point
│ ├── utils.tk # Utility functions
│ └── models.tk # Data models
└── lib/
├── config.tk # Library config
└── helpers.tk # Helper functions
Adding Dependencies
You can easily add third-party packages to your project using the toka add command:
toka add toka_ink
This will automatically query the Toka Registry, resolve the package to its GitHub repository and version tag, and update your package.tk:
// package.tk
pub const PACKAGE = (
name = "my_project",
version = "0.1.0",
dependencies = (
toka_ink = "github.com/lumicore-dev/toka-ink:v0.2.1",
)
)
During toka build, the compiler will automatically fetch these dependencies into your .toka/packages directory.
Library Projects
For reusable libraries, toka new my-lib --lib generates a library structure without a main.tk executable entry point. The configuration format remains the same in package.tk.
Import System
// Import from standard library
import std/io::println
// Import from project source
import src/utils::{helper_fn}
// Import from local library
import lib/config::{Config}
// Import with alias
import std/time as time_lib
Build Output
Running toka run or tokac build produces:
my-project/
└── target/
├── debug/
│ └── my-project # Debug binary
└── release/
└── my-project # Release binary (with --release)
🛠️ Incremental Builds with the Forge Engine
Starting from Toka v0.9.8-03, the official compiler comes with a high-throughput, parallel, and persistent cache build engine named forge. It reads the project's dependency topology declared inside a build.tk script, coordinates workers to compile files in parallel, and persists the build provenance locally.
1. Declaring Build Configuration build.tk
Create a build.tk file in the root directory of your project, utilizing the standard build toolchain to declare your executable or library:
import build::{Executable, run_build}
fn main() -> i32 {
// Instantiate a build project, Executable::make(binary_name, entry_source)
// Since Executable::make is a high-level wrapper around low-level C-FFI, we pass raw FFI pointers.
auto proj# = Executable::make("my-project".as_cstr().raw_ptr(), "src/main.tk".as_cstr().raw_ptr())
return run_build(proj)
}
2. Running Forge for Parallel Incremental Builds
Execute forge in your terminal:
# Compile with concurrency, -j specifies worker thread count (defaults to 4)
forge -j 8
The forge engine will automatically:
- Parse the entry point
src/main.tkand map its static dependency DAG recursively. - Compare source file timestamps and maintain an index database inside
.forge_cache. - Smart Incremental Skip: Instantly bypass compilation for unchanged files, shrinking multi-module project compilation times down to milliseconds!
Language Basics
Now that you have Toka installed and have written your first program, let's dive into the core concepts of the language.
Hello World Revisited
The simplest Toka program needs two things: an import for the standard library, and a main function:
import std/io::println
fn main() -> i32 {
println("Hello, Toka!")
return 0
}
Run it with:
toka run hello.tk
Key Principles
Toka is designed around a few core principles:
- Safety by default — Variables are immutable unless explicitly marked.
- No hidden runtime — What you write is what gets executed. No garbage collector, no VM.
- C-like performance — Built on LLVM 20, Toka generates optimized native code.
- Modern syntax — Clean, readable, with intuitive pattern matching and error handling.
Comment Syntax
Comments in Toka use the standard // syntax:
// This is a single-line comment
/* This is a
multi-line comment */
Next Steps
In the following sections, we'll explore variables, types, functions, and control flow in detail.
Variables & Types
Toka is a statically-typed language with a rich type system. Let's explore how variables and types work.
Variable Declaration
Use the auto keyword to declare variables:
auto x = 10 // i32 (default integer type)
auto y = 3.14 // f64 (default float type)
auto name = "Toka" // str (string slice)
Naming Rules & Hyphen (-) Resolution
Toka has a strict and elegant rule regarding the hyphen (-) character to achieve both modular naming convenience and unambiguous mathematical syntax:
Package Names & Namespaces
Package names and imported namespaces can use hyphens freely. This is common when importing nested folders or external libraries that follow a kebab-case directory structure. You can import them and call their functions using their hyphenated namespaces:
import std/io::println
// Package and namespace names can contain hyphens
import ./nested/toka-ink as toka_ink
fn main() -> i32 {
toka_ink::render()
return 0
}
Variables & Functions
Ordinary variable names, constant names, shape/struct names, and function definitions cannot contain hyphens. They must follow standard alphanumeric/underscore naming rules (such as camelCase or snake_case):
fn main() {
auto max_size = 1024 // OK (snake_case)
auto maxSize = 1024 // OK (camelCase)
// auto max-size = 1024 // Error: Hyphens are not allowed in variable names
}
Unambiguous Subtraction
Because hyphens are strictly forbidden in variable and function names, the subtraction operator (-) is completely unambiguous. Ordinary subtraction expressions without spaces are always parsed correctly without ambiguity:
import std/io::println
fn main() -> i32 {
auto sub1 = 10
auto sub2 = 3
// No spaces are needed! Always parsed as subtraction
auto result = sub1-sub2 // OK: parsed as sub1 - sub2
auto value = 10-3 // OK: parsed as 10 - 3
println("Result: {}, Value: {}", result, value)
return 0
}
Explicit Type Annotations
You can specify the type explicitly with a colon:
auto x: u64 = 10
auto y: f32 = 3.14:f32
auto flag: bool = true
Primitive Types
Toka's type system is defined in lib/core/types.tk and includes:
| Type | Description | Size |
|---|---|---|
i8, i16, i32, i64 | Signed integers | 1-8 bytes |
u8, u16, u32, u64 | Unsigned integers | 1-8 bytes |
f32, f64 | Floating point (IEEE 754) | 4-8 bytes |
bool | Boolean (true / false) | 1 byte |
char | C-style character (i8) | 1 byte |
byte | Raw byte (u8) | 1 byte |
Platform-Dependent Types
pub alias usize = u64 // Pointer-sized unsigned integer
pub alias isize = i64 // Pointer-sized signed integer
On 64-bit systems, usize is u64; on 32-bit systems, it would be u32.
Type Aliases
Toka supports two kinds of type aliases:
Weak alias — Semantically identical, transparent to the compiler:
pub alias usize = u64
Strong type — Same memory layout, but requires explicit conversion:
pub type Addr = u64
pub type OAddr = u64
Constants
Use const for compile-time constant values:
pub const MAX_SIZE = 1024:u64
pub const NAME = "Toka"
Constants are inlined at compile time — no memory is allocated and their address cannot be taken.
Mutability
Variables are immutable by default. Use the # suffix to make them mutable. The # token is only needed in two places:
- At declaration — to mark the variable as mutable
- Before a mutable method call — to present the guard on the variable
import std/io::println
pub shape List(data: i32)
impl List {
pub fn sort(self#) {}
pub fn push(self#, val: i32) {}
}
fn main() -> i32 {
auto y = List(data = 10) // immutable
auto x# = List(data = 10) // mutable — # at declaration
// Reading and assignment don't need #
x = List(data = 20) // OK — plain assignment
println("{}", x.data) // OK — plain read
// Calling a mutable method requires # on the variable
x#.sort() // OK — # on variable before .method()
x#.push(5) // OK
return 0
}
This is an example of Toka's Attribute Token System — the # suffix denotes soul mutability and is placed on the variable itself when declaring it or calling its mutable methods.
Functions
Functions in Toka are first-class citizens. They follow a clean, readable syntax.
Basic Function Syntax
fn add(x: i32, y: i32) -> i32 {
return x + y
}
fnkeyword declares a function- Parameters are typed with
name: Typesyntax - Return type follows
-> - The function body is wrapped in
{ }
Entry Point: main
Every Toka executable needs a main function that returns an exit code:
import std/io::println
fn main() -> i32 {
println("Hello, Toka!")
return 0
}
Return 0 for success, non-zero for errors.
Multiple Parameters
Functions can take multiple parameters of different types:
fn greet(name: str, age: i32, formal: bool) -> str {
if formal {
return "Good day"
}
return "Hey"
}
Mutable Parameters
Parameters are immutable by default. Use the # token at declaration for mutable parameters. Inside the function body, you access the parameter without #:
fn increment(x#: i32) {
x = x + 1 // Inside the body, no # needed
}
Functions with No Return
If a function doesn't return anything, omit ->:
import std/io::println
fn log_message(msg: str) {
println("{}", msg)
}
Method Syntax
Functions can be attached to types using impl blocks:
pub shape Number(val: i32)
impl Number {
pub fn double(self) -> i32 {
return self.val * 2
}
}
fn main() -> i32 {
auto result = Number(val = 5).double() // result = 10
return 0
}
Control Flow
Toka provides modern control flow constructs for building clear, expressive programs.
If / Else
import std/io::println
fn check_temp(temp: i32) {
if temp > 35 {
println("It's hot!")
} else if temp < 10 {
println("It's cold!")
} else {
println("It's mild.")
}
}
Loop Loops
Toka completely abolishes traditional while loops, unifying all loop semantics under the loop keyword. This significantly simplifies compiler control flow analysis and eliminates syntactic ambiguity.
Conditional loops use the loop condition {} syntax structure:
import std/io::println
auto count# = 0
loop count < 5 {
println("Count: {}", count)
count = count + 1
}
Iterating Over Arrays
Toka supports array-based iteration:
import std/io::println
for auto i in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] {
println("{}", i)
}
Currently, Toka does not support a native range operator (like ..). You can iterate over an array of elements or implement custom loops using loop.
Iterating Collections
import std/io::println
auto items = ["apple", "banana", "cherry"]
for auto item in items {
println("{}", item)
}
With index:
import std/io::println
auto items2 = ["apple", "banana", "cherry"]
auto i# = 0
for auto item in items2 {
println("{}: {}", i, item)
i = i + 1
}
Break and Continue
Control loop flow with standard keywords:
import std/io::println
auto iter# = 0
loop iter < 100 {
if iter == 50 {
break // Exit the loop completely
}
if iter % 2 == 0 {
iter = iter + 1
continue // Skip to next iteration
}
println("{}", iter) // Prints odd numbers: 1, 3, 5, ..., 49
iter = iter + 1
}
Match (Pattern Matching)
Toka provides intuitive pattern matching with the match expression:
fn describe(value: i32) -> str {
return match value {
0 => pass "zero"
1 => pass "one"
auto v if v >= 2 && v <= 10 => pass "small"
auto v if v >= 11 && v <= 100 => pass "medium"
_ => pass "large"
}
}
The _ wildcard matches any value, serving as the default case.
Strings
Toka 1.0 implements a highly rigorous, high-performance Three-Track Text & Byte-Stream System. It orthogonally decouples "textual observation" from "physical binary manipulation" in the type system. Simultaneously, it eliminates ambiguous implicit context inference, providing developers with maximum memory safety and speed.
🏛️ 1. String and View Types
Strings in Toka are split into owned heap-allocated containers and zero-copy borrowed views. Built into the Prelude's zero-import scope, you can directly use these types without any import:
| Type | Physical Geometry / LLVM Representation | Memory Ownership | Description |
|---|---|---|---|
str | Fat pointer { i8* head, i64 len } | No Ownership (Borrowed) | UTF-8 read-only text view. No \0 termination guarantee. |
string | Heap container { *#buf, usize, usize } | Exclusive Ownership (Owned) | Heap-allocated mutable UTF-8 string, strictly terminated with \0. |
bytes | Fat pointer { i8* head, i64 len } | No Ownership (Borrowed) | Read-only binary byte-stream view. |
cstr | Single pointer i8* (Zero-cost Shape) | FFI Borrowed | C-style read-only single pointer, 100% statically guaranteed to be \0-terminated. |
⚖️ 2. The Orthogonal Conversion Matrix
Toka strictly adheres to the "Honest Overhead Naming Code":
as_xxxfamily: Represents zero-overhead, zero-allocation view borrowing. The borrowed view's lifetime is strictly guarded at compile time, bound tightly to its host.to_xxxfamily: Represents conversions involving physical copies or heap allocations, explicitly declaring the separation of ownership and physical memory operations.
| Source $\rightarrow$ Target | str (Safe Fat Slice) | string (Heap Owned) | cstr (\0 Borrowed) | *char (FFI Raw Ptr) |
|---|---|---|---|---|
"..." Literal | Default ResolvedType (0-cost) | .to_string()($O(N)$ heap alloc) | .as_cstr()($O(1)$ zero-alloc) | FFI boundary decay (0 friction) |
str | —— | .to_string()($O(N)$ heap alloc) | .to_string().as_cstr()($O(N)$ heap copy) | Implicit banned! Use .to_string().c_str() |
string | .as_str()($O(1)$ zero-alloc) | —— | .as_cstr()($O(1)$ zero-alloc) | .c_str()($O(1)$ address borrow) |
cstr | .as_str()($O(1)$ zero-alloc) | .to_string()($O(N)$ heap alloc) | —— | FFI boundary decay (0 friction) |
🚀 3. Core Operations & Examples
1. Default Zero-Copy Literals
In Toka, regular double-quoted "..." string literals are resolved as static, read-only str views by default:
import std/io::println
fn main() -> i32 {
// Resolved as str by default, resident in .rodata, 100% statically safe
auto greeting: str = "Hello, Toka!"
println("Greeting: {}", greeting)
return 0
}
2. Dynamic string & Concatenation
string is an exclusive heap container. You can mutate it dynamically using push_str.
import std/io::println
fn main() -> i32 {
// zero-import prelude allows direct call to string::from
auto full# = string::from("Hello, ")
full#.push_str("World!")
println("{}", full.as_str()) // Zero-overhead str view borrow
return 0
}
3. Dual-Track Conversion between str and bytes
Use str for high-level text operations, and bytes for low-level binary data and byte-based indexing:
import std/io::println
import core/option::Option
import core/result::Result
fn main() -> i32 {
auto s: str = "Toka 1.0"
// 1. O(1) zero-cost downgrade to bytes view
auto b = s.bytes()
// 2. Bound-checked O(1) byte indexing
auto b0 = b.at(0:usize)
if b0.is_some() {
println("Byte 0: {}", b0.unwrap() as i64) // 84 ('T')
}
// 3. Validate and upgrade back to str
auto res = b.try_to_str()
if res.is_ok() {
auto s_back = res.unwrap()
println("Recovered: {}", s_back)
}
return 0
}
🛡️ 4. FFI Physical Copy Isolation
Because dynamic str slices do not guarantee a \0 terminator, str is strictly prohibited from implicitly decaying to a cstr or raw pointer.
If a dynamic str must cross the FFI boundary, you must explicitly call to_string() to create a \0-terminated copy on the heap:
// Declare FFI interface
pub fn puts(s: cstr)
fn main() -> i32 {
auto my_str: str = "Hello C-FFI"
// Explicitly declare physical copy and \0 sealing for absolute safety
auto temp_c = my_str.to_string()
unsafe {
puts(temp_c.as_cstr())
}
return 0
}
The Hat Principle
The Hat Principle is the foundational design philosophy behind Toka's memory safety model. Rather than forcing developers to write complex lifetime annotations (as in Rust) or relying on a runtime Garbage Collector (as in Go or Java), Toka introduces visible syntactic tokens called "Hats" to track ownership, resource lifetimes, and access permissions at compile time.
What is a Hat?
In Toka, a Hat is a syntactic sigil (symbol) attached directly to an identifier. This explicit notation tells both the programmer and the compiler's safety analysis engine exactly how a resource is stored, owned, and accessed.
Toka defines four primitive Hat typologies:
| Hat Sigil | Typology | Semantic Description | Allocation Context |
|---|---|---|---|
* | Raw Pointer | Unsafe, low-level pointer with manual lifecycle management | Heap or Address-of |
^ | Unique Pointer | Safe, exclusive ownership of a heap-allocated resource (Auto-free) | Heap (new) |
~ | Shared Pointer | Safe, reference-counted shared ownership | Heap (new) |
& | Borrow Pointer | Safe, compile-time borrow/reference to another soul's data | Existing Soul |
Handle vs. Soul: Implicit Dereferencing
One of the key usability features of the Hat Principle is the strict bifurcation between a pointer's container and its underlying data:
- Handle (The Hat): An identifier with its sigil (
^ptr,*ptr,~ptr) represents the pointer container itself (the metadata holding the address). - Soul (The Data): An identifier without its sigil (
ptr) represents the underlying data (the actual value).
By operating directly on the Soul (omitting the sigil), Toka completely eliminates the need for explicit dereferencing operators like C's *p or Rust's *p. For example, ptr.x = 42 directly mutates the underlying heap data without manual dereferencing.
The Pointer Analysis Layer (PAL)
The compiler's Pointer Analysis Layer (PAL) Checker inspects Hat configurations during compilation to statically enforce strict safety guarantees:
- Strict Move Semantics: A Unique Pointer (
^) automatically transfers ownership on assignment. The PAL Checker blocks any "use-after-move" of the original handle. - Deterministic Destruction: When an owned pointer (
^or the last~) exits its declared scope, its resource is automatically freed. - Rigorous Borrow Checking: Borrow Pointers (
&) are checked to ensure they never outlive the underlying Soul, preventing dangling references. - Data Race Prevention: The PAL Checker ensures that mutable access (
#) is exclusive, while shared access is read-only.
Learning Path
To master the Hat Principle, explore the following sections in order:
- Soul & Identity: Master the Handle/Soul split, implicit dereferencing, and the syntax of member borrowings (including standard parentheses and Hat-Terminal Morphology).
- Ownership & Hats: Explore the four ownership models, default moves versus copying, the
cedekeyword, and the visual distinction between mutable local declarations and mutable method call receivers. - Memory Safety & PAL: Deep dive into the Pointer Analysis Layer and see how Toka achieves zero-overhead compile-time memory safety.
Soul & Identity
Toka's Hat Principle introduces a strict, clear distinction between a pointer's Handle (its syntactic container) and its Soul (its underlying data). Mastering this separation is essential to understanding Toka's memory management model.
Handle vs. Soul
Every pointer identifier in Toka possesses two distinct aspects in syntax and memory:
- Handle (The Hat): An identifier prefixed with its pointer sigil (
^p,*p,~p). It represents the physical pointer container in memory—the slot holding the target memory address. - Soul (The Data): An identifier written without its sigil (
p). It represents the underlying data/resource being pointed to.
fn handle_soul_example() {
auto ^p# = new Point(x=10, y=20)
p.x = 30
auto sum = p.x + p.y
println("sum: {}", sum)
}
In the example above, ^p# is the mutable unique handle, while p.x directly accesses the field of the underlying Point soul.
Ergonomic Implicit Dereferencing
Toka completely eliminates the visual clutter of traditional dereferencing operators (such as C's *p / p-> or Rust's *p).
Whenever you need to read, write, or access fields of the data behind a pointer, you simply operate directly on its Soul (the identifier without its sigil). The Toka compiler automatically resolves this to a dereference operation under the hood with zero runtime overhead:
fn implicit_deref_example() {
unsafe {
auto *p# = alloc i32
p = 100
auto val = p
println("val: {}", val)
}
}
This implicit dereferencing brings a clean, script-like syntax to high-performance systems programming, drastically increasing code readability.
Identity: The Address-Of Operator *expr
To retrieve the raw, physical memory address of a local variable or resource, Toka provides the unary *expr syntax. This returns a raw pointer *T pointing to the variable's location:
fn address_of_example() {
auto a# = 42
auto *raw_ptr = *a
println("addr: {}", raw_ptr)
}
[!NOTE] The Hat sigil (like
^or~) does not mean "address of". Sigils are strictly type specifiers for pointer handles, whereas*expris the runtime operator to extract a memory address.
References and the Borrow Pointer &
A Borrow Pointer (&) is a reference that allows safe, temporary, and compiler-checked access to a Soul without taking ownership of it.
Clean Local Variable Borrowing
When borrowing a standard, non-member local variable, no parentheses are needed. Simply prefix the soul with & to create a borrow pointer:
auto x = 42
auto &y = &x // y is a borrow pointer pointing directly to the soul of x
The Member Access Chain Borrow Ambiguity
When attempting to borrow a specific field within a struct or shape (a member access chain), writing &pt.x is syntactically ambiguous. It is unclear whether you want to borrow the entire member chain &(pt.x) or access the member x on a borrowed pointer (&pt).x.
To ensure clarity and type safety, the Toka compiler rejects &pt.x with a compile error and requires one of two clear and legal options:
- Option A (Standard Parentheses): Explicitly wrap the member chain in parentheses:
&(pt.x). - Option B (Hat-Terminal Morphology): Place the
&borrow sigil directly before the terminal field name:pt.&x.
pt.&x is an intuitive form, allowing the borrow operation to integrate directly into the member access chain.
fn borrow_example() {
auto x = 42
auto &y = &x // Clean local variable borrow, no parentheses needed!
auto pt = Point(x = 10, y = 20)
// Member borrow Path A: standard parentheses
auto &rx1 = &(pt.x)
// Member borrow Path B: Hat-Terminal Morphology (Toka's signature aesthetic)
auto &rx2 = pt.&x
println("y: {}, rx1: {}, rx2: {}", y, rx1, rx2)
}
Ownership & Hats
Ownership in Toka is governed entirely by the Hat Principle. Rather than hiding allocations behind a generic type system, Toka uses distinct hat sigils to make a value's ownership model, copy/move semantics, and access permissions immediately clear directly in the syntax.
Four Ownership Models
Toka defines four pointer typologies, each supporting a distinct resource management strategy:
1. * — Raw Pointer (Manual Lifecycle)
Low-level raw pointers provide zero compile-time safety and must be managed manually within an unsafe block:
fn raw_ptr_example() {
unsafe {
auto *p# = alloc i32
p = 99
println("raw: {}", p)
}
}
2. ^ — Unique Pointer (Exclusive Heap Ownership)
Unique pointers enforce strict exclusive ownership. Only one ^ handle can point to a heap resource at a time. When a unique pointer exits its scope, its bound memory is automatically freed:
fn unique_ptr_example() {
auto ^p = new i32(42)
auto ^q = ^p
println("unique: {}", q)
}
3. ~ — Shared Pointer (Reference-Counted Heap Ownership)
Shared pointers allow multiple handles to share ownership of the same heap-allocated resource via thread-safe reference counting. The resource is automatically freed when the last ~ handle goes out of scope:
shape Point(x: i32, y: i32, z: i32)
auto ~s1# = new Point(x = 100, y = 200, z = 0)
auto ~s2# = ~s1 // Shared Copy (ref count increments)
s2.x = 300 // Directly mutates the underlying soul
4. & — Borrow Pointer (Reference Semantics)
Borrow pointers represent temporary, compiler-checked references to existing souls without taking ownership, strictly governed by the Pointer Analysis Layer (PAL).
Moving vs. Copying
Toka has clear rules to distinguish between copying data and transferring ownership:
Default Copy Semantics
By default, standard assignments in Toka perform a copy (value copies for simple scalar types like i32/bool, and shallow copies for standard complex shape types):
fn move_copy_example() {
auto a# = 42
auto b = a
a = 99
println("a: {}, b: {}", a, b)
}
Default Move Semantics (Unique Pointers)
Move semantics apply by default exclusively to Unique Pointers (^). Assigning one unique pointer to another automatically transfers exclusive ownership from the source to the destination, rendering the source handle immediately invalid:
shape Point(x: i32, y: i32, z: i32)
auto ^p1 = new Point(x = 10, y = 20, z = 0)
auto ^p2 = ^p1 // Ownership transfers (moves) to p2; p1 is no longer valid!
Explicit Move with cede
For other types (such as custom shapes or string), copy semantics are used by default. To explicitly transfer ownership of such a value (avoiding copies), you must use the cede keyword. Ceding transfers the resource to the destination and invalidates the source variable:
auto s1 = string::from("hello")
auto s2 = cede s1 // Explicit move: s1 is invalidated
Function Parameters: Zero-Copy Capture
In Toka, function parameters are immutable by default and passed via an incredibly efficient zero-copy implicit reference capture mechanism.
The compiler automatically passes arguments by reference under the hood with zero runtime overhead and zero copying. Therefore, you do not need special pointer sigils for standard parameter passing unless you explicitly intend to pass or rebind pointer handles:
fn process(data: i32) {
println("data: {}", data)
}
fn borrow_func_example() {
auto val = 10
process(val)
println("val: {}", val)
}
The # Mutability Marker & Permission Views
Toka introduces the # marker to explicitly declare and track mutable access. Its usage is governed by strict compiler lifecycles to eliminate redundant code noise while maximizing visual safety.
1. Declaring Mutability
To declare a local variable mutable, you must append # to the identifier at its declaration site:
fn mutable_local_example() {
auto val# = 42
val = 99 // Everyday context assignment: NO '#' suffix allowed here!
println("val: {}", val)
}
2. Forbidden in Everyday Contexts (Assignments)
Once a variable is declared mutable, its mutable state is registered in its Permission View (type system). Therefore, you must NOT append # to the variable in ordinary assignments or expressions:
auto val# = 42
val = 99 // Everyday assignment: NO '#' allowed! Writing 'val# = 99' is a compiler error.
This prevents useless syntactic noise in everyday code.
3. Required at Mutating Method Call Sites (Receiver Suffix)
A key safety feature in Toka is that mutating method calls must be explicitly flagged at the call site.
When invoking a method that mutates a shape, the compiler requires you to append the # suffix to the receiver object (e.g. obj#.mutate()). This makes the state mutation explicit, aiding code readability and auditing:
shape Counter (
val#: i32 = 0
)
impl Counter@encap {
pub fn clone(self) -> Counter {
return Counter(val = self.val)
}
fn drop(self#) {}
pub fn increment(self#) {
self.val = self.val + 1
}
}
fn mutable_method_example() {
auto c# = Counter(val = 10) // Declaration: '#' is required for mutability
c#.increment() // Mutation Site: '#' receiver suffix is required and highly visible!
println("c.val: {}", c.val)
}
By restricting the # suffix to declarations and mutating method call sites, Toka keeps the syntax intuitive while providing clear safety visibility.
Memory Safety & PAL
Toka achieves rigorous, compile-time memory safety without the heavy runtime overhead of a Garbage Collector, and without requiring complex, verbose lifetime annotations.
This is accomplished by the compiler's Pointer Analysis Layer (PAL) Checker, which acts as a static verification engine inspecting the usage of Hat pointer typologies and access permission modifiers.
How the PAL Checker Works
The PAL Checker performs static compile-time flow analysis to track the Permission View and Active Scope (Region) of every resource in your codebase.
By requiring explicit pointer specifiers (Hats like ^, ~, &) and mutation intent markers (like # on declarations and receiver method call sites), the compiler gains enough semantic information to perform rigorous safety checking automatically.
It guarantees four core pillars of memory safety at compile time:
1. No Use After Move (Unique Pointers ^)
A unique pointer ^ represents exclusive heap ownership. When it is assigned to another handle, the original handle is marked as "moved-from." The PAL Checker statically blocks any subsequent read or write to the original variable:
fn no_use_after_move_example() {
auto ^p = new i32(42)
auto ^q = ^p
println("q: {}", q)
}
2. No Dangling References (Borrow Pointers &)
A borrow pointer & must never outlive its referent. The PAL Checker tracks the lifetime bounds of the underlying Soul and rejects compilation if a borrow is returned or held beyond the soul's deallocation scope.
3. No Double Free
For owned resources (^ unique pointers or the last ~ shared pointer), the compiler injects a single, deterministic destruction call when the handle exits its scope. Handlers that have been moved or ceded are bypassed, preventing double frees statically.
4. No Data Races / Aliasing Violations
To prevent data races and modification conflicts, Toka enforces the fundamental aliasing rule: you may have either one active mutable borrow OR multiple read-only borrows, but never both simultaneously.
Because Toka requires mutating method calls to explicitly tag their receiver (e.g. obj#.mutate()), the PAL Checker can trivially and securely verify exclusive mutable access at every method call boundary.
Zero-Copy Argument Safety
Toka's function argument passing utilizes the zero-copy implicit reference capture mechanism. When passing arguments to a function, the PAL Checker automatically captures variables by reference under the hood with zero copying.
Because parameters are immutable inside the function by default, the caller's argument remains perfectly safe from modification:
fn show(x: i32) {
println("x: {}", x)
}
fn borrowing_safe_example() {
auto val = 10
show(val)
println("val: {}", val)
}
To allow a function to modify a parameter, the parameter must be declared mutable (#) in the function signature, and the PAL Checker will then enforce exclusive borrowing at the call site.
PAL Static Assurances at a Glance
The PAL Checker statically analyzes and verifies memory safety during compilation:
| Scenario / Action | PAL Checker Static Response | Safety Context |
|---|---|---|
Accessing a unique pointer ^ after move | ❌ Compile Error | Prevents use-after-move and double-frees |
Holding a borrow & after its Soul is dropped | ❌ Compile Error | Eliminates dangling pointers & use-after-free |
Triggering double unsafe free on * | ❌ Compile Error | Restricts manual deallocation errors |
Overlapping mutable borrow obj#.mutate() with shared borrows | ❌ Compile Error | Guarantees thread safety and no data races |
| Normal borrow parameter passing | ✅ Passed | High-performance, zero-copy read-only access |
| Handle exiting its declared scope | ✅ Passed | Generates zero-overhead automatic resource cleanup |
By shifting the verification of memory safety to the Pointer Analysis Layer (PAL), Toka allows developers to write safe, high-performance code without the need for manual lifetime annotations.
Advanced Features
Having mastered the language basics and gained a deep understanding of the Hat Principle regarding the management of Soul and Handle, you have already conquered Toka's steepest learning curve. Now, we enter the world of Toka's Advanced Features.
Toka's advanced features provide high-level abstract expressiveness while strictly adhering to the Zero-Copy Capture Mechanism and runtime efficiency. Here, you will see the integration of strong type systems and algebraic data types.
Chapter Map
This chapter will guide you through the following four pillars of system development:
flowchart TD
Root("Toka Advanced Features & Abstractions")
Generics("**Generics**")
Error("**Error Handling**")
Pattern("**Pattern Matching**")
Concurrency("**Concurrency**")
GenericsDesc("Zero-overhead ADT representation<br>Avoid Soul Collapse")
ErrorDesc("Algebraic data type contracts<br>Short-circuit (!) propagation")
PatternDesc("Deconstruct Shapes<br>Combined w/ guards")
ConcurrencyDesc("Lightweight fibers<br>Multicore scaling")
Root --> Generics & Error & Pattern & Concurrency
Generics --> GenericsDesc
Error --> ErrorDesc
Pattern --> PatternDesc
Concurrency --> ConcurrencyDesc
style Root fill:#1e1b4b,stroke:#a855f7,stroke-width:2px,color:#fff;
style Generics stroke:#3b82f6,stroke-width:1.5px;
style Error stroke:#10b981,stroke-width:1.5px;
style Pattern stroke:#f59e0b,stroke-width:1.5px;
style Concurrency stroke:#ec4899,stroke-width:1.5px;
1. Generics
Achieve zero-overhead polymorphism while maintaining strong type safety. You will learn about Toka's unique Morphic Generic Types (like 'A) and the strict rule of prefixing corresponding Shape fields with a single quote (e.g., 'first: 'A) to avoid "Soul Collapse".
2. Error Handling
Toka discards runtime exception-throwing mechanisms. Instead, it leverages algebraic data types, specifically Option<T> and Result<T, E>, to represent and propagate errors. Together with the concise ! short-circuit operator, your error handling will be clearer and more direct than nested pattern matches.
3. Pattern Matching
Pattern matching allows you to deconstruct Shapes and dispatch logic intuitively. Here, you will understand how variable patterns binding and shadowing work under the hood, and learn to combine patterns with if guards for precise range and boundary checks.
4. Concurrency
Toka provides coroutines and threading primitives to build concurrent systems safely.
[!TIP] Mental Model Shift When reading this chapter, focus on alternative solutions to inheritance and runtime polymorphism. Toka achieves polymorphism at compile time through Shape deconstruction, Trait constraints, and Pattern Matching. Combined with the Hat Principle, your code will be both safe and highly efficient.
Generics
Generics allow you to write flexible, reusable code that works with any type while maintaining compile-time safety.
In Toka, when defining custom generics, you typically use the intuitive T (without a single quote) form. The single-quoted 'T (Morphic generic) is only needed in generic container scenarios where compatibility with both soul elements and pointer elements is required.
Generic Functions
Define a generic function using the standard T syntax:
fn identity<T>(value: T) -> T {
return value
}
auto x = identity(42) // Works with integers
auto y = identity("hello") // Works with strings
Generic Constraints
Constrain generics with traits:
fn max<T: @PartialOrd>(a: T, b: T) -> T {
if a > b {
return a
}
return b
}
This function works with any type that implements @PartialOrd.
Generic Data Structures
pub shape Pair<'A, 'B>(
'first: 'A,
'second: 'B
)
impl<'A, 'B> Pair<'A, 'B> {
pub fn new(first: 'A, second: 'B) -> Pair<'A, 'B> {
return Pair('first = first, 'second = second)
}
pub fn first(self) -> 'A {
return self.first
}
pub fn second(self) -> 'B {
return self.second
}
}
When defining shapes with Morphic generic types (those with a single quote prefix like 'A), Toka enforces a strict rule: the corresponding fields must also be prefixed with a single quote (e.g., 'first: 'A). This indicates that these fields can dynamically accept different pointer morphology states, avoiding "soul collapse." However, when calling methods or retrieving fields, you refer to them as normal field names (e.g., self.first instead of self.'first).
Choosing Between Morphic and Standard Generics
When defining custom generics or generic parameters, Toka supports both single-quoted (e.g., 'T) and standard (e.g., T) styles. The core differences and use cases are:
T(Standard Generics, without single quote): Generally, custom generics only need thisTform. Whether you are writing high-level business logic, custom standard data structures, or helper functions, standard generics are highly recommended. It completely eliminates single-quote syntax noise, making code cleaner and matching standard developer intuition.'T(Morphic Generics, with single quote): This form is only needed in generic container or low-level infrastructure scenes (such asVec,HashMap,Slab, or concurrentChannel). Because generic containers must be fully compatible with both soul elements (plain values/structures) and pointer elements (such as unique^or shared~pointers) to avoid "soul collapse" and perform zero-cost layouts, Morphic generics adapt dynamically to different memory morphologies.
[!NOTE] For the vast majority of daily programming tasks, you only need to use
Tjust like in other traditional programming languages, which greatly simplifies readability.
Type Inference
Toka can infer generic types in most cases, so you rarely need to specify them explicitly:
fn pair<'A, 'B>(a: 'A, b: 'B) -> Pair<'A, 'B> {
return Pair::new(a, b)
}
auto p = pair(1, "world") // Pair<i32, str>
Generic Requirements
Use trait bounds to specify what operations a generic type must support:
| Trait | Description |
|---|---|
@PartialEq | Equality comparison (==, !=) |
@PartialOrd | Ordering comparison (<, >, <=, >=) |
@Hash | Hash computation |
@encap | Encapsulation / interior mutability |
Error Handling
Toka provides type-safe error handling through the Result and Option types. This model, rooted in Algebraic Data Types (ADTs), traces its origins back to the ML language family (such as Standard ML and OCaml) and Haskell. Rust later popularized this approach within modern systems programming. Toka adopts this model, combining it with language-level syntax—such as the ! operator for error propagation—to deliver a concise developer experience.
The Option Type
Use Option for values that may or may not exist:
import core/option::{Option}
import std/io::println
fn find_user(id: i32) -> Option<string> {
if id == 1 {
return Option<string>::Some(string::from("Alice"))
}
return Option<string>::None
}
fn main() -> i32 {
auto result = find_user(1)
match result {
auto Option<string>::Some(&name) => println("Found: {}", name)
auto Option<string>::None => println("User not found")
}
return 0
}
The Result Type
Use Result for operations that can succeed or fail:
import core/result::{Result}
fn divide(a: f64, b: f64) -> Result<f64, string> {
if b == 0.0 {
return Result<f64, string>::Err(string::from("Division by zero"))
}
return Result<f64, string>::Ok(a / b)
}
The Result type is generic over two parameters:
T— The successful value typeE— The error type
Pattern Matching on Results
import core/result::{Result}
import std/io::println
fn divide(a: f64, b: f64) -> Result<f64, string> {
if b == 0.0 {
return Result<f64, string>::Err(string::from("Division by zero"))
}
return Result<f64, string>::Ok(a / b)
}
fn main() -> i32 {
match divide(10.0, 2.0) {
auto Result<f64, string>::Ok(value) => println("Result: {}", value)
auto Result<f64, string>::Err(&msg) => println("Error: {}", msg)
}
return 0
}
Short-Circuiting with !
Use the ! operator to propagate errors automatically:
import core/result::{Result}
fn read_file(path: string) -> Result<string, string> {
return Result<string, string>::Ok(path)
}
fn parse(content: string) -> Result<string, string> {
return Result<string, string>::Ok(content)
}
fn format_result(parsed: string) -> string {
return parsed
}
fn process_file(path: string) -> Result<string, string> {
auto content = read_file(path)! // Returns early on error
auto parsed = parse(content)! // Same here
return Result<string, string>::Ok(format_result(parsed))
}
This is much cleaner than nested match statements.
The Default Case with or
Provide a default value in case of error:
auto value = parse_int("42").unwrap_or(0) // Uses 0 if parsing fails
Custom Error Types
You can define structured errors:
import core/result::{Result}
pub shape Config()
pub shape ParseError(
line: i32,
col: i32,
message: string
)
fn parse_config(text: string) -> Result<Config, ParseError> {
if text.len() == 0 {
return Result<Config, ParseError>::Err(ParseError(line = 0, col = 0, message = string::from("Empty input")))
}
return Result<Config, ParseError>::Ok(Config())
}
Pattern Matching
Pattern matching is one of Toka's most expressive features, allowing you to destructure and match values concisely.
Basic Match
fn describe(n: i32) -> str {
return match n {
0 => pass "zero"
1 => pass "one"
_ => pass "many"
}
}
The _ wildcard matches any value — it's the default case.
Range Patterns
Match against ranges:
auto score = 85
auto grade = match score {
auto s if s >= 0 && s < 60 => { pass "F" }
auto s if s >= 60 && s < 70 => { pass "D" }
auto s if s >= 70 && s < 80 => { pass "C" }
auto s if s >= 80 && s < 90 => { pass "B" }
auto s if s >= 90 && s <= 100 => { pass "A" }
_ => { pass "invalid score" }
}
Note that Toka does not support native range patterns (like 0..10). Instead, use variables with if guard expressions to perform range checking.
Matching on Options
import std/io::println
import core/option::Option
fn main() -> i32 {
auto id = 1
auto opt: Option<str> = Option<str>::None
match opt {
auto Option<str>::Some(&name) => { println("Found: {}", name) }
auto Option<str>::None => { println("User not found") }
}
return 0
}
Matching on Results
import std/io::println
import core/result::Result
fn main() -> i32 {
auto res: Result<f32, str> = Result<f32, str>::Ok(5.0)
match res {
auto Result<f32, str>::Ok(value) => { println("Result: {}", value) }
auto Result<f32, str>::Err(&msg) => { println("Error: {}", msg) }
}
return 0
}
Destructuring Shapes
Pattern match on custom shapes:
import std/io::println
pub shape Point(x: i32, y: i32)
fn origin(p: Point) {
match p {
auto pt if pt.x == 0 && pt.y == 0 => { println("At origin") }
auto pt if pt.y == 0 => { println("On X-axis") }
auto pt if pt.x == 0 => { println("On Y-axis") }
_ => { println("Somewhere else") }
}
}
Match as Expression
Match returns a value, so you can use it in assignments:
auto score = 85
auto grade = match score {
auto s if s >= 90 && s <= 100 => pass "A"
auto s if s >= 80 && s < 90 => pass "B"
auto s if s >= 70 && s < 80 => pass "C"
auto s if s >= 60 && s < 70 => pass "D"
_ => pass "F"
}
No Type-Based Matching
Toka does not support matching directly on types (such as writing i32 => ... or str => ... inside a match expression). Any type name used in a branch without standard syntax will be treated as a new variable binding pattern (Variable Pattern), which matches any value and shadows other cases, leading to compilation errors or unreachable branches.
Concurrency
Toka provides lightweight concurrency through tasks and message passing, enabling efficient parallel execution.
Tasks
Spawn concurrent tasks with the task module:
import std/io::println
import std/thread::thread_spawn
fn worker(id: i32) -> i32 {
println("Worker {} started", id)
// Do work...
println("Worker {} done", id)
return 0
}
fn main() -> i32 {
auto t1# = thread_spawn<i32>({ => return cede worker(1) })
auto t2# = thread_spawn<i32>({ => return cede worker(2) })
t1#.join()
t2#.join()
return 0
}
MPSC Channels
Communicate between tasks using MPSC (Multi-Producer, Single-Consumer) channels:
import std/channel::channel
import std/thread::thread_spawn
import std/io::println
fn main() -> i32 {
auto pair# = channel<i32>()
auto tx# = cede pair.tx
auto rx# = cede pair.rx
thread_spawn<i32>({ [cede tx] =>
tx#.send(cede 42)
return cede 0
})
thread_spawn<i32>({ [cede rx] =>
auto res_opt = rx#.recv()
println("Got a message!")
return cede 0
})
return 0
}
Atomic Operations
Use atomic types for lock-free concurrent access:
import std/atomic::*
fn main() -> i32 {
auto counter = AtomicI32::new(0)
counter.fetch_add(1, Ordering::SeqCst)
return 0
}
Mutex / Sync
For shared mutable state:
import std/sync::Mutex
fn main() -> i32 {
auto lock# = Mutex<i32>::new(0:i32)
auto g = lock#.lock().unwrap()
// Access shared data safely
// g released automatically when it goes out of scope
return 0
}
Safety Guarantees
Toka's concurrency model prevents:
- Data races: Two tasks cannot simultaneously write to the same memory
- Deadlocks: The compiler analyzes lock ordering
- Send/Sync violations: Types that are not thread-safe cannot be shared across tasks
Standard Library Overview
Toka's standard library provides a comprehensive set of modules for everyday programming tasks.
Module Structure
The standard library is organized into four tiers:
lib/core/ — Core Language Primitives
These modules provide the fundamental types and traits that the language itself depends on:
| Module | Description |
|---|---|
prelude | Auto-imported basics for every Toka program |
types | Type definitions and limits |
traits | Core traits (@Hash, @PartialEq, @PartialOrd) |
char | Unicode character handling and classification |
str | String slice operations |
string | UTF-8 dynamic string type and views |
bytes | Read-only binary byte-stream view |
memory | Memory management primitives |
option | Option<T> type for nullable values |
result | Result<T, E> type for error handling |
task | Fundamental coroutine execution runtime and task base |
lib/sys/ — Low-level System Primitives
These modules provide direct bindings and primitives interacting with the operating system and low-level runtime:
| Module | Description |
|---|---|
libc | Platform-dependent C standard library API declarations and bindings |
termios | Low-level Tty terminal mode and control attributes configuration |
thread | Lower-level OS native thread interfaces |
sync | OS-level synchronization primitives and lock implementations |
linux / macos / windows | Platform-specific system calls and bindings |
lib/std/ — Standard Library
Ready-to-use standard modules for application development:
| Module | Description |
|---|---|
vec | Dynamically growing sequential array container (Vec) |
deque | Double-ended queue based on ring buffers (VecDeque) |
slab | Zero-copy generational slab allocator (Slab / SlabID) |
hashmap | High-performance hash table based map (HashMap) |
hashset | Hash table based unique element collection (HashSet) |
btreemap | B-Tree based ordered key-value map (BTreeMap) |
btreeset | B-Tree based ordered unique set (BTreeSet) |
heap | Priority queue implemented with max/min binary heap (BinaryHeap) |
arena | Region-based arena allocator for high performance allocation (Arena) |
ring | High throughput lock-free ring buffers (RingBuffer) |
io | Buffered standard console I/O (stdin / stdout / println) |
fs | File system read/write and directory metadata operations |
path | Cross-platform file path parsing and manipulation (Path / PathBuf) |
env | Environment variables, process arguments, and system context interaction |
process | Subprocess spawning, state control, and pipe communication |
thread | OS native thread management and lifecycle control (Thread) |
sync | Thread synchronization primitives (Mutex / Condvar / WaitGroup) |
atomic | Hardware-level atomic operations and lock-free sync primitives |
channel | Thread message passing communication channel (channel / sync_channel) |
task | Coroutine task scheduling, context management, and async runtime |
net | TCP & UDP socket network communication interfaces |
math | Common mathematical functions and limit constants |
time | System time and monotonic durations (Duration / Instant) |
fmt | Text formatting and debug printing utilities (Format) |
error | Common error traits (Error) and failure base definitions |
panic | Runtime fatal error triggering, intercepting, and stack hooks |
lib/stdx/ — Experimental / Extended (stdx)
New modules that are still evolving or optional extensions:
| Module | Description |
|---|---|
log | Basic logging utilities and severity filtering |
net/http | HTTP client and server |
net/url | URL parsing |
net/websocket | WebSocket client and server |
serde/json | JSON serialization |
crypto/md5 | MD5 hashing |
crypto/sha1 | SHA-1 hashing |
crypto/sha256 | SHA-256 hashing |
encoding/base64 | Base64 encoding and decoding |
encoding/hex | Hexadecimal encoding and decoding |
io/bufio | Buffered advanced I/O streams (BufReader / BufWriter) |
rand/rand | Pseudo-random number generator and random utilities (Random) |
trace/span | Method execution timing and trace spans |
cli/flag | CLI flag parsing |
Design Philosophy
Toka's standard library follows these principles:
- Zero dependencies — Everything is built with pure Toka and the LLVM backend
- Consistent API — Similar patterns across different modules
- Error-aware — Every fallible operation returns
ResultorOption - Performant — Compiled to native code with no hidden overhead
IO & File System
Toka's IO module provides file operations, console output, and stream handling — all with a clean, consistent API.
Console Output
import std/io::{println, print}
println("Hello with newline") // Prints with trailing newline
print("No newline here") // Prints without trailing newline
Reading Files
import std/fs
import core/result::Result
import std/io::IoStringResult
fn read_config(path: string) -> IoStringResult {
return fs::read_to_string(path)
}
Writing Files
import std/fs
import core/result::Result
fn save_data(path: string, data: string) -> Result<bool, string> {
return fs::write_string(path, data.as_str())
}
File System Operations
The fs module provides standard file operations:
import std/fs
import std/io::println
import core/option::Option
fn manage_files() {
auto path = string::from("output")
if fs::exists(path.clone()) {
fs::remove_dir(path)
}
fs::create_dir(string::from("output"))
auto dir# = fs::read_dir(string::from("."))
loop {
auto entry = dir#.next()
if entry.is_none() { break }
println("{}", entry.unwrap())
}
}
Working with Paths
import std/io::println
import std/path
fn example() {
auto full = path::join(string::from("dir"), string::from("file.txt"))
println("{}", full.c_str())
auto ext = path::extension(string::from("data.json"))
println("{}", ext.c_str())
auto stem = path::file_stem(string::from("data.json"))
println("{}", stem.c_str())
}
Environment Variables
import std/io::println
import std/env
import core/option::Option
fn example() {
auto home_opt = env::var(string::from("HOME"))
auto home = home_opt.unwrap_or(string::from("/tmp"))
println("Home directory: {}", home)
env::set_var(string::from("MY_APP_DEBUG"), string::from("true"))
}
Collections
Toka provides a rich set of data structures for storing and organizing data efficiently.
Vec (Dynamic Array)
import std/io::println
import std/vec::Vec
fn example() {
auto numbers# = Vec<i32>::new()
numbers#.push(1)
numbers#.push(2)
numbers#.push(3)
println("{}", numbers.at(0)) // 1
auto i# = 0:usize
loop i < numbers.len() {
println("{}", numbers.at(i))
i = i + 1:usize
}
}
Map (Hash Map)
import std/io::println
import std/hashmap::HashMap
import core/option::Option
import core/primitives
fn example() {
auto scores# = HashMap<i32, i32>::new()
scores#.insert(1, 95)
scores#.insert(2, 87)
match scores.get(1) {
auto Option<i32>::Some(score) => { println("ID 1: {}", score) }
auto Option<i32>::None => { println("Not found") }
}
}
BTreeMap (Ordered Map)
import std/io::println
import std/btreemap::BTreeMap
import core/primitives
fn example() {
auto items# = BTreeMap<str, i32>::new()
items#.insert("apple", 5)
items#.insert("banana", 3)
items#.insert("cherry", 8)
println("Iterated")
}
BTreeSet (Ordered Set)
import std/io::println
import std/btreeset::BTreeSet
import core/primitives
fn example() {
auto unique# = BTreeSet<i32>::new()
unique#.insert(3)
unique#.insert(1)
unique#.insert(2)
unique#.insert(1) // Duplicate, ignored
println("{}", unique.len())
}
Deque (Double-Ended Queue)
import std/io::println
import std/deque::VecDeque
fn example() {
auto queue# = VecDeque<str>::new()
queue#.push_back("first")
queue#.push_back("second")
queue#.push_front("priority")
queue#.pop_front()
println("{}", queue.len())
}
Slab (Generational Slab Allocator)
Toka completely abolishes linked lists to embrace the contiguous physical memory topology of modern CPUs. Instead, the standard library introduces the generational slab allocator std/slab::Slab. It manages objects as slots inside a contiguous memory buffer (Vec), supporting rapid $O(1)$ insertion, removal, and retrieval. It employs a SlabID generational check technique to fully eliminate the risk of ABA problems or stale index lookups.
The Slab's lookup methods (get and get_mut) implement advanced lifetime dependency declarations (<- self), returning zero-copy Option<&'T> and Option<&#'T> borrows.
import std/slab::{Slab, SlabID}
import std/io::{println}
import core/option::{Option}
shape Entity (
name: string,
val: i32
)
fn main() -> i32 {
auto slab# = Slab<Entity>::new()
// 1. Insert elements, returning a SlabID containing both index and generation
auto id1 = slab#.insert(Entity(name = string::from("alpha"), val = 10))
auto id2 = slab#.insert(Entity(name = string::from("beta"), val = 20))
// 2. Retrieve zero-copy borrowed views safely
auto e1_opt = slab.get(id1.clone())
if e1_opt.is_some() {
auto &e1 = e1_opt.unwrap()
println("ID1 val: {}", e1.val as i64) // 10
}
// 3. Remove an element and reclaim its slot
auto removed = slab#.remove(id1.clone())
// 4. Insert another element: Slab automatically reuses Slot 0!
// However, the generation counter automatically increments to 2!
auto id3 = slab#.insert(Entity(name = string::from("gamma"), val = 30))
// 5. Querying with the stale id1 now safely returns None, preventing ABA reads!
auto old_opt = slab.get(id1)
assert(old_opt.is_none(), "generational ID should prevent ABA stale read")
return 0
}
Heap (Priority Queue)
import std/io::println
import std/heap::BinaryHeap
import core/primitives
fn example() {
auto max_cmp: fn(i32, i32) -> bool = { a, b => a > b }
auto heap# = BinaryHeap<i32>::new(max_cmp)
heap#.push(5)
heap#.push(10)
heap#.push(3)
heap#.pop()
println("10")
}
Set
import std/io::println
import std/hashset::HashSet
import core/primitives
fn example() {
auto tags# = HashSet<i32>::new()
tags#.insert(1)
tags#.insert(2)
tags#.insert(3)
if tags.contains(2) {
println("Found 2!")
}
}
Networking
Toka's networking module provides TCP, UDP, and HTTP capabilities for building networked applications.
HTTP Client
Make HTTP requests with the stdx/net/http module:
import core/result::Result
// Note: HTTP client is under development
fn fetch(url: string) -> Result<string, string> {
// auto response = http::get(url)!
// return Ok(response.body)
return Result<string, string>::Ok(string::from("Response"))
}
HTTP Server
Build a simple HTTP server using the stdx/net/http module:
import stdx/net/http::{HttpResponse, HttpRequest}
// Note: HTTP Server is built by integrating std/net with stdx/net/http
fn main() -> i32 {
// auto listener# = TcpListener::bind(addr)
// auto stream# = listener#.accept()
// auto req# = parse_http_request(req_str)
return 0
}
TCP Connections
Use the net module for raw TCP:
import std/net::TcpStream
import std/io::println
import core/result::Result
fn handle_client(stream#: TcpStream) {
auto buf: [u8; 1024] = [0:u8; 1024]
auto *buf_ptr# = &buf[0] as *u8
auto res = stream#.read(*buf_ptr, 1024:usize)
match res {
auto Result<usize, string>::Ok(n) => {
println("Received {} bytes", n)
stream#.write_string(string::from("ACK"))
}
auto Result<usize, string>::Err(&e) => {
println("Error")
}
}
}
WebSocket
For real-time bidirectional communication, use the stdx/websocket module:
import std/io::println
// Note: WebSocket is under development
fn echo_server() {
// auto ws = websocket::Server::new()
// ws.on_message(|msg| => println("Got: {}", msg))
// ws.listen(9000)
}
URL Parsing
import std/io::println
import core/result::Result
import stdx/net/url::{Url, parse_url}
fn example() {
auto url_res = parse_url(string::from("https://api.example.com:8080/data?id=1#top"))
match url_res {
auto Result<Url, string>::Ok(&url) => {
println("Host: {}", url.host)
println("Port: {}", string::from_int(url.port as i32))
}
auto Result<Url, string>::Err(&err) => {
println("Error: {}", err)
}
}
}
Time & Math
Toka provides comprehensive time handling and mathematical functions for scientific and systems programming.
Getting Current Time
import std/time::{SystemTime}
import std/io::println
fn example() {
auto now = SystemTime::now()
// println("Current timestamp: {}", now.unix())
}
Time Formatting
import std/time::{SystemTime}
import std/io::println
fn format_date() {
auto now = SystemTime::now()
// auto formatted = now.format("%Y-%m-%d %H:%M:%S")
// println("Date: {}", formatted)
}
Durations and Timers
import std/time::{Instant}
import std/io::println
fn measure() {
auto start = Instant::now()
// Do some work...
auto elapsed = start.elapsed()
println("Elapsed: {} ms", elapsed.as_millis())
}
Math Functions
import std/math
import std/io::println
fn calculations() {
auto x = 3.14159
println("{}", math::sin(x)) // Sine
println("{}", math::cos(x)) // Cosine
println("{}", math::sqrt(16.0)) // Square root → 4.0
// println(str(math::abs(-5))) // Absolute value → 5
// println(str(math::pow(2.0, 8))) // Power → 256.0
}
Random Numbers
import stdx/rand/rand::{Random}
import std/io::println
fn gen_random() {
auto rng# = Random::new(123:u64, 1:u64)
auto dice = rng#.next_u32() % 6 + 1
println("You rolled: {}", dice)
// Note: secure_bytes is under development
// auto secure = rand::secure_bytes(32)
}
Math Constants
// Mathematical limits (from lib/core/types.tk)
pub const MY_LIMITS = (
u8 = (min = 0, max = 255, bits = 8),
i32 = (min = -2147483648, max = 2147483647, bits = 32),
u64 = (min = 0, max = 18446744073709551615, bits = 64),
f32 = (
min_positive = 1.17549435e-38,
epsilon = 1.19209290e-07,
bits = (nan = 0x7fc00000, infinity = 0x7f800000, neg_zero = 0x80000000)
)
)
fn dummy_limits() {}
Serialization
Toka provides built-in support for serializing and deserializing data, with JSON support out of the box.
JSON Encoding
Serialize data to JSON:
import stdx/serde/json
import std/io::println
pub shape Person(
name: string,
age: i32,
active: bool
)
fn encode() {
auto person = Person(name = string::from("Alice"), age = 30, active = true)
// auto json_str = json::to_json(person)
// println("{}", json_str) // {"name":"Alice","age":30,"active":true}
}
JSON Decoding
Parse JSON back into Toka types:
import stdx/serde/json
import std/io::println
import core/result::Result
pub shape Person(
name: string,
age: i32,
active: bool
)
fn decode() {
auto data = "{\"name\":\"Bob\",\"age\":25,\"active\":false}"
// auto person_res = json::deserialize_shape<Person>(data)
}
Base64 Encoding
import stdx/encoding/base64
import std/io::println
fn example() {
auto original = string::from("Hello, Toka!")
auto encoded = base64::encode_str(original.as_str())
println("{}", encoded) // SGVsbG8sIFRva2Eh
// Note: base64::decode is under development
// auto decoded = base64::decode(encoded)!
}
Hex Encoding
import stdx/encoding/hex
import std/io::println
fn example() {
auto bytes: [u8; 5] = [0x48, 0x65, 0x6C, 0x6C, 0x6F]
auto hex_str = hex::encode_hex(*bytes as *[u8], 5:usize)
println("{}", hex_str) // 48656C6C6F
// Note: hex::decode is under development
// auto decoded = hex::decode(hex_str)!
}
Custom Serialization
Implement the @Serialize trait for custom types:
import stdx/serde/json::{@ToJson}
pub shape Person(name: string, age: i32, active: bool)
impl Person@ToJson {
pub fn write_json(self, buf#: string) -> string {
buf#.push_str("{\"name\":\"")
buf#.push_str(self.name.as_str())
buf#.push_str("\",\"age\":")
buf#.push_str(string::from_int(self.age).as_str())
buf#.push_str("}")
return buf
}
}
Cryptography
Toka provides a standard cryptography module with support for common hash algorithms.
MD5 Hashing
import std/io::println
import stdx/crypto/md5::Md5
fn example() {
auto md5# = Md5::new()
md5#.update("Hello, Toka!")
auto hash = md5#.finalize_hex()
println("MD5: {}", hash)
}
SHA-1
import std/io::println
import stdx/crypto/sha1::Sha1
fn example() {
auto sha1# = Sha1::new()
sha1#.update("Hello, Toka!")
auto hash = sha1#.finalize_hex()
println("SHA1: {}", hash)
}
SHA-256
import std/io::println
import stdx/crypto/sha256::Sha256
fn example() {
auto sha256# = Sha256::new()
sha256#.update("Hello, Toka!")
auto hash = sha256#.finalize_hex()
println("SHA256: {}", hash)
}
File Hashing
Hash the contents of a file:
import std/fs
import stdx/crypto/sha256::Sha256
import core/result::Result
fn verify_file(path: string) -> Result<bool, string> {
auto content = fs::read_to_string(path).unwrap()
auto sha256# = Sha256::new()
sha256#.update(content.as_str())
auto computed = sha256#.finalize_hex()
return Result<bool, string>::Ok(computed.len() > 0:usize)
}
Secure Random
For cryptographic applications:
import stdx/rand/rand::Random
fn generate_key() -> u64 {
// Pseudorandom bytes (Not secure, for demonstration)
auto rng# = Random::new(12345:u64, 1:u64)
return rng#.next_u64()
}
The crypto module is useful for:
- Password hashing and verification
- File integrity checking
- Data signing and verification
- Secure random number generation
Projects
Putting theory into practice is the single best way to truly master a systems programming language. Having mastered Toka's language basics, the Hat Principle, and advanced abstractions, this chapter will guide you through three carefully designed practical projects to complete your transformation from a syntax learner to a systems developer.
These projects are not detached "toy code." Instead, they directly target the three pillars of low-level software engineering: systems tool development, high-performance concurrent networking, and language/text parsing.
Practical Roadmap
The three projects in this chapter progress in a step-by-step fashion to help you gradually build robust system-level development mental models:
flowchart TD
Step1("**Step 1: CLI Tool**<br>Familiarize with stdx/cli/flag standard APIs")
Step2("**Step 2: HTTP Server**<br>Master raw TCP & Hat concurrency safety")
Step3("**Step 3: Regex Engine**<br>Exercise Token stream parsing & pattern matching")
Champ("🏆 **Full-fledged Systems Developer**")
Step1 --> Step2 --> Step3 --> Champ
style Champ stroke:#a855f7,stroke-width:2px;
1. CLI Tool with stdx/cli/flag
- Engineering Value: This is your first practical project. You will move away from outdated multi-level chain calls and learn to leverage the official standard
stdx/cli/flagextension package. You will elegently parse flags, handle boolean options using the non-chained mutableself#API, and safely collect and filter positional arguments with pattern matching.
2. High-Performance HTTP Server
- Engineering Value: How do you build highly concurrent, low-level network services in Toka? In this project, you will start with low-level TCP listening (
TcpListener) and connection acceptance (accept()). Combined with multi-threaded concurrency, you will handcraft an RFC-compliant concurrent HTTP response server. This is a comprehensive test of your understanding of the Hat Principle and Toka's I/O modules.
3. Regex Engine
- Engineering Value: Text matching and compiler frontends are fascinating areas of systems programming. You will challenge yourself to implement a micro regular expression engine from scratch. In this project, you will deeply train your control over token streams, state machine transitions, deep tree structure representations, and learn to leverage Toka's elegant
matchpattern matching for complex lexical and syntactic branching.
[!IMPORTANT] Development & Debugging Preparation Before starting each project, make sure you have correctly configured your
tokacommand-line environment and that thetokaccompiler works properly. You can quickly run and debug your project using the following basic command:toka run src/main.tk -- -n TokaIn each project chapter, detailed unit tests and execution examples are provided. It is highly recommended that you type every line of code yourself and observe compiler error outputs. This will give you a deep, visceral intuition for Toka's "Handle" and "Soul" rebinding rules.
CLI Tool with stdx/cli/flag
In this project, we'll build a command-line tool using Toka and the standard stdx/cli/flag argument parser library.
Setting Up
Create a new project:
toka init my-cli
cd my-cli
Since stdx/cli/flag is a standard library extension in Toka, you do not need to add any external dependencies via the package manager. It is ready to use out-of-the-box!
Defining Arguments
import stdx/cli/flag::{Parser, ParsedArgs}
import std/env
import std/io
fn main() -> i32 {
// Parser::new takes the program name and a brief description
auto parser# = Parser::new(string::from("my-cli"), string::from("A sample CLI tool built with Toka"))
// Add options to the parser
parser#.add_string(string::from("name"), 110:char, string::from("World"), string::from("Your name"))
parser#.add_bool(string::from("verbose"), 118:char, false, string::from("Enable verbose output"))
auto args = env::args()
auto res = parser.parse(args)
match res {
auto Result<ParsedArgs, string>::Ok(parsed) => {
auto who = parsed.get_string(string::from("name"))
io::println("Hello, %s!", who.c_str())
if parsed.get_bool(string::from("verbose")) {
io::println("[Verbose] Running with debug output")
}
}
auto Result<ParsedArgs, string>::Err(&err) => {
if err == string::from("help requested") {
parser.print_help()
return 0
}
io::println("Error: %s", err.c_str())
return 1
}
}
return 0
}
Running
toka run src/main.tk -- -n Toka
# Output: Hello, Toka!
toka run src/main.tk -- -n Toka -v
# Output: Hello, Toka!
# [Verbose] Running with debug output
Advanced Argument Handling
Toka's current stdx/cli/flag library provides basic options and flags parsing with types like bool, i64, f64, and string.
Positional arguments are captured automatically and can be fetched from the parsed arguments using positionals():
auto pos_args = parsed.positionals()
// pos_args is a Vec<string> containing any arguments not matching flags or options
For complex command-line interfaces requiring nested subcommands (like git status or docker run), you can manually match on pos_args to delegate execution to different parser configurations.
HTTP Server
Build a fully-functional HTTP server using Toka's modern networking library.
Basic Server
Toka's HTTP engine is located in stdx/net/http. You can build a lightweight HTTP server by binding a TcpListener from std/net and processing incoming connections:
import std/io::{println}
import stdx/net/http::{HttpRequest, HttpResponse, parse_http_request}
import std/net::{TcpListener, TcpStream, Ipv4Addr, SocketAddrV4}
import core/result::{Result}
fn handle_client(stream#: TcpStream) {
auto buf: [char; 1024] = [0 as char; 1024]
auto *buf_ptr# = &buf[0] as *u8
auto read_res = stream#.read(*buf_ptr, 1024:usize)
match read_res {
auto Result::Ok(n) => {
if n > 0:usize {
auto req_str = string::from_with_len(&buf[0] as *char, n as i32)
auto req# = parse_http_request(req_str)
auto resp# = HttpResponse::not_found()
if req.path.eq(string::from("/")) {
resp = HttpResponse::html(string::from("<h1>Hello, Toka!</h1>"))
}
auto resp_str = resp#.to_string()
auto *ptr = resp_str.c_str() as *u8
stream#.write(*ptr, resp_str.len() as usize)
}
}
_ => {}
}
stream#.close()
}
fn main() -> i32 {
// Demonstration server setup
return 0
}
API with JSON Responses
You can use the @ToJson trait from stdx/serde/json to automatically serialize custom shape objects and return them as JSON responses:
import stdx/net/http::{HttpResponse}
import stdx/serde/json::{serialize_shape, to_json, @ToJson}
pub shape Message(text: string, status: string)
impl Message@ToJson {
pub fn write_json(self, buf#: string) -> string {
return serialize_shape(self, buf)
}
}
fn api_status() -> HttpResponse {
auto msg = Message(
text = string::from("Server is running"),
status = string::from("ok")
)
auto json_body = to_json(msg)
return HttpResponse(
status_code = 200,
content_type = string::from("application/json"),
body = json_body
)
}
fn main() -> i32 {
return 0
}
Route Parameters
You can easily match dynamic parts of a URL path to extract route parameters manually:
import stdx/net/http::{HttpRequest, HttpResponse}
fn handle_user_route(req: HttpRequest) -> HttpResponse {
// Match dynamic path "/api/users/<id>"
if req.path.as_str().starts_with("/api/users/") {
auto user_id = req.path.substr(11, req.path.len() - 11)
auto body# = string::from("User ID: ")
body#.push_view(user_id)
return HttpResponse(
status_code = 200,
content_type = string::from("text/plain"),
body = body
)
}
return HttpResponse::not_found()
}
fn main() -> i32 {
return 0
}
Middleware
Wrap your routing handlers to inject custom middleware functionality, such as request logging or execution timing:
import stdx/net/http::{HttpRequest, HttpResponse}
import std/io::{println}
fn logger_middleware(req: HttpRequest, next: fn(HttpRequest) -> HttpResponse) -> HttpResponse {
println("Request Path: {}", req.path)
auto res = next(req)
println("Response Status: {}", string::from_int(res.status_code))
return res
}
fn main() -> i32 {
return 0
}
Static Files
Serve local assets by reading files dynamically using the std/fs module:
import stdx/net/http::{HttpResponse}
import std/fs::{read_to_string}
import core/result::{Result}
fn serve_static_file(path: string) -> HttpResponse {
auto file_content = read_to_string(path)
match file_content {
auto Result::Ok(&content) => {
return HttpResponse(
status_code = 200,
content_type = string::from("text/html"),
body = content.clone()
)
}
_ => return HttpResponse::not_found()
}
}
fn main() -> i32 {
return 0
}
Full Example
A complete HTTP server featuring routing, HTML rendering, and custom API JSON responses:
import std/io::{println}
import stdx/net/http::{HttpRequest, HttpResponse, HttpMethod, parse_http_request}
import std/net::{TcpListener, TcpStream, Ipv4Addr, SocketAddrV4}
import core/result::{Result}
fn route_request(req: HttpRequest) -> HttpResponse {
auto is_get# = false
match req.method {
auto HttpMethod::GET => { is_get = true }
_ => {}
}
if is_get && req.path.eq(string::from("/status")) {
return HttpResponse(
status_code = 200,
content_type = string::from("application/json"),
body = string::from("{\"status\":\"ok\"}")
)
}
if is_get && req.path.eq(string::from("/")) {
return HttpResponse::html(string::from("<h1>Toka HTTP Server</h1>"))
}
return HttpResponse::not_found()
}
fn handle_connection(stream#: TcpStream) {
auto buf: [char; 1024] = [0 as char; 1024]
auto *buf_ptr# = &buf[0] as *u8
auto read_res = stream#.read(*buf_ptr, 1024:usize)
match read_res {
auto Result::Ok(n) => {
if n > 0:usize {
auto req_str = string::from_with_len(&buf[0] as *char, n as i32)
auto req# = parse_http_request(req_str)
auto resp# = route_request(req)
auto resp_str = resp#.to_string()
auto *ptr = resp_str.c_str() as *u8
stream#.write(*ptr, resp_str.len() as usize)
}
}
_ => {}
}
stream#.close()
}
fn main() -> i32 {
// Demonstration server setup
return 0
}
Regex Engine
Build a regular expression engine using the toka-regex library, which implements a Thompson NFA (Nondeterministic Finite Automaton).
What is Thompson NFA?
A Thompson NFA compiles a regex pattern into a state machine that can match strings efficiently. It works by:
- Compiling the pattern into a graph of states
- Simulating all possible paths simultaneously
- Accepting if any path reaches an accept state
Basic Usage
import regex::{Regex}
import std/io::println
fn example() {
auto re = Regex::new("hello|world")
if re.test(string::from("hello world")) {
println("Match found!")
}
}
Pattern Syntax
The engine supports standard regex patterns:
| Pattern | Matches |
|---|---|
abc | Literal string "abc" |
| `a | b` |
a* | Zero or more "a"s |
a+ | One or more "a"s |
a? | Optional "a" (zero or one) |
[abc] | Character class: a, b, or c |
[^abc] | Negated character class |
. | Any single character |
Matching
import std/io::println
import core/option::Option
fn example() {
auto re = Regex::new("\\d+") // One or more digits
match re.find(string::from("Order #42")) {
auto Option::Some(m) => println("Found: {}", m.text),
auto Option::None => println("No match")
}
}
Replacing
import std/io::println
fn example() {
auto re = Regex::new("\\s+")
auto result = re.replace(string::from("hello world"), string::from(" "))
println("{}", result) // "hello world"
}
The Engine Architecture
The regex engine is built in pure Toka:
- Pattern Compiler — Parses the regex string into NFA states
- State Simulator — Runs the NFA against input text
- Matcher — Handles capture groups and position tracking
This makes it a great example of how Toka can implement non-trivial algorithms with clean, safe code.
Appendix
To help you fully master the Toka language, this Appendix provides a wealth of reference materials and practical development tools.
For developers coming to Toka from other mainstream languages (such as C++, Rust, or Go), this appendix is designed to help you smoothly transition and migrate to Toka.
Appendix Reference Directory
This appendix consists of the following three core reference modules:
1. Toka vs. Other Languages
- Contents: While Toka draws inspiration from various modern languages, it introduces unique concepts (such as dereferencing "wearing/removing a hat," which is the exact opposite of C++). This chapter provides clear comparison tables and side-by-side code snippets comparing Toka with C/C++, Rust, and Go to help you quickly build an accurate comparative mental model.
2. Migration Guide
- Contents: How do you smoothly migrate existing code logic to Toka? Written for experienced engineers, this chapter focuses on translating traditional "object-oriented" or "imperative pointer-passing" logic into highly efficient systems-level code that conforms to Toka's Shape deconstruction, Hat rebinding, and zero-copy implicit reference capture paradigms.
3. FAQ
- Contents: A compilation of the most common pitfalls and questions from the developer community. From beginner confusion like "why can't I use curly braces to define Shapes" to deeper questions like "how does pointer rebinding work inside method calls," this chapter offers direct answers and diagnostic compiler error explanations to help resolve build-time issues.
[!NOTE] Continuous Learning & Community Support Programming is an art of practice, and Toka is rapidly evolving. If you encounter any issues or discover bugs while coding or reading this manual, you are highly encouraged to submit an Issue or Pull Request to the official repository. We hope this manual becomes a helpful companion in exploring the low-level systems world!
Toka vs Other Languages
How does Toka compare to other languages in the systems programming space?
Toka vs Rust
| Aspect | Rust | Toka |
|---|---|---|
| Memory safety | Borrow checker with lifetimes | Hat Principle + PAL Checker |
| Learning curve | Steep (lifetimes, ownership) | Gentle (hat tokens are intuitive) |
| Compile speed | Slow (monomorphization) | Fast (LLVM backend, ~80k loc/s) |
| Syntax | Complex (macros, attributes) | Clean, Go-like readability |
| GC | No | No (compile-time managed) |
| C interop | Via extern "C" | Native C interop without FFI overhead |
Toka vs Go
| Aspect | Go | Toka |
|---|---|---|
| Memory model | GC with runtime | Compile-time ownership |
| Performance | Good (but GC pauses) | Native speed, zero overhead |
| Safety | GC handles memory | PAL Checker at compile time |
| Concurrency | Goroutines + channels | Tasks + MPSC channels |
| Binary size | ~5-20 MB (includes runtime) | ~250 KB (no runtime) |
Toka vs C
| Aspect | C | Toka |
|---|---|---|
| Safety | Manual memory, buffer overflows | Hat Principle prevents UB |
| Modern features | None (pre-1989 design) | Generics, pattern matching, traits |
| Compile speed | Fast | Fast (compiles itself in <1s) |
| Portability | Universal | LLVM backend, portable |
Toka vs Zig
| Aspect | Zig | Toka |
|---|---|---|
| Philosophy | Better C | Memory safety without annotations |
| Memory | Manual (allocator passed) | Automatic (compile-time tracking) |
| Metaprogramming | comptime | Compile-time features |
| Error handling | Error union types | Result/Option with ! operator |
| Build system | Built-in | package.tk (declarative) |
Migration Guide
Migrating from other languages to Toka? Here's what you need to know.
From Rust
Ownership model:
- Rust: Borrow checker with
'alifetimes - Toka: Hat Principle with
^syntax — no lifetime annotations needed
Error handling:
- Rust:
Result<T, E>with?operator - Toka: Same
Result<T, E>with!operator
Pattern matching:
- Rust:
matchwith exhaustive checking - Toka: Same
matchwith wildcard_
Traits:
- Rust:
trait Foo { fn bar(&self); } - Toka:
pub trait Foo { fn bar(self); }with@syntax
From Go
Error handling:
- Go:
if err != nileverywhere - Toka:
Result/Optionwith!operator — cleaner and safer
Concurrency:
- Go: Goroutines + channels
- Toka: Tasks + MPSC channels
Interfaces:
- Go: Implicit interface satisfaction
- Toka: Explicit trait implementation with
@syntax
From C
Pointers:
- C: Raw pointers
*with no safety - Toka: Tracked hats
^with PAL Checker
Memory:
- C: Manual
malloc/free - Toka: Automatic compile-time memory management
Build:
- C: Makefile / CMakeLists.txt
- Toka: package.tk (declarative)
From Python
Dynamic vs Static:
- Python: Dynamic typing, runtime errors
- Toka: Static typing, compile-time safety
Performance:
- Python: Interpreted, slow
- Toka: Compiled, native speed (via LLVM)
Quick Syntax Reference
| Concept | Toka |
|---|---|
| Variable | auto x = 10 |
| Mutable | auto x# = 10 |
| Function | fn add(a: i32, b: i32) -> i32 |
| If/else | if cond { } else { } |
| Loop | for i in 0..10 { } |
| Match | match val { 1 => "one", _ => "other" } |
| Comment | // Single line |
| Import | import std/io::println |
| Return | return value |
Frequently Asked Questions
General
Q: Is Toka production-ready?
Toka is currently in beta (v0.9.8-03). It's suitable for personal projects, prototyping, and experimentation. The 1.0 release is planned with a focus on production stability.
Q: What platforms does Toka support?
Linux, macOS, and Windows are supported. The LLVM backend provides cross-compilation capabilities.
Q: Is there a package manager?
Toka has a built-in package manager via toka add. Official library registry at pkg.tokalang.dev.
Language Design
Q: How is Toka different from Rust?
Toka achieves memory safety through the Hat Principle instead of a borrow checker. This means no lifetime annotations — just simple hat (^) tokens that make ownership visible in the syntax.
Q: Does Toka have a garbage collector?
No. Memory is managed at compile time through the PAL Checker. There is no runtime GC overhead.
Q: Is Toka compatible with C libraries?
Yes. Toka has native C interop without FFI overhead. You can call C functions directly.
Learning
Q: Do I need to know C or Rust to learn Toka?
Not at all. Toka's syntax is clean and Go-like. If you know any programming language, you can learn Toka in a weekend.
Q: Where can I find more examples?
Check out the examples/ directory in the Toka repository and the tokalibs collection of community libraries.
Q: Is there a community or forum?
Join the Toka community on GitHub Discussions at github.com/tokalang/toka.
Technical
Q: How fast is Toka?
Toka compiles at approximately 80,000 lines of code per second (Clang backend) and produces optimized native binaries via LLVM 20. Performance is comparable to C for equivalent code.
Q: How large are Toka binaries?
A minimal HTTP server binary is approximately 250 KB — no runtime, no VM, just native code.
Q: Does Toka support multithreading?
Yes. Toka provides tasks, MPSC channels, atomic operations, and mutexes for concurrent programming.