ReadonlyNode
On this page

ReadonlyNode

Class in IR

The read-only base of every value node: every literal, parameter, constRef and Let() binding is a ReadonlyNode.

import { ReadonlyNode } from 'typeshade'

The signature, the description and the examples come from the compiler's own source at commit 26de7be8.

Syntax

class ReadonlyNode<K extends string = string>

Exceptions

TypeShadeError

SD0004 from an arithmetic or comparison method whose two operands have incompatible types (vec3<f32> against vec2<f32>, say). The Node<K> phantom key catches most of these at tsc time; this is the runtime check for an operand typed ReadonlyNode<string> or built dynamically.

Description

The read-only base of every value node: every literal, parameter, constRef and Let() binding is a ReadonlyNode. It carries the full chainable API, arithmetic (.add, .sub, …), comparison, swizzles, .at() and .select(), and no .assign(); that method lives only on the mutable Node subtype. Write a helper’s operand type as ReadonlyNode<K> whenever the value is only read: it then accepts a Let, a parameter and a Var alike, since Node is a subtype of this class.

Constructor

new ReadonlyNode(readonly expr: Expr)

Instance properties

__koptionalread only K

Phantom type key. Optional and never assigned, so it carries K covariantly at the type level (a Node<'vec3<f32>'> is not assignable where a Node<'vec2<f32>'> is wanted) at no runtime cost. It is a plain optional field and must stay one: Babel’s TypeScript transform rejects declare class fields.

typeread only ShaderType
xread only Node<ElemKey<K>>
yread only Node<ElemKey<K>>
zread only Node<ElemKey<K>>
wread only Node<ElemKey<K>>
rread only Node<ElemKey<K>>
gread only Node<ElemKey<K>>
bread only Node<ElemKey<K>>
aread only Node<ElemKey<K>>
rgbread only Node<SwizzleKey<K, 'rgb'>>
xyread only Node<SwizzleKey<K, 'xy'>>
xyzread only Node<SwizzleKey<K, 'xyz'>>
zyxread only Node<SwizzleKey<K, 'zyx'>>
zxyread only Node<SwizzleKey<K, 'zxy'>>
yzxread only Node<SwizzleKey<K, 'yzx'>>
bgrread only Node<SwizzleKey<K, 'bgr'>>
bgraread only Node<SwizzleKey<K, 'bgra'>>

Instance methods

add <K2 extends `vec${number}<${K}>`>(this: ReadonlyNode<NonComposite<K>>, o: ReadonlyNode<K2>): Node<K2>
add (this: ReadonlyNode<'f32'>, o: ReadonlyNode<'f64'>): Node<'f64'>
add (o: ArithArg<K>): Node<K>
add (o: NodeLike): Node
sub <K2 extends `vec${number}<${K}>`>(this: ReadonlyNode<NonComposite<K>>, o: ReadonlyNode<K2>): Node<K2>
sub (this: ReadonlyNode<'f32'>, o: ReadonlyNode<'f64'>): Node<'f64'>
sub (o: ArithArg<K>): Node<K>
sub (o: NodeLike): Node
mul <K2 extends `vec${number}<${K}>`>(this: ReadonlyNode<NonComposite<K>>, o: ReadonlyNode<K2>): Node<K2>
mul (this: ReadonlyNode<'f32'>, o: ReadonlyNode<'f64'>): Node<'f64'>
mul (o: ArithArg<K>): Node<K>
mul (o: NodeLike): Node
div <K2 extends `vec${number}<${K}>`>(this: ReadonlyNode<NonComposite<K>>, o: ReadonlyNode<K2>): Node<K2>
div (this: ReadonlyNode<'f32'>, o: ReadonlyNode<'f64'>): Node<'f64'>
div (o: ArithArg<K>): Node<K>
div (o: NodeLike): Node
mod <K2 extends `vec${number}<${K}>`>(this: ReadonlyNode<NonComposite<K>>, o: ReadonlyNode<K2>): Node<K2>
mod (o: ArithArg<K>): Node<K>
mod (o: NodeLike): Node
neg (): Node<K>
lt (this: ReadonlyNode<NonComposite<K>>, o: CmpArg<K>): Node<'bool'>
gt (this: ReadonlyNode<NonComposite<K>>, o: CmpArg<K>): Node<'bool'>
le (this: ReadonlyNode<NonComposite<K>>, o: CmpArg<K>): Node<'bool'>
ge (this: ReadonlyNode<NonComposite<K>>, o: CmpArg<K>): Node<'bool'>
eq (this: ReadonlyNode<NonComposite<K>>, o: CmpArg<K>): Node<'bool'>
ne (this: ReadonlyNode<NonComposite<K>>, o: CmpArg<K>): Node<'bool'>
and (o: ReadonlyNode<'bool'>): Node<'bool'>
or (o: ReadonlyNode<'bool'>): Node<'bool'>
not (this: ReadonlyNode<'bool'>): Node<'bool'>

