wesl 0.6.49 → 0.7.1

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