Builder
Class in Authoring
The statement collector every fn() body writes into.
import { Builder } from 'typeshade'
The signature, the description and the examples come from the compiler's own source at commit 26de7be8.
Syntax
class BuilderDescription
let, var, assign, if,
forRange, switch, ret, break, continue, discard and raw each push one
statement onto .stmts in authored order. fn() hands its body the Builder as the second
argument, and the ambient free functions (Let, Var, If,
Loop, Return and the rest) resolve the innermost active Builder and
forward to it, so b.let(...) and Let(...) emit the same statement. Use the Builder
directly when a statement is authored outside an active fn(), If or Loop scope, for
example when assembling a Stmt[] fragment by hand to splice into a body later; there
the ambient functions throw SD0013 (no active scope).
Examples
Example
// Outside an active fn() scope, assembling a Stmt[] to splice in later.const b = new Builder()const base = b.let('base', fillExpr)b.assign(out.color, vec4(base.swizzle('xyz'), base.w))return b.stmtsConstructor
new Builder(private readonly autoNames: { n: number } = { n: 0 })
Instance properties
stmtsread onlyStmt[]
Instance methods
child(): BuilderA builder for a nested scope (an
if, loop orswitchbody) that shares this builder’s auto-name counter, so generated_v{n}names stay unique across the whole function.let<K extends string>(value: ReadonlyNode<K>): ReadonlyNode<K>Immutable binding,
let name = expr;. The name is optional: omit it and the binding takes a function-unique generated name (_v0,_v1, and so on), which suits a value whose JavaScriptconstalready carries the meaning, at the cost of an opaque name in the emitted source. Returns a read-only node of the bound value’s type.let<K extends string>(name: string, value: ReadonlyNode<K>): ReadonlyNode<K>let<K extends string>(nameOrValue: string | ReadonlyNode<K>, maybeValue?: ReadonlyNode<K>): ReadonlyNode<K>var<T extends ShaderType>(type: T, init?: ReadonlyNode<KeyOf<T>>): Node<KeyOf<T>>Mutable binding,
var name: T = init;. The name is optional, as forlet. Returns a mutable node whose.assignwrites the variable.var<T extends ShaderType>(name: string, type: T, init?: ReadonlyNode<KeyOf<T>>): Node<KeyOf<T>>var<T extends ShaderType>(nameOrType: string | T, typeOrInit?: T | ReadonlyNode<KeyOf<T>>, maybeInit?: ReadonlyNode<KeyOf<T>>): Node<KeyOf<T>>inferredVar(): { ref: (type: ShaderType) => Node; commit: (type: ShaderType) => void; cancel: () => void; }A
varwhose type is filled in after its branch assignments are authored, for a value chosen by a branch (whenuses it). The declaration is pushed now, ahead of the branches;ref(type)makes a node reading the variable once the type is known,commit(type)patches the declaration with it, andcancel()removes the declaration when no branch assigned a value. The build completes synchronously, so the emitter always sees a fully typed declaration.assign<K extends string>(target: ReadonlyNode<K>, value: ReadonlyNode<K>): voidassignOp<K extends string>(target: ReadonlyNode<K>, bop: BinOp, value: ArithArg<K>): voidaddAssign<K extends string>(target: Node<K>, value: ArithArg<K>): voidret(value?: ReadonlyNode): voidbreak(): voidcontinue(): voiddiscard(): voidcall(node: ReadonlyNode): voidPush a call as a statement, kept for its effect and nothing else:
b.call(store(i)). The value is dropped; a userfn’s result is emitted bare, a value-returning builtin’s behind WGSL’s_ =phony assignment.placeholder(tag: string): voidPush a placeholder statement carrying
tag. A host that post-processes the module can walk the body and replace each tagged placeholder with statements of its own. A placeholder left in place emits as the comment// __placeholder: <tag>.raw(payload: RawPayload): voidPush a raw statement, one verbatim spelling per target; the builder form of the free
rawStmtfactory. Useb.raw()inside afn()body: a barerawStmt(...)call there is a discarded expression, since the returned statement is never pushed and nothing is emitted. UserawStmt()when assembling aStmt[]body array by hand. The statement records its source location like every other statement.if(cond: ReadonlyNode<'bool'>, body: (b: Builder) => ReadonlyNode | void): IfChainif / else-if / else chain. Returns a chainer so
.elif().else()reads top-to-bottom. The If stmt is pushed on the first call and mutated in place by subsequent .elif/.else.A branch is a statement block, so a body that returns a value is rejected with
SD0115rather than having that value quietly dropped. WriteReturn(value)for an early return,whenfor a value, or assign to aVar.forRange<K extends string>(init: ReadonlyNode<K>, cond: (i: Node<K>) => ReadonlyNode<'bool'>, body: (b: Builder, i: Node<K>) => ReadonlyNode | void, step?: ReadonlyNode<ScalarKey> | number): voidC-style
forloop:for (var name = init; cond; name = name + step). A numeric or omitted step takes the loop variable’s scalar type, so au32ori32counter emitsi + 1uori + 1; a float step on an integer counter would be rejected by both GPU compilers.forRange<K extends string>(name: string, init: ReadonlyNode<K>, cond: (i: Node<K>) => ReadonlyNode<'bool'>, body: (b: Builder, i: Node<K>) => ReadonlyNode | void, step?: ReadonlyNode<ScalarKey> | number): voidforRange<K extends string>(a: string | ReadonlyNode<K>, b: ReadonlyNode<K> | ((i: Node<K>) => ReadonlyNode<'bool'>), c: ((i: Node<K>) => ReadonlyNode<'bool'>) | ((b: Builder, i: Node<K>) => ReadonlyNode | void), d?: ((b: Builder, i: Node<K>) => ReadonlyNode | void) | ReadonlyNode<ScalarKey> | number, e?: ReadonlyNode<ScalarKey> | number): voidswitch(scrut: ReadonlyNode<'i32' | 'u32'>, cases: Array<[number, (b: Builder) => ReadonlyNode | void]>, defaultBody?: (b: Builder) => ReadonlyNode | void): void
See also
Source
src/core/ir/builder.ts, line 139, at commit 26de7be8