Skip to content

Add Zig language support#1037

Open
gmontana wants to merge 1 commit into
colbymchenry:mainfrom
gmontana:zig-language-support
Open

Add Zig language support#1037
gmontana wants to merge 1 commit into
colbymchenry:mainfrom
gmontana:zig-language-support

Conversation

@gmontana

Copy link
Copy Markdown

Adds Zig as a supported language.

Grammar

Vendored tree-sitter-zig.wasm (same pattern as the existing vendored grammars — pascal/scala/lua/csharp/r), built from tree-sitter-grammars/tree-sitter-zig with a few fixes for current Zig 0.16 syntax: inline-asm clobbers as a struct literal (: .{ .rax = true }), the de-keyworded async family (async/await/suspend/resume are ordinary identifiers now; nosuspend stays a keyword), error-set declarations as struct/union field types, reserved words as block labels, and conditional slice/pointer element types. Parses the 0.16 standard library and several large projects (Ghostty, TigerBeetle, libxev, http.zig, zls) at 100%.

Extractor (src/extraction/languages/zig.ts)

Zig has no classes: a type is a value bound to a const (const Point = struct {...}), a module is const std = @import("std"), and there are no top-level named type declarations. The extractor keys on variable_declaration and fans out by its right-hand side — struct/enum/union/opaque become type nodes, @import becomes an import, otherwise const/var. Methods are detected by scope (Zig has no receiver syntax). Generic types — fn List(comptime T: type) type { return struct {...} } — are indexed as the struct they produce, so List.append resolves like any other method. test "..." blocks become function nodes.

Cross-file resolution (src/resolution/import-resolver.ts)

Calls across files go through the imported namespace const (const m = @import("x.zig"); m.f()), so this adds @import mapping and path resolution (a .zig/relative path is a project file; std/builtin/package names are external). callers/callees/impact connect across files.

Function values (src/extraction/function-ref.ts)

Zig has no closures, so callbacks are inline anonymous-struct methods passed by value (std.sort.sort(..., struct { fn lessThan(...) bool {...} }.lessThan)) and comptime dispatch uses .{ .x = handler } tables — both captured as function-as-value references.

Tests in __tests__/zig-extraction.test.ts.

The vendored wasm is built from a patched grammar; the grammar fixes are being upstreamed to tree-sitter-zig separately.

@gmontana gmontana force-pushed the zig-language-support branch from 50e0111 to a135340 Compare June 28, 2026 23:20
Vendored tree-sitter-zig grammar (built with fixes for current Zig 0.16
syntax — struct-literal asm clobbers, the de-keyworded async family,
error-set field types, reserved-word labels, conditional slice/pointer
element types) plus a Zig extractor for the language's value-as-type idioms:
`const X = struct/enum/union/opaque`, `@import` modules, generic-type
factories (`fn List(T) type { return struct {...} }`), and `test` blocks.

Adds cross-file @import-namespace call resolution (`const m =
@import("x.zig"); m.f()`) and function-as-value capture for inline-struct
callbacks and comptime dispatch tables. Tests in
__tests__/zig-extraction.test.ts. Parses the 0.16 standard library and
several large projects (Ghostty, TigerBeetle, libxev, http.zig, zls) at 100%.
@gmontana gmontana force-pushed the zig-language-support branch from a135340 to 0580b56 Compare June 28, 2026 23:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant