wesl 0.7.26 → 0.7.28

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.
Files changed (67) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +283 -147
  3. package/dist/index.js +1765 -1143
  4. package/package.json +2 -2
  5. package/src/AbstractElems.ts +264 -82
  6. package/src/ClickableError.ts +8 -1
  7. package/src/Linker.ts +63 -67
  8. package/src/LinkerUtil.ts +141 -7
  9. package/src/LowerAndEmit.ts +660 -304
  10. package/src/Mangler.ts +1 -1
  11. package/src/ModuleResolver.ts +15 -4
  12. package/src/ParseWESL.ts +21 -33
  13. package/src/SrcMap.ts +7 -19
  14. package/src/StandardTypes.ts +1 -1
  15. package/src/WeslDevice.ts +1 -0
  16. package/src/debug/ASTtoString.ts +92 -76
  17. package/src/index.ts +1 -1
  18. package/src/parse/AttachComments.ts +289 -0
  19. package/src/parse/ExpressionUtil.ts +3 -3
  20. package/src/parse/Keywords.ts +1 -1
  21. package/src/parse/ParseAttribute.ts +49 -71
  22. package/src/parse/ParseCall.ts +3 -4
  23. package/src/parse/ParseControlFlow.ts +100 -56
  24. package/src/parse/ParseDirective.ts +9 -8
  25. package/src/parse/ParseDoBlock.ts +63 -0
  26. package/src/parse/ParseExpression.ts +11 -10
  27. package/src/parse/ParseFn.ts +11 -33
  28. package/src/parse/ParseGlobalVar.ts +36 -27
  29. package/src/parse/ParseIdent.ts +4 -5
  30. package/src/parse/ParseLocalVar.ts +16 -12
  31. package/src/parse/ParseLoop.ts +76 -39
  32. package/src/parse/ParseModule.ts +65 -19
  33. package/src/parse/ParseSimpleStatement.ts +112 -66
  34. package/src/parse/ParseStatement.ts +40 -67
  35. package/src/parse/ParseStruct.ts +8 -22
  36. package/src/parse/ParseType.ts +10 -13
  37. package/src/parse/ParseUtil.ts +26 -12
  38. package/src/parse/ParseValueDeclaration.ts +18 -31
  39. package/src/parse/ParseWesl.ts +11 -14
  40. package/src/parse/ParsingContext.ts +11 -9
  41. package/src/parse/WeslStream.ts +138 -121
  42. package/src/parse/stream/RegexMatchers.ts +45 -0
  43. package/src/parse/stream/WeslLexer.ts +249 -0
  44. package/src/test/BevyLink.test.ts +18 -11
  45. package/src/test/ConditionalElif.test.ts +4 -2
  46. package/src/test/DeclCommentEmit.test.ts +122 -0
  47. package/src/test/DoBlock.test.ts +164 -0
  48. package/src/test/FilterValidElements.test.ts +4 -6
  49. package/src/test/Linker.test.ts +23 -7
  50. package/src/test/Mangling.test.ts +8 -4
  51. package/src/test/ParseComments.test.ts +234 -6
  52. package/src/test/ParseConditionsV2.test.ts +47 -175
  53. package/src/test/ParseElifV2.test.ts +12 -33
  54. package/src/test/ParseErrorV2.test.ts +0 -0
  55. package/src/test/ParseWeslV2.test.ts +247 -626
  56. package/src/test/StatementEmit.test.ts +143 -0
  57. package/src/test/StripWesl.ts +24 -3
  58. package/src/test/TestLink.ts +19 -3
  59. package/src/test/TestUtil.ts +18 -8
  60. package/src/test/Tokenizer.test.ts +96 -0
  61. package/src/test/__snapshots__/ParseWeslV2.test.ts.snap +10 -42
  62. package/src/RawEmit.ts +0 -103
  63. package/src/Reflection.ts +0 -336
  64. package/src/TransformBindingStructs.ts +0 -320
  65. package/src/parse/ContentsHelpers.ts +0 -70
  66. package/src/parse/stream/CachingStream.ts +0 -48
  67. package/src/parse/stream/MatchersStream.ts +0 -85
