Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
zig-out/
example/
node_modules/
.claude/
.claude/
lib/parser/parser.a
lib/parser/parser.h
3 changes: 2 additions & 1 deletion DOCS/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ The template:

This is the heart of the compiler:

- **`lib/grammar/proto-circ.peg`** — PEG grammar source (regenerate `lib/parser.c` / `lib/parser.h` with [langlang](https://github.com/clarete/langlang) when the grammar changes; the generated sources are vendored).
- **`lib/grammar/proto-circ.peg`** — PEG grammar source. Regenerate `lib/parser/parser.go` with [langlang](https://github.com/clarete/langlang) when the grammar changes; the generated source is vendored.
- **`lib/parser/`** — `parser.go` (langlang-generated) plus `shim/shim.go` (CGo bridge exposing a C-callable surface). `go build -buildmode=c-archive` produces `parser.a` + `parser.h`, which Zig links via `lib/syntax/CParser.zig`.
- **`lib/syntax/`** — `CParser.zig` is the FFI wrapper, `translate.zig` lowers the parse tree to the Zig AST in `ast.zig`, `span.zig` carries source spans through the rest of the pipeline.
- **`lib/resolver/`** — splits into `scan_imports` (auto-imports `<builtin>/` macros if the file participates in a project), `import_cycle` (rejects cyclic imports with `E010`), `file_loader`, `resolve_bodies` (whole-project resolution into per-module IRs), and `builtins` (the in-memory definitions of `or`, `nand`, `nor`, `xor`, `xnor`).
- **`lib/ir/`** — `types.zig` defines `Module` / `Project` / `Component` / `Pin`. `resolver.zig` is the single-file path used by `--inspect`.
Expand Down
14 changes: 7 additions & 7 deletions DOCS/circuit-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,15 @@ The compiled `.wasm` does not expose a programmatic graph-construction API — t

## Grammar

The parser is generated from a PEG grammar at `lib/grammar/proto-circ.peg` using the `langlang` tool. The generated C parser is at `lib/parser.c` / `lib/parser.h` and is imported into Zig via `lib/syntax/CParser.zig`.
The parser is generated from a PEG grammar at `lib/grammar/proto-circ.peg` using the `langlang` tool. The generated Go parser is at `lib/parser/parser.go`; a hand-written CGo shim at `lib/parser/shim/shim.go` exposes it as a C-callable archive (`lib/parser/parser.a` + `lib/parser/parser.h`) that Zig links via `lib/syntax/CParser.zig`.

Parse tree node types used by `lib/syntax/translate.zig`:

| Node type | Meaning |
|---------------------|---------------------------------------|
| `LL_NODE_SEQUENCE` | Ordered list of child nodes |
| `LL_NODE_NODE` | Named grammar rule match |
| `LL_NODE_STRING` | Matched literal text (identifier, etc.) |
| `LL_NODE_ERROR` | Parse failure at this position |
| Node type | Meaning |
|---------------------|------------------------------------------|
| `NodeType_Sequence` | Ordered list of child nodes |
| `NodeType_Node` | Named grammar rule match |
| `NodeType_String` | Matched literal text (identifier, etc.) |
| `NodeType_Error` | Parse failure at this position |

`lib/syntax/nodes/declaration.zig` handles `Declaration` rule nodes and recognises the sub-rules `input`, `output`, and `component` to determine declaration kind.
18 changes: 14 additions & 4 deletions DOCS/decisions/tooling.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,30 @@ The project's runtime guideline is "no dependencies in runtime, Zig-only with as

### langlang version pinning

**Decision.** The project's PEG grammar is processed by [langlang](https://github.com/clarete/langlang) at the version `go/v0.0.12` specifically. The required version is documented in the build instructions and checked at build time when grammar regeneration is requested.
**Decision.** The project's PEG grammar is processed by [langlang](https://github.com/clarete/langlang) at version `v0.0.12` specifically (the git tag is `go/v0.0.12`; Go's module resolver strips the `go/` prefix). Install with `go install github.com/clarete/langlang/go/cmd/langlang@v0.0.12`.

**Rationale.** langlang is the only non-Zig tool in the build pipeline. Pinning to a specific version protects against silent behaviour changes between releases — a regenerated parser must be byte-identical to the vendored one for a given grammar. The `go/v0.0.12` tag is the version the project's grammar has been validated against.
**Rationale.** Pinning to a specific version protects against silent behaviour changes between releases — a regenerated parser must be byte-identical to the vendored one for a given grammar. `v0.0.12` is also the earliest tagged release that ships a stable Go backend. The C output that the project previously consumed lived on an unmerged `c-output` branch and was never part of any tagged release; relying on it meant tracking a moving branch HEAD. Pinning a tagged release is more durable.

**Alternatives.** Tracking the latest langlang release. Lower maintenance but exposes the project to upstream changes that could silently alter parser output. Pinning is the standard tradeoff.

### Generated parser is vendored in the repo

**Decision.** The langlang-generated `lib/parser.c` and `lib/parser.h` files are checked into the repository. langlang is only required when the grammar (`lib/grammar/proto-circ.peg`) changes and the parser needs regeneration. Day-to-day contributors who don't touch the grammar do not need langlang installed.
**Decision.** The langlang-generated `lib/parser/parser.go` file is checked into the repository. langlang is only required when the grammar (`lib/grammar/proto-circ.peg`) changes and the parser needs regeneration. Contributors who don't touch the grammar do not need langlang installed.

**Rationale.** Most users of the repo — including most contributors and all downstream consumers of the CLI — never modify the grammar. Requiring them to install a Go toolchain plus a specific langlang version just to build is friction without benefit. Vendoring the generated sources means the standard build path needs only Zig.
**Rationale.** Most users of the repo — including most contributors and all downstream consumers of the CLI — never modify the grammar. Requiring them to install langlang just to build is friction without benefit. Vendoring the generated source means the standard build path needs only Zig and Go (Go is required at every build because the parser is bridged to Zig via CGo c-archive; see below).

**Alternatives.** Generating the parser at every build. Always-current and avoids the "did someone forget to regenerate?" failure mode, but every build pulls in langlang as a hard prerequisite. The vendored approach trades that for a discipline of regenerating-and-committing whenever the grammar changes — a smaller and more localised burden.

### CGo c-archive as the Zig↔Go bridge

**Decision.** The langlang-generated Go parser is wrapped by a small hand-written shim (`lib/parser/shim/shim.go`) and compiled via `go build -buildmode=c-archive` into `lib/parser/parser.a` + `lib/parser/parser.h`. Zig links the archive the same way it would link any static C library. The `build.zig` step that runs `go build` derives `GOARCH` and `GOOS` from the Zig target, so cross-compiling `circ-compile` also cross-compiles the parser archive.

**Rationale.** langlang's only tagged-release output language is Go (`v0.0.12` lists exactly `case "go":` in its language switch). CGo c-archive is the lowest-friction bridge: it presents a C-style API to Zig that mirrors the prior langlang C runtime layout, so `lib/syntax/translate.zig` retains the same tree-walk structure it had against the C parser. Contributors never have to touch Go code unless they change the shim itself.

**Alternatives.** Subprocess bridge (spawn a precompiled Go binary at compile time, exchange JSON or protobuf): adds per-file process spawn overhead and another embedded binary. Hand-port to Zig: cleanest result but a non-trivial maintenance burden for a parser the upstream tool already maintains.

**Trade-offs.** The c-archive embeds the Go runtime (~2 MB per binary, fixed cost regardless of code size) and means `circ-compile` always links Go's GC and scheduler even though only the parser uses them. Cross-compile to Windows is unsupported because `c-archive` does not target it. Go is now required at every build (not just at parser-regen time).

### Build-time check when regenerating

**Decision.** When the build is asked to regenerate the parser (an explicit build option, not the default path), it checks that the available `langlang` binary reports the expected version. A mismatch is a hard build error.
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ Built-in macros (`or`, `nand`, `nor`, `xor`, `xnor`) are auto-imported from a vi
Prerequisites:

- [Zig](https://ziglang.org/) 0.15.x.
- Optional: [langlang](https://github.com/clarete/langlang) — only needed if you want to regenerate `lib/parser.c` / `lib/parser.h` from `lib/grammar/proto-circ.peg`. The generated sources are vendored in the repo, so day-to-day contributors do not need langlang installed.
- [Go](https://go.dev/) 1.21+ — used to compile the langlang-generated parser into a CGo c-archive that links into the Zig binary. Every build runs `go build` once to (re)produce `lib/parser/parser.a`.
- Optional: [langlang](https://github.com/clarete/langlang) `v0.0.12` — only needed if you want to regenerate `lib/parser/parser.go` from `lib/grammar/proto-circ.peg`. The generated source is vendored in the repo, so contributors who don't touch the grammar do not need langlang installed. Install with `go install github.com/clarete/langlang/go/cmd/langlang@v0.0.12`.

Build the CLI:

Expand Down
Loading
Loading