Skip to content
← zdeceptron docs

Getting started

ZDeceptron 0.1.0 is released. One command puts the compiler on your PATH, and it needs nothing alongside it — no Node, no package manager, no bundler.

The major version is 0, which is a promise about change rather than about quality: a program is guaranteed to keep compiling across a patch release and is not guaranteed to across a minor one. Anything that breaks a program is written down in the changelog, with the repair.

Install

curl -fsSL https://raw.githubusercontent.com/DrDrewCain/zdeceptron/main/scripts/install.sh | sh

That fetches the binary for your platform from the GitHub release and checks it against the SHA-256 published beside it. Or, with a Rust toolchain:

cargo install zdc-cli

The crate is zdc-cli and the binary is zdc — twenty crates go up with each release, because reaching the binary from crates.io means everything it links is there too, and cargo fetches them for you.

Either way:

zdc --version
# zdc 0.1.0

Building from source still works and is the same compiler:

git clone https://github.com/DrDrewCain/zdeceptron
cd zdeceptron && cargo build --release

Your first program

Put this in hello.zd — and then type in the box, because this is that program, running:

hello.zdcompiled and running

// this one runs in your browser, so it needs JavaScript

Not a screenshot and not a transcription. zdc compiled examples/hello.zd during this site's build, and the page imported the client.js it emitted.

the file that was compiled
# hello.zd — the smallest ZDeceptron program.
#
# One piece of client state, one view. No build config, no bundler entry
# point, no framework import. The file is the program.

state name is client Text starting "world"

view
    Column
        Heading "Hello, ZDeceptron"
        Input name, hint is "your name"
        Text name

There is no string interpolation: a value reaches the page by being named, as Text name does. One phrasing per construct is a rule the language keeps deliberately.

Build it yourself:

zdc build hello.zd -o dist

You now have a dist/ containing exactly this:

boot.js  client.js  index.html  manifest.json  runtime/  styles.css

Open index.html. Type in the box; the text below it follows.

client.js is an ES module exporting main(container), and boot.js is the two lines that call it with #app. That is the whole of the bundle's contract with a page, and it is why the example above can share this one: every program mounts into a node you hand it rather than taking over a document.

There is no config file, no bundler entry point and no framework import. The file is the program.

While you work

zdc dev serves the file and rebuilds as you edit, with live reload and compile errors rendered on the page rather than hidden in a terminal:

zdc dev hello.zd

The whole command surface

CommandWhat it does
zdc checkResolve every name and typecheck. No output written.
zdc buildCompile into a runnable bundle.
zdc devServe, rebuilding and reloading as you edit.
zdc explainPrint the rule behind a diagnostic code.
zdc parsePrint the syntax tree.
zdc deployGenerate everything one platform needs — and report what it cannot do.
zdc lspLanguage server, over stdin and stdout.

When something is refused

Diagnostics carry a code, and the code is a question you can ask:

Error: Expected a placement after `is`, found `Whole`.
 1 │ state votes is Whole starting 0
   │                ──┬──
   │                  ╰──── `Whole` is the type, and a placement goes before it
   │ Help: run 'zdc explain E0101' for the rule
   │ Note: the line as it would be accepted: state votes is client Whole starting 0

The inline message stays deliberately short — reading a compiler error costs about as much as reading source code, so the message carries only what you need in order to act. Everything about why a rule exists lives one command away:

zdc explain E0101

Next

Read how the language works. It is one idea, and you can hold it in your head in about five minutes.