package/src/Reflection.ts DELETED
@@ -1,336 +0,0 @@
1
- import type {
2
- BindingStructElem,
3
- NameElem,
4
- StructMemberElem,
5
- TextElem,
6
- TranslateTimeExpressionElem,
7
- TypeRefElem,
8
- UnknownExpressionElem,
9
- } from "./AbstractElems.ts";
10
- import { assertThat } from "./Assertions.ts";
11
- import type { TransformedAST, WeslJsPlugin } from "./Linker.ts";
12
- import { identElemLog } from "./LinkerUtil.ts";
13
- import { matchOneOf } from "./parse/stream/RegexHelpers.ts";
14
- import type { RefIdent } from "./Scope.ts";
15
- import {
16
- multisampledTextureTypes,
17
- sampledTextureTypes,
18
- textureStorageTypes,
19
- } from "./StandardTypes.ts";
20
- import { findMap } from "./Util.ts";
21
-
22
- export type BindingStructReportFn = (structs: BindingStructElem[]) => void;
23
- export const textureStorage = matchOneOf(textureStorageTypes);
24
-
25
- export function reportBindingStructsPlugin(
26
- fn: BindingStructReportFn,
27
- ): WeslJsPlugin {
28
- return {
29
- transform: reportBindingStructs(fn),
30
- };
31
- }
32
- /**
33
- * Linker plugin that generates TypeScript strings for GPUBindingGroupLayouts
34
- * based on the binding structs in the WESL source
35
- *
36
- * requires the enableBindingStructs() transform to be enabled
37
- *
38
- * @param fn a function that will be called with the binding structs
39
- * (Normally the caller will pass a function that uses bindingGroupLayoutTs()
40
- * to generate the TypeScript)
41
- *
42
- * The generated TypeScript looks looks roughly like this
43
-
44
- export function MyBindingLayout(device: GPUDevice): GPUBindGroupLayout {
45
- return device.createBindGroupLayout({
46
- entries: [
47
- {
48
- binding: 0,
49
- visibility: GPUShaderStage.COMPUTE,
50
- buffer: {
51
- type: "storage",
52
- },
53
- },
54
- ],
55
- });
56
- }
57
- */
58
- export function reportBindingStructs(
59
- fn: BindingStructReportFn,
60
- ): (ast: TransformedAST) => TransformedAST {
61
- return (ast: TransformedAST) => {
62
- const structs = ast.notableElems.bindingStructs as BindingStructElem[];
63
- fn(structs);
64
- return ast;
65
- };
66
- }
67
-
68
- function firstLetterLower(s: string): string {
69
- return s[0].toLowerCase() + s.slice(1);
70
- }
71
-
72
- /**
73
- * @return a string containing a generated TypeScript function that creates
74
- * a GPUBindingGroupLayout instance to align with the binding structures
75
- * in wesl source.
76
- */
77
- export function bindingGroupLayoutTs(
78
- struct: BindingStructElem,
79
- typeScript = true,
80
- ): string {
81
- if (!struct) {
82
- console.log("no struct!???");
83
- return "";
84
- }
85
- const structName = firstLetterLower(struct.name.ident.mangledName!);
86
- const visibility = shaderVisiblity(struct);
87
- const entries = struct.members
88
- .map(m => memberToLayoutEntry(m, visibility))
89
- .join(",");
90
-
91
- const fnName = `${structName}Layout`;
92
- const entriesName = `${structName}Entries`;
93
-
94
- const fnParams = typeScript
95
- ? "(device: GPUDevice): GPUBindGroupLayout"
96
- : "(device)";
97
-
98
- const src = `
99
- const ${entriesName} = [ ${entries} ];
100
- function ${fnName}${fnParams} {
101
- return device.createBindGroupLayout({
102
- entries: ${entriesName}
103
- });
104
- }
105
-
106
- export const layoutFunctions = { ${fnName} };
107
- export const layouts = { ${entriesName} };
108
- `;
109
- return src;
110
- }
111
-
112
- /** return the shader stage visibility for a binding struct, based on
113
- * the shader entry function that has the binding struct as a parameter.
114
- *
115
- * The shader entry function is attached to the binding struct
116
- * by the enableBindingStructs() transform.
117
- */
118
- function shaderVisiblity(struct: BindingStructElem): string {
119
- const { entryFn } = struct;
120
- if (!entryFn) {
121
- identElemLog(struct.name, "missing entry function for binding struct");
122
- } else {
123
- const { attributes = [] } = entryFn;
124
- if (
125
- attributes.find(
126
- ({ attribute: a }) => a.kind === "@attribute" && a.name === "compute",
127
- )
128
- ) {
129
- return "GPUShaderStage.COMPUTE";
130
- }
131
- if (
132
- attributes.find(
133
- ({ attribute: a }) => a.kind === "@attribute" && a.name === "vertex",
134
- )
135
- ) {
136
- return "GPUShaderStage.VERTEX";
137
- }
138
- if (
139
- attributes.find(
140
- ({ attribute: a }) => a.kind === "@attribute" && a.name === "fragment",
141
- )
142
- ) {
143
- return "GPUShaderStage.FRAGMENT";
144
- }
145
- }
146
- identElemLog(struct.name, "unknown entry point type for binding struct");
147
- return "GPUShaderStage.COMPUTE";
148
- }
149
-
150
- /**
151
- * @return a GPUBindGroupLayoutEntry corresponding to one member
152
- * of a WESL binding struct.
153
- */
154
- function memberToLayoutEntry(
155
- member: StructMemberElem,
156
- visibility: string,
157
- ): string {
158
- const bindingParam = findMap(member.attributes ?? [], ({ attribute: a }) =>
159
- a.kind === "@attribute" && a.name === "binding" ? a : undefined,
160
- )?.params?.[0];
161
- const binding = bindingParam ? paramText(bindingParam) : "?";
162
-
163
- const src = `
164
- {
165
- binding: ${binding},
166
- visibility: ${visibility},
167
- ${layoutEntry(member)}
168
- }`;
169
- return src;
170
- }
171
-
172
- /** @return the guts of the GPUBindGroupLayoutEntry for this binding struct member.
173
- * ptr references to storage arrays become 'buffer' GPUBufferBindingLayout intances,
174
- * references to WGSL samplers become 'sampler' GPUSamplerBindingLayout instances, etc.
175
- */
176
- function layoutEntry(member: StructMemberElem): string {
177
- const { typeRef } = member;
178
- let entry: string | undefined;
179
- const { name: typeName } = typeRef;
180
- entry = ptrLayoutEntry(typeRef) ?? storageTextureLayoutEntry(typeRef);
181
- if (!entry && typeof typeName !== "string" && typeName.std) {
182
- entry =
183
- samplerLayoutEntry(typeRef) ??
184
- textureLayoutEntry(typeRef) ??
185
- externalTextureLayoutEntry(typeRef);
186
- }
187
- if (!entry) {
188
- console.error("unhandled type", typeName);
189
- entry = "{ }";
190
- }
191
- return entry;
192
- }
193
-
194
- function ptrLayoutEntry(typeRef: TypeRefElem): string | undefined {
195
- if (typeRef.name.originalName === "ptr") {
196
- const param1 = typeRef.templateParams?.[0];
197
- const param3 = typeRef.templateParams?.[2];
198
- if (param1?.kind === "type" && param1.name.originalName === "uniform") {
199
- return `buffer: { type: "uniform" }`;
200
- } else if (
201
- param1?.kind === "type" &&
202
- param1.name.originalName === "storage"
203
- ) {
204
- if (param3?.kind === "type" && param3.name.originalName === "read") {
205
- return `buffer: { type: "read-only-storage" }`;
206
- } else {
207
- return `buffer: { type: "storage" }`;
208
- }
209
- // LATER what do we do with the element type (2nd parameter)
210
- // LATER should there be an ability to set hasDynamicOffset?
211
- }
212
- }
213
- }
214
-
215
- function samplerLayoutEntry(typeRef: TypeRefElem): string | undefined {
216
- const { originalName } = typeRef.name as RefIdent;
217
- if (originalName === "sampler") {
218
- // LATER how do we set: type GPUSamplerBindingType = | "filtering" | "non-filtering";
219
- // (just assuming filtering as a placeholder for now)
220
- return `sampler: { type: "filtering" }`;
221
- }
222
- if (originalName === "sampler_comparison") {
223
- return `sampler: { type: "comparison" }`;
224
- }
225
- }
226
-
227
- const textureTypes = matchOneOf(sampledTextureTypes);
228
- const multiNames = matchOneOf(multisampledTextureTypes);
229
-
230
- function textureLayoutEntry(typeRef: TypeRefElem): string | undefined {
231
- const { originalName } = typeRef.name as RefIdent;
232
- const multisampled = multiNames.test(originalName)
233
- ? ", multisampled: true, "
234
- : "";
235
- if (multisampled || textureTypes.test(originalName)) {
236
- // LATER viewDimension
237
- const sampleType = getSampleType(typeRef);
238
- return `texture: { sampleType: "${sampleType}"${multisampled} }`;
239
- }
240
- return undefined;
241
-
242
- function getSampleType(typeRef: TypeRefElem): GPUTextureSampleType {
243
- const firstParam = typeRef.templateParams?.[0] as TypeRefElem;
244
- const texelType = (firstParam.name as RefIdent)
245
- .originalName as WgslTexelType;
246
- const sampleType = texelTypeToSampleType(texelType);
247
- return sampleType;
248
- }
249
- }
250
-
251
- function storageTextureLayoutEntry(typeRef: TypeRefElem): string | undefined {
252
- if (textureStorage.test(typeRef.name.originalName)) {
253
- const firstParam = typeRef.templateParams?.[0];
254
- const secondParam = typeRef.templateParams?.[1];
255
- assertThat(firstParam?.kind === "type"); // LATER: Temp hack
256
- assertThat(secondParam?.kind === "type"); // LATER: Temp hack
257
- const sampleType = formatToTextureSampleType(
258
- firstParam.name.originalName as GPUTextureFormat,
259
- );
260
- const access = accessMode(secondParam.name.originalName);
261
- return `storageTexture: { format: "${firstParam.name.originalName}", sampleType: "${sampleType}", access: "${access}" }`;
262
- }
263
- return undefined;
264
- }
265
-
266
- function externalTextureLayoutEntry(typeRef: TypeRefElem): string | undefined {
267
- const { originalName } = typeRef.name as RefIdent;
268
- if (originalName === "texture_external") {
269
- // LATER. how would we set the required source: HTMLVideoElement or VideoFrame?
270
- }
271
- return undefined;
272
- }
273
-
274
- function paramText(
275
- expression: UnknownExpressionElem | NameElem | TranslateTimeExpressionElem,
276
- ): string {
277
- assertThat(
278
- expression.kind === "expression",
279
- "Only expression elements are supported in this position",
280
- );
281
- const text = expression.contents[0] as TextElem;
282
- return text.srcModule.src.slice(expression.start, expression.end);
283
- }
284
-
285
- export function formatToTextureSampleType(
286
- format: GPUTextureFormat,
287
- float32Filterable = false,
288
- ): GPUTextureSampleType {
289
- if (format.includes("32float")) {
290
- return float32Filterable ? "float" : "unfilterable-float";
291
- }
292
- if (format.includes("float") || format.includes("unorm")) {
293
- return "float";
294
- }
295
- if (format.includes("uint")) {
296
- return "uint";
297
- }
298
- if (format.includes("sint")) {
299
- return "sint";
300
- }
301
- throw new Error(`native sample type unknwon for texture format ${format}`);
302
- }
303
-
304
- export type WgslTexelType = "f32" | "u32" | "i32";
305
-
306
- /** return the wgsl element type for a given texture format */
307
- export function formatToTexelType(format: GPUTextureFormat): WgslTexelType {
308
- if (format.includes("float")) return "f32";
309
- if (format.includes("unorm")) return "f32";
310
- if (format.includes("uint")) return "u32";
311
- if (format.includes("sint")) return "i32";
312
- throw new Error(`unknown format ${format}`);
313
- }
314
-
315
- /** @return the webgpu GPUTextureSampleType from the wgsl texel type */
316
- export function texelTypeToSampleType(
317
- type: WgslTexelType,
318
- ): GPUTextureSampleType {
319
- if (type === "f32") return "float";
320
- if (type === "u32") return "uint";
321
- if (type === "i32") return "sint";
322
- throw new Error(`unknown texel type ${type}`);
323
- }
324
-
325
- export function accessMode(access: string): GPUStorageTextureAccess {
326
- if (access === "read") {
327
- return "read-only";
328
- }
329
- if (access === "write") {
330
- return "write-only";
331
- }
332
- if (access === "read_write") {
333
- return "read-write";
334
- }
335
- throw new Error(`unknown access mode: ${access}`);
336
- }
@@ -1,320 +0,0 @@
1
- import type {
2
- AbstractElem,
3
- AttributeElem,
4
- BindingStructElem,
5
- DeclarationElem,
6
- FnElem,
7
- ModuleElem,
8
- SimpleMemberRef,
9
- StructElem,
10
- SyntheticElem,
11
- TypeTemplateParameter,
12
- } from "./AbstractElems.ts";
13
- import type { TransformedAST, WeslJsPlugin } from "./Linker.ts";
14
- import { visitAst } from "./LinkerUtil.ts";
15
- import { debug } from "./Logging.ts";
16
- import { findDecl } from "./LowerAndEmit.ts";
17
- import { minimallyMangledName } from "./Mangler.ts";
18
- import {
19
- attributeToString,
20
- contentsToString,
21
- typeListToString,
22
- typeParamToString,
23
- } from "./RawEmit.ts";
24
- import { textureStorage } from "./Reflection.ts";
25
- import type { DeclIdent, Ident, RefIdent } from "./Scope.ts";
26
- import { filterMap } from "./Util.ts";
27
-
28
- export function bindingStructsPlugin(): WeslJsPlugin {
29
- return {
30
- transform: lowerBindingStructs,
31
- };
32
- }
33
-
34
- /**
35
- * Transform binding structures into binding variables by mutating the AST.
36
- *
37
- * First we find all the binding structs:
38
- * . find all the structs in the module by filtering the moduleElem.contents
39
- * . for each struct:
40
- * . mark any structs with that contain @group or @binding annotations as 'binding structs' and save them in a list
41
- * . (later) create reverse links from structs to struct members
42
- * . (later) visit all the binding structs and traverse to referencing structs, marking the referencing structs as binding structs too
43
- * Generate synethic AST nodes for binding variables
44
- *
45
- * Find all references to binding struct members
46
- * . find the componound idents by traversing moduleElem.contents
47
- * . filter to find the compound idents that refer to 'binding structs'
48
- * . go from each ident to its declaration,
49
- * . declaration to typeRef reference
50
- * . typeRef to type declaration
51
- * . check type declaration to see if it's a binding struct
52
- * . record the intermediate declaration (e.g. a fn param b:Bindings from 'fn(b:Bindings)' )
53
- * rewrite references to binding struct members as synthetic elements
54
- *
55
- * Remove the binding structs from the AST
56
- * Remove the intermediate fn param declarations from the AST
57
- * Add the new binding variables to the AST
58
- *
59
- * @return the binding structs and the mutated AST
60
- */
61
- export function lowerBindingStructs(ast: TransformedAST): TransformedAST {
62
- const clonedAst = structuredClone(ast);
63
- const { moduleElem, globalNames, notableElems } = clonedAst;
64
- const bindingStructs = markBindingStructs(moduleElem); // CONSIDER should we only mark bining structs referenced from the entry point?
65
- markEntryTypes(moduleElem, bindingStructs);
66
- const newVars = bindingStructs.flatMap(s =>
67
- transformBindingStruct(s, globalNames),
68
- );
69
- const bindingRefs = findRefsToBindingStructs(moduleElem);
70
-
71
- // convert references 'b.particles' to references to the synthetic var 'particles'
72
- bindingRefs.forEach(({ memberRef, struct }) => {
73
- transformBindingReference(memberRef, struct);
74
- });
75
- // remove intermediate fn param declaration b:Bindings from 'fn(b:Bindings)'
76
- bindingRefs.forEach(({ intermediates }) => {
77
- intermediates.forEach(e => {
78
- e.contents = [];
79
- });
80
- });
81
- const contents = removeBindingStructs(moduleElem);
82
- moduleElem.contents = [...newVars, ...contents];
83
- notableElems.bindingStructs = bindingStructs;
84
- return { ...clonedAst, moduleElem };
85
- }
86
-
87
- export function markEntryTypes(
88
- moduleElem: ModuleElem,
89
- bindingStructs: BindingStructElem[],
90
- ): void {
91
- const fns = moduleElem.contents.filter(e => e.kind === "fn");
92
- const fnFound = fnReferencesBindingStruct(fns, bindingStructs);
93
- if (fnFound) {
94
- const { fn, struct } = fnFound;
95
- struct.entryFn = fn;
96
- }
97
- }
98
-
99
- function fnReferencesBindingStruct(
100
- fns: FnElem[],
101
- bindingStructs: BindingStructElem[],
102
- ): { fn: FnElem; struct: BindingStructElem } | undefined {
103
- for (const fn of fns) {
104
- const { params } = fn;
105
- for (const p of params) {
106
- const ref = p.name?.typeRef?.name as RefIdent | undefined;
107
- const referencedElem = (ref?.refersTo as DeclIdent)
108
- ?.declElem as StructElem;
109
- const struct = bindingStructs.find(s => s === referencedElem);
110
- if (struct) {
111
- return { fn, struct };
112
- }
113
- }
114
- }
115
- }
116
-
117
- function removeBindingStructs(moduleElem: ModuleElem): AbstractElem[] {
118
- return moduleElem.contents.filter(
119
- elem => elem.kind !== "struct" || !elem.bindingStruct,
120
- );
121
- }
122
-
123
- /** mutate the AST, marking StructElems as bindingStructs
124
- * (if they contain ptrs with @group @binding annotations)
125
- * @return the binding structs
126
- */
127
- export function markBindingStructs(
128
- moduleElem: ModuleElem,
129
- ): BindingStructElem[] {
130
- const structs = moduleElem.contents.filter(elem => elem.kind === "struct");
131
- const bindingStructs = structs.filter(containsBinding);
132
- bindingStructs.forEach(struct => {
133
- struct.bindingStruct = true;
134
- });
135
- // LATER also mark structs that reference a binding struct..
136
- return bindingStructs as BindingStructElem[];
137
- }
138
-
139
- /** @return true if this struct contains a member with marked with @binding or @group */
140
- function containsBinding(struct: StructElem): boolean {
141
- return struct.members.some(({ attributes }) => bindingAttribute(attributes));
142
- }
143
-
144
- function bindingAttribute(attributes?: AttributeElem[]): boolean {
145
- if (!attributes) return false;
146
- return attributes.some(
147
- ({ attribute }) =>
148
- attribute.kind === "@attribute" &&
149
- (attribute.name === "binding" || attribute.name === "group"),
150
- );
151
- }
152
-
153
- /** convert each member of the binding struct into a synthetic global variable */
154
- export function transformBindingStruct(
155
- s: StructElem,
156
- globalNames: Set<string>,
157
- ): SyntheticElem[] {
158
- return s.members.map(member => {
159
- const { typeRef, name: memberName } = member;
160
- const { name: typeName } = typeRef!; // members should always have a typeRef.. LATER fix typing to show this
161
- const typeParameters = typeRef?.templateParams;
162
-
163
- const varName = minimallyMangledName(memberName.name, globalNames);
164
- member.mangledVarName = varName; // save new name so we can rewrite references to this member later
165
- globalNames.add(varName);
166
-
167
- const attributes =
168
- member.attributes?.map(attributeToString).join(" ") ?? "";
169
- const varTypes =
170
- lowerPtrMember(typeName, typeParameters) ??
171
- lowerStdTypeMember(typeName, typeParameters) ??
172
- lowerStorageTextureMember(typeName, typeParameters);
173
- if (!varTypes) {
174
- console.log("unhandled case transforming member", typeName);
175
- return syntheticVar(attributes, varName, "", "??");
176
- }
177
-
178
- const { storage: storageType, varType } = varTypes;
179
- return syntheticVar(attributes, varName, storageType, varType);
180
- });
181
- }
182
-
183
- interface LoweredVarTypes {
184
- storage: string;
185
- varType: string;
186
- }
187
-
188
- function lowerPtrMember(
189
- typeName: RefIdent,
190
- typeParameters: TypeTemplateParameter[] | undefined,
191
- ): LoweredVarTypes | undefined {
192
- if (typeName.originalName === "ptr") {
193
- const origParams = typeParameters ?? [];
194
- const newParams = [origParams[0]];
195
- if (origParams[2]) newParams.push(origParams[2]);
196
- const storage = typeListToString(newParams);
197
-
198
- const varType = typeParamToString(origParams?.[1]);
199
- return { storage, varType };
200
- }
201
- }
202
-
203
- function lowerStdTypeMember(
204
- typeName: RefIdent,
205
- typeParameters: TypeTemplateParameter[] | undefined,
206
- ): LoweredVarTypes | undefined {
207
- if (typeof typeName !== "string") {
208
- const varBaseType = typeName.std ? typeName.originalName : "??";
209
- const params = typeParameters ? typeListToString(typeParameters) : "";
210
- const varType = varBaseType + params;
211
-
212
- return { varType, storage: "" };
213
- }
214
- }
215
-
216
- function lowerStorageTextureMember(
217
- typeName: RefIdent,
218
- typeParameters: TypeTemplateParameter[] | undefined,
219
- ): LoweredVarTypes | undefined {
220
- if (textureStorage.test(typeName.originalName)) {
221
- const params = typeParameters ? typeListToString(typeParameters) : "";
222
- const varType = typeName + params;
223
- return { varType, storage: "" };
224
- }
225
- }
226
-
227
- function syntheticVar(
228
- attributes: string,
229
- varName: string,
230
- storageTemplate: string,
231
- varType: string,
232
- ): SyntheticElem {
233
- const varText = `${attributes} var${storageTemplate} ${varName} : ${varType};\n`;
234
-
235
- const elem: SyntheticElem = {
236
- kind: "synthetic",
237
- text: varText,
238
- };
239
- return elem;
240
- }
241
-
242
- interface MemberRefToStruct extends StructTrace {
243
- memberRef: SimpleMemberRef; // e.g. the memberRef 'b.particles'
244
- }
245
-
246
- interface StructTrace {
247
- struct: StructElem; // e.g. the struct Bindings
248
- intermediates: DeclarationElem[]; // e.g. the fn param b:Bindings from 'fn(b:Bindings)'
249
- }
250
-
251
- /** find all simple member references in the module that refer to binding structs */
252
- export function findRefsToBindingStructs(
253
- moduleElem: ModuleElem,
254
- ): MemberRefToStruct[] {
255
- const members: SimpleMemberRef[] = [];
256
- visitAst(moduleElem, elem => {
257
- if (elem.kind === "memberRef") members.push(elem);
258
- });
259
- return filterMap(members, refersToBindingStruct);
260
- }
261
-
262
- /** @return true if this memberRef refers to a binding struct */
263
- function refersToBindingStruct(
264
- memberRef: SimpleMemberRef,
265
- ): MemberRefToStruct | undefined {
266
- const found = traceToStruct(memberRef.name.ident);
267
-
268
- if (found?.struct.bindingStruct) {
269
- return { memberRef, ...found };
270
- }
271
- }
272
-
273
- /** If this identifier ultimately refers to a struct type, return the struct declaration */
274
- function traceToStruct(ident: RefIdent): StructTrace | undefined {
275
- const decl = findDecl(ident);
276
- const declElem = decl.declElem;
277
- // for now only handle the case where the reference points at a fn parameter
278
- if (declElem && declElem.kind === "param") {
279
- const name = declElem.name.typeRef?.name;
280
- if (typeof name !== "string") {
281
- if (name?.std) {
282
- return undefined;
283
- }
284
-
285
- const paramDecl = findDecl(name as Ident);
286
- const structElem = paramDecl.declElem;
287
- if (structElem?.kind === "struct") {
288
- return { struct: structElem, intermediates: [declElem] };
289
- }
290
- return undefined;
291
- }
292
- } else {
293
- // LATER presumably handle other cases? Should this be more general, e.g. traceToType()?
294
- // elemLog(
295
- // ident.refIdentElem!,
296
- // `unhandled case in traceToStruct: decl ${declElem.kind} not yet implemented`,
297
- // );
298
- }
299
- }
300
-
301
- /** Mutate the member reference elem to instead contain synthetic elem text.
302
- * The new text is the mangled var name of the struct member that the memberRef refers to. */
303
- export function transformBindingReference(
304
- memberRef: SimpleMemberRef,
305
- struct: StructElem,
306
- ): SyntheticElem {
307
- const refName = memberRef.member.name;
308
- const structMember = struct.members.find(m => m.name.name === refName)!;
309
- if (!structMember || !structMember.mangledVarName) {
310
- if (debug) console.log(`missing mangledVarName for ${refName}`);
311
- return { kind: "synthetic", text: refName };
312
- }
313
- const { extraComponents } = memberRef;
314
- const extraText = extraComponents ? contentsToString(extraComponents) : "";
315
-
316
- const text = structMember.mangledVarName + extraText;
317
- const synthElem: SyntheticElem = { kind: "synthetic", text };
318
- memberRef.contents = [synthElem];
319
- return synthElem;
320
- }