An open API service indexing awesome lists of open source software.

https://github.com/ydah/zwgsl

Ruby-inspired shading language that compiles to WGSL and GLSL ES 3.0, with HM-style inference, pattern matching, an LSP server, and a WebGPU playground.
https://github.com/ydah/zwgsl

compiler glsl graphics-programming lsp playground programming-language shader shading-language type-inference webgpu wgsl zig

Last synced: 10 days ago
JSON representation

Ruby-inspired shading language that compiles to WGSL and GLSL ES 3.0, with HM-style inference, pattern matching, an LSP server, and a WebGPU playground.

Awesome Lists containing this project

README

          

# zwgsl

A Ruby-inspired shading language that compiles to WGSL (WebGPU) and GLSL ES 3.0.
Built with Zig for fast compilation, a small runtime surface, and tooling that can
ship as a native library, an LSP server, or a browser wasm module.

Try it in the [Playground](https://ydah.github.io/zwgsl/).

## Why zwgsl?

GPU shading languages are powerful, but the authoring experience is usually rigid:
braces everywhere, repetitive type annotations, and weak structure for reusable
shader logic. `zwgsl` keeps shader code close to Ruby-style flow and expression
syntax while still targeting modern GPU backends.

- `end` blocks instead of braces
- layout-aware indentation and implicit statement separators
- method chains, postfix conditionals, and implicit returns
- `let` bindings, `where` clauses, `type`, `match`, `trait`, and `impl`
- WGSL-first output with a retained GLSL ES 3.0 path

## Features

- Ruby-like syntax with `def`, `do`, `end`, symbols, method calls, and postfix `if` / `unless`
- Indentation-sensitive layout resolver that inserts virtual indent / dedent / statement separators
- `let` bindings and function-local `where` clauses
- HM-style local type inference with let-generalization
- Algebraic data types and constructor registration
- Pattern matching with constructor, wildcard, binding, literal, and guarded arms
- Dependent dimension matching for `Vec(N)`, `Mat(M, N)`, and tensor-style type applications
- Generic structs, constructor inference, and phantom-type-safe annotations
- `trait` / `impl` support with compile-time specialization and inline trait methods in WGSL output
- Multi-stage WGSL pipeline with entry-point-aware HIR and SSA-style CFG MIR: `AST -> HIR -> MIR -> WGSL`
- WGSL sampler lowering for uniforms, function parameters, and immutable local aliases
- Source-aware LSP support: diagnostics, hover with lowering previews, completion, signature help, goto-definition, document symbols, semantic tokens
- Browser playground with Monaco, wasm compilation, and compiler-backed worker tooling
- C API surface for embedding the compiler in other tools

## Documentation

- [Language Reference](docs/language.md)
- [Grammar Summary](docs/spec.md)
- [Feature Matrix](docs/feature-matrix.md)
- [Builtins](docs/builtins.md)
- [C API](docs/c-api.md)
- [Gotchas](docs/gotchas.md)
- [Migration From WGSL And GLSL](docs/migration.md)
- [Design Notes](docs/design-notes.md)
- [Architecture](docs/architecture.md)
- [Roadmap](ROADMAP.md)
- [Security Policy](SECURITY.md)
- [Editor Setup](docs/editor-setup.md)
- [Examples](examples/README.md)
- [Contributing](CONTRIBUTING.md)

## Quick Example

`zwgsl` source:

```ruby
version "300 es"
precision :fragment, :highp

uniform :model_matrix, Mat4
uniform :view_matrix, Mat4
uniform :projection_matrix, Mat4
uniform :light_pos, Vec3
uniform :base_color, Vec4

def phong_strength(normal: Vec3, light_dir: Vec3) -> Float
max(dot(normal.normalize, light_dir.normalize), 0.0)
end

vertex do
input :position, Vec3, location: 0
input :normal, Vec3, location: 1
varying :v_normal, Vec3
varying :v_world_pos, Vec3

def main
world_pos = model_matrix * vec4(position, 1.0)
self.v_normal = mat3(model_matrix) * normal
self.v_world_pos = world_pos.xyz
gl_Position = projection_matrix * view_matrix * world_pos
end
end

fragment do
varying :v_normal, Vec3
varying :v_world_pos, Vec3
output :frag_color, Vec4, location: 0

def main
light_dir = light_pos - v_world_pos
light = phong_strength(v_normal, light_dir)
frag_color = vec4(base_color.rgb * (0.2 + 0.8 * light), base_color.a)
end
end
```

Compiled WGSL vertex output:

```wgsl
struct VertexInput {
@location(0) position: vec3f,
@location(1) normal: vec3f,
}

struct VertexOutput {
@builtin(position) gl_Position: vec4f,
@location(0) v_normal: vec3f,
@location(1) v_world_pos: vec3f,
}

@group(0) @binding(0) var model_matrix: mat4x4f;
@group(0) @binding(1) var view_matrix: mat4x4f;
@group(0) @binding(2) var projection_matrix: mat4x4f;
struct _zwgsl_uniform_light_pos {
@align(16) value: vec3f,
}
@group(0) @binding(3) var light_pos: _zwgsl_uniform_light_pos;
@group(0) @binding(4) var base_color: vec4f;

var gl_Position: vec4f;
var position: vec3f;
var normal: vec3f;
var v_normal: vec3f;
var v_world_pos: vec3f;

fn _zwgsl_vertex_main() {
let world_pos: vec4f = model_matrix * vec4f(position, 1.0);
v_normal = mat3x3f(model_matrix[0].xyz, model_matrix[1].xyz, model_matrix[2].xyz) * normal;
v_world_pos = world_pos.xyz;
gl_Position = projection_matrix * view_matrix * world_pos;
}

@vertex
fn main(input: VertexInput) -> VertexOutput {
position = input.position;
normal = input.normal;
_zwgsl_vertex_main();
var output: VertexOutput;
output.gl_Position = gl_Position;
output.v_normal = v_normal;
output.v_world_pos = v_world_pos;
return output;
}
```

Compiled WGSL fragment output:

```wgsl
struct FragmentInput {
@location(0) v_normal: vec3f,
@location(1) v_world_pos: vec3f,
}

struct FragmentOutput {
@location(0) frag_color: vec4f,
}

@group(0) @binding(0) var model_matrix: mat4x4f;
@group(0) @binding(1) var view_matrix: mat4x4f;
@group(0) @binding(2) var projection_matrix: mat4x4f;
struct _zwgsl_uniform_light_pos {
@align(16) value: vec3f,
}
@group(0) @binding(3) var light_pos: _zwgsl_uniform_light_pos;
@group(0) @binding(4) var base_color: vec4f;

var v_normal: vec3f;
var v_world_pos: vec3f;
var frag_color: vec4f;

fn phong_strength(normal: vec3f, light_dir: vec3f) -> f32 {
return max(dot(normalize(normal), normalize(light_dir)), 0.0);
}

fn _zwgsl_fragment_main() {
let light_dir: vec3f = light_pos.value - v_world_pos;
let light: f32 = phong_strength(v_normal, light_dir);
frag_color = vec4f(base_color.rgb * (0.2 + 0.8 * light), base_color.a);
}

@fragment
fn main(input: FragmentInput) -> FragmentOutput {
v_normal = input.v_normal;
v_world_pos = input.v_world_pos;
_zwgsl_fragment_main();
var output: FragmentOutput;
output.frag_color = frag_color;
return output;
}
```

## Advanced Examples

Pattern matching over ADTs ([Open in Playground](https://ydah.github.io/zwgsl/?sample=adt-match)):

```ruby
type Shape
Circle(radius: Float)
Rect(width: Float, height: Float)
Point
end

def area(shape: Shape) -> Float
match shape
when Circle(radius)
3.14159 * radius * radius
when Rect(width, height)
width * height
when Point
0.0
end
end

compute do
def main
value: Float = area(Circle(2.0))
end
end
```

Dependent dimensions that lower to fixed-size WGSL types ([Open in Playground](https://ydah.github.io/zwgsl/?sample=dependent-dimensions)):

```ruby
compute do
def main
transform: Mat(4, 4) = mat4(1.0)
value: Vec(4) = vec4(1.0)
energy: Float = dot(value, value)
end
end
```

Trait-constrained specialization:

```ruby
trait Numeric
def add(other: Self) -> Self end
def mul(other: Self) -> Self end
end

impl Numeric for Float
def add(other: Self) -> Self
self + other
end

def mul(other: Self) -> Self
self * other
end
end

def lerp(a: T, b: T, t: Float) -> T where T: Numeric
a.mul(1.0 - t).add(b.mul(t))
end

compute do
def main
value: Float = lerp(1.0, 2.0, 0.5)
end
end
```

## Installation

### From Source

Requires Zig 0.15.x.

```sh
git clone https://github.com/ydah/zwgsl
cd zwgsl
zig build -Doptimize=ReleaseFast
```

### Test Suite

```sh
zig build test
bash script/validate_generated_wgsl.sh
zig build benchmark
```

`script/validate_generated_wgsl.sh` validates generated WGSL fixtures when
`naga`, `tint`, or `WGSL_VALIDATOR` is available.
Use `WGSL_VALIDATOR_REQUIRED=1 bash script/validate_generated_wgsl.sh` or
`bash script/validate_generated_wgsl.sh --require-validator` when a missing
external validator should fail the run.
`zig build benchmark` prints CSV compile-time measurements for representative
examples.

### CLI

```sh
zig build
zig-out/bin/zwgsl compile --target wgsl examples/hello_triangle.zw
zig-out/bin/zwgsl check examples/hello_triangle.zw
zig-out/bin/zwgsl fmt --check examples/hello_triangle.zw
zig-out/bin/zwgsl source-map --stage vertex examples/hello_triangle.zw
zig-out/bin/zwgsl lsp
```

Use `--target glsl-es-300` for GLSL ES 3.0 output, `--stage` to select a
single generated stage, and `-o` / `--output` to write compile output to a file.
Use `zwgsl fmt --write ` to rewrite a source file in place.
Use `zwgsl source-map` to emit a JSON mapping from generated WGSL lines back to
source lines when debugging output.

### Browser Wasm Build

```sh
zig build wasm
```

That installs `zig-out/bin/zwgsl.wasm`, which the playground syncs into
`playground/src/generated/zwgsl.wasm` so Vite can fingerprint the asset.

## Artifacts

`zig build` installs:

- `zig-out/lib/libzwgsl.a`
- `zig-out/lib/libzwgsl.dylib` or the platform equivalent shared library
- `zig-out/include/zwgsl.h`
- `zig-out/bin/zwgsl`
- `zig-out/bin/zwgsl-lsp`

The repository also includes a `Dockerfile`, a Homebrew formula template under
`packaging/homebrew/`, and an npm compiler package scaffold under
`packages/compiler/`.

Tagged releases attach a Linux x86_64 CLI/LSP/library archive, a standalone
`zwgsl.wasm` asset, and an `@zwgsl/compiler` npm package tarball with checksum
files for each asset.

## C API

See [C API](docs/c-api.md) for ABI/versioning, options, ownership, and C++
integration notes.

```c
#include "zwgsl.h"

ZwgslOptions options = zwgsl_options_default();
options.target = ZWGSL_TARGET_WGSL;

ZwgslResult result = zwgsl_compile(source, source_len, options);

if (result.error_count == 0) {
if (result.vertex_source != NULL) {
puts(result.vertex_source);
}
if (result.fragment_source != NULL) {
puts(result.fragment_source);
}
if (result.compute_source != NULL) {
puts(result.compute_source);
}
}

zwgsl_free(&result);
```

## LSP

The server entry point is `zwgsl-lsp`, implemented under `src/lsp/`.
See [Editor Setup](docs/editor-setup.md) for Neovim, Helix, VS Code, and Zed
configuration notes, including the local VS Code extension in `editors/vscode`.

Current editor-facing features:

- incremental `didChange` with full-document change compatibility
- diagnostics from compiler errors and warnings
- hover with source-aware type / declaration info and method lowering previews
- completion for locals, declarations, stage builtins, constructors, fields, and methods
- signature help for functions, constructors, and supported builtins
- code actions for common stage declaration, unused uniform, and type/constructor casing fixes
- goto-definition for values, functions, and type declarations
- document symbols for stages, declarations, functions, types, traits, and impls
- document formatting through the same formatter used by `zwgsl fmt`
- rename for resolved document-local symbols
- semantic tokens for keywords, functions, variables, uniforms, varyings, builtins, constructors, traits, types, numbers, strings, comments, operators, and properties

## Playground

The playground lives under `playground/` and uses Monaco plus the real wasm compiler build.

```sh
cd playground
npm install
npm run dev
```

The repository also includes a GitHub Pages workflow that publishes the production
build to `https://ydah.github.io/zwgsl/` when Pages is enabled with `GitHub Actions`
as the publishing source.

Current capabilities:

- Monaco language registration for `zwgsl`
- sample selector backed by repository examples and focused feature fixtures, with `?sample=` direct links and `?source=` share links
- live WGSL compilation through `zwgsl.wasm`
- output tabs for combined WGSL, stage-specific WGSL, diagnostics, and generated resource layout
- compiler-backed diagnostics, hover, completion, signature help, and goto-definition from the wasm build
- WebGPU preview surface with animated `iTime` / `iResolution` uniforms, persisted generated controls with color pickers, sampler placeholders, and 2D texture upload
- `npm run sync-wasm` to refresh the generated wasm asset from `zig-out/bin/zwgsl.wasm`

For a GitHub Pages build, set the base path before running Vite:

```sh
cd playground
PLAYGROUND_BASE_PATH=/zwgsl/ npm run build
```

## Architecture

See [Architecture](docs/architecture.md) for the responsibilities of the
frontend, HIR/MIR WGSL path, retained GLSL path, diagnostics, and tooling entry
points.

```mermaid
flowchart TD
Source["Source (.zw)"] --> Lexer
Lexer --> LayoutResolver["Layout Resolver"]
LayoutResolver --> Parser
Parser --> AST

AST --> HM["HM Inference + Sema"]
HM --> TypedAST["Typed AST"]

TypedAST --> HIR
TypedAST --> IR

HIR --> MIR
MIR --> WGSLEmitter["WGSL Emitter"]
WGSLEmitter --> WGSL["WGSL / WebGPU"]

IR --> GLSLEmitter["GLSL Emitter"]
GLSLEmitter --> GLSL["GLSL ES 3.0"]
```

## Project Status

Implemented behavior is tracked here and in the [Feature Matrix](docs/feature-matrix.md).
Forward-looking work is tracked separately in the [Roadmap](ROADMAP.md).

| Area | Status |
| --- | --- |
| Package version | 0.1.0 |
| Verified Zig | 0.15.2 in CI; requires Zig 0.15.x |
| Verified Node.js | 24 in CI for the playground |
| Lexer + layout resolver | Implemented |
| Parser + source positions | Implemented |
| `let` / `where` | Implemented |
| HM-style local inference | Implemented for local bindings and let-generalization |
| ADTs + pattern matching | Implemented |
| Dependent dimensions | Implemented for `Vec(N)` / `Mat(M, N)` matching and WGSL type lowering |
| Generic structs + phantom tags | Implemented |
| `trait` / `impl` specialization | Implemented as compile-time static dispatch with inline trait methods in WGSL output |
| WGSL pipeline | Implemented as `AST -> HIR -> MIR -> WGSL`, with entry-point HIR and SSA-style CFG-based MIR lowering |
| GLSL ES 3.0 backend | Implemented |
| LSP | Implemented |
| Playground | Implemented |

## Repository Layout

```text
src/
ast.zig
compiler.zig
hir.zig
hir_builder.zig
ir.zig
ir_builder.zig
layout.zig
lsp/
mir.zig
mir_builder.zig
parser.zig
sema.zig
typeclass.zig
wgsl_emitter.zig
tests/
fixtures/
examples/
editors/
vscode/
playground/
include/
```

## License

Licensed under the [MIT License](LICENSE).