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.
Files changed (141) hide show
  1. package/README.md +31 -0
  2. package/dist/index.js +4468 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/minified.js +3426 -0
  5. package/dist/minified.js.map +1 -0
  6. package/dist/tools/packages/wesl/src/AbstractElems.d.ts +322 -0
  7. package/dist/tools/packages/wesl/src/Assertions.d.ts +27 -0
  8. package/dist/tools/packages/wesl/src/BindIdents.d.ts +70 -0
  9. package/dist/tools/packages/wesl/src/Conditions.d.ts +6 -0
  10. package/dist/tools/packages/wesl/src/FlattenTreeImport.d.ts +11 -0
  11. package/dist/tools/packages/wesl/src/LinkedWesl.d.ts +50 -0
  12. package/dist/tools/packages/wesl/src/Linker.d.ts +87 -0
  13. package/dist/tools/packages/wesl/src/LinkerUtil.d.ts +3 -0
  14. package/dist/tools/packages/wesl/src/LiveDeclarations.d.ts +12 -0
  15. package/dist/tools/packages/wesl/src/LowerAndEmit.d.ts +31 -0
  16. package/dist/tools/packages/wesl/src/Mangler.d.ts +39 -0
  17. package/dist/tools/packages/wesl/src/ParseWESL.d.ts +60 -0
  18. package/dist/tools/packages/wesl/src/ParsedRegistry.d.ts +29 -0
  19. package/dist/tools/packages/wesl/src/PathUtil.d.ts +6 -0
  20. package/dist/tools/packages/wesl/src/RawEmit.d.ts +6 -0
  21. package/dist/tools/packages/wesl/src/Reflection.d.ts +45 -0
  22. package/dist/tools/packages/wesl/src/Scope.d.ts +81 -0
  23. package/dist/tools/packages/wesl/src/StandardTypes.d.ts +13 -0
  24. package/dist/tools/packages/wesl/src/TransformBindingStructs.d.ts +52 -0
  25. package/dist/tools/packages/wesl/src/Util.d.ts +43 -0
  26. package/dist/tools/packages/wesl/src/WESLCollect.d.ts +94 -0
  27. package/dist/tools/packages/wesl/src/WeslBundle.d.ts +13 -0
  28. package/dist/tools/packages/wesl/src/WeslDevice.d.ts +25 -0
  29. package/dist/tools/packages/wesl/src/debug/ASTtoString.d.ts +5 -0
  30. package/dist/tools/packages/wesl/src/debug/ImportToString.d.ts +2 -0
  31. package/dist/tools/packages/wesl/src/debug/LineWrapper.d.ts +21 -0
  32. package/dist/tools/packages/wesl/src/debug/ScopeToString.d.ts +6 -0
  33. package/dist/tools/packages/wesl/src/index.d.ts +11 -0
  34. package/dist/tools/packages/wesl/src/parse/ImportGrammar.d.ts +5 -0
  35. package/dist/tools/packages/wesl/src/parse/Keywords.d.ts +4 -0
  36. package/dist/tools/packages/wesl/src/parse/WeslBaseGrammar.d.ts +5 -0
  37. package/dist/tools/packages/wesl/src/parse/WeslExpression.d.ts +13 -0
  38. package/dist/tools/packages/wesl/src/parse/WeslGrammar.d.ts +80 -0
  39. package/dist/tools/packages/wesl/src/parse/WeslStream.d.ts +44 -0
  40. package/dist/tools/packages/wesl/src/test/BindWESL.test.d.ts +1 -0
  41. package/dist/tools/packages/wesl/src/test/ConditionLinking.test.d.ts +1 -0
  42. package/dist/tools/packages/wesl/src/test/ConditionalTranslationCases.test.d.ts +1 -0
  43. package/dist/tools/packages/wesl/src/test/ErrorLogging.test.d.ts +1 -0
  44. package/dist/tools/packages/wesl/src/test/Expression.test.d.ts +1 -0
  45. package/dist/tools/packages/wesl/src/test/FlattenTreeImport.test.d.ts +1 -0
  46. package/dist/tools/packages/wesl/src/test/ImportCases.test.d.ts +1 -0
  47. package/dist/tools/packages/wesl/src/test/ImportSyntaxCases.test.d.ts +1 -0
  48. package/dist/tools/packages/wesl/src/test/LinkGlob.test.d.ts +1 -0
  49. package/dist/tools/packages/wesl/src/test/LinkPackage.test.d.ts +1 -0
  50. package/dist/tools/packages/wesl/src/test/Linker.test.d.ts +1 -0
  51. package/dist/tools/packages/wesl/src/test/Mangling.test.d.ts +1 -0
  52. package/dist/tools/packages/wesl/src/test/ParseComments.test.d.ts +1 -0
  53. package/dist/tools/packages/wesl/src/test/ParseConditions.test.d.ts +1 -0
  54. package/dist/tools/packages/wesl/src/test/ParseError.test.d.ts +1 -0
  55. package/dist/tools/packages/wesl/src/test/ParseWESL.test.d.ts +1 -0
  56. package/dist/tools/packages/wesl/src/test/PathUtil.test.d.ts +1 -0
  57. package/dist/tools/packages/wesl/src/test/PrettyGrammar.test.d.ts +1 -0
  58. package/dist/tools/packages/wesl/src/test/Reflection.test.d.ts +1 -0
  59. package/dist/tools/packages/wesl/src/test/ScopeWESL.test.d.ts +1 -0
  60. package/dist/tools/packages/wesl/src/test/TestLink.d.ts +21 -0
  61. package/dist/tools/packages/wesl/src/test/TestSetup.d.ts +1 -0
  62. package/dist/tools/packages/wesl/src/test/TestUtil.d.ts +40 -0
  63. package/dist/tools/packages/wesl/src/test/Tokenizer.test.d.ts +1 -0
  64. package/dist/tools/packages/wesl/src/test/TransformBindingStructs.test.d.ts +1 -0
  65. package/dist/tools/packages/wesl/src/test/Util.test.d.ts +1 -0
  66. package/dist/tools/packages/wesl/src/test/VirtualModules.test.d.ts +1 -0
  67. package/dist/tools/packages/wesl/src/test/WeslDevice.test.d.ts +1 -0
  68. package/dist/tools/packages/wesl/src/test/WgslTests.d.ts +0 -0
  69. package/dist/tools/packages/wesl/src/vlq/vlq.d.ts +11 -0
  70. package/package.json +46 -0
  71. package/src/AbstractElems.ts +446 -0
  72. package/src/Assertions.ts +51 -0
  73. package/src/BindIdents.ts +523 -0
  74. package/src/Conditions.ts +74 -0
  75. package/src/FlattenTreeImport.ts +55 -0
  76. package/src/LinkedWesl.ts +184 -0
  77. package/src/Linker.ts +284 -0
  78. package/src/LinkerUtil.ts +29 -0
  79. package/src/LiveDeclarations.ts +31 -0
  80. package/src/LowerAndEmit.ts +413 -0
  81. package/src/Mangler.ts +94 -0
  82. package/src/ParseWESL.ts +157 -0
  83. package/src/ParsedRegistry.ts +120 -0
  84. package/src/PathUtil.ts +31 -0
  85. package/src/RawEmit.ts +102 -0
  86. package/src/Reflection.ts +334 -0
  87. package/src/Scope.ts +162 -0
  88. package/src/StandardTypes.ts +97 -0
  89. package/src/TransformBindingStructs.ts +319 -0
  90. package/src/Util.ts +194 -0
  91. package/src/WESLCollect.ts +614 -0
  92. package/src/WeslBundle.ts +16 -0
  93. package/src/WeslDevice.ts +209 -0
  94. package/src/debug/ASTtoString.ts +290 -0
  95. package/src/debug/ImportToString.ts +29 -0
  96. package/src/debug/LineWrapper.ts +70 -0
  97. package/src/debug/ScopeToString.ts +79 -0
  98. package/src/index.ts +11 -0
  99. package/src/parse/ImportGrammar.ts +157 -0
  100. package/src/parse/Keywords.ts +26 -0
  101. package/src/parse/WeslBaseGrammar.ts +8 -0
  102. package/src/parse/WeslExpression.ts +207 -0
  103. package/src/parse/WeslGrammar.ts +856 -0
  104. package/src/parse/WeslStream.ts +279 -0
  105. package/src/test/BindWESL.test.ts +57 -0
  106. package/src/test/ConditionLinking.test.ts +91 -0
  107. package/src/test/ConditionalTranslationCases.test.ts +56 -0
  108. package/src/test/ErrorLogging.test.ts +30 -0
  109. package/src/test/Expression.test.ts +22 -0
  110. package/src/test/FlattenTreeImport.test.ts +74 -0
  111. package/src/test/ImportCases.test.ts +56 -0
  112. package/src/test/ImportSyntaxCases.test.ts +24 -0
  113. package/src/test/LinkGlob.test.ts +25 -0
  114. package/src/test/LinkPackage.test.ts +26 -0
  115. package/src/test/Linker.test.ts +125 -0
  116. package/src/test/Mangling.test.ts +45 -0
  117. package/src/test/ParseComments.test.ts +36 -0
  118. package/src/test/ParseConditions.test.ts +183 -0
  119. package/src/test/ParseError.test.ts +36 -0
  120. package/src/test/ParseWESL.test.ts +1572 -0
  121. package/src/test/PathUtil.test.ts +34 -0
  122. package/src/test/PrettyGrammar.test.ts +20 -0
  123. package/src/test/Reflection.test.ts +172 -0
  124. package/src/test/ScopeWESL.test.ts +462 -0
  125. package/src/test/TestLink.ts +82 -0
  126. package/src/test/TestSetup.ts +4 -0
  127. package/src/test/TestUtil.ts +126 -0
  128. package/src/test/Tokenizer.test.ts +135 -0
  129. package/src/test/TransformBindingStructs.test.ts +230 -0
  130. package/src/test/Util.test.ts +22 -0
  131. package/src/test/VirtualModules.test.ts +37 -0
  132. package/src/test/WeslDevice.test.ts +265 -0
  133. package/src/test/WgslTests.ts +0 -0
  134. package/src/test/__snapshots__/ParseDirectives.test.ts.snap +25 -0
  135. package/src/test/__snapshots__/ParseWESL.test.ts.snap +119 -0
  136. package/src/test/__snapshots__/RustDirective.test.ts.snap +359 -0
  137. package/src/test/wgsl_1/main.wgsl +3 -0
  138. package/src/test/wgsl_1/util.wgsl +1 -0
  139. package/src/test/wgsl_2/main2.wgsl +3 -0
  140. package/src/test/wgsl_2/util2.wgsl +1 -0
  141. package/src/vlq/vlq.ts +94 -0
