A small language for building and simulating logic circuits, made for people learning how computers work.
// half_adder.circ
import xor "<builtin>/xor.circ"
input a, b
xor s(a=a, b=b)
and c(a=a, b=b)
output sum(in=s.out)
output carry(in=c.out) ╭───╮ ╭───╮ ╭───────╮
│ a ├○─●─▶┤ │ ╭──────▶┤ carry │
╰───╯ │ │AND├○╯ ╰───────╯
╭┼─▶┤ │
││ ╰───╯
││
╭───╮ ││ ╭───────╮ ╭─────╮
│ b ├○●╰─▶┤ │ ╭──▶┤ sum │
╰───╯ │ │[xor:s]├○╯ ╰─────╯
╰──▶┤ │
╰───────╯ A half-adder. Two inputs, an XOR macro for the sum, an AND for the carry.
What it is
circ is a declarative language. Programs are flat lists of
declarations: name an input pin, instantiate a gate, wire its ports to
signals from other components. The primitives are and,
not, led, and wire; macros for
or, nand, nor, xor,
and xnor expand to those primitives at compile time.
Larger circuits live in their own .circ file and get
pulled in with import.
What it isn't
circ is a v0. There are no multi-bit buses, no clocked
registers, no analog signals, no tri-state lines. A wire
is a single-bit pass-through, not a let-binding for a vector. The
language deliberately stops at the same boundary as the early
Nand2Tetris hardware chapters.
Where it runs
circ-compile produces a self-contained WebAssembly
module: drive its input pins from JavaScript, read its outputs back,
run it from Node or a browser. There's also a --preview
flag that prints an ASCII schematic to your terminal — handy for
sanity-checking the wiring before you simulate.
If you've enjoyed Nand2Tetris or building NANDs from scratch in Petzold's Code, this is a language for doing more of that.