/Users/2n/dev/miniswift/metal — links as libMetal.aA four-pass C library that takes Apple Metal Shading Language source and produces WebGPU Shading Language plus a JSON pipeline-metadata sidecar. No LLVM, no codegen runtime — just lexer → parser → lower → emit. 224 tests, 100% pass.
Four passes — each surfaces structured diagnostics on failure, never NULL or a corrupt result.
Tokenises MSL — 106 keywords, full C99 operator set, double-bracket attributes [[position]] / [[buffer(0)]], preprocessor directives. Single-pass, malloc-free hot loop.
Recursive descent for declarations + statements, Pratt precedence climbing for expressions. Handles the full MSL surface: structs, function templates (single-param + multi-param + non-type int), vertex/fragment/kernel qualifiers, attributes, default arguments, function overloading.
Type mapping (90+ scalar/vector/matrix/texture entries), attribute → builtin/location resolution, ~90 builtin function rewrites, template monomorphization, address-space tracking, function-constant detection.
MIR → WGSL text + JSON pipeline metadata. Operator-precedence-aware parenthesization, struct entry-point I/O detection, storage texture format/access threading.
#include resolution + simple #define substitution. 21 predefined macros (__METAL__, __METAL_VERSION__, …).
msl_lex + msl_semantic_tokens for IDE highlighting; LSP-compatible 1-based line/column diagnostics.
All 106 reserved words tokenised correctly. Grouped here by purpose — each item lexes; what happens after lex is governed by parser/lower coverage.
90+ scalar / vector / matrix / texture / sampler entries cross-walked from MSL to WGSL. Highlights:
char / short → i32; uchar / ushort → u32. WGSL only has 32-bit integer scalars, so narrower MSL ints widen with explicit conversion at boundaries.
half / halfN → f16 / vecN<f16>. Pack/unpack helpers (pack_half_to_unorm2x16 etc.) emitted as helper functions.
MSL matrices are column-major; WGSL matrices are also column-major. floatCxR → matCxRf with the same indexing.
Read-only texture2d<float> → texture_2d<f32>; access mode (access::read / ::write / ::read_write) determines storage texture vs sampleable texture.
Tracked end-to-end: constant & → var<uniform>, device * → var<storage>, threadgroup → var<workgroup>, thread → var<function>.
sampler + access::sample mapped to WGSL sampler; sampler descriptor metadata routed into pipeline JSON.
Recognised; widens to f32 when target environment lacks bf16.
Tile-memory threadgroup_imageblock tokenises but lowers conservatively.
~90 builtin rewrites — direct passthrough where WGSL has the same name, helper-fn emission where it doesn't. Helpers are declared once at the top of the WGSL output, never inlined per call.
__sincos(x, &s, &c) helper; calls sin + cos under the hood.atomicLoad / atomicStore / atomicExchange.atomicCompareExchangeWeak.storageBarrier() / workgroupBarrier() by scope.pack2x16snorm et al.textureSample / textureSampleLevel / textureSampleGrad.textureLoad / textureStore.textureDimensions / textureNumLevels.textureGather / textureGatherCompare.Double-bracket attributes [[...]] resolve to WGSL @builtin(...) / @location(...) / @group / @binding.
@builtin(position).@builtin(vertex_index) / @builtin(instance_index).@builtin(front_facing).@location(N).@builtin(global_invocation_id).@builtin(local_invocation_id).@builtin(workgroup_id).@builtin(local_invocation_index).@group(0) @binding(N). Group/binding numbers tracked in pipeline JSON.@id(N).@location(N).@location(N).The fourth pass turns MIR into two artifacts: a WGSL shader text and a JSON pipeline-metadata sidecar.
Operator-precedence-aware parenthesization (no over-parenthesizing nor missing parens). Struct entry-point I/O is detected and rewritten.
Operations WGSL doesn't expose (sincos, quad reductions, saturating conv, half pack/unpack) are emitted as __name(...) helpers declared once at the top.
Format + access mode threaded into the WGSL declaration: texture_storage_2d<rgba8unorm, write>.
Lists entry points (vertex / fragment / compute), each with its bindings (group / binding / kind), vertex-attribute layout (location → format), and any specialisation constants.
1-based line/column ranges (LSP-compatible). Each diagnostic carries severity (ERROR / WARNING / INFO / HINT) + structured message.
AST + MIR allocate from chunk-based arenas; msl_free(result) releases everything at once. Zero ref-counting overhead in the hot loop.
MSL features without a WGSL equivalent emit structured diagnostics with migration hints — never silently produce gibberish WGSL.
raytracing::* namespace, ray_data, intersection_query. WebGPU doesn't expose ray tracing.
mesh, object, amplification qualifiers + mesh_grid_properties. No WGSL equivalent.
constexpr sampler s(...) at file scope. WGSL samplers must be uniform-bound.
Closures, visible_function_table, indirect dispatch. WGSL has no first-class functions.
Detected during lower; rejected with cycle trace. WGSL is non-recursive by spec.
simdgroup_matrix + matrix multiply ops. WGSL subgroup matrix extension not yet shipping.
imageblock<T> tile-memory storage. Tokenises but lower returns a TODO diagnostic.
Tier-2 argument buffers with descriptor indexing. WebGPU supports limited indexing only.
Indirect callables for ray-tracing intersection / visibility programs.
Hull / domain shaders + patch primitives. WebGPU has no tessellation pipeline.
[[stage_in]] with no buffer attribute → vertex-fetch attribute pulling. Falls back to per-attribute bindings.
counter, visibility_buffer. No corresponding WGSL feature.