Zamak is a statically typed language that compiles to native executables. It's built for a fast loop of editing, checking, and building code, for both people and AI coding agents.
개요
Zamak is a statically typed language that compiles to native executables. It's built for a fast loop of editing, checking, and building code, for both people and AI coding agents. This program counts how often each word appears in its input: Zamak is a prototype (version 0.1.0). Use it to learn, experiment, and write small command-line tools. Syntax and cache formats might change, so don't use it in production yet. - zam check checks types and ownership without calling a C compiler. zam build caches each module by content, so a one-line edit rebuilds only the module you changed. On the project's 10-module, 5,250-line benchmark (Windows, MSVC, one machine), a one-line edit rebuilds in about 0.75 seconds, and zam check finishes in less than 0.1 seconds. For the method and raw numbers, see docs/ENGINEERING.md and docs/PERFORMANCE.md (in Chinese). - Strings, structs, lists, and maps move when you assign them.
README
Zamak
English | 中文
Zamak is a statically typed language that compiles to native executables. It’s built for a fast loop of editing, checking, and building code, for both people and AI coding agents.
This program counts how often each word appears in its input:
use std/io
use std/string
fn main() -> !string:
let text = io.read_all()?
let words = string.words(&text)
let mut counts: {string: i64} = {}
for word in &words:
counts[word] = counts.get(word, 0) + 1
for word in &counts:
let n = counts[word]
println("{word}: {n}")
Run it with some input:
$ echo "to be or not to be" | zam run count.zm
to: 2
be: 2
or: 1
not: 1
[!NOTE] Zamak is a prototype (version 0.1.0). Use it to learn, experiment, and write small command-line tools. Syntax and cache formats might change, so don’t use it in production yet.
Why Zamak
- Fast feedback.
zam checkchecks types and ownership without calling a C compiler.zam buildcaches each module by content, so a one-line edit rebuilds only the module you changed. On the project’s 10-module, 5,250-line benchmark (Windows, MSVC, one machine), a one-line edit rebuilds in about 0.75 seconds, andzam checkfinishes in less than 0.1 seconds. For the method and raw numbers, see docs/ENGINEERING.md and docs/PERFORMANCE.md (in Chinese). - Ownership without a garbage collector. Strings, structs, lists, and maps move when you assign them. A borrow (
&xor&mut x) lasts only for one call, so you never write lifetime annotations. Zamak checks integer overflow, division by zero, and out-of-bounds access at run time, and it removes the checks that a conservative range analysis proves redundant. - Diagnostics built for tools. Every error has a stable code such as
ZM301, a category, a position, and usually a suggested fix.zam check --jsonwrites them as one line of JSON. Zamak reports type errors per function, so an agent can fix several of them in one pass. - Learnable from one page. In a benchmark with three models and eight small command-line tasks, each model read only a one-page language reference. All 24 Zamak programs, written in the current indentation-based syntax, passed every test on the first try. The Rust and Go versions also passed all 24 tasks on the first try, so these tasks are too easy to rank the languages. For the method, tasks, and data, see bench/ai/live.
- Native, standalone output. Zamak generates C and compiles it with Microsoft C++ (MSVC), GCC, or Clang. The executable runs without Zamak or Rust installed. On the integer benchmarks in docs/PERFORMANCE.md, its runtime performance is in the same range as Rust and Go.
Install Zamak
To build Zamak from source, you need Rust stable and a C compiler. In the repository root, run:
cargo install --path zam --locked
Zamak finds a C compiler on each platform as follows:
| Platform | C compiler |
|---|---|
| Windows x64 | Visual Studio Build Tools with the Desktop development with C++ workload, located through vswhere. If it isn’t installed, Zamak uses gcc (for example, MinGW-w64) or clang from PATH. |
| Linux | cc, clang, or gcc from PATH. On Debian and Ubuntu, install build-essential. |
| macOS | Xcode Command Line Tools. To install them, run xcode-select --install. |
To choose a GCC-style compiler yourself, set the CC environment variable. zam check doesn’t need a C compiler. Continuous integration (CI) runs the full test suite on all three platforms.
Create your first project
zam new hello # creates hello/zam.toml and hello/src/main.zm
zam run hello # prints: hello from zamak
zam check hello # type and borrow check only
zam fmt hello # normalizes indentation
zam test hello # runs every fn test_...() in the project
zam build hello # writes build/hello-(.exe)
zam run app.zm -- a b # pass arguments to the program
zam writes build progress to standard error, so standard output contains only your program’s output.
Language tour
This program uses structs, error values, lists, maps, and string interpolation:
use std/string
struct Person:
name: string
age: i64
// Errors are values: `T!string` returns either a T or an error message.
fn parse_age(text: &string) -> i64!string:
let age = string.parse_i64(text)?
if age < 0:
fail "age must not be negative"
age
fn main() -> !string:
let mut ages: {string: i64} = {}
let mut names: [string] = []
for i in 0..3:
names.push("user{i}")
for name in &names:
ages[name] = string.byte_len(name) * 10
let raw = "42"
let age = parse_age(&raw) catch e:
println("bad input: {e}")
return
let p = Person(name: "Ada", age: age)
println("{p.name} is {p.age}")
- Blocks. Blocks use indentation, like Python. A line that ends with
:opens a block, and the block’s lines are indented 4 spaces deeper. A block with one statement can stay on the same line, such asif n < 1: return n. Writepassfor an empty block. Braces only appear in maps and string interpolation. Indent with spaces;zam fmtreplaces tabs and normalizes indentation and blank lines. - Types. Zamak has
i64,f64,bool,string(UTF-8), named structs, fixed arrays[i64; N], lists[T], and maps{K: V}. List elements and map values can bei64,f64,bool,string, a struct, or another list or map, such as[[i64]]or{string: [string]}, and map keys can bei64orstring.grid[i][j]reads and writes an inner element. Lists havepush,pop,insert,remove,sort,sort_by,reverse,contains,index_of,slice, andextend; maps haveget,contains,remove,keys, andvalues. A character literal such as'#'is ani64code point. - Numbers.
f64follows IEEE 754, so dividing by zero givesinforNaN. Zamak never converts betweeni64andf64implicitly. Writef64(n)ori64(x), wherei64(x)truncates toward zero.printlnprints the shortest text that reads back as the samef64, such as0.1or2.0, andfmt.fixed(x, 2)rounds to two decimal places. - Structs. Declare
struct Point:with one field per indented line, and build a value withPoint(x: 1, y: 2), naming every field. Function names start with a lowercase letter, soPoint(...)always means a struct. - Methods. Write methods inside the struct or enum block with
&self,&mut self, orselfas the first parameter, and call them asp.norm(). A function withoutself, such asPoint.new(1, 2), is called on the type. - Struct borrows and struct lists. A parameter typed
&Pointborrows a struct for one call, and&mut Pointlets the function change it. In a list of structs, read or write a field withitems[i].qty, borrow an element with&items[i], and loop withfor item in &items. Structs move on assignment, so take an element out withitems.pop()or make a deep copy withcopy(&items[i]). Structs, enums, lists, and maps compare with==and print withprintln, as inPoint(x: 1, y: 2). - Enums and match. Declare
enum Shape:with one variant per indented line, such asCircle(r: f64)orEmpty, and build values withShape.Circle(1.0)orShape.Empty. Amatchlists one arm per line, such asCircle(r): 3.14 * r * r, or ends with_: .... Zamak reports an error when an arm is missing. Amatchborrows the value, so arms read the data without moving it. Amatchcan also branch on integers, strings, and booleans, as in1 | 2: "small". - Control flow. Use
if,elif,else,while,break, andcontinue. To choose a value, writelet v = if c: a elif d: b else: e. Combine conditions withand,or, andnot, which follow Python’s precedence and short-circuit. Loop over a range withfor i in 0..n, over a list withfor x in &listorfor i, x in &list, and over a map in insertion order withfor k in &maporfor k, v in &map. Write&mutto change elements in place, as infor x in &mut list: x += 1. - Errors. A function that can fail returns
T!Eor!E, whereEisstring, a struct, or an enum. Usefailto return an error,?to pass it to the caller, andcatch e:followed by an indented block to handle it in place, for example withmatch e. - Generic functions. Write
fn largest[T](items: &[T]) -> T. Zamak infersTat each call and compiles a separate version per type. - Generic types and Option. Declare
struct Stack[T]orenum Tree[T]and write the type asStack[string]. Methods inside a generic type can useT. The built-inOption[T]has the variantsOption.Some(value)andOption.None. - Function values. A function type is written
fn(i64) -> bool. Pass a named function by name, or write an anonymous fn such asfn(x): x * 2. An anonymous fn can read variables of the enclosing function; such a closure is passed directly as an argument and never outlives the call, so no garbage collector is needed. - Tests.
assert condorassert cond, "message"stops the program whencondis false and prints the condition, and fora == bboth values.zam testruns every function namedtest_...in its own process, so a crash fails only that test, and prints a summary. Pass a word to run only matching tests:zam test . parse. - Tuples. Group values as
(3, "three")with type(i64, string), read items witht.0, and return several values from a function:let (q, r) = divmod(17, 5). - Constants. Declare
const LIMIT = 100at the top level. Zamak computes the value at compile time from literals, earlier constants, and operators. - Modules. Each file is a module. Import one with
use name, and export items withpub fn,pub struct, andpub const. - Packages. Find libraries in the package index with
zam search json, and add one withzam add mathx, which writesmathx = "^1.4.0". Version requirements follow Cargo:^1.2,~1.2,=1.2.3, or>=1.0, <2.0, andzampicks the highest version every package accepts. Remove a dependency withzam remove mathx. You can also add a library straight from a Git repository withzam add mathx https://github.com/you/mathx v1.0, or from a local directory withzam add util ../util. To share your own library, givezam.tomlaversion, tag it, and runzam publish.zam.tomllists dependencies under[dependencies], andzam.lockpins each Git dependency to a commit so every build uses the same code, offline too. Import a package’s modules asuse mathxoruse mathx/geo. Runzam updateto move to the latest commit of eachrev. - C libraries. Declare a C function with
extern fn sqrt(x: f64) -> f64and call it like any other function. Parameters can bei64,f64,bool, or&string(passed as aconst char *that ends with\0), and results can be those types orstring. List C files, include directories, and libraries in a[native]section ofzam.toml, such assources = ["c/shim.c"]andlibraries = ["sqlite3"]. A package can carry its own[native]section, so a Zamak library can wrap a C library for other projects. Seeexamples/ffi. - Operators. Integers support the bitwise operators
&,|,^,~,>with Python’s precedence, and literals such as0xFF,0b1010, and0o17. - Strings. Join strings with
+, compare them with==or<, and update variables with+=,-=,*=,/=, and%=. Interpolation accepts expressions such as"{a + b}"and"{xs[0]}". - Standard library.
std/ioreads standard input and prints to standard output or standard error,std/fsreads and writes whole files, andstd/envreads arguments and environment variables and sets the exit status.abs,min, andmaxwork on bothi64andf64without an import.std/stringmeasures, compares, parses, trims, searches, replaces, converts case, slices, splits, joins, pads, and repeats strings, and converts them to and from characters, code points, and UTF-8 bytes.std/mathprovides square roots, powers, logarithms, trigonometry, rounding, and the constantsmath.pi,math.e, andmath.inf.
The tutorial (in Chinese) covers every feature with runnable examples. For a larger program, see examples/md2html, a Markdown-to-HTML converter with four modules and about 270 lines. It handles headings, lists, quotes, fenced code, inline formatting, links, and HTML escaping. To run it:
zam run examples/md2html -- examples/md2html/sample.md
Diagnostics
When a check fails, zam shows the error code, the location, the source line, and a suggested fix:
error[ZM301]: use after move: name
--> moved.zm:8:5
|
8 | println(name)
| ^^^^^^^^^^^^^
= help: borrow the value or move it only after the last use
zam check --json moved.zm reports the same error as one line of JSON:
{"command":"check","ok":false,"diagnostics":[{"file":"moved.zm","line":8,"column":5,"function":"main:main","code":"ZM301","category":"ownership","message":"use after move: name","suggestion":"borrow the value or move it only after the last use"}]}
For the list of error codes and the recommended loop for AI agents (check, make the smallest fix, format, check, build, and run), see docs/AI_WORKFLOW.md (in Chinese). To give a coding agent instructions for this loop, create your project with zam new --template ai, which adds an AI.md file.
Current limitations
Zamak doesn’t support threads, networking, or directory listing yet. C functions take and return only numbers, booleans, and strings; pass pointers and structs through a small C wrapper. There’s no language server or read-eval-print loop (REPL) yet. When you use an unsupported construct, zam check reports a diagnostic instead of compiling incorrect code.
Editor and agent support
- VS Code. The extension in editors/vscode adds syntax highlighting, bracket and quote matching, automatic indentation after a line that ends with
:, and folding by indentation. It’s declarative and has no runtime dependencies. - AI coding agents. .agents/skills/zamak/SKILL.md describes the commands, syntax, and common diagnostics for agents that read
.agents/skills.
Contribute
For the development guide, see CONTRIBUTING.md (in Chinese). Before you submit a change, make sure these commands pass in the zam folder:
cargo test --locked
cargo fmt --check
cargo clippy --all-targets --locked -- -D warnings
License
You can use Zamak under either the MIT license or the Apache License 2.0. Unless you state otherwise, you license your contributions under the same terms.
추천 도구
다른 키워드를 입력하거나 필터를 제거해 보세요.
설치
npx skillfish add rayl-k/zamak