wesl 0.6.0-pre10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dist/index.js +4468 -0
- package/dist/index.js.map +1 -0
- package/dist/minified.js +3426 -0
- package/dist/minified.js.map +1 -0
- package/dist/tools/packages/wesl/src/AbstractElems.d.ts +322 -0
- package/dist/tools/packages/wesl/src/Assertions.d.ts +27 -0
- package/dist/tools/packages/wesl/src/BindIdents.d.ts +70 -0
- package/dist/tools/packages/wesl/src/Conditions.d.ts +6 -0
- package/dist/tools/packages/wesl/src/FlattenTreeImport.d.ts +11 -0
- package/dist/tools/packages/wesl/src/LinkedWesl.d.ts +50 -0
- package/dist/tools/packages/wesl/src/Linker.d.ts +87 -0
- package/dist/tools/packages/wesl/src/LinkerUtil.d.ts +3 -0
- package/dist/tools/packages/wesl/src/LiveDeclarations.d.ts +12 -0
- package/dist/tools/packages/wesl/src/LowerAndEmit.d.ts +31 -0
- package/dist/tools/packages/wesl/src/Mangler.d.ts +39 -0
- package/dist/tools/packages/wesl/src/ParseWESL.d.ts +60 -0
- package/dist/tools/packages/wesl/src/ParsedRegistry.d.ts +29 -0
- package/dist/tools/packages/wesl/src/PathUtil.d.ts +6 -0
- package/dist/tools/packages/wesl/src/RawEmit.d.ts +6 -0
- package/dist/tools/packages/wesl/src/Reflection.d.ts +45 -0
- package/dist/tools/packages/wesl/src/Scope.d.ts +81 -0
- package/dist/tools/packages/wesl/src/StandardTypes.d.ts +13 -0
- package/dist/tools/packages/wesl/src/TransformBindingStructs.d.ts +52 -0
- package/dist/tools/packages/wesl/src/Util.d.ts +43 -0
- package/dist/tools/packages/wesl/src/WESLCollect.d.ts +94 -0
- package/dist/tools/packages/wesl/src/WeslBundle.d.ts +13 -0
- package/dist/tools/packages/wesl/src/WeslDevice.d.ts +25 -0
- package/dist/tools/packages/wesl/src/debug/ASTtoString.d.ts +5 -0
- package/dist/tools/packages/wesl/src/debug/ImportToString.d.ts +2 -0
- package/dist/tools/packages/wesl/src/debug/LineWrapper.d.ts +21 -0
- package/dist/tools/packages/wesl/src/debug/ScopeToString.d.ts +6 -0
- package/dist/tools/packages/wesl/src/index.d.ts +11 -0
- package/dist/tools/packages/wesl/src/parse/ImportGrammar.d.ts +5 -0
- package/dist/tools/packages/wesl/src/parse/Keywords.d.ts +4 -0
- package/dist/tools/packages/wesl/src/parse/WeslBaseGrammar.d.ts +5 -0
- package/dist/tools/packages/wesl/src/parse/WeslExpression.d.ts +13 -0
- package/dist/tools/packages/wesl/src/parse/WeslGrammar.d.ts +80 -0
- package/dist/tools/packages/wesl/src/parse/WeslStream.d.ts +44 -0
- package/dist/tools/packages/wesl/src/test/BindWESL.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ConditionLinking.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ConditionalTranslationCases.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ErrorLogging.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/Expression.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/FlattenTreeImport.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ImportCases.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ImportSyntaxCases.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/LinkGlob.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/LinkPackage.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/Linker.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/Mangling.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ParseComments.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ParseConditions.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ParseError.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ParseWESL.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/PathUtil.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/PrettyGrammar.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/Reflection.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ScopeWESL.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/TestLink.d.ts +21 -0
- package/dist/tools/packages/wesl/src/test/TestSetup.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/TestUtil.d.ts +40 -0
- package/dist/tools/packages/wesl/src/test/Tokenizer.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/TransformBindingStructs.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/Util.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/VirtualModules.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/WeslDevice.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/WgslTests.d.ts +0 -0
- package/dist/tools/packages/wesl/src/vlq/vlq.d.ts +11 -0
- package/package.json +46 -0
- package/src/AbstractElems.ts +446 -0
- package/src/Assertions.ts +51 -0
- package/src/BindIdents.ts +523 -0
- package/src/Conditions.ts +74 -0
- package/src/FlattenTreeImport.ts +55 -0
- package/src/LinkedWesl.ts +184 -0
- package/src/Linker.ts +284 -0
- package/src/LinkerUtil.ts +29 -0
- package/src/LiveDeclarations.ts +31 -0
- package/src/LowerAndEmit.ts +413 -0
- package/src/Mangler.ts +94 -0
- package/src/ParseWESL.ts +157 -0
- package/src/ParsedRegistry.ts +120 -0
- package/src/PathUtil.ts +31 -0
- package/src/RawEmit.ts +102 -0
- package/src/Reflection.ts +334 -0
- package/src/Scope.ts +162 -0
- package/src/StandardTypes.ts +97 -0
- package/src/TransformBindingStructs.ts +319 -0
- package/src/Util.ts +194 -0
- package/src/WESLCollect.ts +614 -0
- package/src/WeslBundle.ts +16 -0
- package/src/WeslDevice.ts +209 -0
- package/src/debug/ASTtoString.ts +290 -0
- package/src/debug/ImportToString.ts +29 -0
- package/src/debug/LineWrapper.ts +70 -0
- package/src/debug/ScopeToString.ts +79 -0
- package/src/index.ts +11 -0
- package/src/parse/ImportGrammar.ts +157 -0
- package/src/parse/Keywords.ts +26 -0
- package/src/parse/WeslBaseGrammar.ts +8 -0
- package/src/parse/WeslExpression.ts +207 -0
- package/src/parse/WeslGrammar.ts +856 -0
- package/src/parse/WeslStream.ts +279 -0
- package/src/test/BindWESL.test.ts +57 -0
- package/src/test/ConditionLinking.test.ts +91 -0
- package/src/test/ConditionalTranslationCases.test.ts +56 -0
- package/src/test/ErrorLogging.test.ts +30 -0
- package/src/test/Expression.test.ts +22 -0
- package/src/test/FlattenTreeImport.test.ts +74 -0
- package/src/test/ImportCases.test.ts +56 -0
- package/src/test/ImportSyntaxCases.test.ts +24 -0
- package/src/test/LinkGlob.test.ts +25 -0
- package/src/test/LinkPackage.test.ts +26 -0
- package/src/test/Linker.test.ts +125 -0
- package/src/test/Mangling.test.ts +45 -0
- package/src/test/ParseComments.test.ts +36 -0
- package/src/test/ParseConditions.test.ts +183 -0
- package/src/test/ParseError.test.ts +36 -0
- package/src/test/ParseWESL.test.ts +1572 -0
- package/src/test/PathUtil.test.ts +34 -0
- package/src/test/PrettyGrammar.test.ts +20 -0
- package/src/test/Reflection.test.ts +172 -0
- package/src/test/ScopeWESL.test.ts +462 -0
- package/src/test/TestLink.ts +82 -0
- package/src/test/TestSetup.ts +4 -0
- package/src/test/TestUtil.ts +126 -0
- package/src/test/Tokenizer.test.ts +135 -0
- package/src/test/TransformBindingStructs.test.ts +230 -0
- package/src/test/Util.test.ts +22 -0
- package/src/test/VirtualModules.test.ts +37 -0
- package/src/test/WeslDevice.test.ts +265 -0
- package/src/test/WgslTests.ts +0 -0
- package/src/test/__snapshots__/ParseDirectives.test.ts.snap +25 -0
- package/src/test/__snapshots__/ParseWESL.test.ts.snap +119 -0
- package/src/test/__snapshots__/RustDirective.test.ts.snap +359 -0
- package/src/test/wgsl_1/main.wgsl +3 -0
- package/src/test/wgsl_1/util.wgsl +1 -0
- package/src/test/wgsl_2/main2.wgsl +3 -0
- package/src/test/wgsl_2/util2.wgsl +1 -0
- package/src/vlq/vlq.ts +94 -0
package/src/Scope.ts
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { DeclarationElem, IfAttribute, RefIdentElem } from "./AbstractElems.ts";
|
|
2
|
+
import { assertThatDebug } from "./Assertions.ts";
|
|
3
|
+
import { scopeValid } from "./Conditions.ts";
|
|
4
|
+
import { WeslAST } from "./ParseWESL.ts";
|
|
5
|
+
|
|
6
|
+
export interface SrcModule {
|
|
7
|
+
/** module path "rand_pkg::sub::foo", or "package::main" */
|
|
8
|
+
modulePath: string;
|
|
9
|
+
|
|
10
|
+
/** file path to the module for user error reporting e.g "rand_pkg:sub/foo.wesl", or "./sub/foo.wesl" */
|
|
11
|
+
debugFilePath: string;
|
|
12
|
+
|
|
13
|
+
/** original src for module */
|
|
14
|
+
src: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** a src declaration or reference to an ident */
|
|
18
|
+
export type Ident = DeclIdent | RefIdent;
|
|
19
|
+
|
|
20
|
+
/** LATER change this to a Map, so that `toString` isn't accidentally a condition */
|
|
21
|
+
export type Conditions = Record<string, boolean>;
|
|
22
|
+
|
|
23
|
+
interface IdentBase {
|
|
24
|
+
originalName: string; // name in the source code for ident matching (may be mangled in the output)
|
|
25
|
+
id?: number; // for debugging
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface RefIdent extends IdentBase {
|
|
29
|
+
kind: "ref";
|
|
30
|
+
|
|
31
|
+
// LATER these fields are set during binding, not parsing. Make a naming scheme _refersTo or a separate interface (BindingRefIdent) to make that clear
|
|
32
|
+
refersTo?: Ident; // import or decl ident in scope to which this ident refers. undefined before binding
|
|
33
|
+
std?: true; // true if this is a standard wgsl identifier (like sin, or u32)
|
|
34
|
+
|
|
35
|
+
// TODO consider tracking the current ast in BindIdents so that this field is unnecessary
|
|
36
|
+
ast: WeslAST; // AST from module that contains this ident (to find imports during decl binding)
|
|
37
|
+
|
|
38
|
+
refIdentElem: RefIdentElem; // for error reporting and mangling
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface DeclIdent extends IdentBase {
|
|
42
|
+
kind: "decl";
|
|
43
|
+
mangledName?: string; // name in the output code
|
|
44
|
+
declElem?: DeclarationElem; // link to AST so that we can traverse scopes and know what elems to emit // LATER make separate GlobalDecl kind with this required
|
|
45
|
+
scope: Scope; // scope for the references within this declaration
|
|
46
|
+
isGlobal: boolean; // true if this is a global declaration (e.g. not a local variable)
|
|
47
|
+
srcModule: SrcModule; // To figure out which module this declaration is from.
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** tree of ident references, organized by lexical scope and partialScope . */
|
|
51
|
+
export type Scope = LexicalScope | PartialScope;
|
|
52
|
+
|
|
53
|
+
/** A wgsl scope */
|
|
54
|
+
export interface LexicalScope extends ScopeBase {
|
|
55
|
+
kind: "scope";
|
|
56
|
+
|
|
57
|
+
/** @if condition for conditionally translating this scope */
|
|
58
|
+
ifAttribute?: IfAttribute;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Efficient access to declarations in this scope.
|
|
62
|
+
* constructed on demand, for module root scopes only */ // LATER consider make a special kind for root scopes
|
|
63
|
+
scopeDecls?: Map<string, DeclIdent>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** A synthetic partial scope to contain @if conditioned idents.
|
|
67
|
+
* PartialScope idents are considered to be in the wgsl lexical scope of their parent. */
|
|
68
|
+
export interface PartialScope extends ScopeBase {
|
|
69
|
+
kind: "partial";
|
|
70
|
+
|
|
71
|
+
/** @if condition for conditionally translating this scope */
|
|
72
|
+
ifAttribute?: IfAttribute; // LATER this is required, consider changing type to reflect that
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** common scope elements */
|
|
76
|
+
interface ScopeBase {
|
|
77
|
+
/** id for debugging */
|
|
78
|
+
id: number;
|
|
79
|
+
|
|
80
|
+
/** null for root scope in a module */
|
|
81
|
+
parent: Scope | null;
|
|
82
|
+
|
|
83
|
+
/* Child scopes and idents in lexical order */
|
|
84
|
+
contents: (Ident | Scope)[];
|
|
85
|
+
|
|
86
|
+
/** @if conditions for conditionally translating this scope */
|
|
87
|
+
ifAttribute?: IfAttribute;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Combine two scope siblings.
|
|
91
|
+
* The first scope is mutated to append the contents of the second. */
|
|
92
|
+
export function mergeScope(a: Scope, b: Scope): void {
|
|
93
|
+
assertThatDebug(a.kind === b.kind);
|
|
94
|
+
assertThatDebug(a.parent === b.parent);
|
|
95
|
+
assertThatDebug(!b.ifAttribute);
|
|
96
|
+
a.contents = a.contents.concat(b.contents);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** reset scope and ident debugging ids */
|
|
100
|
+
export function resetScopeIds() {
|
|
101
|
+
scopeId = 0;
|
|
102
|
+
identId = 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let scopeId = 0;
|
|
106
|
+
let identId = 0;
|
|
107
|
+
|
|
108
|
+
export function nextIdentId(): number {
|
|
109
|
+
return identId++;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** make a new Scope object */
|
|
113
|
+
export function emptyScope(
|
|
114
|
+
parent: Scope | null,
|
|
115
|
+
kind: Scope["kind"] = "scope",
|
|
116
|
+
): Omit<Scope, "ifAttribute"> {
|
|
117
|
+
const id = scopeId++;
|
|
118
|
+
return { id, kind, parent, contents: [] };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** For debugging,
|
|
122
|
+
* @return true if a scope is in the rootScope tree somewhere */
|
|
123
|
+
export function containsScope(rootScope: Scope, scope: Scope): boolean {
|
|
124
|
+
if (scope === rootScope) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
for (const child of rootScope.contents) {
|
|
128
|
+
if (childScope(child) && containsScope(child, scope)) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** @returns true if the provided element of a Scope
|
|
136
|
+
* is itself a Scope (and not an Ident) */
|
|
137
|
+
export function childScope(child: Scope | Ident): child is Scope {
|
|
138
|
+
const { kind } = child;
|
|
139
|
+
return kind === "partial" || kind === "scope";
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** @returns true if the provided element of a Scope
|
|
143
|
+
* is an Ident (and not a child Scope) */
|
|
144
|
+
export function childIdent(child: Scope | Ident): child is Ident {
|
|
145
|
+
return !childScope(child);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** find a public declaration with the given original name */
|
|
149
|
+
export function publicDecl(
|
|
150
|
+
scope: Scope,
|
|
151
|
+
name: string,
|
|
152
|
+
conditions: Conditions,
|
|
153
|
+
): DeclIdent | undefined {
|
|
154
|
+
for (const elem of scope.contents) {
|
|
155
|
+
if (elem.kind === "decl" && elem.originalName === name) {
|
|
156
|
+
return elem;
|
|
157
|
+
} else if (elem.kind === "partial" && scopeValid(elem, conditions)) {
|
|
158
|
+
const found = publicDecl(elem, name, conditions);
|
|
159
|
+
if (found) return found;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// From https://www.w3.org/TR/WGSL/#predeclared
|
|
2
|
+
// Use https://github.com/wgsl-tooling-wg/wgsl-spec to regenerate these list in the future
|
|
3
|
+
|
|
4
|
+
export const stdFns = `bitcast all any select arrayLength
|
|
5
|
+
abs acos acosh asin asinh atan atanh atan2 ceil clamp cos cosh
|
|
6
|
+
countLeadingZeros countOneBits countTrailingZeros cross
|
|
7
|
+
degrees determinant distance dot dot4U8Packed dot4I8Packed
|
|
8
|
+
exp exp2 extractBits faceForward firstLeadingBit firstTrailingBit
|
|
9
|
+
floor fma fract frexp insertBits inverseSqrt ldexp length log log2
|
|
10
|
+
max min mix modf normalize pow quantizeToF16 radians reflect refract
|
|
11
|
+
reverseBits round saturate sign sin sinh smoothstep sqrt step tan tanh
|
|
12
|
+
transpose trunc
|
|
13
|
+
dpdx dpdxCoarse dpdxFine dpdy dpdyCoarse dpdyFine fwidth
|
|
14
|
+
fwidthCoarse fwidthFine
|
|
15
|
+
textureDimensions textureGather textureGatherCompare textureLoad
|
|
16
|
+
textureNumLayers textureNumLevels textureNumSamples
|
|
17
|
+
textureSample textureSampleBias textureSampleCompare textureSampleCompareLevel
|
|
18
|
+
textureSampleGrad textureSampleLevel textureSampleBaseClampToEdge
|
|
19
|
+
textureStore
|
|
20
|
+
atomicLoad atomicStore atomicAdd atomicSub atomicMax atomicMin
|
|
21
|
+
atomicAnd atomicOr atomicXor atomicExchange atomicCompareExchangeWeak
|
|
22
|
+
pack4x8snorm pack4x8unorm pack4xI8 pack4xU8 pack4xI8Clamp pack4xU8Clamp
|
|
23
|
+
pack2x16snorm pack2x16unorm pack2x16float
|
|
24
|
+
unpack4x8snorm unpack4x8unorm unpack4xI8 unpack4xU8
|
|
25
|
+
unpack2x16snorm unpack2x16unorm unpack2x16float
|
|
26
|
+
storageBarrier textureBarrier workgroupBarrier workgroupUniformLoad
|
|
27
|
+
subgroupAdd subgroupAll subgroupAnd subgroupAny subgroupBallot
|
|
28
|
+
subgroupBroadcast subgroupBroadcastFirst subgroupElect
|
|
29
|
+
subgroupExclusiveAdd subgroupExclusiveMul subgroupInclusiveAdd
|
|
30
|
+
subgroupInclusiveMul subgroupMax subgroupMin subgroupMul subgroupOr
|
|
31
|
+
subgroupShuffle subgroupShuffleUp subgroupShuffleXor subgroupXor
|
|
32
|
+
quadBroadcast quadSwapDiagonal quadSwapX quadSwapY`.split(/\s+/);
|
|
33
|
+
|
|
34
|
+
export const sampledTextureTypes = `
|
|
35
|
+
texture_1d texture_2d texture_2d_array texture_3d
|
|
36
|
+
texture_cube texture_cube_array
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
export const multisampledTextureTypes = `
|
|
40
|
+
texture_multisampled_2d texture_depth_multisampled_2d
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
export const textureStorageTypes = `
|
|
44
|
+
texture_storage_1d texture_storage_2d texture_storage_2d_array
|
|
45
|
+
texture_storage_3d
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
export const stdTypes = `array atomic bool f16 f32 i32
|
|
49
|
+
mat2x2 mat2x3 mat2x4 mat3x2 mat3x3 mat3x4 mat4x2 mat4x3 mat4x4
|
|
50
|
+
mat2x2f mat2x3f mat2x4f mat3x2f mat3x3f mat3x4f
|
|
51
|
+
mat4x2f mat4x3f mat4x4f
|
|
52
|
+
mat2x2h mat2x3h mat2x4h mat3x2h mat3x3h mat3x4h
|
|
53
|
+
mat4x2h mat4x3h mat4x4h
|
|
54
|
+
u32 vec2 vec3 vec4 ptr
|
|
55
|
+
vec2i vec3i vec4i vec2u vec3u vec4u
|
|
56
|
+
vec2f vec3f vec4f vec2h vec3h vec4h
|
|
57
|
+
${sampledTextureTypes}
|
|
58
|
+
${multisampledTextureTypes}
|
|
59
|
+
texture_external
|
|
60
|
+
${textureStorageTypes}
|
|
61
|
+
texture_depth_2d texture_depth_2d_array texture_depth_cube
|
|
62
|
+
texture_depth_cube_array
|
|
63
|
+
sampler sampler_comparison
|
|
64
|
+
rgba8unorm rgba8snorm rgba8uint rgba8sint
|
|
65
|
+
rgba16uint rgba16sint rgba16float
|
|
66
|
+
r32uint r32sint r32float rg32uint rg32sint rg32float
|
|
67
|
+
rgba32uint rgba32sint rgba32float
|
|
68
|
+
bgra8unorm`.split(/\s+/);
|
|
69
|
+
|
|
70
|
+
/** https://www.w3.org/TR/WGSL/#predeclared-enumerants */
|
|
71
|
+
export const stdEnumerants = `read write read_write
|
|
72
|
+
function private workgroup uniform storage
|
|
73
|
+
rgba8unorm rgba8snorm rgba8uint rgba8sint
|
|
74
|
+
rgba16uint rgba16sint rgba16float
|
|
75
|
+
r32uint r32sint r32float rg32uint rg32sint rg32float
|
|
76
|
+
rgba32uint rgba32sint rgba32float bgra8unorm`.split(/\s+/);
|
|
77
|
+
|
|
78
|
+
/* Note the texel formats like rgba8unorm are here because they appear in type position
|
|
79
|
+
in <templates> for texture_storage_* types.
|
|
80
|
+
(We could parse texture_storage types specially, but user code is unlikely to alias
|
|
81
|
+
the texture format names with e.g. a 'struct rbga8unorm .)
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
/** return true if the name is for a built in type (not a user struct) */
|
|
85
|
+
export function stdType(name: string): boolean {
|
|
86
|
+
return stdTypes.includes(name);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** return true if the name is for a built in fn (not a user function) */
|
|
90
|
+
export function stdFn(name: string): boolean {
|
|
91
|
+
return stdFns.includes(name) || stdType(name);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** return true if the name is for a built in enumerant */
|
|
95
|
+
export function stdEnumerant(name: string): boolean {
|
|
96
|
+
return stdEnumerants.includes(name);
|
|
97
|
+
}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { tracing } from "mini-parse";
|
|
2
|
+
import {
|
|
3
|
+
AbstractElem,
|
|
4
|
+
AttributeElem,
|
|
5
|
+
BindingStructElem,
|
|
6
|
+
DeclarationElem,
|
|
7
|
+
FnElem,
|
|
8
|
+
ModuleElem,
|
|
9
|
+
SimpleMemberRef,
|
|
10
|
+
StructElem,
|
|
11
|
+
StructMemberElem,
|
|
12
|
+
SyntheticElem,
|
|
13
|
+
TypeTemplateParameter,
|
|
14
|
+
} from "./AbstractElems.ts";
|
|
15
|
+
import { TransformedAST, WeslJsPlugin } from "./Linker.ts";
|
|
16
|
+
import { visitAst } from "./LinkerUtil.ts";
|
|
17
|
+
import { findDecl } from "./LowerAndEmit.ts";
|
|
18
|
+
import { minimallyMangledName } from "./Mangler.ts";
|
|
19
|
+
import {
|
|
20
|
+
attributeToString,
|
|
21
|
+
contentsToString,
|
|
22
|
+
typeListToString,
|
|
23
|
+
typeParamToString,
|
|
24
|
+
} from "./RawEmit.ts";
|
|
25
|
+
import { textureStorage } from "./Reflection.ts";
|
|
26
|
+
import { DeclIdent, RefIdent } from "./Scope.ts";
|
|
27
|
+
import { filterMap } from "./Util.ts";
|
|
28
|
+
|
|
29
|
+
export function bindingStructsPlugin(): WeslJsPlugin {
|
|
30
|
+
return {
|
|
31
|
+
transform: lowerBindingStructs,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Transform binding structures into binding variables by mutating the AST.
|
|
37
|
+
*
|
|
38
|
+
* First we find all the binding structs:
|
|
39
|
+
* . find all the structs in the module by filtering the moduleElem.contents
|
|
40
|
+
* . for each struct:
|
|
41
|
+
* . mark any structs with that contain @group or @binding annotations as 'binding structs' and save them in a list
|
|
42
|
+
* . (later) create reverse links from structs to struct members
|
|
43
|
+
* . (later) visit all the binding structs and traverse to referencing structs, marking the referencing structs as binding structs too
|
|
44
|
+
* Generate synethic AST nodes for binding variables
|
|
45
|
+
*
|
|
46
|
+
* Find all references to binding struct members
|
|
47
|
+
* . find the componound idents by traversing moduleElem.contents
|
|
48
|
+
* . filter to find the compound idents that refer to 'binding structs'
|
|
49
|
+
* . go from each ident to its declaration,
|
|
50
|
+
* . declaration to typeRef reference
|
|
51
|
+
* . typeRef to type declaration
|
|
52
|
+
* . check type declaration to see if it's a binding struct
|
|
53
|
+
* . record the intermediate declaration (e.g. a fn param b:Bindings from 'fn(b:Bindings)' )
|
|
54
|
+
* rewrite references to binding struct members as synthetic elements
|
|
55
|
+
*
|
|
56
|
+
* Remove the binding structs from the AST
|
|
57
|
+
* Remove the intermediate fn param declarations from the AST
|
|
58
|
+
* Add the new binding variables to the AST
|
|
59
|
+
*
|
|
60
|
+
* @return the binding structs and the mutated AST
|
|
61
|
+
*/
|
|
62
|
+
export function lowerBindingStructs(ast: TransformedAST): TransformedAST {
|
|
63
|
+
const clonedAst = structuredClone(ast);
|
|
64
|
+
const { moduleElem, globalNames, notableElems } = clonedAst;
|
|
65
|
+
const bindingStructs = markBindingStructs(moduleElem); // CONSIDER should we only mark bining structs referenced from the entry point?
|
|
66
|
+
markEntryTypes(moduleElem, bindingStructs);
|
|
67
|
+
const newVars = bindingStructs.flatMap(s =>
|
|
68
|
+
transformBindingStruct(s, globalNames),
|
|
69
|
+
);
|
|
70
|
+
const bindingRefs = findRefsToBindingStructs(moduleElem);
|
|
71
|
+
|
|
72
|
+
// convert references 'b.particles' to references to the synthetic var 'particles'
|
|
73
|
+
bindingRefs.forEach(({ memberRef, struct }) =>
|
|
74
|
+
transformBindingReference(memberRef, struct),
|
|
75
|
+
);
|
|
76
|
+
// remove intermediate fn param declaration b:Bindings from 'fn(b:Bindings)'
|
|
77
|
+
bindingRefs.forEach(({ intermediates }) =>
|
|
78
|
+
intermediates.forEach(e => (e.contents = [])),
|
|
79
|
+
);
|
|
80
|
+
const contents = removeBindingStructs(moduleElem);
|
|
81
|
+
moduleElem.contents = [...newVars, ...contents];
|
|
82
|
+
notableElems.bindingStructs = bindingStructs;
|
|
83
|
+
return { ...clonedAst, moduleElem };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function markEntryTypes(
|
|
87
|
+
moduleElem: ModuleElem,
|
|
88
|
+
bindingStructs: BindingStructElem[],
|
|
89
|
+
): void {
|
|
90
|
+
const fns = moduleElem.contents.filter(e => e.kind === "fn");
|
|
91
|
+
const fnFound = fnReferencesBindingStruct(fns, bindingStructs);
|
|
92
|
+
if (fnFound) {
|
|
93
|
+
const { fn, struct } = fnFound;
|
|
94
|
+
struct.entryFn = fn;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function fnReferencesBindingStruct(
|
|
99
|
+
fns: FnElem[],
|
|
100
|
+
bindingStructs: BindingStructElem[],
|
|
101
|
+
): { fn: FnElem; struct: BindingStructElem } | undefined {
|
|
102
|
+
for (const fn of fns) {
|
|
103
|
+
const { params } = fn;
|
|
104
|
+
for (const p of params) {
|
|
105
|
+
const ref = p.name?.typeRef?.name as RefIdent | undefined;
|
|
106
|
+
const referencedElem = (ref?.refersTo as DeclIdent)
|
|
107
|
+
?.declElem as StructElem;
|
|
108
|
+
const struct = bindingStructs.find(s => s === referencedElem);
|
|
109
|
+
if (struct) {
|
|
110
|
+
return { fn, struct };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function removeBindingStructs(moduleElem: ModuleElem): AbstractElem[] {
|
|
117
|
+
return moduleElem.contents.filter(
|
|
118
|
+
elem => elem.kind !== "struct" || !elem.bindingStruct,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** mutate the AST, marking StructElems as bindingStructs
|
|
123
|
+
* (if they contain ptrs with @group @binding annotations)
|
|
124
|
+
* @return the binding structs
|
|
125
|
+
*/
|
|
126
|
+
export function markBindingStructs(
|
|
127
|
+
moduleElem: ModuleElem,
|
|
128
|
+
): BindingStructElem[] {
|
|
129
|
+
const structs = moduleElem.contents.filter(elem => elem.kind === "struct");
|
|
130
|
+
const bindingStructs = structs.filter(containsBinding);
|
|
131
|
+
bindingStructs.forEach(struct => (struct.bindingStruct = true));
|
|
132
|
+
// LATER also mark structs that reference a binding struct..
|
|
133
|
+
return bindingStructs as BindingStructElem[];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** @return true if this struct contains a member with marked with @binding or @group */
|
|
137
|
+
function containsBinding(struct: StructElem): boolean {
|
|
138
|
+
return struct.members.some(({ attributes }) => bindingAttribute(attributes));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function bindingAttribute(attributes?: AttributeElem[]): boolean {
|
|
142
|
+
if (!attributes) return false;
|
|
143
|
+
return attributes.some(
|
|
144
|
+
({ attribute }) =>
|
|
145
|
+
attribute.kind === "@attribute" &&
|
|
146
|
+
(attribute.name === "binding" || attribute.name === "group"),
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** convert each member of the binding struct into a synthetic global variable */
|
|
151
|
+
export function transformBindingStruct(
|
|
152
|
+
s: StructElem,
|
|
153
|
+
globalNames: Set<string>,
|
|
154
|
+
): SyntheticElem[] {
|
|
155
|
+
return s.members.map(member => {
|
|
156
|
+
const { typeRef, name: memberName } = member;
|
|
157
|
+
const { name: typeName } = typeRef!; // members should always have a typeRef.. TODO fix typing to show this
|
|
158
|
+
const typeParameters = typeRef?.templateParams;
|
|
159
|
+
|
|
160
|
+
const varName = minimallyMangledName(memberName.name, globalNames);
|
|
161
|
+
member.mangledVarName = varName; // save new name so we can rewrite references to this member later
|
|
162
|
+
globalNames.add(varName);
|
|
163
|
+
|
|
164
|
+
const attributes =
|
|
165
|
+
member.attributes?.map(attributeToString).join(" ") ?? "";
|
|
166
|
+
const varTypes =
|
|
167
|
+
lowerPtrMember(member, typeName, typeParameters, varName) ??
|
|
168
|
+
lowerStdTypeMember(typeName, typeParameters) ??
|
|
169
|
+
lowerStorageTextureMember(typeName, typeParameters);
|
|
170
|
+
if (!varTypes) {
|
|
171
|
+
console.log("unhandled case transforming member", typeName);
|
|
172
|
+
return syntheticVar(attributes, varName, "", "??");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const { storage: storageType, varType } = varTypes;
|
|
176
|
+
return syntheticVar(attributes, varName, storageType, varType);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
interface LoweredVarTypes {
|
|
181
|
+
storage: string;
|
|
182
|
+
varType: string;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function lowerPtrMember(
|
|
186
|
+
member: StructMemberElem,
|
|
187
|
+
typeName: RefIdent,
|
|
188
|
+
typeParameters: TypeTemplateParameter[] | undefined,
|
|
189
|
+
varName: string,
|
|
190
|
+
): LoweredVarTypes | undefined {
|
|
191
|
+
if (typeName.originalName === "ptr") {
|
|
192
|
+
const origParams = typeParameters ?? [];
|
|
193
|
+
const newParams = [origParams[0]];
|
|
194
|
+
if (origParams[2]) newParams.push(origParams[2]);
|
|
195
|
+
const storage = typeListToString(newParams);
|
|
196
|
+
|
|
197
|
+
const varType = typeParamToString(origParams?.[1]);
|
|
198
|
+
return { storage, varType };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function lowerStdTypeMember(
|
|
203
|
+
typeName: RefIdent,
|
|
204
|
+
typeParameters: TypeTemplateParameter[] | undefined,
|
|
205
|
+
): LoweredVarTypes | undefined {
|
|
206
|
+
if (typeof typeName !== "string") {
|
|
207
|
+
const varBaseType = typeName.std ? typeName.originalName : "??";
|
|
208
|
+
const params = typeParameters ? typeListToString(typeParameters) : "";
|
|
209
|
+
const varType = varBaseType + params;
|
|
210
|
+
|
|
211
|
+
return { varType, storage: "" };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function lowerStorageTextureMember(
|
|
216
|
+
typeName: RefIdent,
|
|
217
|
+
typeParameters: TypeTemplateParameter[] | undefined,
|
|
218
|
+
): LoweredVarTypes | undefined {
|
|
219
|
+
if (textureStorage.test(typeName.originalName)) {
|
|
220
|
+
const params = typeParameters ? typeListToString(typeParameters) : "";
|
|
221
|
+
const varType = typeName + params;
|
|
222
|
+
return { varType, storage: "" };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function syntheticVar(
|
|
227
|
+
attributes: string,
|
|
228
|
+
varName: string,
|
|
229
|
+
storageTemplate: string,
|
|
230
|
+
varType: string,
|
|
231
|
+
): SyntheticElem {
|
|
232
|
+
const varText = `${attributes} var${storageTemplate} ${varName} : ${varType};\n`;
|
|
233
|
+
|
|
234
|
+
const elem: SyntheticElem = {
|
|
235
|
+
kind: "synthetic",
|
|
236
|
+
text: varText,
|
|
237
|
+
};
|
|
238
|
+
return elem;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
interface MemberRefToStruct extends StructTrace {
|
|
242
|
+
memberRef: SimpleMemberRef; // e.g. the memberRef 'b.particles'
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
interface StructTrace {
|
|
246
|
+
struct: StructElem; // e.g. the struct Bindings
|
|
247
|
+
intermediates: DeclarationElem[]; // e.g. the fn param b:Bindings from 'fn(b:Bindings)'
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** find all simple member references in the module that refer to binding structs */
|
|
251
|
+
export function findRefsToBindingStructs(
|
|
252
|
+
moduleElem: ModuleElem,
|
|
253
|
+
): MemberRefToStruct[] {
|
|
254
|
+
const members: SimpleMemberRef[] = [];
|
|
255
|
+
visitAst(moduleElem, elem => {
|
|
256
|
+
if (elem.kind === "memberRef") members.push(elem);
|
|
257
|
+
});
|
|
258
|
+
return filterMap(members, refersToBindingStruct);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** @return true if this memberRef refers to a binding struct */
|
|
262
|
+
function refersToBindingStruct(
|
|
263
|
+
memberRef: SimpleMemberRef,
|
|
264
|
+
): MemberRefToStruct | undefined {
|
|
265
|
+
const found = traceToStruct(memberRef.name.ident);
|
|
266
|
+
|
|
267
|
+
if (found && found.struct.bindingStruct) {
|
|
268
|
+
return { memberRef, ...found };
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** If this identifier ultimately refers to a struct type, return the struct declaration */
|
|
273
|
+
function traceToStruct(ident: RefIdent): StructTrace | undefined {
|
|
274
|
+
const decl = findDecl(ident);
|
|
275
|
+
const declElem = decl.declElem;
|
|
276
|
+
// for now only handle the case where the reference points at a fn parameter
|
|
277
|
+
if (declElem && declElem.kind === "param") {
|
|
278
|
+
const name = declElem.name.typeRef!.name;
|
|
279
|
+
if (typeof name !== "string") {
|
|
280
|
+
if (name.std) {
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const paramDecl = findDecl(name);
|
|
285
|
+
const structElem = paramDecl.declElem;
|
|
286
|
+
if (structElem?.kind === "struct") {
|
|
287
|
+
return { struct: structElem, intermediates: [declElem] };
|
|
288
|
+
}
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
} else {
|
|
292
|
+
// LATER presumably handle other cases? Should this be more general, e.g. traceToType()?
|
|
293
|
+
// elemLog(
|
|
294
|
+
// ident.refIdentElem!,
|
|
295
|
+
// `unhandled case in traceToStruct: decl ${declElem.kind} not yet implemented`,
|
|
296
|
+
// );
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** Mutate the member reference elem to instead contain synthetic elem text.
|
|
301
|
+
* The new text is the mangled var name of the struct member that the memberRef refers to. */
|
|
302
|
+
export function transformBindingReference(
|
|
303
|
+
memberRef: SimpleMemberRef,
|
|
304
|
+
struct: StructElem,
|
|
305
|
+
): SyntheticElem {
|
|
306
|
+
const refName = memberRef.member.name;
|
|
307
|
+
const structMember = struct.members.find(m => m.name.name === refName)!;
|
|
308
|
+
if (!structMember || !structMember.mangledVarName) {
|
|
309
|
+
if (tracing) console.log(`missing mangledVarName for ${refName}`);
|
|
310
|
+
return { kind: "synthetic", text: refName };
|
|
311
|
+
}
|
|
312
|
+
const { extraComponents } = memberRef;
|
|
313
|
+
const extraText = extraComponents ? contentsToString(extraComponents) : "";
|
|
314
|
+
|
|
315
|
+
const text = structMember.mangledVarName + extraText;
|
|
316
|
+
const synthElem: SyntheticElem = { kind: "synthetic", text };
|
|
317
|
+
memberRef.contents = [synthElem];
|
|
318
|
+
return synthElem;
|
|
319
|
+
}
|