Logical negation, the ! of both targets. It completes the .and / .or set, which until now could say every boolean expression except the simplest one.

The node it builds is this == false, which is what the "use typeshade" compiler lowers a source-level !a to. That is the point: the two authoring surfaces meet in the IR, so a helper moved from one to the other keeps its emit. The emitted text is (a == false) rather than !a.

bitAnd (o: ReadonlyNode<K & ('i32' | 'u32')> | number): Node<K>
bitOr (o: ReadonlyNode<K & ('i32' | 'u32')> | number): Node<K>
bitXor (o: ReadonlyNode<K & ('i32' | 'u32')> | number): Node<K>
shl (o: ReadonlyNode<'u32'> | number): Node<K>
shr (o: ReadonlyNode<'u32'> | number): Node<K>
comp (field: 'x' | 'y' | 'z' | 'w'): Node<ElemKey<K>>

Vector component access: .x, .y, .z or .w, returning the element scalar.

swizzle <S extends string>(comps: S): Node<SwizzleKey<K, S>>

Vector swizzle: .rgb, .xy, .a, and so on. One component gives a scalar; N components give a vecN of the same element type. The result key is inferred from the components string, so v4.swizzle('yxz') is Node<'vec3<f32>'> for an f32 source and element-typed for u32 and i32 vectors too. The components are validated: xyzw or rgba, one set per swizzle, each within the source’s component count.

swizzle (comps: string): Node
f32 (this: ReadonlyNode<NonComposite<K>>): Node<'f32'>

Convert this scalar to f32, the method form of toF32. Emits f32(x) on WGSL and float(x) on GLSL, and narrows an f64 exactly (the sum of its hi and lo halves).

i32 (this: ReadonlyNode<NonComposite<K>>): Node<'i32'>

Convert this scalar to i32, the method form of toI32. Emits i32(x) on WGSL and int(x) on GLSL.

u32 (this: ReadonlyNode<NonComposite<K>>): Node<'u32'>

Convert this scalar to u32, the method form of toU32. Emits u32(x) on WGSL and uint(x) on GLSL.

f64 (this: ReadonlyNode<'f32'>): Node<'f64'>

Widen this f32 to the emulated double f64, exactly — the result is the pair (x, 0.0). The method form of toF64, and bounded to an f32 receiver for the same reason that is: no other scalar has an exact widening the fp64 lowering implements.

at <T extends ShaderType>(idx: ReadonlyNode<ScalarKey> | number, elem: T): Node<KeyOf<T>>

Array index, base[idx]. A JS number index lifts to a u32 literal, since WGSL indices are integers.

The element token is optional on an array-keyed node: xs.at(i) reads the element from the array’s own key, so an array<f32, 4> indexes to Node<'f32'> with nothing restated. It is the spelling a storageBuffer handle’s .at(i) already had, and the one the "use typeshade" surface’s xs[i] matches. Forgetting the token used to be a runtime TypeError rather than a tsc error, since the element ShaderType was the only place the type came from.

Pass the token for a node whose key does not carry the element — a widened ReadonlyNode<string>, or a struct read the phantom key cannot see into.

at (this: ReadonlyNode<`array<${string}>`>, idx: ReadonlyNode<ScalarKey> | number): Node<ArrayElemKey<K>>
at (idx: ReadonlyNode<ScalarKey> | number, elem?: ShaderType): Node
select (this: ReadonlyNode<'bool'>, a: number, b: number): Node<'f32'>

this ? a : b, valid only on a bool node (enforced through the this: bound). Both branches must share a key. Emits WGSL select(b, a, this).

select <R extends string>(this: ReadonlyNode<'bool'>, a: ReadonlyNode<R> | number, b: ReadonlyNode<R> | number): Node<R>
select <R extends string = 'f32'>(this: ReadonlyNode<'bool'>, a: ReadonlyNode<R> | number, b: ReadonlyNode<R> | number): Node<R>

See also

Source

src/core/ir/node.ts, line 346, at commit 26de7be8

Edit this page Report a problem