@@ -0,0 +1,413 @@
1
+ import { srcLog, SrcMapBuilder } from "mini-parse";
2
+ import {
3
+ AbstractElem,
4
+ AttributeElem,
5
+ ContainerElem,
6
+ DeclIdentElem,
7
+ DirectiveElem,
8
+ ElemWithAttributes,
9
+ ExpressionElem,
10
+ FnElem,
11
+ NameElem,
12
+ RefIdentElem,
13
+ StructElem,
14
+ SyntheticElem,
15
+ TextElem,
16
+ } from "./AbstractElems.ts";
17
+ import {
18
+ assertThatDebug,
19
+ assertUnreachable,
20
+ assertUnreachableSilent,
21
+ } from "./Assertions.ts";
22
+ import { isGlobal } from "./BindIdents.ts";
23
+ import { elementValid } from "./Conditions.ts";
24
+ import { identToString } from "./debug/ScopeToString.ts";
25
+ import { Conditions, DeclIdent, Ident } from "./Scope.ts";
26
+
27
+ /** passed to the emitters */
28
+ interface EmitContext {
29
+ srcBuilder: SrcMapBuilder; // constructing the linked output
30
+ conditions: Conditions; // settings for conditional compilation
31
+ extracting: boolean; // are we extracting or copying the root module
32
+ }
33
+
34
+ /** traverse the AST, starting from root elements, emitting wgsl for each */
35
+ export function lowerAndEmit(
36
+ srcBuilder: SrcMapBuilder,
37
+ rootElems: AbstractElem[],
38
+ conditions: Conditions,
39
+ extracting = true,
40
+ ): void {
41
+ const emitContext: EmitContext = { conditions, srcBuilder, extracting };
42
+ // rootElems.forEach(r => console.log(astToString(r) + "\n"));
43
+ rootElems.forEach(e => lowerAndEmitElem(e, emitContext));
44
+ }
45
+
46
+ export function lowerAndEmitElem(e: AbstractElem, ctx: EmitContext): void {
47
+ if (!conditionsValid(e, ctx.conditions)) return;
48
+
49
+ switch (e.kind) {
50
+ // import statements are dropped from from emitted text
51
+ case "import":
52
+ return;
53
+
54
+ // terminal elements copy strings to the output
55
+ case "text":
56
+ return emitText(e, ctx);
57
+ case "name":
58
+ return emitName(e, ctx);
59
+ case "synthetic":
60
+ return emitSynthetic(e, ctx);
61
+
62
+ // identifiers are copied to the output, but with potentially mangled names
63
+ case "ref":
64
+ return emitRefIdent(e, ctx);
65
+ case "decl":
66
+ return emitDeclIdent(e, ctx);
67
+
68
+ // container elements just emit their child elements
69
+ case "param":
70
+ case "var":
71
+ case "typeDecl":
72
+ case "let":
73
+ case "module":
74
+ case "member":
75
+ case "memberRef":
76
+ case "expression":
77
+ case "type":
78
+ case "statement":
79
+ case "stuff":
80
+ case "switch-clause":
81
+ return emitContents(e, ctx);
82
+
83
+ // root level container elements get some extra newlines to make the output prettier
84
+ case "override":
85
+ case "const":
86
+ case "assert":
87
+ case "alias":
88
+ case "gvar":
89
+ emitRootElemNl(ctx);
90
+ return emitContents(e, ctx);
91
+
92
+ case "fn":
93
+ emitRootElemNl(ctx);
94
+ return emitFn(e, ctx);
95
+
96
+ case "struct":
97
+ emitRootElemNl(ctx);
98
+ return emitStruct(e, ctx);
99
+
100
+ case "attribute":
101
+ return emitAttribute(e, ctx);
102
+ case "directive":
103
+ return emitDirective(e, ctx);
104
+
105
+ default:
106
+ assertUnreachable(e);
107
+ }
108
+ }
109
+
110
+ /** emit root elems with a blank line inbetween */
111
+ function emitRootElemNl(ctx: EmitContext): void {
112
+ if (ctx.extracting) {
113
+ ctx.srcBuilder.addNl();
114
+ ctx.srcBuilder.addNl();
115
+ }
116
+ }
117
+
118
+ export function emitText(e: TextElem, ctx: EmitContext): void {
119
+ ctx.srcBuilder.addCopy(e.start, e.end);
120
+ }
121
+
122
+ export function emitName(e: NameElem, ctx: EmitContext): void {
123
+ ctx.srcBuilder.add(e.name, e.start, e.end);
124
+ }
125
+
126
+ /** emit function explicitly so we can control commas between conditional parameters */
127
+ export function emitFn(e: FnElem, ctx: EmitContext): void {
128
+ const { attributes, name, params, returnAttributes, returnType, body } = e;
129
+ const { conditions, srcBuilder: builder } = ctx;
130
+
131
+ emitAttributes(attributes, ctx);
132
+
133
+ builder.add("fn ", name.start - 3, name.start);
134
+ emitDeclIdent(name, ctx);
135
+
136
+ builder.appendNext("(");
137
+ const validParams = params.filter(p => conditionsValid(p, conditions));
138
+ validParams.forEach((p, i) => {
139
+ emitContentsNoWs(p, ctx);
140
+ if (i < validParams.length - 1) {
141
+ builder.appendNext(", ");
142
+ }
143
+ });
144
+ builder.appendNext(") ");
145
+
146
+ if (returnType) {
147
+ builder.appendNext("-> ");
148
+ emitAttributes(returnAttributes, ctx);
149
+ emitContents(returnType, ctx);
150
+ builder.appendNext(" ");
151
+ }
152
+
153
+ emitContents(body, ctx);
154
+ }
155
+
156
+ function emitAttributes(
157
+ attributes: AttributeElem[] | undefined,
158
+ ctx: EmitContext,
159
+ ): void {
160
+ attributes?.forEach(a => {
161
+ emitAttribute(a, ctx);
162
+ ctx.srcBuilder.add(" ", a.start, a.end);
163
+ });
164
+ }
165
+
166
+ /** emit structs explicitly so we can control commas between conditional members */
167
+ export function emitStruct(e: StructElem, ctx: EmitContext): void {
168
+ const { name, members, start, end } = e;
169
+ const { srcBuilder } = ctx;
170
+
171
+ const validMembers = members.filter(m => conditionsValid(m, ctx.conditions));
172
+ const validLength = validMembers.length;
173
+
174
+ if (validLength === 0) {
175
+ warnEmptyStruct(e);
176
+ return;
177
+ }
178
+
179
+ srcBuilder.add("struct ", start, name.start);
180
+ emitDeclIdent(name, ctx);
181
+
182
+ if (validLength === 1) {
183
+ srcBuilder.add(" { ", name.end, members[0].start);
184
+ emitContentsNoWs(validMembers[0], ctx);
185
+ srcBuilder.add(" }\n", end - 1, end);
186
+ } else {
187
+ srcBuilder.add(" {\n", name.end, members[0].start);
188
+
189
+ validMembers.forEach(m => {
190
+ srcBuilder.add(" ", m.start - 1, m.start);
191
+ emitContentsNoWs(m, ctx);
192
+ srcBuilder.add(",", m.end, m.end + 1);
193
+ srcBuilder.addNl();
194
+ });
195
+
196
+ srcBuilder.add("}\n", end - 1, end);
197
+ }
198
+ }
199
+
200
+ function warnEmptyStruct(e: StructElem): void {
201
+ const { name, members } = e;
202
+ const condStr = members.length ? "(with current conditions)" : "";
203
+ const { debugFilePath: filePath } = name.srcModule;
204
+ srcLog(
205
+ name.srcModule.src,
206
+ e.start,
207
+ `struct ${name.ident.originalName} in ${filePath} has no members ${condStr}`,
208
+ );
209
+ }
210
+
211
+ export function emitSynthetic(e: SyntheticElem, ctx: EmitContext): void {
212
+ const { text } = e;
213
+ ctx.srcBuilder.addSynthetic(text, text, 0, text.length);
214
+ }
215
+
216
+ export function emitContents(elem: ContainerElem, ctx: EmitContext): void {
217
+ elem.contents.forEach(e => lowerAndEmitElem(e, ctx));
218
+ }
219
+
220
+ /** emit contents w/o white space */
221
+ function emitContentsNoWs(elem: ContainerElem, ctx: EmitContext): void {
222
+ elem.contents.forEach(e => {
223
+ if (e.kind === "text") {
224
+ const { srcModule, start, end } = e;
225
+ const text = srcModule.src.slice(start, end);
226
+ if (text.trim() === "") {
227
+ return;
228
+ }
229
+ }
230
+ lowerAndEmitElem(e, ctx);
231
+ });
232
+ }
233
+
234
+ export function emitRefIdent(e: RefIdentElem, ctx: EmitContext): void {
235
+ if (e.ident.std) {
236
+ ctx.srcBuilder.add(e.ident.originalName, e.start, e.end);
237
+ } else {
238
+ const declIdent = findDecl(e.ident);
239
+ const mangledName = displayName(declIdent);
240
+ ctx.srcBuilder.add(mangledName!, e.start, e.end);
241
+ }
242
+ }
243
+
244
+ export function emitDeclIdent(e: DeclIdentElem, ctx: EmitContext): void {
245
+ const mangledName = displayName(e.ident);
246
+ ctx.srcBuilder.add(mangledName!, e.start, e.end);
247
+ }
248
+
249
+ function emitAttribute(e: AttributeElem, ctx: EmitContext): void {
250
+ const { kind } = e.attribute;
251
+ // LATER emit more precise source map info by making use of all the spans
252
+ // Like the first case does
253
+ if (kind === "@attribute") {
254
+ const { params } = e.attribute;
255
+ if (!params || params.length === 0) {
256
+ ctx.srcBuilder.add("@" + e.attribute.name, e.start, e.end);
257
+ } else {
258
+ ctx.srcBuilder.add(
259
+ "@" + e.attribute.name + "(",
260
+ e.start,
261
+ params[0].start,
262
+ );
263
+ for (let i = 0; i < params.length; i++) {
264
+ emitContents(params[i], ctx);
265
+ if (i < params.length - 1) {
266
+ ctx.srcBuilder.add(",", params[i].end, params[i + 1].start);
267
+ }
268
+ }
269
+ ctx.srcBuilder.add(")", params[params.length - 1].end, e.end);
270
+ }
271
+ } else if (kind === "@builtin") {
272
+ ctx.srcBuilder.add(
273
+ "@builtin(" + e.attribute.param.name + ")",
274
+ e.start,
275
+ e.end,
276
+ );
277
+ } else if (kind === "@diagnostic") {
278
+ ctx.srcBuilder.add(
279
+ "@diagnostic" +
280
+ diagnosticControlToString(e.attribute.severity, e.attribute.rule),
281
+ e.start,
282
+ e.end,
283
+ );
284
+ } else if (kind === "@if") {
285
+ // (@if is wesl only, dropped from wgsl)
286
+ } else if (kind === "@interpolate") {
287
+ ctx.srcBuilder.add(
288
+ `@interpolate(${e.attribute.params.map(v => v.name).join(", ")})`,
289
+ e.start,
290
+ e.end,
291
+ );
292
+ } else {
293
+ assertUnreachable(kind);
294
+ }
295
+ }
296
+
297
+ export function diagnosticControlToString(
298
+ severity: NameElem,
299
+ rule: [NameElem, NameElem | null],
300
+ ): string {
301
+ const ruleStr = rule[0].name + (rule[1] !== null ? "." + rule[1].name : "");
302
+ return `(${severity.name}, ${ruleStr})`;
303
+ }
304
+
305
+ export function expressionToString(elem: ExpressionElem): string {
306
+ const { kind } = elem;
307
+ if (kind === "binary-expression") {
308
+ return `${expressionToString(elem.left)} ${elem.operator.value} ${expressionToString(elem.right)}`;
309
+ } else if (kind === "unary-expression") {
310
+ return `${elem.operator.value}${expressionToString(elem.expression)}`;
311
+ } else if (kind === "ref") {
312
+ return elem.ident.originalName;
313
+ } else if (kind === "literal") {
314
+ return elem.value;
315
+ } else if (kind === "translate-time-feature") {
316
+ return elem.name;
317
+ } else if (kind === "parenthesized-expression") {
318
+ return `(${expressionToString(elem.expression)})`;
319
+ } else if (kind === "component-expression") {
320
+ return `${expressionToString(elem.base)}[${elem.access}]`;
321
+ } else if (kind === "component-member-expression") {
322
+ return `${expressionToString(elem.base)}.${elem.access}`;
323
+ } else if (kind === "call-expression") {
324
+ return `${elem.function.ident.originalName}(${elem.arguments.map(expressionToString).join(", ")})`;
325
+ } else {
326
+ assertUnreachable(kind);
327
+ }
328
+ }
329
+
330
+ function emitDirective(e: DirectiveElem, ctx: EmitContext): void {
331
+ const { directive } = e;
332
+ const { kind } = directive;
333
+ if (kind === "diagnostic") {
334
+ ctx.srcBuilder.add(
335
+ `diagnostic${diagnosticControlToString(directive.severity, directive.rule)};`,
336
+ e.start,
337
+ e.end,
338
+ );
339
+ } else if (kind === "enable") {
340
+ ctx.srcBuilder.add(
341
+ `enable ${directive.extensions.map(v => v.name).join(", ")};`,
342
+ e.start,
343
+ e.end,
344
+ );
345
+ } else if (kind === "requires") {
346
+ ctx.srcBuilder.add(
347
+ `requires ${directive.extensions.map(v => v.name).join(", ")};`,
348
+ e.start,
349
+ e.end,
350
+ );
351
+ } else {
352
+ assertUnreachable(kind);
353
+ }
354
+ }
355
+
356
+ function displayName(declIdent: DeclIdent): string {
357
+ if (isGlobal(declIdent)) {
358
+ assertThatDebug(
359
+ declIdent.mangledName,
360
+ `ERR: mangled name not found for decl ident ${identToString(declIdent)}`,
361
+ );
362
+ // mangled name was set in binding step
363
+ return declIdent.mangledName!;
364
+ }
365
+
366
+ return declIdent.mangledName || declIdent.originalName;
367
+ }
368
+
369
+ /** trace through refersTo links in reference Idents until we find the declaration
370
+ * expects that bindIdents has filled in all refersTo: links
371
+ */
372
+ export function findDecl(ident: Ident): DeclIdent {
373
+ let i: Ident | undefined = ident;
374
+ do {
375
+ if (i.kind === "decl") {
376
+ return i;
377
+ }
378
+ i = i.refersTo;
379
+ } while (i);
380
+
381
+ // TODO show source position if this can happen in a non buggy linker.
382
+ throw new Error(`unresolved identifer: ${ident.originalName}`);
383
+ }
384
+
385
+ /** check if the element is visible with the current current conditional compilation settings */
386
+ export function conditionsValid(
387
+ elem: AbstractElem,
388
+ conditions: Conditions,
389
+ ): true | false | undefined {
390
+ const attrElem = elem as ElemWithAttributes;
391
+ const { kind } = attrElem;
392
+
393
+ switch (kind) {
394
+ case "alias":
395
+ case "assert":
396
+ case "const":
397
+ case "directive":
398
+ case "member":
399
+ case "var":
400
+ case "let":
401
+ case "statement":
402
+ case "switch-clause":
403
+ case "override":
404
+ case "gvar":
405
+ case "fn":
406
+ case "struct":
407
+ case "param":
408
+ return elementValid(attrElem, conditions);
409
+ default:
410
+ assertUnreachableSilent(kind);
411
+ }
412
+ return true;
413
+ }
package/src/Mangler.ts ADDED
@@ -0,0 +1,94 @@
1
+ import { DeclIdent, SrcModule } from "./Scope.ts";
2
+ /**
3
+ * A function for constructing a unique identifier name for a global declaration.
4
+ * Global names must be unique in the linked wgsl.
5
+ *
6
+ * Three manglers are currently available:
7
+ * . minimalMangle preserves original source names where possible
8
+ * . underscoreMangle constructs long but predictable names for every declaration
9
+ * . lengthPrefixMangle constructs long but predictable names for every declaration
10
+ */
11
+ export type ManglerFn = (
12
+ /** global declaration that needs a name */
13
+ decl: DeclIdent,
14
+
15
+ /** module that contains the declaration */
16
+ srcModule: SrcModule,
17
+
18
+ /** name at use site (possibly import as renamed from declaration) */
19
+ proposedName: string,
20
+
21
+ /** current set of mangled root level names for the linked result (read only) */
22
+ globalNames: Set<string>,
23
+ ) => string;
24
+
25
+ /**
26
+ * Construct a globally unique name based on the declaration
27
+ * module path separated by underscores.
28
+ * Corresponds to "Underscore-count mangling" from [NameMangling.md](https://github.com/wgsl-tooling-wg/wesl-spec/blob/main/NameMangling.md)
29
+ */
30
+ export function underscoreMangle(
31
+ decl: DeclIdent,
32
+ srcModule: SrcModule,
33
+ ): string {
34
+ const { modulePath } = srcModule;
35
+ return [...modulePath.split("::"), decl.originalName]
36
+ .map(v => {
37
+ const underscoreCount = (v.match(/_/g) ?? []).length;
38
+ if (underscoreCount > 0) {
39
+ return "_" + underscoreCount + v;
40
+ } else {
41
+ return v;
42
+ }
43
+ })
44
+ .join("_");
45
+ }
46
+
47
+ /**
48
+ * Construct a globally unique name based on the declaration
49
+ */
50
+ export function lengthPrefixMangle(
51
+ decl: DeclIdent,
52
+ srcModule: SrcModule,
53
+ ): string {
54
+ function codepointCount(text: string): number {
55
+ return [...text].length;
56
+ }
57
+ const qualifiedIdent = [
58
+ ...srcModule.modulePath.split("::"),
59
+ decl.originalName,
60
+ ];
61
+ return "_" + qualifiedIdent.map(v => codepointCount(v) + v).join("");
62
+ }
63
+
64
+ /**
65
+ * ManglerFn to construct a globally unique name
66
+ * using the requested name plus a uniquing number suffix if necessary
67
+ */
68
+ export function minimalMangle(
69
+ _d: DeclIdent,
70
+ _s: SrcModule,
71
+ proposedName: string,
72
+ globalNames: Set<string>,
73
+ ): string {
74
+ return minimallyMangledName(proposedName, globalNames);
75
+ }
76
+
77
+ /**
78
+ * Construct a globally unique name by using the requested name if possible
79
+ * and appending a number suffix necessary
80
+ */
81
+ export function minimallyMangledName(
82
+ proposedName: string,
83
+ globalNames: Set<string>,
84
+ ): string {
85
+ let renamed = proposedName;
86
+ let conflicts = 0;
87
+
88
+ // create a unique name
89
+ while (globalNames.has(renamed)) {
90
+ renamed = proposedName + conflicts++;
91
+ }
92
+
93
+ return renamed;
94
+ }
@@ -0,0 +1,157 @@
1
+ import { AppState, ParseError, ParserInit, SrcMap } from "mini-parse";
2
+ import {
3
+ ConstAssertElem,
4
+ ImportStatement,
5
+ ModuleElem,
6
+ } from "./AbstractElems.ts";
7
+ import { FlatImport, flattenTreeImport } from "./FlattenTreeImport.ts";
8
+ import { weslRoot } from "./parse/WeslGrammar.ts";
9
+ import { WeslStream } from "./parse/WeslStream.ts";
10
+ import { emptyScope, Scope, SrcModule } from "./Scope.ts";
11
+ import { errorHighlight, offsetToLineNumber } from "./Util.ts";
12
+ import { OpenElem } from "./WESLCollect.ts";
13
+ import { throwClickableError } from "./WeslDevice.ts";
14
+
15
+ /** result of a parse for one wesl module (e.g. one .wesl file)
16
+ *
17
+ * The parser constructs the AST constructed into three sections
18
+ * for convenient access by the binding stage.
19
+ * - import statements
20
+ * - language elements (fn, struct, etc)
21
+ * - scopes
22
+ *
23
+ */
24
+ export interface WeslAST {
25
+ /** source text for this module */
26
+ srcModule: SrcModule;
27
+
28
+ /** root module element */
29
+ moduleElem: ModuleElem;
30
+
31
+ /** root scope for this module */
32
+ rootScope: Scope;
33
+
34
+ /** imports found in this module */
35
+ imports: ImportStatement[];
36
+
37
+ /** module level const_assert statements */
38
+ moduleAsserts?: ConstAssertElem[];
39
+ }
40
+
41
+ /** an extended version of the AST */
42
+ export interface BindingAST extends WeslAST {
43
+ /* a flattened version of the import statements constructed on demand from import trees, and cached here */
44
+ _flatImports?: FlatImport[];
45
+ }
46
+
47
+ /** stable and unstable state used during parsing */
48
+ export interface WeslParseState
49
+ extends AppState<WeslParseContext, StableState> {
50
+ context: WeslParseContext;
51
+ stable: StableState;
52
+ }
53
+
54
+ /** stable values used or accumulated during parsing */
55
+ export type StableState = WeslAST;
56
+
57
+ /** unstable values used during parse collection */
58
+ export interface WeslParseContext {
59
+ scope: Scope; // current scope (points somewhere in rootScope)
60
+ openElems: OpenElem[]; // elems that are collecting their contents
61
+ }
62
+
63
+ /**
64
+ * An error when parsing WESL fails. Designed to be human-readable.
65
+ */
66
+ export class WeslParseError extends Error {
67
+ position: number;
68
+ src: SrcModule;
69
+ constructor(opts: { cause: ParseError; src: SrcModule }) {
70
+ const source = opts.src.src;
71
+ const [lineNum, linePos] = offsetToLineNumber(opts.cause.position, source);
72
+ let message = `${opts.src.debugFilePath}:${lineNum}:${linePos}`;
73
+ message += ` error: ${opts.cause.message}\n`;
74
+ message += errorHighlight(source, [
75
+ opts.cause.position,
76
+ opts.cause.position + 1,
77
+ ]).join("\n");
78
+ super(message, {
79
+ cause: opts.cause,
80
+ });
81
+ this.position = opts.cause.position;
82
+ this.src = opts.src;
83
+ }
84
+ }
85
+
86
+ /** Parse a WESL file. Throws on error. */
87
+ export function parseSrcModule(srcModule: SrcModule, srcMap?: SrcMap): WeslAST {
88
+ const stream = new WeslStream(srcModule.src);
89
+
90
+ const appState = blankWeslParseState(srcModule);
91
+
92
+ const init: ParserInit = { stream, appState };
93
+ try {
94
+ const parseResult = weslRoot.parse(init);
95
+ if (parseResult === null) {
96
+ throw new Error("parseWESL failed");
97
+ }
98
+ } catch (e) {
99
+ if (e instanceof ParseError) {
100
+ const [lineNumber, lineColumn] = offsetToLineNumber(
101
+ e.position,
102
+ srcModule.src,
103
+ );
104
+ const error = new WeslParseError({ cause: e, src: srcModule });
105
+ throwClickableError({
106
+ url: srcModule.debugFilePath,
107
+ text: srcModule.src,
108
+ error,
109
+ lineNumber,
110
+ lineColumn,
111
+ length: 1,
112
+ });
113
+ } else {
114
+ throw e;
115
+ }
116
+ }
117
+
118
+ return appState.stable as WeslAST;
119
+ }
120
+
121
+ export function parseWESL(src: string, srcMap?: SrcMap): WeslAST {
122
+ const srcModule: SrcModule = {
123
+ modulePath: "package::test", // TODO this ought not be used outside of tests
124
+ debugFilePath: "./test.wesl",
125
+ src,
126
+ };
127
+
128
+ return parseSrcModule(srcModule, srcMap);
129
+ }
130
+
131
+ export function blankWeslParseState(srcModule: SrcModule): WeslParseState {
132
+ const rootScope = emptyScope(null);
133
+ const moduleElem = null as any; // we'll fill this in later
134
+ return {
135
+ context: { scope: rootScope, openElems: [] },
136
+ stable: { srcModule, imports: [], rootScope, moduleElem },
137
+ };
138
+ }
139
+
140
+ export function syntheticWeslParseState(): WeslParseState {
141
+ const srcModule: SrcModule = {
142
+ modulePath: "package::test",
143
+ debugFilePath: "./test.wesl",
144
+ src: "",
145
+ };
146
+
147
+ return blankWeslParseState(srcModule);
148
+ }
149
+
150
+ /** @return a flattened form of the import tree for convenience in binding idents. */
151
+ export function flatImports(ast: BindingAST): FlatImport[] {
152
+ if (ast._flatImports) return ast._flatImports;
153
+
154
+ const flat = ast.imports.flatMap(flattenTreeImport);
155
+ ast._flatImports = flat;
156
+ return flat;
157
+ }