wesl 0.7.26 → 0.7.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +283 -147
  3. package/dist/index.js +1765 -1143
  4. package/package.json +2 -2
  5. package/src/AbstractElems.ts +264 -82
  6. package/src/ClickableError.ts +8 -1
  7. package/src/Linker.ts +63 -67
  8. package/src/LinkerUtil.ts +141 -7
  9. package/src/LowerAndEmit.ts +660 -304
  10. package/src/Mangler.ts +1 -1
  11. package/src/ModuleResolver.ts +15 -4
  12. package/src/ParseWESL.ts +21 -33
  13. package/src/SrcMap.ts +7 -19
  14. package/src/StandardTypes.ts +1 -1
  15. package/src/WeslDevice.ts +1 -0
  16. package/src/debug/ASTtoString.ts +92 -76
  17. package/src/index.ts +1 -1
  18. package/src/parse/AttachComments.ts +289 -0
  19. package/src/parse/ExpressionUtil.ts +3 -3
  20. package/src/parse/Keywords.ts +1 -1
  21. package/src/parse/ParseAttribute.ts +49 -71
  22. package/src/parse/ParseCall.ts +3 -4
  23. package/src/parse/ParseControlFlow.ts +100 -56
  24. package/src/parse/ParseDirective.ts +9 -8
  25. package/src/parse/ParseDoBlock.ts +63 -0
  26. package/src/parse/ParseExpression.ts +11 -10
  27. package/src/parse/ParseFn.ts +11 -33
  28. package/src/parse/ParseGlobalVar.ts +36 -27
  29. package/src/parse/ParseIdent.ts +4 -5
  30. package/src/parse/ParseLocalVar.ts +16 -12
  31. package/src/parse/ParseLoop.ts +76 -39
  32. package/src/parse/ParseModule.ts +65 -19
  33. package/src/parse/ParseSimpleStatement.ts +112 -66
  34. package/src/parse/ParseStatement.ts +40 -67
  35. package/src/parse/ParseStruct.ts +8 -22
  36. package/src/parse/ParseType.ts +10 -13
  37. package/src/parse/ParseUtil.ts +26 -12
  38. package/src/parse/ParseValueDeclaration.ts +18 -31
  39. package/src/parse/ParseWesl.ts +11 -14
  40. package/src/parse/ParsingContext.ts +11 -9
  41. package/src/parse/WeslStream.ts +138 -121
  42. package/src/parse/stream/RegexMatchers.ts +45 -0
  43. package/src/parse/stream/WeslLexer.ts +249 -0
  44. package/src/test/BevyLink.test.ts +18 -11
  45. package/src/test/ConditionalElif.test.ts +4 -2
  46. package/src/test/DeclCommentEmit.test.ts +122 -0
  47. package/src/test/DoBlock.test.ts +164 -0
  48. package/src/test/FilterValidElements.test.ts +4 -6
  49. package/src/test/Linker.test.ts +23 -7
  50. package/src/test/Mangling.test.ts +8 -4
  51. package/src/test/ParseComments.test.ts +234 -6
  52. package/src/test/ParseConditionsV2.test.ts +47 -175
  53. package/src/test/ParseElifV2.test.ts +12 -33
  54. package/src/test/ParseErrorV2.test.ts +0 -0
  55. package/src/test/ParseWeslV2.test.ts +247 -626
  56. package/src/test/StatementEmit.test.ts +143 -0
  57. package/src/test/StripWesl.ts +24 -3
  58. package/src/test/TestLink.ts +19 -3
  59. package/src/test/TestUtil.ts +18 -8
  60. package/src/test/Tokenizer.test.ts +96 -0
  61. package/src/test/__snapshots__/ParseWeslV2.test.ts.snap +10 -42
  62. package/src/RawEmit.ts +0 -103
  63. package/src/Reflection.ts +0 -336
  64. package/src/TransformBindingStructs.ts +0 -320
  65. package/src/parse/ContentsHelpers.ts +0 -70
  66. package/src/parse/stream/CachingStream.ts +0 -48
  67. package/src/parse/stream/MatchersStream.ts +0 -85
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "wesl",
3
- "version": "0.7.26",
3
+ "version": "0.7.28",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
7
7
  "src"
