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,614 @@
1
+ import { dlog } from "berry-pretty";
2
+ import { CollectContext, CollectPair, srcLog, tracing } from "mini-parse";
3
+ import {
4
+ AbstractElem,
5
+ AliasElem,
6
+ Attribute,
7
+ AttributeElem,
8
+ ConstAssertElem,
9
+ ConstElem,
10
+ ContainerElem,
11
+ DeclarationElem,
12
+ DeclIdentElem,
13
+ DirectiveElem,
14
+ DirectiveVariant,
15
+ FnElem,
16
+ FnParamElem,
17
+ GlobalVarElem,
18
+ GrammarElem,
19
+ HasAttributes,
20
+ IfAttribute,
21
+ ImportElem,
22
+ LetElem,
23
+ ModuleElem,
24
+ NameElem,
25
+ OverrideElem,
26
+ RefIdentElem,
27
+ SimpleMemberRef,
28
+ StandardAttribute,
29
+ StatementElem,
30
+ StructElem,
31
+ StructMemberElem,
32
+ StuffElem,
33
+ SwitchClauseElem,
34
+ TextElem,
35
+ TypedDeclElem,
36
+ TypeRefElem,
37
+ UnknownExpressionElem,
38
+ VarElem,
39
+ } from "./AbstractElems.ts";
40
+ import {
41
+ StableState,
42
+ WeslAST,
43
+ WeslParseContext,
44
+ WeslParseState,
45
+ } from "./ParseWESL.ts";
46
+ import {
47
+ DeclIdent,
48
+ emptyScope,
49
+ Ident,
50
+ mergeScope,
51
+ nextIdentId,
52
+ PartialScope,
53
+ RefIdent,
54
+ Scope,
55
+ } from "./Scope.ts";
56
+ import { filterMap } from "./Util.ts";
57
+
58
+ export function importElem(cc: CollectContext) {
59
+ const importElems = cc.tags.owo?.[0] as ImportElem[]; // LATER ts typing
60
+ for (const importElem of importElems) {
61
+ (cc.app.stable as StableState).imports.push(importElem.imports);
62
+ addToOpenElem(cc, importElem as AbstractElem);
63
+ }
64
+ }
65
+
66
+ /** add an elem to the .contents array of the currently containing element */
67
+ function addToOpenElem(cc: CollectContext, elem: AbstractElem): void {
68
+ const weslContext: WeslParseContext = cc.app.context;
69
+ const { openElems } = weslContext;
70
+ if (openElems && openElems.length) {
71
+ const open = openElems[openElems.length - 1];
72
+ open.contents.push(elem);
73
+ }
74
+ }
75
+
76
+ /** create reference Ident and add to context */
77
+ export function refIdent(cc: CollectContext): RefIdentElem {
78
+ const { src, start, end } = cc;
79
+ const app = cc.app as WeslParseState;
80
+ const { srcModule } = app.stable;
81
+ const originalName = src.slice(start, end);
82
+
83
+ const kind = "ref";
84
+ const ident: RefIdent = {
85
+ kind,
86
+ originalName,
87
+ ast: cc.app.stable,
88
+ id: nextIdentId(),
89
+ refIdentElem: null as any, // set below
90
+ };
91
+ const identElem: RefIdentElem = { kind, start, end, srcModule, ident };
92
+ ident.refIdentElem = identElem;
93
+
94
+ saveIdent(cc, identElem);
95
+ addToOpenElem(cc, identElem);
96
+ return identElem;
97
+ }
98
+
99
+ /** create declaration Ident and add to context */
100
+ export function declCollect(cc: CollectContext): DeclIdentElem {
101
+ return declCollectInternal(cc, false);
102
+ }
103
+
104
+ /** create global declaration Ident and add to context */
105
+ export function globalDeclCollect(cc: CollectContext): DeclIdentElem {
106
+ return declCollectInternal(cc, true);
107
+ }
108
+
109
+ function declCollectInternal(
110
+ cc: CollectContext,
111
+ isGlobal: boolean,
112
+ ): DeclIdentElem {
113
+ const { src, start, end } = cc;
114
+ const app = cc.app as WeslParseState;
115
+ const { scope } = app.context;
116
+ const { srcModule } = app.stable;
117
+ const originalName = src.slice(start, end);
118
+
119
+ const kind = "decl";
120
+ const declElem = null as any; // we'll set declElem later
121
+ const ident: DeclIdent = {
122
+ declElem,
123
+ kind,
124
+ originalName,
125
+ scope,
126
+ isGlobal,
127
+ id: nextIdentId(),
128
+ srcModule,
129
+ };
130
+ const identElem: DeclIdentElem = { kind, start, end, srcModule, ident };
131
+
132
+ saveIdent(cc, identElem);
133
+ addToOpenElem(cc, identElem);
134
+ return identElem;
135
+ }
136
+
137
+ export const typedDecl = collectElem(
138
+ "typeDecl",
139
+ (cc: CollectContext, openElem: PartElem<TypedDeclElem>) => {
140
+ const decl = cc.tags.decl_elem?.[0] as DeclIdentElem;
141
+ const typeRef = cc.tags.typeRefElem?.[0] as TypeRefElem | undefined;
142
+ const partial: TypedDeclElem = { ...openElem, decl, typeRef };
143
+ const elem = withTextCover(partial, cc);
144
+
145
+ return elem;
146
+ },
147
+ );
148
+
149
+ /** add Ident to current open scope, add IdentElem to current open element */
150
+ function saveIdent(
151
+ cc: CollectContext,
152
+ identElem: RefIdentElem | DeclIdentElem,
153
+ ) {
154
+ const { ident } = identElem;
155
+ ident.id = nextIdentId();
156
+ const weslContext: WeslParseContext = cc.app.context;
157
+ weslContext.scope.contents.push(ident);
158
+ }
159
+
160
+ /** start a new child lexical Scope */
161
+ function startScope(cc: CollectContext) {
162
+ startSomeScope("scope", cc);
163
+ }
164
+
165
+ /** start a new child partial Scope */
166
+ function startPartialScope(cc: CollectContext) {
167
+ startSomeScope("partial", cc);
168
+ }
169
+
170
+ /** start a new lexical or partial scope */
171
+ function startSomeScope(kind: Scope["kind"], cc: CollectContext): void {
172
+ const { scope } = cc.app.context as WeslParseContext;
173
+ const newScope = emptyScope(scope, kind);
174
+
175
+ scope.contents.push(newScope);
176
+ cc.app.context.scope = newScope;
177
+ }
178
+
179
+ /* close current Scope and set current scope to parent */
180
+ function completeScope(cc: CollectContext): Scope {
181
+ const weslContext = cc.app.context as WeslParseContext;
182
+ const completedScope = weslContext.scope;
183
+ const ifAttributes = collectIfAttributes(cc);
184
+
185
+ const { parent } = completedScope;
186
+ if (parent) {
187
+ weslContext.scope = parent;
188
+ } else if (tracing) {
189
+ console.log("ERR: completeScope, no parent scope", completedScope.contents);
190
+ }
191
+ completedScope.ifAttribute = ifAttributes?.[0];
192
+ return completedScope;
193
+ }
194
+
195
+ /** return @if attributes from the 'attribute' tag */
196
+ function collectIfAttributes(cc: CollectContext): IfAttribute[] | undefined {
197
+ const attributes = cc.tags.attribute as AttributeElem[] | undefined;
198
+ return filterIfAttributes(attributes);
199
+ }
200
+
201
+ function filterIfAttributes(
202
+ attributes?: AttributeElem[],
203
+ ): IfAttribute[] | undefined {
204
+ if (!attributes) return;
205
+ return filterMap(attributes, a =>
206
+ a.attribute.kind === "@if" ? a.attribute : undefined,
207
+ );
208
+ }
209
+
210
+ // prettier-ignore
211
+ export type OpenElem<T extends ContainerElem = ContainerElem> =
212
+ Pick< T, "kind" | "contents">;
213
+
214
+ // prettier-ignore
215
+ export type PartElem<T extends ContainerElem = ContainerElem > =
216
+ Pick< T, "kind" | "start" | "end" | "contents"> ;
217
+
218
+ // prettier-ignore
219
+ type VarLikeElem =
220
+ | GlobalVarElem
221
+ | VarElem
222
+ | LetElem
223
+ | ConstElem
224
+ | OverrideElem;
225
+
226
+ export function collectVarLike<E extends VarLikeElem>(
227
+ kind: E["kind"],
228
+ ): CollectPair<E> {
229
+ return collectElem(kind, (cc: CollectContext, openElem: PartElem<E>) => {
230
+ const name = cc.tags.var_name?.[0] as TypedDeclElem;
231
+ const decl_scope = cc.tags.decl_scope?.[0] as Scope;
232
+ const attributes = cc.tags.attribute as AttributeElem[] | undefined;
233
+ const partElem = { ...openElem, name, attributes } as E;
234
+ const varElem = withTextCover(partElem, cc);
235
+ (name.decl.ident as DeclIdent).declElem = varElem as DeclarationElem;
236
+ name.decl.ident.scope = decl_scope;
237
+ return varElem;
238
+ });
239
+ }
240
+
241
+ export const aliasCollect = collectElem(
242
+ "alias",
243
+ (cc: CollectContext, openElem: PartElem<AliasElem>) => {
244
+ const name = cc.tags.alias_name?.[0] as DeclIdentElem;
245
+ const alias_scope = cc.tags.alias_scope?.[0] as Scope;
246
+ const typeRef = cc.tags.typeRefElem?.[0] as TypeRefElem;
247
+ const attributes: AttributeElem[] = cc.tags.attributes?.flat() ?? [];
248
+ const partElem: AliasElem = { ...openElem, name, attributes, typeRef };
249
+ const aliasElem = withTextCover(partElem, cc);
250
+ name.ident.scope = alias_scope;
251
+ name.ident.declElem = aliasElem;
252
+ return aliasElem;
253
+ },
254
+ );
255
+
256
+ /**
257
+ * Collect a FnElem and associated scopes.
258
+ *
259
+ * Scope definition is a bit complicated in wgsl and wesl for fns.
260
+ * Here's what we collect for scopes for this example function:
261
+ * @if(true) fn foo(a: u32) -> @location(x) R { let y = a; }
262
+ *
263
+ * -{ // partial scope in case the whole shebang is prefixed by an `@if`
264
+ * %foo
265
+ *
266
+ * {<=%foo // foo decl references this header+returnType+body scope (for tracing dependencies from decls)
267
+ * x // for @location(x) (contains no decls, so ok to merge for tracing)
268
+ * %a u32 // merged from header scope
269
+ * R // merged from return type (contains no decls, so ok to merge for tracing)
270
+ * %y a // merged body scope
271
+ * }
272
+ * }
273
+ */
274
+ export const fnCollect = collectElem(
275
+ "fn",
276
+ (cc: CollectContext, openElem: PartElem<FnElem>) => {
277
+ // extract tags we care about
278
+ const ourTags = fnTags(cc);
279
+ const { name, headerScope, returnScope, bodyScope, body, params } = ourTags;
280
+ const { attributes, returnAttributes, returnType, fnScope } = ourTags;
281
+
282
+ // create the fn element
283
+ const fnElem: FnElem = {
284
+ ...openElem,
285
+ ...{ name, attributes, params, returnAttributes, body, returnType },
286
+ };
287
+
288
+ // --- setup the various scopes --
289
+
290
+ // attach ifAttributes to outermost partial scope
291
+ fnScope.ifAttribute = filterIfAttributes(attributes)?.[0];
292
+
293
+ // merge the header, return and body scopes into the one scope
294
+ const mergedScope = headerScope;
295
+ returnScope && mergeScope(mergedScope, returnScope);
296
+ mergeScope(mergedScope, bodyScope);
297
+
298
+ // rewrite scope contents to remove old scopes and add merged scope
299
+ const filtered: (Ident | Scope)[] = [];
300
+ for (const e of fnScope.contents) {
301
+ if (e === headerScope || e == returnScope) {
302
+ continue;
303
+ } else if (e === bodyScope) {
304
+ filtered.push(mergedScope);
305
+ } else {
306
+ filtered.push(e);
307
+ }
308
+ }
309
+ fnScope.contents = filtered;
310
+
311
+ name.ident.declElem = fnElem;
312
+ name.ident.scope = mergedScope;
313
+
314
+ return fnElem;
315
+ },
316
+ );
317
+
318
+ /** Fetch and cast the collection tags for fnCollect
319
+ * LATER typechecking for collect! */
320
+ function fnTags(cc: CollectContext) {
321
+ const { fn_attributes, fn_name, fn_param, return_attributes } = cc.tags;
322
+ const { return_type } = cc.tags;
323
+ const { header_scope, return_scope, body_scope, body_statement } = cc.tags;
324
+ const { fn_partial_scope } = cc.tags;
325
+
326
+ const name = fn_name?.[0] as DeclIdentElem;
327
+ const headerScope = header_scope?.[0] as Scope;
328
+ const returnScope = return_scope?.[0] as Scope | undefined;
329
+ const bodyScope = body_scope?.[0] as Scope;
330
+ const body = body_statement?.[0] as StatementElem;
331
+ const params: FnParamElem[] = fn_param?.flat(3) ?? [];
332
+ const attributes: AttributeElem[] | undefined = fn_attributes?.flat();
333
+ const returnAttributes: AttributeElem[] | undefined =
334
+ return_attributes?.flat();
335
+ const returnType: TypeRefElem | undefined = return_type?.flat(3)[0];
336
+ const fnScope = fn_partial_scope?.[0] as PartialScope;
337
+
338
+ return {
339
+ ...{ name, headerScope, returnScope, bodyScope, body, params },
340
+ ...{ attributes, returnAttributes, returnType, fnScope },
341
+ };
342
+ }
343
+
344
+ export const collectFnParam = collectElem(
345
+ "param",
346
+ (cc: CollectContext, openElem: PartElem<FnParamElem>) => {
347
+ const name = cc.tags.param_name?.[0]! as TypedDeclElem;
348
+ const attributes: AttributeElem[] = cc.tags.attributes?.flat() ?? [];
349
+ const elem: FnParamElem = { ...openElem, name, attributes };
350
+ const paramElem = withTextCover(elem, cc);
351
+ name.decl.ident.declElem = paramElem; // TODO is this right?
352
+
353
+ return paramElem;
354
+ },
355
+ );
356
+
357
+ export const collectStruct = collectElem(
358
+ "struct",
359
+ (cc: CollectContext, openElem: PartElem<StructElem>) => {
360
+ const name = cc.tags.type_name?.[0] as DeclIdentElem;
361
+ const members = cc.tags.members as StructMemberElem[];
362
+ const attributes: AttributeElem[] = cc.tags.attributes?.flat() ?? [];
363
+ name.ident.scope = cc.tags.struct_scope?.[0] as Scope;
364
+ const structElem = { ...openElem, name, attributes, members };
365
+ const elem = withTextCover(structElem, cc);
366
+ (name.ident as DeclIdent).declElem = elem as DeclarationElem;
367
+
368
+ return elem;
369
+ },
370
+ );
371
+
372
+ export const collectStructMember = collectElem(
373
+ "member",
374
+ (cc: CollectContext, openElem: PartElem<StructMemberElem>) => {
375
+ const name = cc.tags.nameElem?.[0]!;
376
+ const typeRef = cc.tags.typeRefElem?.[0];
377
+ const attributes = cc.tags.attribute?.flat(3) as AttributeElem[];
378
+ const partElem = { ...openElem, name, attributes, typeRef };
379
+ return withTextCover(partElem, cc);
380
+ },
381
+ );
382
+
383
+ export const specialAttribute = collectElem(
384
+ "attribute",
385
+ (cc: CollectContext, openElem: PartElem<AttributeElem>) => {
386
+ const attribute = cc.tags.attr_variant?.[0] as Attribute;
387
+ const attrElem: AttributeElem = { ...openElem, attribute };
388
+ return attrElem;
389
+ },
390
+ );
391
+
392
+ /** debug routine to log tags at collect() */
393
+ export function logCollect(msg?: string): (cc: CollectContext) => void {
394
+ return function _log(cc: CollectContext) {
395
+ dlog(msg ?? "log", { tags: [...Object.keys(cc.tags)] });
396
+ };
397
+ }
398
+
399
+ export const assertCollect = attrElemCollect<ConstAssertElem>("assert");
400
+ export const statementCollect = attrElemCollect<StatementElem>("statement");
401
+ export const switchClauseCollect =
402
+ attrElemCollect<SwitchClauseElem>("switch-clause");
403
+
404
+ /** @return a collector for container elem types that have only an attributes field */
405
+ function attrElemCollect<T extends ContainerElem & HasAttributes>(
406
+ kind: T["kind"],
407
+ ): CollectPair<T> {
408
+ return collectElem(kind, (cc: CollectContext, openElem: PartElem<T>) => {
409
+ const attributes = cc.tags.attribute?.flat(3) as AttributeElem[];
410
+ const partElem = { ...openElem, attributes };
411
+ return withTextCover(partElem as T, cc);
412
+ });
413
+ }
414
+
415
+ export const collectAttribute = collectElem(
416
+ "attribute",
417
+ (cc: CollectContext, openElem: PartElem<AttributeElem>) => {
418
+ const params = cc.tags.attrParam as UnknownExpressionElem[] | undefined;
419
+ const name = cc.tags.name?.[0]! as string;
420
+ const kind = "@attribute";
421
+ const stdAttribute: StandardAttribute = { kind, name, params };
422
+ const attrElem: AttributeElem = { ...openElem, attribute: stdAttribute };
423
+ return attrElem;
424
+ },
425
+ );
426
+
427
+ export const typeRefCollect = collectElem(
428
+ "type",
429
+ // @ts-ignore
430
+ (cc: CollectContext, openElem: PartElem<TypeRefElem>) => {
431
+ let templateParamsTemp: any[] | undefined = cc.tags.templateParam?.flat(3);
432
+
433
+ const typeRef = cc.tags.typeRefName?.[0] as string | RefIdentElem;
434
+ const name = typeof typeRef === "string" ? typeRef : typeRef.ident;
435
+ const partElem = {
436
+ ...openElem,
437
+ name,
438
+ templateParams: templateParamsTemp as any[],
439
+ };
440
+ // @ts-ignore
441
+ return withTextCover(partElem, cc);
442
+ },
443
+ );
444
+
445
+ // LATER This creates useless unknown-expression elements
446
+ export const expressionCollect = collectElem(
447
+ "expression",
448
+ (cc: CollectContext, openElem: PartElem<UnknownExpressionElem>) => {
449
+ const partElem = { ...openElem };
450
+ return withTextCover(partElem, cc);
451
+ },
452
+ );
453
+
454
+ export function globalAssertCollect(cc: CollectContext): void {
455
+ const globalAssert = cc.tags.const_assert?.flat()[0];
456
+ const ast = cc.app.stable as WeslAST;
457
+ if (!ast.moduleAsserts) ast.moduleAsserts = [];
458
+ ast.moduleAsserts.push(globalAssert);
459
+ }
460
+
461
+ export const stuffCollect = collectElem(
462
+ "stuff",
463
+ (cc: CollectContext, openElem: PartElem<StuffElem>) => {
464
+ const partElem = { ...openElem };
465
+ return withTextCover(partElem, cc);
466
+ },
467
+ );
468
+
469
+ export const memberRefCollect = collectElem(
470
+ "memberRef",
471
+ (cc: CollectContext, openElem: PartElem<SimpleMemberRef>) => {
472
+ const { component, structRef, extra_components } = cc.tags;
473
+ const member = component![0] as NameElem;
474
+ const name = structRef?.flat()[0] as RefIdentElem;
475
+ const extraComponents = extra_components?.flat()[0] as StuffElem;
476
+
477
+ const partElem: SimpleMemberRef = {
478
+ ...openElem,
479
+ name,
480
+ member,
481
+ extraComponents,
482
+ };
483
+ return withTextCover(partElem, cc) as any;
484
+ },
485
+ );
486
+
487
+ export function nameCollect(cc: CollectContext): NameElem {
488
+ const { start, end, src, app } = cc;
489
+ const name = src.slice(start, end);
490
+ const elem: NameElem = { kind: "name", start, end, name };
491
+ addToOpenElem(cc, elem);
492
+ return elem;
493
+ }
494
+
495
+ export const collectModule = collectElem(
496
+ "module",
497
+ (cc: CollectContext, openElem: PartElem<ModuleElem>) => {
498
+ const ccComplete = { ...cc, start: 0, end: cc.src.length }; // force module to cover entire source despite ws skipping
499
+ const moduleElem: ModuleElem = withTextCover(openElem, ccComplete);
500
+ const weslState: StableState = cc.app.stable;
501
+ weslState.moduleElem = moduleElem;
502
+ return moduleElem;
503
+ },
504
+ );
505
+
506
+ export function directiveCollect(cc: CollectContext): DirectiveElem {
507
+ const { start, end } = cc;
508
+ const directive: DirectiveVariant = cc.tags.directive?.flat()[0];
509
+ const attributes: AttributeElem[] | undefined = cc.tags.attribute?.flat();
510
+
511
+ const kind = "directive";
512
+ const elem: DirectiveElem = { kind, attributes, start, end, directive };
513
+ addToOpenElem(cc, elem);
514
+ return elem;
515
+ }
516
+
517
+ /**
518
+ * Collect a LexicalScope.
519
+ *
520
+ * The scope starts encloses all idents and subscopes inside the parser to which
521
+ * .collect is attached
522
+ */
523
+ export const scopeCollect: CollectPair<Scope> = {
524
+ before: startScope,
525
+ after: completeScope,
526
+ };
527
+
528
+ /**
529
+ * Collect a PartialScope.
530
+ *
531
+ * The scope starts encloses all idents and subscopes inside the parser to which
532
+ * .collect is attached
533
+ */
534
+ export const partialScopeCollect: CollectPair<Scope> = {
535
+ before: startPartialScope,
536
+ after: completeScope,
537
+ };
538
+
539
+ /** utility to collect an ElemWithContents
540
+ * starts the new element as the collection point corresponding
541
+ * to the start of the attached grammar and completes
542
+ * the element in the at the end of the grammar.
543
+ *
544
+ * In between the start and the end, the new element is available
545
+ * as an 'open' element in the collection context. While this element
546
+ * is 'open', other collected are added to the 'contents' field of this
547
+ * open element.
548
+ */
549
+ function collectElem<V extends ContainerElem>(
550
+ kind: V["kind"],
551
+ fn: (cc: CollectContext, partialElem: PartElem<V>) => V,
552
+ ): CollectPair<V> {
553
+ return {
554
+ before: (cc: CollectContext) => {
555
+ const partialElem = { kind, contents: [] };
556
+ const weslContext: WeslParseContext = cc.app.context;
557
+ weslContext.openElems.push(partialElem);
558
+ },
559
+ after: (cc: CollectContext) => {
560
+ // TODO refine start?
561
+ const weslContext: WeslParseContext = cc.app.context;
562
+ const partialElem = weslContext.openElems.pop()!;
563
+ console.assert(partialElem && partialElem.kind === kind);
564
+ const elem = fn(cc, { ...partialElem, start: cc.start, end: cc.end });
565
+ if (elem) addToOpenElem(cc, elem as AbstractElem);
566
+ return elem;
567
+ },
568
+ };
569
+ }
570
+
571
+ /**
572
+ * @return a copy of the element with contents extended
573
+ * to include TextElems to cover the entire range.
574
+ */
575
+ function withTextCover<T extends ContainerElem>(
576
+ elem: T,
577
+ cc: CollectContext,
578
+ ): T {
579
+ const contents = coverWithText(cc, elem);
580
+ return { ...elem, contents };
581
+ }
582
+
583
+ /** cover the entire source range with Elems by creating TextElems to
584
+ * cover any parts of the source that are not covered by other elems
585
+ * @returns the existing elems combined with any new TextElems, in src order */
586
+ function coverWithText(cc: CollectContext, elem: ContainerElem): GrammarElem[] {
587
+ let { start: pos } = cc;
588
+ const ast: WeslAST = cc.app.stable;
589
+ const { contents, end } = elem;
590
+ const sorted = (contents as GrammarElem[]).sort((a, b) => a.start - b.start);
591
+
592
+ const elems: GrammarElem[] = [];
593
+ for (const elem of sorted) {
594
+ if (pos < elem.start) {
595
+ elems.push(makeTextElem(elem.start));
596
+ }
597
+ elems.push(elem);
598
+ pos = elem.end;
599
+ }
600
+ if (pos < end) {
601
+ elems.push(makeTextElem(end));
602
+ }
603
+
604
+ return elems;
605
+
606
+ function makeTextElem(end: number): TextElem {
607
+ return { kind: "text", start: pos, end, srcModule: ast.srcModule };
608
+ }
609
+ }
610
+
611
+ function collectLog(cc: CollectContext, ...messages: any[]): void {
612
+ const { src, start, end } = cc;
613
+ srcLog(src, [start, end], ...messages);
614
+ }
@@ -0,0 +1,16 @@
1
+ export interface WeslBundle {
2
+ /** name of the package, e.g. random_wgsl */
3
+ name: string;
4
+
5
+ /** wesl edition of the code e.g. wesl_unstable_2024_1 */
6
+ edition: string;
7
+
8
+ /** map of wesl/wgsl modules:
9
+ * keys are file paths, relative to package root (e.g. "./lib.wgsl")
10
+ * values are wgsl/wesl code strings
11
+ */
12
+ modules: Record<string, string>;
13
+
14
+ /** packages referenced by this package */
15
+ dependencies?: WeslBundle[];
16
+ }