wesl-link 0.6.6 → 0.6.7

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.
@@ -0,0 +1,1510 @@
1
+ #!/usr/bin/env node
2
+ import { hideBin } from "yargs/helpers";
3
+ import path from "node:path";
4
+ import "diff";
5
+ import { CachingStream, MatchersStream, ParseError, RegexMatchers, collectArray, delimited, enableTracing, eof, fn, kind, log, matchOneOf, not, opt, or, preceded, repeat, repeatPlus, req, separated_pair, seq, seqObj, span, tagScope, terminated, text, token, tokenKind, tokenOf, tracing, withSep, withSepPlus, withStreamAction, yes } from "mini-parse";
6
+ import { astToString, link, scopeToString } from "wesl";
7
+ import { loadModules, versionFromPackageJson } from "wesl-tooling";
8
+ import yargs from "yargs";
9
+
10
+ //#region ../wesl/src/Util.ts
11
+ /** filter an array, returning the truthy results of the filter function */
12
+ function filterMap(arr, fn$1) {
13
+ const out = [];
14
+ for (const t of arr) {
15
+ const u = fn$1(t);
16
+ if (u) out.push(u);
17
+ }
18
+ return out;
19
+ }
20
+ /**
21
+ * Maps an character position in a string to a 1-indexed line number, and 1-indexed column.
22
+ */
23
+ function offsetToLineNumber(offset, text$1) {
24
+ const safeOffset = Math.min(text$1.length, Math.max(0, offset));
25
+ let lineStartOffset = 0;
26
+ let lineNum = 1;
27
+ while (true) {
28
+ const lineEnd = text$1.indexOf("\n", lineStartOffset);
29
+ if (lineEnd === -1 || safeOffset <= lineEnd) {
30
+ const linePos = 1 + (safeOffset - lineStartOffset);
31
+ return [lineNum, linePos];
32
+ } else {
33
+ lineStartOffset = lineEnd + 1;
34
+ lineNum += 1;
35
+ }
36
+ }
37
+ }
38
+ /** Highlights an error.
39
+ *
40
+ * Returns a string with the line, and a string with the ^^^^ carets
41
+ */
42
+ function errorHighlight(source, span$1) {
43
+ let lineStartOffset = source.lastIndexOf("\n", span$1[0]);
44
+ if (lineStartOffset === -1) lineStartOffset = 0;
45
+ let lineEndOffset = source.indexOf("\n", span$1[0]);
46
+ if (lineEndOffset === -1) lineEndOffset = source.length;
47
+ const errorLength = span$1[1] - span$1[0];
48
+ const caretCount = Math.max(1, errorLength);
49
+ const linePos = span$1[0] - lineStartOffset;
50
+ return [source.slice(lineStartOffset, lineEndOffset), " ".repeat(linePos) + "^".repeat(caretCount)];
51
+ }
52
+
53
+ //#endregion
54
+ //#region ../wesl/src/vlq/vlq.ts
55
+ /*!
56
+ Copyright (c) 2017-2021 [these people](https://github.com/Rich-Harris/vlq/graphs/contributors)
57
+
58
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
59
+
60
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
61
+
62
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
63
+ */
64
+ const char_to_integer = {};
65
+ const integer_to_char = {};
66
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".split("").forEach((char, i) => {
67
+ char_to_integer[char] = i;
68
+ integer_to_char[i] = char;
69
+ });
70
+ function encodeVlq(value) {
71
+ if (typeof value === "number") return encode_integer(value);
72
+ let result = "";
73
+ for (let i = 0; i < value.length; i += 1) result += encode_integer(value[i]);
74
+ return result;
75
+ }
76
+ function encode_integer(num) {
77
+ let result = "";
78
+ let enc;
79
+ if (num < 0) enc = -num << 1 | 1;
80
+ else enc = num << 1;
81
+ do {
82
+ let clamped = enc & 31;
83
+ enc >>>= 5;
84
+ if (enc > 0) clamped |= 32;
85
+ result += integer_to_char[clamped];
86
+ } while (enc > 0);
87
+ return result;
88
+ }
89
+
90
+ //#endregion
91
+ //#region ../wesl/src/ClickableError.ts
92
+ /** Throw an error with an embedded source map so that browser users can
93
+ * click on the error in the browser debug console and see the wesl source code. */
94
+ function throwClickableError(params) {
95
+ const { url, text: text$1, lineNumber, lineColumn, length, error } = params;
96
+ const mappings = encodeVlq([
97
+ 0,
98
+ 0,
99
+ Math.max(0, lineNumber - 1),
100
+ Math.max(0, lineColumn - 1)
101
+ ]) + "," + encodeVlq([
102
+ 18,
103
+ 0,
104
+ Math.max(0, lineNumber - 1),
105
+ Math.max(0, lineColumn - 1) + length
106
+ ]);
107
+ const sourceMap = {
108
+ version: 3,
109
+ file: null,
110
+ sources: [url],
111
+ sourcesContent: [text$1 ?? null],
112
+ names: [],
113
+ mappings
114
+ };
115
+ let generatedCode = `throw new Error(${JSON.stringify(error.message + "")})`;
116
+ generatedCode += "\n//# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));
117
+ generatedCode += "\n//# sourceURL=" + sourceMap.sources[0];
118
+ let oldLimit = 0;
119
+ if ("stackTraceLimit" in Error) {
120
+ oldLimit = Error.stackTraceLimit;
121
+ Error.stackTraceLimit = 1;
122
+ }
123
+ try {
124
+ (0, eval)(generatedCode);
125
+ } catch (e) {
126
+ if ("stackTraceLimit" in Error) Error.stackTraceLimit = oldLimit;
127
+ error.message = "";
128
+ if (tracing) e.cause = error;
129
+ throw e;
130
+ }
131
+ }
132
+
133
+ //#endregion
134
+ //#region ../wesl/src/Assertions.ts
135
+ /** checks whether a condition is true, otherwise throws */
136
+ function assertThat(condition, msg) {
137
+ if (!condition) throw new Error(msg);
138
+ }
139
+ /** when debug testing is enabled, checks whether a condition is true, otherwise throws */
140
+ function assertThatDebug(condition, msg) {
141
+ tracing && assertThat(condition, msg);
142
+ }
143
+ /**
144
+ * Useful to validate that all cases are handled,
145
+ * TypeScript should complain if this statement could possibly be executed.
146
+ *
147
+ * If this is somehow executed at runtime, throw an exception.
148
+ */
149
+ function assertUnreachable(value) {
150
+ throw new ErrorWithData("Unreachable value", { data: value });
151
+ }
152
+ var ErrorWithData = class extends Error {
153
+ data;
154
+ constructor(message, options) {
155
+ super(message, options);
156
+ this.data = options?.data;
157
+ }
158
+ };
159
+
160
+ //#endregion
161
+ //#region ../wesl/src/Scope.ts
162
+ /** Combine two scope siblings.
163
+ * The first scope is mutated to append the contents of the second. */
164
+ function mergeScope(a, b) {
165
+ if (!b) return;
166
+ assertThatDebug(a.kind === b.kind);
167
+ assertThatDebug(a.parent === b.parent);
168
+ assertThatDebug(!b.ifAttribute);
169
+ a.contents = a.contents.concat(b.contents);
170
+ }
171
+ /** reset scope and ident debugging ids */
172
+ function resetScopeIds() {
173
+ scopeId = 0;
174
+ identId = 0;
175
+ }
176
+ let scopeId = 0;
177
+ let identId = 0;
178
+ function nextIdentId() {
179
+ return identId++;
180
+ }
181
+ /** make a new Scope object */
182
+ function emptyScope(parent, kind$1 = "scope") {
183
+ const id = scopeId++;
184
+ return {
185
+ id,
186
+ kind: kind$1,
187
+ parent,
188
+ contents: []
189
+ };
190
+ }
191
+
192
+ //#endregion
193
+ //#region ../../node_modules/.pnpm/berry-pretty@0.0.5/node_modules/berry-pretty/dist/index.js
194
+ var spaces = memoize((nesting) => {
195
+ return " ".repeat(nesting);
196
+ });
197
+ var defaultCallerSize = 20;
198
+ var multiLinePad = "\n" + spaces(defaultCallerSize + 3);
199
+ function memoize(fn$1) {
200
+ const cache = /* @__PURE__ */ new Map();
201
+ return function(...args) {
202
+ const key = JSON.stringify(args);
203
+ if (cache.has(key)) return cache.get(key);
204
+ else {
205
+ const value = fn$1(...args);
206
+ cache.set(key, value);
207
+ return value;
208
+ }
209
+ };
210
+ }
211
+ if (typeof DOMRect === "undefined") globalThis.DOMRect = function() {};
212
+
213
+ //#endregion
214
+ //#region ../wesl/src/WESLCollect.ts
215
+ function importElem(cc) {
216
+ const importElems = cc.tags.owo?.[0];
217
+ for (const importElem$1 of importElems) {
218
+ cc.app.stable.imports.push(importElem$1.imports);
219
+ addToOpenElem(cc, importElem$1);
220
+ }
221
+ }
222
+ /** add an elem to the .contents array of the currently containing element */
223
+ function addToOpenElem(cc, elem) {
224
+ const weslContext = cc.app.context;
225
+ const { openElems } = weslContext;
226
+ if (openElems?.length) {
227
+ const open = openElems[openElems.length - 1];
228
+ open.contents.push(elem);
229
+ }
230
+ }
231
+ /** create reference Ident and add to context */
232
+ function refIdent(cc) {
233
+ const { src, start, end } = cc;
234
+ const app = cc.app;
235
+ const { srcModule } = app.stable;
236
+ const originalName = src.slice(start, end);
237
+ const kind$1 = "ref";
238
+ const ident$1 = {
239
+ kind: kind$1,
240
+ originalName,
241
+ ast: cc.app.stable,
242
+ id: nextIdentId(),
243
+ refIdentElem: null
244
+ };
245
+ const identElem = {
246
+ kind: kind$1,
247
+ start,
248
+ end,
249
+ srcModule,
250
+ ident: ident$1
251
+ };
252
+ ident$1.refIdentElem = identElem;
253
+ saveIdent(cc, identElem);
254
+ addToOpenElem(cc, identElem);
255
+ return identElem;
256
+ }
257
+ /** create declaration Ident and add to context */
258
+ function declCollect(cc) {
259
+ return declCollectInternal(cc, false);
260
+ }
261
+ /** create global declaration Ident and add to context */
262
+ function globalDeclCollect(cc) {
263
+ return declCollectInternal(cc, true);
264
+ }
265
+ function declCollectInternal(cc, isGlobal) {
266
+ const { src, start, end } = cc;
267
+ const app = cc.app;
268
+ const { scope: containingScope } = app.context;
269
+ const { srcModule } = app.stable;
270
+ const originalName = src.slice(start, end);
271
+ const kind$1 = "decl";
272
+ const declElem = null;
273
+ const ident$1 = {
274
+ declElem,
275
+ kind: kind$1,
276
+ originalName,
277
+ containingScope,
278
+ isGlobal,
279
+ id: nextIdentId(),
280
+ srcModule
281
+ };
282
+ const identElem = {
283
+ kind: kind$1,
284
+ start,
285
+ end,
286
+ srcModule,
287
+ ident: ident$1
288
+ };
289
+ saveIdent(cc, identElem);
290
+ addToOpenElem(cc, identElem);
291
+ return identElem;
292
+ }
293
+ const typedDecl = collectElem("typeDecl", (cc, openElem) => {
294
+ const decl = cc.tags.decl_elem?.[0];
295
+ const typeRef = cc.tags.typeRefElem?.[0];
296
+ const typeScope = cc.tags.decl_type?.[0];
297
+ const partial = {
298
+ ...openElem,
299
+ decl,
300
+ typeScope,
301
+ typeRef
302
+ };
303
+ const elem = withTextCover(partial, cc);
304
+ return elem;
305
+ });
306
+ /** add Ident to current open scope, add IdentElem to current open element */
307
+ function saveIdent(cc, identElem) {
308
+ const { ident: ident$1 } = identElem;
309
+ ident$1.id = nextIdentId();
310
+ const weslContext = cc.app.context;
311
+ weslContext.scope.contents.push(ident$1);
312
+ }
313
+ /** start a new child lexical Scope */
314
+ function startScope(cc) {
315
+ startSomeScope("scope", cc);
316
+ }
317
+ /** start a new child partial Scope */
318
+ function startPartialScope(cc) {
319
+ startSomeScope("partial", cc);
320
+ }
321
+ /** start a new lexical or partial scope */
322
+ function startSomeScope(kind$1, cc) {
323
+ const { scope } = cc.app.context;
324
+ const newScope = emptyScope(scope, kind$1);
325
+ scope.contents.push(newScope);
326
+ cc.app.context.scope = newScope;
327
+ }
328
+ function completeScope(cc) {
329
+ return completeScopeInternal(cc, true);
330
+ }
331
+ function completeScopeNoIf(cc) {
332
+ return completeScopeInternal(cc, false);
333
+ }
334
+ function completeScopeInternal(cc, attachIfs) {
335
+ const weslContext = cc.app.context;
336
+ const completedScope = weslContext.scope;
337
+ const { parent } = completedScope;
338
+ if (parent) weslContext.scope = parent;
339
+ else if (tracing) console.log("ERR: completeScope, no parent scope", completedScope.contents);
340
+ if (attachIfs) {
341
+ const ifAttributes = collectIfAttributes(cc);
342
+ completedScope.ifAttribute = ifAttributes?.[0];
343
+ }
344
+ return completedScope;
345
+ }
346
+ /** return @if attributes from the 'attribute' tag */
347
+ function collectIfAttributes(cc) {
348
+ const attributes = cc.tags.attribute;
349
+ return filterIfAttributes(attributes);
350
+ }
351
+ function filterIfAttributes(attributes) {
352
+ if (!attributes) return;
353
+ return filterMap(attributes, (a) => a.attribute.kind === "@if" ? a.attribute : void 0);
354
+ }
355
+ function collectVarLike(kind$1) {
356
+ return collectElem(kind$1, (cc, openElem) => {
357
+ const name$1 = cc.tags.var_name?.[0];
358
+ const decl_scope = cc.tags.decl_scope?.[0];
359
+ const attributes = cc.tags.attribute;
360
+ const partElem = {
361
+ ...openElem,
362
+ name: name$1,
363
+ attributes
364
+ };
365
+ const varElem = withTextCover(partElem, cc);
366
+ const declIdent = name$1.decl.ident;
367
+ declIdent.declElem = varElem;
368
+ if (name$1.typeScope) {
369
+ mergeScope(name$1.typeScope, decl_scope);
370
+ declIdent.dependentScope = name$1.typeScope;
371
+ } else declIdent.dependentScope = decl_scope;
372
+ return varElem;
373
+ });
374
+ }
375
+ const aliasCollect = collectElem("alias", (cc, openElem) => {
376
+ const name$1 = cc.tags.alias_name?.[0];
377
+ const alias_scope = cc.tags.alias_scope?.[0];
378
+ const typeRef = cc.tags.typeRefElem?.[0];
379
+ const attributes = cc.tags.attributes?.flat() ?? [];
380
+ const partElem = {
381
+ ...openElem,
382
+ name: name$1,
383
+ attributes,
384
+ typeRef
385
+ };
386
+ const aliasElem = withTextCover(partElem, cc);
387
+ name$1.ident.dependentScope = alias_scope;
388
+ name$1.ident.declElem = aliasElem;
389
+ return aliasElem;
390
+ });
391
+ /**
392
+ * Collect a FnElem and associated scopes.
393
+ *
394
+ * Scope definition is a bit complicated in wgsl and wesl for fns.
395
+ * Here's what we collect for scopes for this example function:
396
+ * @if(true) fn foo(a: u32) -> @location(x) R { let y = a; }
397
+ *
398
+ * -{ // partial scope in case the whole shebang is prefixed by an `@if`
399
+ * %foo
400
+ *
401
+ * {<=%foo // foo decl references this header+returnType+body scope (for tracing dependencies from decls)
402
+ * x // for @location(x) (contains no decls, so ok to merge for tracing)
403
+ * %a u32 // merged from header scope
404
+ * R // merged from return type (contains no decls, so ok to merge for tracing)
405
+ * %y a // merged body scope
406
+ * }
407
+ * }
408
+ */
409
+ const fnCollect = collectElem("fn", (cc, openElem) => {
410
+ const ourTags = fnTags(cc);
411
+ const { name: name$1, headerScope, returnScope, bodyScope, body, params } = ourTags;
412
+ const { attributes, returnAttributes, returnType, fnScope } = ourTags;
413
+ const fnElem = {
414
+ ...openElem,
415
+ name: name$1,
416
+ attributes,
417
+ params,
418
+ returnAttributes,
419
+ body,
420
+ returnType
421
+ };
422
+ fnScope.ifAttribute = filterIfAttributes(attributes)?.[0];
423
+ const mergedScope = headerScope;
424
+ returnScope && mergeScope(mergedScope, returnScope);
425
+ mergeScope(mergedScope, bodyScope);
426
+ const filtered = [];
427
+ for (const e of fnScope.contents) if (e === headerScope || e === returnScope) continue;
428
+ else if (e === bodyScope) filtered.push(mergedScope);
429
+ else filtered.push(e);
430
+ fnScope.contents = filtered;
431
+ name$1.ident.declElem = fnElem;
432
+ name$1.ident.dependentScope = mergedScope;
433
+ return fnElem;
434
+ });
435
+ /** Fetch and cast the collection tags for fnCollect
436
+ * LATER typechecking for collect! */
437
+ function fnTags(cc) {
438
+ const { fn_attributes, fn_name, fn_param, return_attributes } = cc.tags;
439
+ const { return_type } = cc.tags;
440
+ const { header_scope, return_scope, body_scope, body_statement } = cc.tags;
441
+ const { fn_partial_scope } = cc.tags;
442
+ const name$1 = fn_name?.[0];
443
+ const headerScope = header_scope?.[0];
444
+ const returnScope = return_scope?.[0];
445
+ const bodyScope = body_scope?.[0];
446
+ const body = body_statement?.[0];
447
+ const params = fn_param?.flat(3) ?? [];
448
+ const attributes = fn_attributes?.flat();
449
+ const returnAttributes = return_attributes?.flat();
450
+ const returnType = return_type?.flat(3)[0];
451
+ const fnScope = fn_partial_scope?.[0];
452
+ return {
453
+ name: name$1,
454
+ headerScope,
455
+ returnScope,
456
+ bodyScope,
457
+ body,
458
+ params,
459
+ attributes,
460
+ returnAttributes,
461
+ returnType,
462
+ fnScope
463
+ };
464
+ }
465
+ const collectFnParam = collectElem("param", (cc, openElem) => {
466
+ const name$1 = cc.tags.param_name?.[0];
467
+ const attributes = cc.tags.attributes?.flat() ?? [];
468
+ const elem = {
469
+ ...openElem,
470
+ name: name$1,
471
+ attributes
472
+ };
473
+ const paramElem = withTextCover(elem, cc);
474
+ name$1.decl.ident.declElem = paramElem;
475
+ return paramElem;
476
+ });
477
+ const collectStruct = collectElem("struct", (cc, openElem) => {
478
+ const name$1 = cc.tags.type_name?.[0];
479
+ const members = cc.tags.members;
480
+ const attributes = cc.tags.attributes?.flat() ?? [];
481
+ name$1.ident.dependentScope = cc.tags.struct_scope?.[0];
482
+ const structElem = {
483
+ ...openElem,
484
+ name: name$1,
485
+ attributes,
486
+ members
487
+ };
488
+ const elem = withTextCover(structElem, cc);
489
+ name$1.ident.declElem = elem;
490
+ return elem;
491
+ });
492
+ const collectStructMember = collectElem("member", (cc, openElem) => {
493
+ const name$1 = cc.tags.nameElem?.[0];
494
+ const typeRef = cc.tags.typeRefElem?.[0];
495
+ const attributes = cc.tags.attribute?.flat(3);
496
+ const partElem = {
497
+ ...openElem,
498
+ name: name$1,
499
+ attributes,
500
+ typeRef
501
+ };
502
+ return withTextCover(partElem, cc);
503
+ });
504
+ const specialAttribute = collectElem("attribute", (cc, openElem) => {
505
+ const attribute = cc.tags.attr_variant?.[0];
506
+ const attrElem = {
507
+ ...openElem,
508
+ attribute
509
+ };
510
+ return attrElem;
511
+ });
512
+ const assertCollect = attrElemCollect("assert");
513
+ const statementCollect = attrElemCollect("statement");
514
+ const switchClauseCollect = attrElemCollect("switch-clause");
515
+ /** @return a collector for container elem types that have only an attributes field */
516
+ function attrElemCollect(kind$1) {
517
+ return collectElem(kind$1, (cc, openElem) => {
518
+ const attributes = cc.tags.attribute?.flat(3);
519
+ const partElem = {
520
+ ...openElem,
521
+ attributes
522
+ };
523
+ return withTextCover(partElem, cc);
524
+ });
525
+ }
526
+ const collectAttribute = collectElem("attribute", (cc, openElem) => {
527
+ const params = cc.tags.attrParam;
528
+ const name$1 = cc.tags.name?.[0];
529
+ const kind$1 = "@attribute";
530
+ const stdAttribute = {
531
+ kind: kind$1,
532
+ name: name$1,
533
+ params
534
+ };
535
+ const attrElem = {
536
+ ...openElem,
537
+ attribute: stdAttribute
538
+ };
539
+ return attrElem;
540
+ });
541
+ const typeRefCollect = collectElem("type", (cc, openElem) => {
542
+ const templateParamsTemp = cc.tags.templateParam?.flat(3);
543
+ const typeRef = cc.tags.typeRefName?.[0];
544
+ const name$1 = typeof typeRef === "string" ? typeRef : typeRef.ident;
545
+ const partElem = {
546
+ ...openElem,
547
+ name: name$1,
548
+ templateParams: templateParamsTemp
549
+ };
550
+ return withTextCover(partElem, cc);
551
+ });
552
+ const expressionCollect = collectElem("expression", (cc, openElem) => {
553
+ const partElem = { ...openElem };
554
+ return withTextCover(partElem, cc);
555
+ });
556
+ function globalAssertCollect(cc) {
557
+ const globalAssert = cc.tags.const_assert?.flat()[0];
558
+ const ast = cc.app.stable;
559
+ if (!ast.moduleAsserts) ast.moduleAsserts = [];
560
+ ast.moduleAsserts.push(globalAssert);
561
+ }
562
+ const stuffCollect = collectElem("stuff", (cc, openElem) => {
563
+ const partElem = { ...openElem };
564
+ return withTextCover(partElem, cc);
565
+ });
566
+ const memberRefCollect = collectElem("memberRef", (cc, openElem) => {
567
+ const { component, structRef, extra_components } = cc.tags;
568
+ const member = component?.[0];
569
+ const name$1 = structRef?.flat()[0];
570
+ const extraComponents = extra_components?.flat()[0];
571
+ const partElem = {
572
+ ...openElem,
573
+ name: name$1,
574
+ member,
575
+ extraComponents
576
+ };
577
+ return withTextCover(partElem, cc);
578
+ });
579
+ function nameCollect(cc) {
580
+ const { start, end, src } = cc;
581
+ const name$1 = src.slice(start, end);
582
+ const elem = {
583
+ kind: "name",
584
+ start,
585
+ end,
586
+ name: name$1
587
+ };
588
+ addToOpenElem(cc, elem);
589
+ return elem;
590
+ }
591
+ const collectModule = collectElem("module", (cc, openElem) => {
592
+ const ccComplete = {
593
+ ...cc,
594
+ start: 0,
595
+ end: cc.src.length
596
+ };
597
+ const moduleElem = withTextCover(openElem, ccComplete);
598
+ const weslState = cc.app.stable;
599
+ weslState.moduleElem = moduleElem;
600
+ return moduleElem;
601
+ });
602
+ function directiveCollect(cc) {
603
+ const { start, end } = cc;
604
+ const directive = cc.tags.directive?.flat()[0];
605
+ const attributes = cc.tags.attribute?.flat();
606
+ const kind$1 = "directive";
607
+ const elem = {
608
+ kind: kind$1,
609
+ attributes,
610
+ start,
611
+ end,
612
+ directive
613
+ };
614
+ addToOpenElem(cc, elem);
615
+ return elem;
616
+ }
617
+ /**
618
+ * Collect a LexicalScope.
619
+ *
620
+ * The scope starts encloses all idents and subscopes inside the parser to which
621
+ * .collect is attached
622
+ */
623
+ const scopeCollect = {
624
+ before: startScope,
625
+ after: completeScope
626
+ };
627
+ /**
628
+ * Collect a LexicalScope.
629
+ *
630
+ * The scope starts encloses all idents and subscopes inside the parser to which
631
+ * .collect is attached
632
+ *
633
+ * '@if' attributes are not attached to the scope.
634
+ */
635
+ const scopeCollectNoIf = {
636
+ before: startScope,
637
+ after: completeScopeNoIf
638
+ };
639
+ /**
640
+ * Collect a PartialScope.
641
+ *
642
+ * The scope starts encloses all idents and subscopes inside the parser to which
643
+ * .collect is attached
644
+ */
645
+ const partialScopeCollect = {
646
+ before: startPartialScope,
647
+ after: completeScope
648
+ };
649
+ /** utility to collect an ElemWithContents
650
+ * starts the new element as the collection point corresponding
651
+ * to the start of the attached grammar and completes
652
+ * the element in the at the end of the grammar.
653
+ *
654
+ * In between the start and the end, the new element is available
655
+ * as an 'open' element in the collection context. While this element
656
+ * is 'open', other collected are added to the 'contents' field of this
657
+ * open element.
658
+ */
659
+ function collectElem(kind$1, fn$1) {
660
+ return {
661
+ before: (cc) => {
662
+ const partialElem = {
663
+ kind: kind$1,
664
+ contents: []
665
+ };
666
+ const weslContext = cc.app.context;
667
+ weslContext.openElems.push(partialElem);
668
+ },
669
+ after: (cc) => {
670
+ const weslContext = cc.app.context;
671
+ const partialElem = weslContext.openElems.pop();
672
+ console.assert(partialElem && partialElem.kind === kind$1);
673
+ const elem = fn$1(cc, {
674
+ ...partialElem,
675
+ start: cc.start,
676
+ end: cc.end
677
+ });
678
+ if (elem) addToOpenElem(cc, elem);
679
+ return elem;
680
+ }
681
+ };
682
+ }
683
+ /**
684
+ * @return a copy of the element with contents extended
685
+ * to include TextElems to cover the entire range.
686
+ */
687
+ function withTextCover(elem, cc) {
688
+ const contents = coverWithText(cc, elem);
689
+ return {
690
+ ...elem,
691
+ contents
692
+ };
693
+ }
694
+ /** cover the entire source range with Elems by creating TextElems to
695
+ * cover any parts of the source that are not covered by other elems
696
+ * @returns the existing elems combined with any new TextElems, in src order */
697
+ function coverWithText(cc, elem) {
698
+ let { start: pos } = cc;
699
+ const ast = cc.app.stable;
700
+ const { contents, end } = elem;
701
+ const sorted = contents.sort((a, b) => a.start - b.start);
702
+ const elems = [];
703
+ for (const elem$1 of sorted) {
704
+ if (pos < elem$1.start) elems.push(makeTextElem(elem$1.start));
705
+ elems.push(elem$1);
706
+ pos = elem$1.end;
707
+ }
708
+ if (pos < end) elems.push(makeTextElem(end));
709
+ return elems;
710
+ function makeTextElem(end$1) {
711
+ return {
712
+ kind: "text",
713
+ start: pos,
714
+ end: end$1,
715
+ srcModule: ast.srcModule
716
+ };
717
+ }
718
+ }
719
+
720
+ //#endregion
721
+ //#region ../wesl/src/parse/WeslBaseGrammar.ts
722
+ const word = kind("word");
723
+ const keyword = kind("keyword");
724
+ const qualified_ident = withSepPlus("::", or(word, keyword, "package", "super"));
725
+ const number = kind("number");
726
+
727
+ //#endregion
728
+ //#region ../wesl/src/parse/ImportGrammar.ts
729
+ function makeStatement(segments, finalSegment) {
730
+ return {
731
+ kind: "import-statement",
732
+ segments,
733
+ finalSegment
734
+ };
735
+ }
736
+ function makeSegment(name$1) {
737
+ return {
738
+ kind: "import-segment",
739
+ name: name$1
740
+ };
741
+ }
742
+ function makeCollection(subtrees) {
743
+ return {
744
+ kind: "import-collection",
745
+ subtrees
746
+ };
747
+ }
748
+ function makeItem(name$1, as) {
749
+ return {
750
+ kind: "import-item",
751
+ name: name$1,
752
+ as
753
+ };
754
+ }
755
+ function prependSegments(segments, statement$1) {
756
+ statement$1.segments = segments.concat(statement$1.segments);
757
+ return statement$1;
758
+ }
759
+ let import_collection = null;
760
+ const segment_blacklist = or("super", "package", "import", "as");
761
+ /** words allowed in an import segment (after the package:: or super::super:: part if any) */
762
+ const packageWord = preceded(not(segment_blacklist), or(word, keyword));
763
+ const import_path_or_item = seq(packageWord, or(preceded("::", req(or(fn(() => import_collection), fn(() => import_path_or_item)), "invalid import, expected '{' or name")), preceded("as", req(word, "invalid alias, expected name")).map((v) => makeItem("", v)), yes().map(() => makeItem("")))).map(([name$1, next]) => {
764
+ if (next.kind === "import-collection") return makeStatement([makeSegment(name$1)], next);
765
+ else if (next.kind === "import-statement") return prependSegments([makeSegment(name$1)], next);
766
+ else if (next.kind === "import-item") {
767
+ next.name = name$1;
768
+ return makeStatement([], next);
769
+ } else assertUnreachable(next);
770
+ });
771
+ import_collection = delimited("{", withSepPlus(",", () => import_path_or_item).map(makeCollection), req("}", "invalid import collection, expected }"));
772
+ const import_relative = or(terminated("package", req("::", "invalid import, expected '::'")).map((v) => [makeSegment(v)]), repeatPlus(terminated("super", req("::", "invalid import, expected '::'")).map(makeSegment)));
773
+ const import_statement = span(delimited("import", seqObj({
774
+ relative: opt(import_relative),
775
+ collection_or_statement: req(or(import_collection, import_path_or_item), "invalid import, expected { or name")
776
+ }).map(({ relative, collection_or_statement }) => {
777
+ if (collection_or_statement.kind === "import-statement") return prependSegments(relative ?? [], collection_or_statement);
778
+ else return makeStatement(relative ?? [], collection_or_statement);
779
+ }), req(";", "invalid import, expected ';'"))).map((v) => ({
780
+ kind: "import",
781
+ imports: v.value,
782
+ start: v.span[0],
783
+ end: v.span[1]
784
+ }));
785
+ /** parse a WESL style wgsl import statement. */
786
+ const weslImports = tagScope(repeat(import_statement).ptag("owo").collect(importElem));
787
+ if (tracing) {
788
+ const names = {
789
+ import_collection,
790
+ import_path_or_item,
791
+ import_relative,
792
+ import_statement,
793
+ weslImports
794
+ };
795
+ Object.entries(names).forEach(([name$1, parser]) => {
796
+ parser.setTraceName(name$1);
797
+ });
798
+ }
799
+
800
+ //#endregion
801
+ //#region ../wesl/src/parse/Keywords.ts
802
+ /** https://www.w3.org/TR/WGSL/#keyword-summary */
803
+ const keywords = `alias break case const const_assert continue continuing
804
+ default diagnostic discard else enable false fn for if
805
+ let loop override requires return struct switch true var while`.split(/\s+/);
806
+ /** https://www.w3.org/TR/WGSL/#reserved-words */
807
+ const reservedWords = `NULL Self abstract active alignas alignof as asm asm_fragment async attribute auto await
808
+ become binding_array cast catch class co_await co_return co_yield coherent column_major
809
+ common compile compile_fragment concept const_cast consteval constexpr constinit crate
810
+ debugger decltype delete demote demote_to_helper do dynamic_cast
811
+ enum explicit export extends extern external fallthrough filter final finally friend from fxgroup
812
+ get goto groupshared highp impl implements import inline instanceof interface layout lowp
813
+ macro macro_rules match mediump meta mod module move mut mutable
814
+ namespace new nil noexcept noinline nointerpolation non_coherent noncoherent noperspective null nullptr
815
+ of operator package packoffset partition pass patch pixelfragment precise precision premerge
816
+ priv protected pub public readonly ref regardless register reinterpret_cast require resource restrict
817
+ self set shared sizeof smooth snorm static static_assert static_cast std subroutine super
818
+ target template this thread_local throw trait try type typedef typeid typename typeof
819
+ union unless unorm unsafe unsized use using varying virtual volatile wgsl where with writeonly yield`.split(/\s+/);
820
+
821
+ //#endregion
822
+ //#region ../wesl/src/parse/WeslStream.ts
823
+ /** Whitespaces including new lines */
824
+ const blankspaces = /[ \t\n\v\f\r\u{0085}\u{200E}\u{200F}\u{2028}\u{2029}]+/u;
825
+ const symbolSet = "& && -> @ / ! [ ] { } :: : , == = != >>= >> >= > <<= << <= < % - -- . + ++ | || ( ) ; * ~ ^ // /* */ += -= *= /= %= &= |= ^= _";
826
+ const ident = /(?:(?:[_\p{XID_Start}][\p{XID_Continue}]+)|(?:[\p{XID_Start}]))/u;
827
+ const keywordOrReserved = new Set(keywords.concat(reservedWords));
828
+ const digits = new RegExp(/(?:0[fh])|(?:[1-9][0-9]*[fh])/.source + /|(?:[0-9]*\.[0-9]+(?:[eE][+-]?[0-9]+)?[fh]?)/.source + /|(?:[0-9]+\.[0-9]*(?:[eE][+-]?[0-9]+)?[fh]?)/.source + /|(?:[0-9]+[eE][+-]?[0-9]+[fh]?)/.source + /|(?:0[xX][0-9a-fA-F]*\.[0-9a-fA-F]+(?:[pP][+-]?[0-9]+[fh]?)?)/.source + /|(?:0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*(?:[pP][+-]?[0-9]+[fh]?)?)/.source + /|(?:0[xX][0-9a-fA-F]+[pP][+-]?[0-9]+[fh]?)/.source + /|(?:0[xX][0-9a-fA-F]+[iu]?)/.source + /|(?:0[iu]?)|(?:[1-9][0-9]*[iu]?)/.source);
829
+ const commentStart = /\/\/|\/\*/;
830
+ const weslMatcher = new RegexMatchers({
831
+ word: ident,
832
+ number: digits,
833
+ blankspaces,
834
+ commentStart,
835
+ symbol: matchOneOf(symbolSet),
836
+ invalid: /[^]/
837
+ });
838
+ /** To mark parts of the grammar implementation that are WESL specific extensions */
839
+ function weslExtension(combinator) {
840
+ return combinator;
841
+ }
842
+ /** A stream that produces WESL tokens, skipping over comments and white space */
843
+ var WeslStream = class {
844
+ stream;
845
+ /** New line */
846
+ eolPattern = /[\n\v\f\u{0085}\u{2028}\u{2029}]|\r\n?/gu;
847
+ /** Block comments */
848
+ blockCommentPattern = /\/\*|\*\//g;
849
+ constructor(src) {
850
+ this.src = src;
851
+ this.stream = new CachingStream(new MatchersStream(src, weslMatcher));
852
+ }
853
+ checkpoint() {
854
+ return this.stream.checkpoint();
855
+ }
856
+ reset(position) {
857
+ this.stream.reset(position);
858
+ }
859
+ nextToken() {
860
+ while (true) {
861
+ const token$1 = this.stream.nextToken();
862
+ if (token$1 === null) return null;
863
+ const kind$1 = token$1.kind;
864
+ if (kind$1 === "blankspaces") continue;
865
+ else if (kind$1 === "commentStart") if (token$1.text === "//") this.stream.reset(this.skipToEol(token$1.span[1]));
866
+ else this.stream.reset(this.skipBlockComment(token$1.span[1]));
867
+ else if (kind$1 === "word") {
868
+ const returnToken = token$1;
869
+ if (keywordOrReserved.has(token$1.text)) returnToken.kind = "keyword";
870
+ return returnToken;
871
+ } else if (kind$1 === "invalid") throw new ParseError("Invalid token " + token$1.text, token$1.span);
872
+ else return token$1;
873
+ }
874
+ }
875
+ skipToEol(position) {
876
+ this.eolPattern.lastIndex = position;
877
+ const result = this.eolPattern.exec(this.src);
878
+ if (result === null) return this.src.length;
879
+ else return this.eolPattern.lastIndex;
880
+ }
881
+ skipBlockComment(start) {
882
+ let position = start;
883
+ while (true) {
884
+ this.blockCommentPattern.lastIndex = position;
885
+ const result = this.blockCommentPattern.exec(this.src);
886
+ if (result === null) throw new ParseError("Unclosed block comment!", [position, position]);
887
+ else if (result[0] === "*/") return this.blockCommentPattern.lastIndex;
888
+ else if (result[0] === "/*") position = this.skipBlockComment(this.blockCommentPattern.lastIndex);
889
+ else throw new Error("Unreachable, invalid block comment pattern");
890
+ }
891
+ }
892
+ /**
893
+ * Only matches the `<` token if it is a template
894
+ * Precondition: An ident was parsed right before this.
895
+ * Runs the [template list discovery algorithm](https://www.w3.org/TR/WGSL/#template-list-discovery).
896
+ */
897
+ nextTemplateStartToken() {
898
+ const startPosition = this.stream.checkpoint();
899
+ const token$1 = this.nextToken();
900
+ this.stream.reset(startPosition);
901
+ if (token$1 === null) return null;
902
+ if (token$1.kind !== "symbol") return null;
903
+ if (token$1.text === "<") if (this.isTemplateStart(token$1.span[1])) {
904
+ this.stream.reset(token$1.span[1]);
905
+ return token$1;
906
+ } else {
907
+ this.stream.reset(startPosition);
908
+ return null;
909
+ }
910
+ else return null;
911
+ }
912
+ nextTemplateEndToken() {
913
+ const startPosition = this.stream.checkpoint();
914
+ const token$1 = this.nextToken();
915
+ this.stream.reset(startPosition);
916
+ if (token$1 === null) return null;
917
+ if (token$1.kind === "symbol" && token$1.text[0] === ">") {
918
+ const tokenPosition = token$1.span[0];
919
+ this.stream.reset(tokenPosition + 1);
920
+ return {
921
+ kind: "symbol",
922
+ span: [tokenPosition, tokenPosition + 1],
923
+ text: ">"
924
+ };
925
+ } else return null;
926
+ }
927
+ isTemplateStart(afterToken) {
928
+ this.stream.reset(afterToken);
929
+ let pendingCounter = 1;
930
+ while (true) {
931
+ const nextToken = this.stream.nextToken();
932
+ if (nextToken === null) return false;
933
+ if (nextToken.kind !== "symbol") continue;
934
+ if (nextToken.text === "<") pendingCounter += 1;
935
+ else if (nextToken.text[0] === ">") {
936
+ if (nextToken.text === ">" || nextToken.text === ">=") pendingCounter -= 1;
937
+ else if (nextToken.text === ">>=" || nextToken.text === ">>") pendingCounter -= 2;
938
+ else throw new Error("This case should never be reached, looks like we forgot one of the tokens that start with >");
939
+ if (pendingCounter <= 0) return true;
940
+ } else if (nextToken.text === "(") this.skipBracketsTo(")");
941
+ else if (nextToken.text === "[") this.skipBracketsTo("]");
942
+ else if (nextToken.text === "==" || nextToken.text === "!=" || nextToken.text === ";" || nextToken.text === "{" || nextToken.text === ":" || nextToken.text === "&&" || nextToken.text === "||") return false;
943
+ }
944
+ }
945
+ /**
946
+ * Call this after consuming an opening bracket.
947
+ * Skips until a closing bracket. This also consumes the closing bracket.
948
+ */
949
+ skipBracketsTo(closingBracket) {
950
+ while (true) {
951
+ const nextToken = this.stream.nextToken();
952
+ if (nextToken === null) {
953
+ const after = this.stream.checkpoint();
954
+ throw new ParseError("Unclosed bracket!", [after, after]);
955
+ }
956
+ if (nextToken.kind !== "symbol") continue;
957
+ if (nextToken.text === "(") this.skipBracketsTo(")");
958
+ else if (nextToken.text === "[") this.skipBracketsTo("]");
959
+ else if (nextToken.text === closingBracket) return;
960
+ }
961
+ }
962
+ };
963
+ const templateOpen = withStreamAction((stream) => {
964
+ return stream.nextTemplateStartToken();
965
+ });
966
+ const templateClose = withStreamAction((stream) => {
967
+ return stream.nextTemplateEndToken();
968
+ });
969
+
970
+ //#endregion
971
+ //#region ../wesl/src/parse/WeslExpression.ts
972
+ const opt_template_list = opt(seq(templateOpen, withSepPlus(",", () => template_parameter), req(templateClose, "invalid template, expected '>'")));
973
+ const other_address_space = or("private", "workgroup", "uniform", "function");
974
+ const storage_address_space = seq("storage", opt(seq(",", or("read", "read_write"))));
975
+ const var_template_list = opt(seq(templateOpen, or(storage_address_space, other_address_space), req(templateClose, "invalid template, expected '>'")));
976
+ const template_elaborated_ident = seq(qualified_ident.collect(refIdent), opt_template_list);
977
+ const literal = or("true", "false", number);
978
+ const paren_expression = seq("(", () => expression, req(")", "invalid expression, expected ')'"));
979
+ const primary_expression = or(literal, paren_expression, seq(template_elaborated_ident, opt(fn(() => argument_expression_list))));
980
+ const component_or_swizzle = repeatPlus(or(preceded(".", word), collectArray(delimited("[", () => expression, req("]", "invalid expression, expected ']'")))));
981
+ /** parse simple struct.member style references specially, for binding struct lowering */
982
+ const simple_component_reference = tagScope(seq(qualified_ident.collect(refIdent, "structRef"), seq(".", word.collect(nameCollect, "component")), opt(component_or_swizzle.collect(stuffCollect, "extra_components"))).collect(memberRefCollect));
983
+ const unary_expression = or(seq(tokenOf("symbol", [
984
+ "!",
985
+ "&",
986
+ "*",
987
+ "-",
988
+ "~"
989
+ ]), () => unary_expression), or(simple_component_reference, seq(primary_expression, opt(component_or_swizzle))));
990
+ const bitwise_post_unary = or(repeatPlus(seq("&", unary_expression)), repeatPlus(seq("^", unary_expression)), repeatPlus(seq("|", unary_expression)));
991
+ const multiplicative_operator = or("%", "*", "/");
992
+ const additive_operator = or("+", "-");
993
+ const shift_post_unary = (inTemplate) => {
994
+ const shift_left = seq("<<", unary_expression);
995
+ const shift_right = seq(">>", unary_expression);
996
+ const mul_add = seq(repeat(seq(multiplicative_operator, unary_expression)), repeat(seq(additive_operator, unary_expression, repeat(seq(multiplicative_operator, unary_expression)))));
997
+ return inTemplate ? or(shift_left, mul_add) : or(shift_left, shift_right, mul_add);
998
+ };
999
+ const relational_post_unary = (inTemplate) => {
1000
+ return seq(shift_post_unary(inTemplate), opt(seq(inTemplate ? tokenOf("symbol", [
1001
+ "<",
1002
+ "<=",
1003
+ "!=",
1004
+ "=="
1005
+ ]) : tokenOf("symbol", [
1006
+ ">",
1007
+ ">=",
1008
+ "<",
1009
+ "<=",
1010
+ "!=",
1011
+ "=="
1012
+ ]), unary_expression, shift_post_unary(inTemplate))));
1013
+ };
1014
+ /** The expression parser exists in two variants
1015
+ * `true` is template-expression: Refuses to parse parse symbols like `&&` and `||`.
1016
+ * `false` is maybe-template-expression: Does the template disambiguation.
1017
+ */
1018
+ const expressionParser = (inTemplate) => {
1019
+ return seq(unary_expression, or(bitwise_post_unary, seq(relational_post_unary(inTemplate), inTemplate ? yes() : or(repeatPlus(seq("||", seq(unary_expression, relational_post_unary(false)))), repeatPlus(seq("&&", seq(unary_expression, relational_post_unary(false)))), yes().map(() => [])))));
1020
+ };
1021
+ const maybe_template = false;
1022
+ const expression = expressionParser(maybe_template);
1023
+ const is_template = true;
1024
+ const template_arg_expression = expressionParser(is_template);
1025
+ const std_type_specifier = seq(qualified_ident.collect(refIdent, "typeRefName"), () => opt_template_list).collect(typeRefCollect);
1026
+ const type_specifier = tagScope(std_type_specifier).ctag("typeRefElem");
1027
+ /** a template_arg_expression with additional collection for parameters
1028
+ * that are types like array<f32> vs. expressions like 1+2 */
1029
+ const template_parameter = or(type_specifier.ctag("templateParam"), template_arg_expression.collect(expressionCollect, "templateParam"));
1030
+ const argument_expression_list = seq("(", withSep(",", expression), req(")", "invalid fn arguments, expected ')'"));
1031
+ if (tracing) {
1032
+ const names = {
1033
+ opt_template_list,
1034
+ other_address_space,
1035
+ storage_address_space,
1036
+ var_template_list,
1037
+ template_elaborated_ident,
1038
+ primary_expression,
1039
+ literal,
1040
+ paren_expression,
1041
+ component_or_swizzle,
1042
+ simple_component_reference,
1043
+ unary_expression,
1044
+ bitwise_post_unary,
1045
+ multiplicative_operator,
1046
+ additive_operator,
1047
+ expression,
1048
+ template_arg_expression,
1049
+ std_type_specifier,
1050
+ type_specifier,
1051
+ template_parameter,
1052
+ argument_expression_list
1053
+ };
1054
+ Object.entries(names).forEach(([name$1, parser]) => {
1055
+ parser.setTraceName(name$1);
1056
+ });
1057
+ }
1058
+
1059
+ //#endregion
1060
+ //#region ../wesl/src/parse/WeslGrammar.ts
1061
+ const name = tokenKind("word").map(makeName);
1062
+ const diagnostic_rule_name = seq(name, opt(preceded(".", req(name, "invalid diagnostic rule name, expected name"))));
1063
+ const diagnostic_control = delimited("(", req(separated_pair(name, ",", diagnostic_rule_name), "invalid diagnostic control, expected rule name"), seq(opt(","), req(")", "invalid diagnostic control, expected ')'")));
1064
+ /** list of words that aren't identifiers (e.g. for @interpolate) */
1065
+ const name_list = withSep(",", name, { requireOne: true });
1066
+ const special_attribute = tagScope(preceded("@", or(or("compute", "const", "fragment", "invariant", "must_use", "vertex").map((name$1) => makeStandardAttribute([name$1, []])), preceded("interpolate", req(delimited("(", name_list, ")"), "invalid @interpolate, expected '('")).map(makeInterpolateAttribute), preceded("builtin", req(delimited("(", name, ")"), "invalid @builtin, expected '('")).map(makeBuiltinAttribute), preceded("diagnostic", req(diagnostic_control, "invalid @diagnostic, expected '('")).map(makeDiagnosticAttribute)).ptag("attr_variant")).collect(specialAttribute));
1067
+ const if_attribute = tagScope(preceded(seq("@", weslExtension("if")), span(delimited("(", fn(() => attribute_if_expression), seq(opt(","), ")"))).map(makeTranslateTimeExpressionElem)).map(makeIfAttribute).ptag("attr_variant").collect(specialAttribute));
1068
+ const normal_attribute = tagScope(preceded("@", or(seq(or("workgroup_size", "align", "binding", "blend_src", "group", "id", "location", "size").ptag("name"), req(() => attribute_argument_list, "invalid attribute, expected '('")), seq(word.ptag("name"), opt(() => attribute_argument_list)))).collect(collectAttribute));
1069
+ const attribute_argument_list = delimited("(", withSep(",", span(fn(() => expression)).collect(expressionCollect, "attrParam")), req(")", "invalid attribute arguments, expected ')'"));
1070
+ const attribute_no_if = or(special_attribute, normal_attribute).ctag("attribute");
1071
+ const attribute_incl_if = or(if_attribute, special_attribute, normal_attribute).ctag("attribute");
1072
+ const opt_attributes = repeat(attribute_incl_if);
1073
+ const opt_attributes_no_if = repeat(attribute_no_if);
1074
+ const globalTypeNameDecl = req(word.collect(globalDeclCollect, "type_name"), "invalid type name, expected a name");
1075
+ const fnNameDecl = req(word.collect(globalDeclCollect, "fn_name"), "missing fn name");
1076
+ const optionally_typed_ident = tagScope(seq(word.collect(declCollect, "decl_elem"), opt(seq(":", type_specifier))).collect(typedDecl)).ctag("var_name");
1077
+ const req_optionally_typed_ident = req(optionally_typed_ident, "invalid ident");
1078
+ const global_ident = tagScope(req(seq(word.collect(globalDeclCollect, "decl_elem"), opt(seq(":", type_specifier.collect(scopeCollect, "decl_type")))).collect(typedDecl), "expected identifier")).ctag("var_name");
1079
+ const struct_member = tagScope(seq(opt_attributes, word.collect(nameCollect, "nameElem"), req(":", "invalid struct member, expected ':'"), req(type_specifier, "invalid struct member, expected type specifier")).collect(collectStructMember)).ctag("members");
1080
+ const struct_decl = seq(weslExtension(opt_attributes).collect((cc) => cc.tags.attribute, "attributes"), "struct", req(globalTypeNameDecl, "invalid struct, expected name"), seq(req("{", "invalid struct, expected '{'"), withSepPlus(",", struct_member), req("}", "invalid struct, expected '}'")).collect(scopeCollect, "struct_scope")).collect(collectStruct);
1081
+ /** Also covers func_call_statement.post.ident */
1082
+ const fn_call = seq(qualified_ident.collect(refIdent), () => opt_template_list, argument_expression_list);
1083
+ const fnParam = tagScope(seq(opt_attributes.collect((cc) => cc.tags.attribute, "attributes"), word.collect(declCollect, "decl_elem"), opt(seq(":", req(type_specifier, "invalid fn parameter, expected type specifier"))).collect(typedDecl, "param_name")).collect(collectFnParam)).ctag("fn_param");
1084
+ const fnParamList = seq("(", withSep(",", fnParam), ")");
1085
+ const local_variable_decl = seq("var", () => var_template_list, req_optionally_typed_ident, opt(seq("=", () => expression))).collect(collectVarLike("var"));
1086
+ const global_variable_decl = seq(opt_attributes, "var", () => var_template_list, global_ident, opt(seq("=", () => expression.collect(scopeCollect, "decl_scope"))), ";").collect(collectVarLike("gvar")).collect(partialScopeCollect);
1087
+ const attribute_if_primary_expression = or(tokenOf("keyword", ["true", "false"]).map(makeLiteral), delimited(token("symbol", "("), fn(() => attribute_if_expression), token("symbol", ")")).map(makeParenthesizedExpression), tokenKind("word").map(makeTranslateTimeFeature));
1088
+ const attribute_if_unary_expression = or(seq(token("symbol", "!").map(makeUnaryOperator), fn(() => attribute_if_unary_expression)).map(makeUnaryExpression), attribute_if_primary_expression);
1089
+ const attribute_if_expression = weslExtension(seq(attribute_if_unary_expression, or(repeatPlus(seq(token("symbol", "||").map(makeBinaryOperator), req(attribute_if_unary_expression, "invalid expression, expected expression"))), repeatPlus(seq(token("symbol", "&&").map(makeBinaryOperator), req(attribute_if_unary_expression, "invalid expression, expected expression"))), yes().map(() => []))).map(makeRepeatingBinaryExpression));
1090
+ const unscoped_compound_statement = seq(opt_attributes, text("{"), repeat(() => statement), req("}", "invalid block, expected }")).collect(statementCollect);
1091
+ const compound_statement = tagScope(seq(opt_attributes, seq(text("{"), repeat(() => statement), req("}", "invalid block, expected '}'")).collect(scopeCollect)).collect(statementCollect));
1092
+ const for_init = seq(opt_attributes, or(fn_call, () => variable_or_value_statement, () => variable_updating_statement));
1093
+ const for_update = seq(opt_attributes, or(fn_call, () => variable_updating_statement));
1094
+ const for_statement = seq("for", seq(req("(", "invalid for loop, expected '('"), opt(for_init), req(";", "invalid for loop, expected ';'"), opt(expression), req(";", "invalid for loop, expected ';'"), opt(for_update), req(")", "invalid for loop, expected ')'"), unscoped_compound_statement).collect(scopeCollect));
1095
+ const if_statement = seq("if", req(seq(expression, compound_statement), "invalid if statement"), repeat(seq("else", "if", req(seq(expression, compound_statement), "invalid else if branch"))), opt(seq("else", req(compound_statement, "invalid else branch, expected '{'"))));
1096
+ const loop_statement = seq("loop", opt_attributes_no_if, req(seq("{", repeat(() => statement), opt(tagScope(seq(opt_attributes, "continuing", opt_attributes_no_if, "{", repeat(() => statement), tagScope(opt(seq(opt_attributes, seq("break", "if", expression, ";")).collect(statementCollect))), "}").collect(statementCollect).collect(scopeCollect))), "}"), "invalid loop statement")).collect(scopeCollect);
1097
+ const case_selector = or("default", expression);
1098
+ const switch_clause = tagScope(seq(opt_attributes, or(seq("case", withSep(",", case_selector, { requireOne: true }), opt(":"), compound_statement), seq("default", opt(":"), compound_statement)).collect(switchClauseCollect)));
1099
+ const switch_body = seq(opt_attributes, "{", repeatPlus(switch_clause), "}");
1100
+ const switch_statement = seq("switch", expression, switch_body);
1101
+ const while_statement = seq("while", expression, compound_statement);
1102
+ const regular_statement = or(for_statement, if_statement, loop_statement, switch_statement, while_statement, seq("break", ";"), seq("continue", req(";", "invalid statement, expected ';'")), seq(";"), () => const_assert, seq("discard", req(";", "invalid statement, expected ';'")), seq("return", opt(expression), req(";", "invalid statement, expected ';'")), seq(fn_call, req(";", "invalid statement, expected ';'")), seq(() => variable_or_value_statement, req(";", "invalid statement, expected ';'")), seq(() => variable_updating_statement, req(";", "invalid statement, expected ';'")));
1103
+ const conditional_statement = tagScope(seq(opt_attributes, regular_statement).collect(statementCollect).collect(partialScopeCollect));
1104
+ const unconditional_statement = tagScope(seq(opt_attributes_no_if, regular_statement));
1105
+ const statement = or(compound_statement, unconditional_statement, conditional_statement);
1106
+ const lhs_expression = or(simple_component_reference, seq(qualified_ident.collect(refIdent), opt(component_or_swizzle)), seq("(", () => lhs_expression, ")", opt(component_or_swizzle)), seq("&", () => lhs_expression), seq("*", () => lhs_expression));
1107
+ const variable_or_value_statement = tagScope(or(local_variable_decl, seq("const", req_optionally_typed_ident, req("=", "invalid const declaration, expected '='"), expression), seq("let", req_optionally_typed_ident, req("=", "invalid let declaration, expected '='"), expression)));
1108
+ const variable_updating_statement = or(seq(lhs_expression, or("=", "<<=", ">>=", "%=", "&=", "*=", "+=", "-=", "/=", "^=", "|="), expression), seq(lhs_expression, or("++", "--")), seq("_", "=", expression));
1109
+ const fn_decl = seq(tagScope(opt_attributes.collect((cc) => cc.tags.attribute || [])).ctag("fn_attributes"), text("fn"), req(fnNameDecl, "invalid fn, expected function name"), seq(req(fnParamList, "invalid fn, expected function parameters").collect(scopeCollect, "header_scope"), opt(seq("->", opt_attributes.collect((cc) => cc.tags.attribute, "return_attributes"), type_specifier.ctag("return_type").collect(scopeCollect, "return_scope"))), req(unscoped_compound_statement, "invalid fn, expected function body").ctag("body_statement").collect(scopeCollect, "body_scope"))).collect(partialScopeCollect, "fn_partial_scope").collect(fnCollect);
1110
+ const global_value_decl = or(seq(opt_attributes, "override", global_ident, seq(opt(seq("=", expression.collect(scopeCollectNoIf, "decl_scope")))), ";").collect(collectVarLike("override")), seq(opt_attributes, "const", global_ident, "=", seq(expression).collect(scopeCollectNoIf, "decl_scope"), ";").collect(collectVarLike("const"))).collect(partialScopeCollect);
1111
+ const global_alias = seq(weslExtension(opt_attributes).collect((cc) => cc.tags.attribute, "attributes"), "alias", req(word, "invalid alias, expected name").collect(globalDeclCollect, "alias_name"), req("=", "invalid alias, expected '='"), req(type_specifier, "invalid alias, expected type").collect(scopeCollect, "alias_scope"), req(";", "invalid alias, expected ';'")).collect(aliasCollect);
1112
+ const const_assert = tagScope(seq(opt_attributes, "const_assert", req(expression, "invalid const_assert, expected expression"), req(";", "invalid statement, expected ';'")).collect(assertCollect)).ctag("const_assert");
1113
+ const global_directive = tagScope(seq(opt_attributes, terminated(or(preceded("diagnostic", diagnostic_control).map(makeDiagnosticDirective), preceded("enable", name_list).map(makeEnableDirective), preceded("requires", name_list).map(makeRequiresDirective)).ptag("directive"), ";")).collect(directiveCollect));
1114
+ const global_decl = tagScope(or(fn_decl, global_variable_decl, global_value_decl, ";", global_alias, const_assert.collect(globalAssertCollect), struct_decl));
1115
+ const weslRoot = seq(weslExtension(weslImports), repeat(global_directive), repeat(global_decl), req(eof(), "invalid WESL, expected EOF")).collect(collectModule, "collectModule");
1116
+ function makeDiagnosticDirective([severity, rule]) {
1117
+ return {
1118
+ kind: "diagnostic",
1119
+ severity,
1120
+ rule
1121
+ };
1122
+ }
1123
+ function makeEnableDirective(extensions) {
1124
+ return {
1125
+ kind: "enable",
1126
+ extensions
1127
+ };
1128
+ }
1129
+ function makeRequiresDirective(extensions) {
1130
+ return {
1131
+ kind: "requires",
1132
+ extensions
1133
+ };
1134
+ }
1135
+ function makeStandardAttribute([name$1, params]) {
1136
+ return {
1137
+ kind: "@attribute",
1138
+ name: name$1,
1139
+ params
1140
+ };
1141
+ }
1142
+ function makeInterpolateAttribute(params) {
1143
+ return {
1144
+ kind: "@interpolate",
1145
+ params
1146
+ };
1147
+ }
1148
+ function makeBuiltinAttribute(param) {
1149
+ return {
1150
+ kind: "@builtin",
1151
+ param
1152
+ };
1153
+ }
1154
+ function makeDiagnosticAttribute([severity, rule]) {
1155
+ return {
1156
+ kind: "@diagnostic",
1157
+ severity,
1158
+ rule
1159
+ };
1160
+ }
1161
+ function makeIfAttribute(param) {
1162
+ return {
1163
+ kind: "@if",
1164
+ param
1165
+ };
1166
+ }
1167
+ function makeTranslateTimeExpressionElem(args) {
1168
+ return {
1169
+ kind: "translate-time-expression",
1170
+ expression: args.value,
1171
+ span: args.span
1172
+ };
1173
+ }
1174
+ function makeName(token$1) {
1175
+ return {
1176
+ kind: "name",
1177
+ name: token$1.text,
1178
+ start: token$1.span[0],
1179
+ end: token$1.span[1]
1180
+ };
1181
+ }
1182
+ function makeLiteral(token$1) {
1183
+ return {
1184
+ kind: "literal",
1185
+ value: token$1.text,
1186
+ span: token$1.span
1187
+ };
1188
+ }
1189
+ function makeTranslateTimeFeature(token$1) {
1190
+ return {
1191
+ kind: "translate-time-feature",
1192
+ name: token$1.text,
1193
+ span: token$1.span
1194
+ };
1195
+ }
1196
+ function makeParenthesizedExpression(expression$1) {
1197
+ return {
1198
+ kind: "parenthesized-expression",
1199
+ expression: expression$1
1200
+ };
1201
+ }
1202
+ function makeUnaryOperator(token$1) {
1203
+ return {
1204
+ value: token$1.text,
1205
+ span: token$1.span
1206
+ };
1207
+ }
1208
+ function makeBinaryOperator(token$1) {
1209
+ return {
1210
+ value: token$1.text,
1211
+ span: token$1.span
1212
+ };
1213
+ }
1214
+ function makeUnaryExpression([operator, expression$1]) {
1215
+ return {
1216
+ kind: "unary-expression",
1217
+ operator,
1218
+ expression: expression$1
1219
+ };
1220
+ }
1221
+ /** A list of left-to-right associative binary expressions */
1222
+ function makeRepeatingBinaryExpression([start, repeating]) {
1223
+ let result = start;
1224
+ for (const [op, left] of repeating) result = makeBinaryExpression([
1225
+ result,
1226
+ op,
1227
+ left
1228
+ ]);
1229
+ return result;
1230
+ }
1231
+ function makeBinaryExpression([left, operator, right]) {
1232
+ return {
1233
+ kind: "binary-expression",
1234
+ operator,
1235
+ left,
1236
+ right
1237
+ };
1238
+ }
1239
+ if (tracing) {
1240
+ const names = {
1241
+ name,
1242
+ diagnostic_rule_name,
1243
+ diagnostic_control,
1244
+ name_list,
1245
+ special_attribute,
1246
+ if_attribute,
1247
+ normal_attribute,
1248
+ attribute_argument_list,
1249
+ attribute_no_if,
1250
+ attribute_incl_if,
1251
+ opt_attributes,
1252
+ opt_attributes_no_if,
1253
+ globalTypeNameDecl,
1254
+ fnNameDecl,
1255
+ optionally_typed_ident,
1256
+ req_optionally_typed_ident,
1257
+ global_ident,
1258
+ struct_member,
1259
+ struct_decl,
1260
+ fn_call,
1261
+ fnParam,
1262
+ fnParamList,
1263
+ local_variable_decl,
1264
+ global_variable_decl,
1265
+ attribute_if_primary_expression,
1266
+ attribute_if_unary_expression,
1267
+ attribute_if_expression,
1268
+ unscoped_compound_statement,
1269
+ compound_statement,
1270
+ for_init,
1271
+ for_update,
1272
+ for_statement,
1273
+ if_statement,
1274
+ loop_statement,
1275
+ case_selector,
1276
+ switch_clause,
1277
+ switch_body,
1278
+ switch_statement,
1279
+ while_statement,
1280
+ regular_statement,
1281
+ conditional_statement,
1282
+ statement,
1283
+ lhs_expression,
1284
+ variable_or_value_statement,
1285
+ variable_updating_statement,
1286
+ fn_decl,
1287
+ global_value_decl,
1288
+ global_alias,
1289
+ const_assert,
1290
+ global_directive,
1291
+ global_decl,
1292
+ weslRoot,
1293
+ qualified_ident,
1294
+ word
1295
+ };
1296
+ Object.entries(names).forEach(([name$1, parser]) => {
1297
+ parser.setTraceName(name$1);
1298
+ });
1299
+ }
1300
+
1301
+ //#endregion
1302
+ //#region ../wesl/src/ParseWESL.ts
1303
+ /**
1304
+ * An error when parsing WESL fails. Designed to be human-readable.
1305
+ */
1306
+ var WeslParseError = class extends Error {
1307
+ span;
1308
+ src;
1309
+ constructor(opts) {
1310
+ const source = opts.src.src;
1311
+ const [lineNum, linePos] = offsetToLineNumber(opts.cause.span[0], source);
1312
+ let message = `${opts.src.debugFilePath}:${lineNum}:${linePos}`;
1313
+ message += ` error: ${opts.cause.message}\n`;
1314
+ message += errorHighlight(source, opts.cause.span).join("\n");
1315
+ super(message, { cause: opts.cause });
1316
+ this.span = opts.cause.span;
1317
+ this.src = opts.src;
1318
+ }
1319
+ };
1320
+ /** Parse a WESL file. Throws on error. */
1321
+ function parseSrcModule(srcModule) {
1322
+ const stream = new WeslStream(srcModule.src);
1323
+ const appState = blankWeslParseState(srcModule);
1324
+ const init = {
1325
+ stream,
1326
+ appState
1327
+ };
1328
+ try {
1329
+ const parseResult = weslRoot.parse(init);
1330
+ if (parseResult === null) throw new Error("parseWESL failed");
1331
+ } catch (e) {
1332
+ if (e instanceof ParseError) {
1333
+ const [lineNumber, lineColumn] = offsetToLineNumber(e.span[0], srcModule.src);
1334
+ const error = new WeslParseError({
1335
+ cause: e,
1336
+ src: srcModule
1337
+ });
1338
+ throwClickableError({
1339
+ url: srcModule.debugFilePath,
1340
+ text: srcModule.src,
1341
+ error,
1342
+ lineNumber,
1343
+ lineColumn,
1344
+ length: e.span[1] - e.span[0]
1345
+ });
1346
+ } else throw e;
1347
+ }
1348
+ return appState.stable;
1349
+ }
1350
+ function blankWeslParseState(srcModule) {
1351
+ const rootScope = emptyScope(null);
1352
+ const moduleElem = null;
1353
+ return {
1354
+ context: {
1355
+ scope: rootScope,
1356
+ openElems: []
1357
+ },
1358
+ stable: {
1359
+ srcModule,
1360
+ imports: [],
1361
+ rootScope,
1362
+ moduleElem
1363
+ }
1364
+ };
1365
+ }
1366
+
1367
+ //#endregion
1368
+ //#region ../wesl/src/PathUtil.ts
1369
+ /** simplistic path manipulation utilities */
1370
+ /** return path with ./ and foo/.. elements removed */
1371
+ function normalize(path$1) {
1372
+ const segments = path$1.split("/");
1373
+ const noDots = segments.filter((s) => s !== ".");
1374
+ const noDbl = [];
1375
+ noDots.forEach((s) => {
1376
+ if (s !== "") if (s === ".." && noDbl.length && noDbl[noDbl.length - 1] !== "..") noDbl.pop();
1377
+ else noDbl.push(s);
1378
+ });
1379
+ return noDbl.join("/");
1380
+ }
1381
+ /** return path w/o a suffix.
1382
+ * e.g. /foo/bar.wgsl => /foo/bar */
1383
+ function noSuffix(path$1) {
1384
+ const lastSlash = path$1.lastIndexOf("/");
1385
+ const lastStart = lastSlash === -1 ? 0 : lastSlash + 1;
1386
+ const suffix = path$1.indexOf(".", lastStart);
1387
+ const suffixStart = suffix === -1 ? path$1.length : suffix;
1388
+ return path$1.slice(0, suffixStart);
1389
+ }
1390
+
1391
+ //#endregion
1392
+ //#region ../wesl/src/ParsedRegistry.ts
1393
+ function parsedRegistry() {
1394
+ resetScopeIds();
1395
+ return { modules: {} };
1396
+ }
1397
+ /**
1398
+ * @param srcFiles map of source strings by file path
1399
+ * key is '/' separated relative path (relative to srcRoot, not absolute file path )
1400
+ * value is wesl source string
1401
+ * @param registry add parsed modules to this registry
1402
+ * @param packageName name of package
1403
+ */
1404
+ function parseIntoRegistry(srcFiles, registry, packageName = "package", debugWeslRoot) {
1405
+ let weslRoot$1 = debugWeslRoot;
1406
+ if (weslRoot$1 === void 0) weslRoot$1 = "";
1407
+ else if (!weslRoot$1.endsWith("/")) weslRoot$1 += "/";
1408
+ const srcModules = Object.entries(srcFiles).map(([filePath, src]) => {
1409
+ const modulePath = fileToModulePath(filePath, packageName);
1410
+ return {
1411
+ modulePath,
1412
+ debugFilePath: weslRoot$1 + filePath,
1413
+ src
1414
+ };
1415
+ });
1416
+ srcModules.forEach((mod) => {
1417
+ const parsed = parseSrcModule(mod);
1418
+ if (registry.modules[mod.modulePath]) throw new Error(`duplicate module path: '${mod.modulePath}'`);
1419
+ registry.modules[mod.modulePath] = parsed;
1420
+ });
1421
+ }
1422
+ const libRegex = /^lib\.w[eg]sl$/i;
1423
+ /** convert a file path (./foo/bar.wesl)
1424
+ * to a module path (package::foo::bar) */
1425
+ function fileToModulePath(filePath, packageName) {
1426
+ if (filePath.includes("::")) return filePath;
1427
+ if (packageName !== "package" && libRegex.test(filePath)) return packageName;
1428
+ const strippedPath = noSuffix(normalize(filePath));
1429
+ const moduleSuffix = strippedPath.replaceAll("/", "::");
1430
+ const modulePath = packageName + "::" + moduleSuffix;
1431
+ return modulePath;
1432
+ }
1433
+
1434
+ //#endregion
1435
+ //#region src/cli.ts
1436
+ async function cli(rawArgs$1) {
1437
+ enableTracing();
1438
+ const argv = await parseArgs(rawArgs$1);
1439
+ await linkNormally(argv);
1440
+ }
1441
+ async function parseArgs(args) {
1442
+ const toolDir = path.join(import.meta.url, "..");
1443
+ const appVersion = await versionFromPackageJson(toolDir);
1444
+ return yargs(args).version(appVersion).option("src", {
1445
+ type: "string",
1446
+ default: "./shaders/*.w[eg]sl",
1447
+ describe: "WGSL/WESL files to bundle in the package (glob syntax)"
1448
+ }).option("rootModule", {
1449
+ type: "string",
1450
+ default: "main",
1451
+ describe: "start linking from this module name"
1452
+ }).option("define", {
1453
+ type: "array",
1454
+ describe: "definitions for preprocessor and linking"
1455
+ }).option("baseDir", {
1456
+ requiresArg: true,
1457
+ type: "string",
1458
+ default: "./shaders",
1459
+ describe: "root directory for shaders"
1460
+ }).option("projectDir", {
1461
+ requiresArg: true,
1462
+ type: "string",
1463
+ default: ".",
1464
+ describe: "directory containing package.json"
1465
+ }).option("details", {
1466
+ type: "boolean",
1467
+ default: false,
1468
+ hidden: true,
1469
+ describe: "show details about parsed files"
1470
+ }).option("emit", {
1471
+ type: "boolean",
1472
+ default: true,
1473
+ hidden: true,
1474
+ describe: "emit linked result"
1475
+ }).help().parse();
1476
+ }
1477
+ async function linkNormally(argv) {
1478
+ const weslRoot$1 = argv.baseDir || process.cwd();
1479
+ const weslSrc = await loadModules(argv.projectDir, weslRoot$1, argv.src);
1480
+ if (argv.emit) {
1481
+ const linked = await link({
1482
+ weslSrc,
1483
+ rootModuleName: argv.rootModule
1484
+ });
1485
+ log(linked.dest);
1486
+ }
1487
+ if (argv.details) {
1488
+ const registry = parsedRegistry();
1489
+ try {
1490
+ parseIntoRegistry(weslSrc, registry, "package");
1491
+ } catch (e) {
1492
+ console.error(e);
1493
+ }
1494
+ Object.entries(registry.modules).forEach(([modulePath, ast]) => {
1495
+ log(`---\n${modulePath}`);
1496
+ log(`\n->ast`);
1497
+ log(astToString(ast.moduleElem));
1498
+ log(`\n->scope`);
1499
+ log(scopeToString(ast.rootScope));
1500
+ log();
1501
+ });
1502
+ }
1503
+ }
1504
+
1505
+ //#endregion
1506
+ //#region src/main.ts
1507
+ const rawArgs = hideBin(process.argv);
1508
+ cli(rawArgs);
1509
+
1510
+ //#endregion