8
8
  ],
9
- "repository": "github:wgsl-tooling-wg/wesl-js",
9
+ "repository": "github:webgpu-tools/wesl-js",
10
10
  "exports": {
11
11
  ".": {
12
12
  "types": "./dist/index.d.ts",
@@ -2,57 +2,65 @@ import type { DeclIdent, RefIdent, Scope, SrcModule } from "./Scope.ts";
2
2
  import type { Span } from "./Span.ts";
3
3
 
4
4
  /**
5
- * AST structures describing 'interesting' parts of WESL source.
6
- *
7
- * Parts needing further analysis are pulled into these structures.
8
- * Uninteresting parts are 'TextElem' nodes, copied to output WGSL.
5
+ * AST structures describing WESL source. Each element holds its interesting
6
+ * parts as typed child fields (name, type, init, body, ...); emit walks those
7
+ * fields and reconstructs the surrounding WGSL syntax rather than copying spans.
9
8
  */
10
- export type AbstractElem = GrammarElem | SyntheticElem | ExpressionElem;
11
-
12
- export type GrammarElem = ContainerElem | TerminalElem;
9
+ export type AbstractElem =
10
+ | GrammarElem
11
+ | SyntheticElem
12
+ | ExpressionElem
13
+ | UnknownExpressionElem;
13
14
 
14
- export type ContainerElem =
15
+ export type GrammarElem =
16
+ | ModuleElem
15
17
  | AttributeElem
18
+ | Statement
19
+ | SwitchClauseElem
16
20
  | AliasElem
17
- | ConstAssertElem
18
- | ConstElem
19
- | ContinuingElem
20
- | UnknownExpressionElem
21
- | SimpleMemberRef
22
- | FnElem
23
- | TypedDeclElem
24
21
  | GlobalVarElem
25
- | LetElem
26
- | ModuleElem
27
22
  | OverrideElem
28
- | FnParamElem
29
23
  | StructElem
30
24
  | StructMemberElem
31
- | StuffElem
32
- | TypeRefElem
33
- | VarElem
34
- | StatementElem
35
- | SwitchClauseElem;
25
+ | FnElem
26
+ | FnParamElem
27
+ | TypedDeclElem
28
+ | TerminalElem
29
+ | DoBlockElem;
36
30
 
37
31
  /** Map from element kind string to element type, for type-safe element construction. */
38
32
  export type ElemKindMap = {
39
33
  alias: AliasElem;
40
34
  assert: ConstAssertElem;
35
+ block: BlockElem;
41
36
  const: ConstElem;
42
37
  continuing: ContinuingElem;
38
+ if: IfElem;
39
+ for: ForElem;
40
+ while: WhileElem;
41
+ loop: LoopElem;
42
+ switch: SwitchElem;
43
+ return: ReturnElem;
44
+ break: BreakElem;
45
+ continue: ContinueElem;
46
+ discard: DiscardElem;
47
+ assign: AssignElem;
48
+ increment: IncrementElem;
49
+ decrement: DecrementElem;
50
+ call: CallElem;
51
+ empty: EmptyElem;
43
52
  gvar: GlobalVarElem;
44
53
  let: LetElem;
45
54
  member: StructMemberElem;
46
55
  override: OverrideElem;
47
56
  param: FnParamElem;
48
- statement: StatementElem;
49
57
  struct: StructElem;
50
58
  "switch-clause": SwitchClauseElem;
51
59
  type: TypeRefElem;
52
60
  var: VarElem;
53
61
  };
54
62
 
55
- /** Inspired by https://github.com/wgsl-tooling-wg/wesl-rs/blob/3b2434eac1b2ebda9eb8bfb25f43d8600d819872/crates/wgsl-parse/src/syntax.rs#L364 */
63
+ /** Inspired by https://github.com/webgpu-tools/wesl-rs/blob/3b2434eac1b2ebda9eb8bfb25f43d8600d819872/crates/wgsl-parse/src/syntax.rs#L364 */
56
64
  export type ExpressionElem =
57
65
  | Literal
58
66
  | RefIdentElem
@@ -69,12 +77,12 @@ export type TerminalElem =
69
77
  | DeclIdentElem //
70
78
  | NameElem
71
79
  | RefIdentElem
72
- | TextElem
73
80
  | ImportElem;
74
81
 
75
82
  export type GlobalDeclarationElem =
76
83
  | AliasElem
77
84
  | ConstElem
85
+ | DoBlockElem
78
86
  | FnElem
79
87
  | GlobalVarElem
80
88
  | OverrideElem
@@ -92,10 +100,25 @@ export interface AbstractElemBase {
92
100
  kind: AbstractElem["kind"];
93
101
  start: number;
94
102
  end: number;
103
+ /** Comments leading this element (on their own line above it). */
104
+ commentsBefore?: CommentElem[];
105
+ /** Comments trailing this element (on the same line after it). */
106
+ commentsAfter?: CommentElem[];
95
107
  }
96
108
 
97
- export interface ElemWithContentsBase extends AbstractElemBase {
98
- contents: AbstractElem[];
109
+ /**
110
+ * A source comment attached to an element as leading or trailing trivia.
111
+ * Not part of the {@link AbstractElem} union: comments are node metadata
112
+ * (in `commentsBefore` / `commentsAfter`), never `contents` children.
113
+ */
114
+ export interface CommentElem {
115
+ kind: "comment";
116
+ style: "line" | "block";
117
+ start: number;
118
+ end: number;
119
+ srcModule: SrcModule;
120
+ /** Preserve one blank line above this comment (leading comments only). */
121
+ blankBefore?: boolean;
99
122
  }
100
123
 
101
124
  export interface HasAttributes {
@@ -104,12 +127,6 @@ export interface HasAttributes {
104
127
 
105
128
  /* ------ Terminal Elements (don't contain other elements) ------ */
106
129
 
107
- /** Raw text copied to linked WGSL (e.g., 'var' or '@diagnostic(off,derivative_uniformity)'). */
108
- export interface TextElem extends AbstractElemBase {
109
- kind: "text";
110
- srcModule: SrcModule;
111
- }
112
-
113
130
  /** A name that doesn't need to be an Ident (e.g., struct member, diagnostic rule). */
114
131
  export interface NameElem extends AbstractElemBase {
115
132
  kind: "name";
@@ -170,10 +187,10 @@ export interface SyntheticElem {
170
187
  text: string;
171
188
  }
172
189
 
173
- /* ------ Container Elements (contain other elements) ------ */
190
+ /* ------ Composite Elements (hold child elements in typed fields) ------ */
174
191
 
175
192
  /** A declaration identifier with an optional type. */
176
- export interface TypedDeclElem extends ElemWithContentsBase {
193
+ export interface TypedDeclElem extends AbstractElemBase {
177
194
  kind: "typeDecl";
178
195
  decl: DeclIdentElem;
179
196
  typeRef?: TypeRefElem; // LATER Consider a variant for fn params and alias where typeRef is required
@@ -181,14 +198,14 @@ export interface TypedDeclElem extends ElemWithContentsBase {
181
198
  }
182
199
 
183
200
  /** An alias statement. */
184
- export interface AliasElem extends ElemWithContentsBase, HasAttributes {
201
+ export interface AliasElem extends AbstractElemBase, HasAttributes {
185
202
  kind: "alias";
186
203
  name: DeclIdentElem;
187
204
  typeRef: TypeRefElem;
188
205
  }
189
206
 
190
207
  /** An attribute like '@compute' or '@binding(0)'. */
191
- export interface AttributeElem extends ElemWithContentsBase {
208
+ export interface AttributeElem extends AbstractElemBase {
192
209
  kind: "attribute";
193
210
  attribute: Attribute;
194
211
  }
@@ -243,26 +260,30 @@ export interface ElseAttribute {
243
260
  export type ConditionalAttribute = IfAttribute | ElifAttribute | ElseAttribute;
244
261
 
245
262
  /** A const_assert statement. */
246
- export interface ConstAssertElem extends ElemWithContentsBase, HasAttributes {
263
+ export interface ConstAssertElem extends AbstractElemBase, HasAttributes {
247
264
  kind: "assert";
265
+ expression: ExpressionElem;
248
266
  }
249
267
 
250
268
  /** A const declaration. */
251
- export interface ConstElem extends ElemWithContentsBase, HasAttributes {
269
+ export interface ConstElem extends AbstractElemBase, HasAttributes {
252
270
  kind: "const";
253
271
  name: TypedDeclElem;
272
+ init?: ExpressionElem;
254
273
  }
255
274
 
256
275
  /** An expression without special handling, used in attribute parameters. */
257
- export interface UnknownExpressionElem extends ElemWithContentsBase {
276
+ export interface UnknownExpressionElem extends AbstractElemBase {
258
277
  kind: "expression";
278
+ expression: ExpressionElem;
259
279
  }
260
280
 
261
281
  /** An expression that can be safely evaluated at compile time. */
262
282
  export interface TranslateTimeExpressionElem {
263
283
  kind: "translate-time-expression";
264
284
  expression: ExpressionElem;
265
- span: Span;
285
+ start: number;
286
+ end: number;
266
287
  }
267
288
 
268
289
  /** A literal value (boolean or number) in WESL source. */
@@ -310,15 +331,23 @@ export interface BinaryExpression extends AbstractElemBase {
310
331
  export interface FunctionCallExpression extends AbstractElemBase {
311
332
  kind: "call-expression";
312
333
  function: RefIdentElem | TypeRefElem; // template_elaborated_ident
334
+ /** Only populated for function calls; constructor calls carry templates on `function`. */
313
335
  templateArgs?: TypeTemplateParameter[];
314
336
  arguments: ExpressionElem[];
315
337
  }
316
338
 
317
339
  export interface UnaryOperator {
318
340
  value: "!" | "&" | "*" | "-" | "~";
319
- span: Span;
341
+ start: number;
342
+ end: number;
320
343
  }
321
344
 
345
+ /** Uses span (not inline start/end) so each operator object stays in a smaller
346
+ * V8 size class. Inline start/end uses slightly less memory overall (no separate
347
+ * span tuple) but each object is bigger and more likely to be promoted to old
348
+ * gen under sustained allocation pressure, triggering more major GCs and
349
+ * ~5% slower wall time on math-heavy parse workloads (bevy_env_map/parse).
350
+ * A rare case where allocating more bytes wins on GC. */
322
351
  export interface BinaryOperator {
323
352
  value:
324
353
  | ("||" | "&&" | "+" | "-" | "*" | "/" | "%" | "==")
@@ -353,106 +382,259 @@ export interface RequiresDirective {
353
382
  extensions: NameElem[];
354
383
  }
355
384
 
385
+ /**
386
+ * A `do` block: a CPU-side dispatch script (`do name(params) { ... }`).
387
+ *
388
+ * Module-local: its name and body idents never enter bindIdents/the scope
389
+ * tree, and the linker drops it entirely from emitted WGSL. The parsed body
390
+ * is retained for the interpreter, which resolves names by AST match.
391
+ */
392
+ export interface DoBlockElem extends AbstractElemBase, HasAttributes {
393
+ kind: "do";
394
+ name: NameElem;
395
+ params: FnParamElem[];
396
+ body: BlockElem;
397
+ }
398
+
356
399
  /** A function declaration. */
357
- export interface FnElem extends ElemWithContentsBase, HasAttributes {
358
- // LATER doesn't need contents
400
+ export interface FnElem extends AbstractElemBase, HasAttributes {
359
401
  kind: "fn";
360
402
  name: DeclIdentElem;
361
403
  params: FnParamElem[];
362
- body: StatementElem;
404
+ body: BlockElem;
363
405
  returnAttributes?: AttributeElem[];
364
406
  returnType?: TypeRefElem;
365
407
  }
366
408
 
367
409
  /** A global variable declaration (at the root level). */
368
- export interface GlobalVarElem extends ElemWithContentsBase, HasAttributes {
410
+ export interface GlobalVarElem extends AbstractElemBase, HasAttributes {
369
411
  kind: "gvar";
370
412
  name: TypedDeclElem;
413
+ /** Address-space / access-mode enumerants, e.g. `<storage, read_write>`. */
414
+ template?: NameElem[];
415
+ init?: ExpressionElem;
371
416
  }
372
417
 
373
418
  /** An entire file. */
374
- export interface ModuleElem extends ElemWithContentsBase {
419
+ export interface ModuleElem extends AbstractElemBase {
375
420
  kind: "module";
421
+ /** Top-level children (imports, directives, declarations, and any synthetic
422
+ * vars added by transforms) in source order. */
423
+ decls: AbstractElem[];
376
424
  }
377
425
 
378
426
  /** An override declaration. */
379
- export interface OverrideElem extends ElemWithContentsBase, HasAttributes {
427
+ export interface OverrideElem extends AbstractElemBase, HasAttributes {
380
428
  kind: "override";
381
429
  name: TypedDeclElem;
430
+ init?: ExpressionElem;
382
431
  }
383
432
 
384
433
  /** A parameter in a function declaration. */
385
- export interface FnParamElem extends ElemWithContentsBase, HasAttributes {
434
+ export interface FnParamElem extends AbstractElemBase, HasAttributes {
386
435
  kind: "param";
387
436
  name: TypedDeclElem;
388
437
  }
389
438
 
390
- /** Simple struct references like `myStruct.bar` (for binding struct transforms). */
391
- export interface SimpleMemberRef extends ElemWithContentsBase {
392
- kind: "memberRef";
393
- name: RefIdentElem;
394
- member: NameElem;
395
- extraComponents?: StuffElem;
396
- }
397
-
398
439
  /** A struct declaration. */
399
- export interface StructElem extends ElemWithContentsBase, HasAttributes {
440
+ export interface StructElem extends AbstractElemBase, HasAttributes {
400
441
  kind: "struct";
401
442
  name: DeclIdentElem;
402
443
  members: StructMemberElem[];
403
- bindingStruct?: true; // used later during binding struct transformation
404
- }
405
-
406
- /** Generic container of other elements. */
407
- export interface StuffElem extends ElemWithContentsBase {
408
- kind: "stuff";
409
- }
410
-
411
- /** A struct declaration marked as a binding struct. */
412
- export interface BindingStructElem extends StructElem {
413
- bindingStruct: true;
414
- entryFn?: FnElem;
415
444
  }
416
445
 
417
446
  /** A member of a struct declaration. */
418
- export interface StructMemberElem extends ElemWithContentsBase, HasAttributes {
447
+ export interface StructMemberElem extends AbstractElemBase, HasAttributes {
419
448
  kind: "member";
420
449
  name: NameElem;
421
450
  typeRef: TypeRefElem;
422
- mangledVarName?: string; // root name if transformed to a var (for binding struct transformation)
423
451
  }
424
452
 
425
453
  export type TypeTemplateParameter = ExpressionElem;
426
454
 
427
455
  /** A type reference like 'f32', 'MyStruct', or 'ptr<storage, array<f32>, read_only>'. */
428
- export interface TypeRefElem extends ElemWithContentsBase {
456
+ export interface TypeRefElem extends AbstractElemBase {
429
457
  kind: "type";
430
458
  name: RefIdent;
431
459
  templateParams?: TypeTemplateParameter[];
432
460
  }
433
461
 
434
462
  /** A variable declaration. */
435
- export interface VarElem extends ElemWithContentsBase, HasAttributes {
463
+ export interface VarElem extends AbstractElemBase, HasAttributes {
436
464
  kind: "var";
437
465
  name: TypedDeclElem;
466
+ /** Address-space / access-mode enumerants, e.g. `<storage, read_write>`. */
467
+ template?: NameElem[];
468
+ init?: ExpressionElem;
438
469
  }
439
470
 
440
- export interface LetElem extends ElemWithContentsBase, HasAttributes {
471
+ export interface LetElem extends AbstractElemBase, HasAttributes {
441
472
  kind: "let";
442
473
  name: TypedDeclElem;
474
+ init?: ExpressionElem;
443
475
  }
444
476
 
445
- export interface StatementElem extends ElemWithContentsBase, HasAttributes {
446
- kind: "statement";
477
+ /* ------ Statement Elements ------ */
478
+
479
+ /** Any WGSL statement inside a function or block body. */
480
+ export type Statement =
481
+ | BlockElem
482
+ | IfElem
483
+ | ForElem
484
+ | WhileElem
485
+ | LoopElem
486
+ | ContinuingElem
487
+ | SwitchElem
488
+ | ReturnElem
489
+ | BreakElem
490
+ | ContinueElem
491
+ | DiscardElem
492
+ | AssignElem
493
+ | IncrementElem
494
+ | DecrementElem
495
+ | CallElem
496
+ | ConstAssertElem
497
+ | EmptyElem
498
+ | VarElem
499
+ | LetElem
500
+ | ConstElem;
501
+
502
+ /** A bare `;` statement. Emits nothing, but kept in the AST as a statement node. */
503
+ export interface EmptyElem extends AbstractElemBase, HasAttributes {
504
+ kind: "empty";
505
+ }
506
+
507
+ /** A `{ ... }` compound statement. */
508
+ export interface BlockElem extends AbstractElemBase, HasAttributes {
509
+ kind: "block";
510
+ body: Statement[];
511
+ /** Comments inside an otherwise empty block, with no statement to attach to. */
512
+ innerComments?: CommentElem[];
513
+ }
514
+
515
+ /** An if / else-if / else chain. `else` nests: else-if is an IfElem, plain else a BlockElem. */
516
+ export interface IfElem extends AbstractElemBase, HasAttributes {
517
+ kind: "if";
518
+ condition: ExpressionElem;
519
+ body: BlockElem;
520
+ else?: IfElem | BlockElem;
521
+ }
522
+
523
+ /** A for loop. */
524
+ export interface ForElem extends AbstractElemBase, HasAttributes {
525
+ kind: "for";
526
+ init?: ForInit;
527
+ condition?: ExpressionElem;
528
+ update?: ForUpdate;
529
+ body: BlockElem;
447
530
  }
448
531
 
449
- export interface ContinuingElem extends ElemWithContentsBase, HasAttributes {
532
+ export type ForInit =
533
+ | VarElem
534
+ | LetElem
535
+ | ConstElem
536
+ | AssignElem
537
+ | IncrementElem
538
+ | DecrementElem
539
+ | CallElem;
540
+ export type ForUpdate = AssignElem | IncrementElem | DecrementElem | CallElem;
541
+
542
+ /** A while loop. */
543
+ export interface WhileElem extends AbstractElemBase, HasAttributes {
544
+ kind: "while";
545
+ condition: ExpressionElem;
546
+ body: BlockElem;
547
+ }
548
+
549
+ /** A loop, optionally ending with a continuing block. */
550
+ export interface LoopElem extends AbstractElemBase, HasAttributes {
551
+ kind: "loop";
552
+ body: BlockElem;
553
+ continuing?: ContinuingElem;
554
+ }
555
+
556
+ /** A continuing block, optionally ending with `break if expr`. */
557
+ export interface ContinuingElem extends AbstractElemBase, HasAttributes {
450
558
  kind: "continuing";
559
+ body: BlockElem;
560
+ breakIf?: ExpressionElem;
451
561
  }
452
562
 
453
- /** Statement or continuing - used in loop body parsing. */
454
- export type BlockStatement = StatementElem | ContinuingElem;
563
+ /** A switch statement. */
564
+ export interface SwitchElem extends AbstractElemBase, HasAttributes {
565
+ kind: "switch";
566
+ selector: ExpressionElem;
567
+ clauses: SwitchClauseElem[];
568
+ /** Attributes on the switch body, between the selector and `{`. */
569
+ bodyAttributes?: AttributeElem[];
570
+ }
455
571
 
456
- export interface SwitchClauseElem extends ElemWithContentsBase, HasAttributes {
572
+ /** A case or default clause. `"default"` sentinel marks the default selector. */
573
+ export interface SwitchClauseElem extends AbstractElemBase, HasAttributes {
457
574
  kind: "switch-clause";
575
+ selectors: (ExpressionElem | "default")[];
576
+ body: BlockElem;
577
+ }
578
+
579
+ /** A return statement, with an optional value. */
580
+ export interface ReturnElem extends AbstractElemBase, HasAttributes {
581
+ kind: "return";
582
+ value?: ExpressionElem;
583
+ }
584
+
585
+ /** A break statement, or `break if expr`. */
586
+ export interface BreakElem extends AbstractElemBase, HasAttributes {
587
+ kind: "break";
588
+ condition?: ExpressionElem;
589
+ }
590
+
591
+ /** A continue statement. */
592
+ export interface ContinueElem extends AbstractElemBase, HasAttributes {
593
+ kind: "continue";
594
+ }
595
+
596
+ /** A discard statement. */
597
+ export interface DiscardElem extends AbstractElemBase, HasAttributes {
598
+ kind: "discard";
599
+ }
600
+
601
+ /** Assignment, compound assignment, or phony `_ = expr`. */
602
+ export interface AssignElem extends AbstractElemBase, HasAttributes {
603
+ kind: "assign";
604
+ lhs: ExpressionElem | PhonyTarget;
605
+ op: AssignOp;
606
+ rhs: ExpressionElem;
607
+ }
608
+
609
+ /** `i++` */
610
+ export interface IncrementElem extends AbstractElemBase, HasAttributes {
611
+ kind: "increment";
612
+ target: ExpressionElem;
613
+ }
614
+
615
+ /** `i--` */
616
+ export interface DecrementElem extends AbstractElemBase, HasAttributes {
617
+ kind: "decrement";
618
+ target: ExpressionElem;
619
+ }
620
+
621
+ /** A bare function call statement. */
622
+ export interface CallElem extends AbstractElemBase, HasAttributes {
623
+ kind: "call";
624
+ call: FunctionCallExpression;
625
+ }
626
+
627
+ /** The `_` discard target of a phony assignment; never enters the ident system. */
628
+ export interface PhonyTarget {
629
+ kind: "phony";
630
+ span: Span;
631
+ }
632
+
633
+ /** An assignment operator. Uses a span tuple (not inline start/end) to match
634
+ * BinaryOperator's smaller V8 size class; see the note there. */
635
+ export interface AssignOp {
636
+ value:
637
+ | ("=" | "+=" | "-=" | "*=" | "/=" | "%=")
638
+ | ("&=" | "|=" | "^=" | "<<=" | ">>=");
639
+ span: Span;
458
640
  }
@@ -9,6 +9,8 @@ export interface WeslErrorLocation {
9
9
  line: number;
10
10
  column: number;
11
11
  length: number;
12
+ /** byte offset into the source file */
13
+ offset: number;
12
14
  }
13
15
 
14
16
  export interface ClickableErrorParams {
@@ -27,6 +29,9 @@ export interface ClickableErrorParams {
27
29
  /** number of characters in the error section */
28
30
  length: number;
29
31
 
32
+ /** byte offset of the error section within the source text */
33
+ offset: number;
34
+
30
35
  /** the original error */
31
36
  error: Error;
32
37
  }
@@ -36,12 +41,13 @@ const isBrowser = "document" in globalThis;
36
41
  /** Throw an error with an embedded source map so that browser users can
37
42
  * click on the error in the browser debug console and see the wesl source code. */
38
43
  export function throwClickableError(params: ClickableErrorParams): void {
39
- const { url, text, lineNumber, lineColumn, length, error } = params;
44
+ const { url, text, lineNumber, lineColumn, length, offset, error } = params;
40
45
  const weslLocation: WeslErrorLocation = {
41
46
  file: url,
42
47
  line: lineNumber,
43
48
  column: lineColumn,
44
49
  length,
50
+ offset,
45
51
  };
46
52
  (error as any).weslLocation = weslLocation;
47
53
  if (!debug || !isBrowser) throw error;
@@ -142,6 +148,7 @@ export function failIdentElem(
142
148
  lineNumber,
143
149
  lineColumn,
144
150
  length,
151
+ offset: start,
145
152
  error: new Error(detailedMessage),
146
153
  });
147
154
  }