wesl 0.6.0-pre2

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 (110) hide show
  1. package/dist/index.cjs +2617 -0
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.js +2617 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/linker/packages/linker/src/AbstractElems.d.ts +104 -0
  6. package/dist/linker/packages/linker/src/BindIdents.d.ts +16 -0
  7. package/dist/linker/packages/linker/src/CommentsGrammar.d.ts +6 -0
  8. package/dist/linker/packages/linker/src/FlattenTreeImport.d.ts +11 -0
  9. package/dist/linker/packages/linker/src/ImportGrammar.d.ts +13 -0
  10. package/dist/linker/packages/linker/src/ImportTree.d.ts +17 -0
  11. package/dist/linker/packages/linker/src/Linker.d.ts +26 -0
  12. package/dist/linker/packages/linker/src/LowerAndEmit.d.ts +25 -0
  13. package/dist/linker/packages/linker/src/ParseWESL.d.ts +36 -0
  14. package/dist/linker/packages/linker/src/ParsedRegistry.d.ts +26 -0
  15. package/dist/linker/packages/linker/src/PathUtil.d.ts +9 -0
  16. package/dist/linker/packages/linker/src/Scope.d.ts +55 -0
  17. package/dist/linker/packages/linker/src/Slicer.d.ts +26 -0
  18. package/dist/linker/packages/linker/src/StandardTypes.d.ts +6 -0
  19. package/dist/linker/packages/linker/src/Util.d.ts +26 -0
  20. package/dist/linker/packages/linker/src/WESLCollect.d.ts +29 -0
  21. package/dist/linker/packages/linker/src/WESLGrammar.d.ts +23 -0
  22. package/dist/linker/packages/linker/src/WESLTokens.d.ts +42 -0
  23. package/dist/linker/packages/linker/src/WgslBundle.d.ts +13 -0
  24. package/dist/linker/packages/linker/src/debug/ASTtoString.d.ts +3 -0
  25. package/dist/linker/packages/linker/src/debug/ImportToString.d.ts +2 -0
  26. package/dist/linker/packages/linker/src/debug/LineWrapper.d.ts +21 -0
  27. package/dist/linker/packages/linker/src/debug/ScopeToString.d.ts +4 -0
  28. package/dist/linker/packages/linker/src/index.d.ts +7 -0
  29. package/dist/linker/packages/linker/src/test/ErrorLogging.test.d.ts +1 -0
  30. package/dist/linker/packages/linker/src/test/Expression.test.d.ts +1 -0
  31. package/dist/linker/packages/linker/src/test/FlattenTreeImport.test.d.ts +1 -0
  32. package/dist/linker/packages/linker/src/test/ImportCases.test.d.ts +1 -0
  33. package/dist/linker/packages/linker/src/test/ImportSyntaxCases.test.d.ts +1 -0
  34. package/dist/linker/packages/linker/src/test/LinkGlob.test.d.ts +1 -0
  35. package/dist/linker/packages/linker/src/test/LinkPackage.test.d.ts +1 -0
  36. package/dist/linker/packages/linker/src/test/Linker.test.d.ts +1 -0
  37. package/dist/linker/packages/linker/src/test/MatchWgslD.test.d.ts +1 -0
  38. package/dist/linker/packages/linker/src/test/ParseComments.test.d.ts +1 -0
  39. package/dist/linker/packages/linker/src/test/ParseWESL.test.d.ts +1 -0
  40. package/dist/linker/packages/linker/src/test/PathUtil.test.d.ts +1 -0
  41. package/dist/linker/packages/linker/src/test/PrettyGrammar.test.d.ts +1 -0
  42. package/dist/linker/packages/linker/src/test/ScopeWESL.test.d.ts +1 -0
  43. package/dist/linker/packages/linker/src/test/Slicer.test.d.ts +1 -0
  44. package/dist/linker/packages/linker/src/test/TestSetup.d.ts +1 -0
  45. package/dist/linker/packages/linker/src/test/TestUtil.d.ts +15 -0
  46. package/dist/linker/packages/linker/src/test/Util.test.d.ts +1 -0
  47. package/dist/linker/packages/linker/src/test/WgslTests.d.ts +0 -0
  48. package/dist/linker/packages/linker/src/test/shared/StringUtil.d.ts +8 -0
  49. package/dist/linker/packages/linker/src/test/shared/test/StringUtil.test.d.ts +1 -0
  50. package/dist/minified.cjs +2 -0
  51. package/dist/minified.cjs.map +1 -0
  52. package/dist/minified.js +2617 -0
  53. package/dist/minified.js.map +1 -0
  54. package/dist/wesl-testsuite/src/test-cases/BulkTests.d.ts +4 -0
  55. package/dist/wesl-testsuite/src/test-cases/ImportCases.d.ts +3 -0
  56. package/dist/wesl-testsuite/src/test-cases/ImportSyntaxCases.d.ts +3 -0
  57. package/package.json +45 -0
  58. package/src/AbstractElems.ts +148 -0
  59. package/src/BindIdents.ts +277 -0
  60. package/src/CommentsGrammar.ts +44 -0
  61. package/src/FlattenTreeImport.ts +59 -0
  62. package/src/ImportGrammar.ts +142 -0
  63. package/src/ImportTree.ts +19 -0
  64. package/src/Linker.ts +151 -0
  65. package/src/LowerAndEmit.ts +143 -0
  66. package/src/ParseWESL.ts +106 -0
  67. package/src/ParsedRegistry.ts +97 -0
  68. package/src/PathUtil.ts +52 -0
  69. package/src/Scope.ts +100 -0
  70. package/src/Slicer.ts +127 -0
  71. package/src/StandardTypes.ts +66 -0
  72. package/src/Util.ts +112 -0
  73. package/src/WESLCollect.ts +336 -0
  74. package/src/WESLGrammar.ts +538 -0
  75. package/src/WESLTokens.ts +97 -0
  76. package/src/WgslBundle.ts +16 -0
  77. package/src/debug/ASTtoString.ts +149 -0
  78. package/src/debug/ImportToString.ts +21 -0
  79. package/src/debug/LineWrapper.ts +65 -0
  80. package/src/debug/ScopeToString.ts +51 -0
  81. package/src/index.ts +7 -0
  82. package/src/test/ErrorLogging.test.ts +14 -0
  83. package/src/test/Expression.test.ts +22 -0
  84. package/src/test/FlattenTreeImport.test.ts +56 -0
  85. package/src/test/ImportCases.test.ts +440 -0
  86. package/src/test/ImportSyntaxCases.test.ts +22 -0
  87. package/src/test/LinkGlob.test.ts +25 -0
  88. package/src/test/LinkPackage.test.ts +26 -0
  89. package/src/test/Linker.test.ts +120 -0
  90. package/src/test/MatchWgslD.test.ts +16 -0
  91. package/src/test/ParseComments.test.ts +74 -0
  92. package/src/test/ParseWESL.test.ts +902 -0
  93. package/src/test/PathUtil.test.ts +34 -0
  94. package/src/test/PrettyGrammar.test.ts +21 -0
  95. package/src/test/ScopeWESL.test.ts +272 -0
  96. package/src/test/Slicer.test.ts +103 -0
  97. package/src/test/TestSetup.ts +4 -0
  98. package/src/test/TestUtil.ts +52 -0
  99. package/src/test/Util.test.ts +22 -0
  100. package/src/test/WgslTests.ts +0 -0
  101. package/src/test/__snapshots__/ParseDirectives.test.ts.snap +25 -0
  102. package/src/test/__snapshots__/ParseWESL.test.ts.snap +119 -0
  103. package/src/test/__snapshots__/ParseWESL2.test.ts.snap +67 -0
  104. package/src/test/__snapshots__/RustDirective.test.ts.snap +359 -0
  105. package/src/test/shared/StringUtil.ts +59 -0
  106. package/src/test/shared/test/StringUtil.test.ts +32 -0
  107. package/src/test/wgsl_1/main.wgsl +3 -0
  108. package/src/test/wgsl_1/util.wgsl +1 -0
  109. package/src/test/wgsl_2/main2.wgsl +3 -0
  110. package/src/test/wgsl_2/util2.wgsl +1 -0
@@ -0,0 +1,148 @@
1
+ import { ImportTree } from "./ImportTree.ts";
2
+ import { DeclIdent, RefIdent, SrcModule } from "./Scope.ts";
3
+
4
+ export type AbstractElem =
5
+ | AliasElem
6
+ | ConstElem
7
+ | ImportElem
8
+ | ConstAssertElem
9
+ | FnElem
10
+ | RefIdentElem
11
+ | DeclIdentElem
12
+ | ModuleElem
13
+ | NameElem
14
+ | OverrideElem
15
+ | ParamElem
16
+ | StructElem
17
+ | StructMemberElem
18
+ | TextElem
19
+ | GlobalVarElem
20
+ | VarElem;
21
+
22
+ export type DeclarationElem =
23
+ | AliasElem
24
+ | ConstElem
25
+ | OverrideElem
26
+ | ParamElem
27
+ | FnElem
28
+ | StructElem
29
+ | GlobalVarElem
30
+ | VarElem;
31
+
32
+ export interface AbstractElemBase {
33
+ kind: string;
34
+ start: number;
35
+ end: number;
36
+ }
37
+
38
+ export interface ElemWithContents extends AbstractElemBase {
39
+ contents: AbstractElem[];
40
+ }
41
+
42
+ export interface ImportElem extends ElemWithContents {
43
+ kind: "import";
44
+ imports: ImportTree;
45
+ }
46
+
47
+ /** an identifier in WESL source */
48
+ export interface RefIdentElem extends AbstractElemBase {
49
+ kind: RefIdent["kind"];
50
+ ident: RefIdent;
51
+ srcModule: SrcModule;
52
+ }
53
+
54
+ /** an identifier in WESL source */
55
+ export interface DeclIdentElem extends AbstractElemBase {
56
+ kind: DeclIdent["kind"];
57
+ ident: DeclIdent;
58
+ srcModule: SrcModule;
59
+ }
60
+
61
+ /** a raw bit of text in WESL source that's typically copied to the linked WGSL.
62
+ e.g. a keyword like 'var' or '@diagnostic(off,derivative_uniformity)'
63
+ */
64
+ export interface TextElem extends AbstractElemBase {
65
+ kind: "text";
66
+ srcModule: SrcModule;
67
+ }
68
+
69
+ /** a parameter in a function declaration */
70
+ export interface ParamElem extends ElemWithContents {
71
+ kind: "param";
72
+ name: DeclIdentElem;
73
+ typeRef: RefIdentElem;
74
+ }
75
+
76
+ /** a variable declaration */
77
+ export interface VarElem extends ElemWithContents {
78
+ kind: "var";
79
+ name: DeclIdentElem;
80
+ typeRef?: RefIdentElem;
81
+ }
82
+
83
+ /** a global variable declaration (at the root level) */
84
+ export interface GlobalVarElem extends ElemWithContents {
85
+ kind: "gvar";
86
+ name: DeclIdentElem;
87
+ typeRef?: RefIdentElem;
88
+ }
89
+
90
+ /** a const declaration */
91
+ export interface ConstElem extends ElemWithContents {
92
+ kind: "const";
93
+ name: DeclIdentElem;
94
+ typeRef?: RefIdentElem;
95
+ }
96
+
97
+ /** an override declaration */
98
+ export interface OverrideElem extends ElemWithContents {
99
+ kind: "override";
100
+ name: DeclIdentElem;
101
+ typeRef?: RefIdentElem;
102
+ }
103
+
104
+ /** an entire file */
105
+ export interface ModuleElem extends ElemWithContents {
106
+ kind: "module";
107
+ }
108
+
109
+ /** an alias statement */
110
+ export interface AliasElem extends ElemWithContents {
111
+ kind: "alias";
112
+ name: DeclIdentElem;
113
+ typeRef: RefIdentElem;
114
+ }
115
+
116
+ /** a const_assert statement */
117
+ export interface ConstAssertElem extends ElemWithContents {
118
+ kind: "assert";
119
+ }
120
+
121
+ /** a struct declaration */
122
+ export interface StructElem extends ElemWithContents {
123
+ kind: "struct";
124
+ name: DeclIdentElem;
125
+ members: StructMemberElem[];
126
+ }
127
+
128
+ /** a member of a struct declaration */
129
+ export interface StructMemberElem extends ElemWithContents {
130
+ kind: "member";
131
+ name: NameElem;
132
+ typeRef: RefIdentElem;
133
+ }
134
+
135
+ /** a name (e.g. a struct member name) that doesn't need to be an Ident */
136
+ export interface NameElem extends AbstractElemBase {
137
+ kind: "name";
138
+ name: string;
139
+ srcModule: SrcModule;
140
+ }
141
+
142
+ /** a function declaration */
143
+ export interface FnElem extends ElemWithContents {
144
+ kind: "fn";
145
+ name: DeclIdentElem;
146
+ params: ParamElem[];
147
+ returnType?: RefIdentElem;
148
+ }
@@ -0,0 +1,277 @@
1
+ import { debugNames, srcLog } from "mini-parse";
2
+ import { DeclarationElem } from "./AbstractElems.ts";
3
+ import { FlatImport } from "./FlattenTreeImport.ts";
4
+ import { ParsedRegistry } from "./ParsedRegistry.ts";
5
+ import { flatImports, WeslAST } from "./ParseWESL.ts";
6
+ import { DeclIdent, exportDecl, RefIdent, Scope } from "./Scope.ts";
7
+ import { identToString } from "./debug/ScopeToString.ts";
8
+ import { stdFn, stdType } from "./StandardTypes.ts";
9
+ import { last, overlapTail } from "./Util.ts";
10
+
11
+ /**
12
+ * Bind active reference idents to declaration Idents by mutating the refersTo: field
13
+ * Also in this pass, set the mangledName: field for all active global declaration idents.
14
+ *
15
+ * @param parsed
16
+ * @param conditions only bind to/from idents that are valid with the current condition set
17
+ * @return any new declaration elements found (they will need to be emitted)
18
+ */
19
+ export function bindIdents(
20
+ ast: WeslAST,
21
+ parsed: ParsedRegistry,
22
+ conditions: Record<string, any>,
23
+ ): DeclarationElem[] {
24
+ /*
25
+ For each module's scope, search through the scope tree to find all ref idents
26
+ - For each ref ident, search up the scope tree to find a matching decl ident
27
+ - If no local match is found, check for partial matches with import statements
28
+ - combine ident with import statement to match a decl in exporting module
29
+
30
+ As global decl idents are found, mutate their mangled name to be globally unique.
31
+ */
32
+ const { rootScope } = ast;
33
+
34
+ const globalNames = new Set<string>();
35
+ const knownDecls = new Set<DeclIdent>();
36
+ rootScope.idents.forEach(ident => {
37
+ if (ident.kind === "decl") {
38
+ ident.mangledName = ident.originalName;
39
+ globalNames.add(ident.originalName);
40
+ knownDecls.add(ident);
41
+ }
42
+ });
43
+
44
+ const bindContext = {
45
+ parsed,
46
+ conditions,
47
+ knownDecls,
48
+ foundScopes: new Set<Scope>(),
49
+ globalNames,
50
+ };
51
+ const decls = bindIdentsRecursive(rootScope, bindContext);
52
+ return decls.flatMap(d =>
53
+ d.declElem && isGlobal(d.declElem) ? [d.declElem] : [],
54
+ );
55
+ }
56
+
57
+ interface BindContext {
58
+ parsed: ParsedRegistry;
59
+ conditions: Record<string, any>;
60
+ knownDecls: Set<DeclIdent>; // decl idents discovered so far
61
+ foundScopes: Set<Scope>; // save work by not processing scopes multiple times
62
+ globalNames: Set<string>; // root level names used so far
63
+ }
64
+
65
+ /**
66
+ * Recursively bind references to declarations in this scope and
67
+ * any child scopes referenced by these declarations.
68
+ * Uses a hash set of found declarations to avoid duplication
69
+ * @ return any new declarations found
70
+ */
71
+ function bindIdentsRecursive(
72
+ scope: Scope,
73
+ bindContext: BindContext,
74
+ ): DeclIdent[] {
75
+ // early exist if we've processed this scope before
76
+ const { foundScopes } = bindContext;
77
+ if (foundScopes.has(scope)) return [];
78
+ foundScopes.add(scope);
79
+
80
+ // dlog(scopeIdentTree(scope));
81
+
82
+ const { parsed, conditions } = bindContext;
83
+ const { globalNames, knownDecls } = bindContext;
84
+ const newDecls: DeclIdent[] = []; // new decl idents to process (and return)
85
+
86
+ scope.idents.forEach(ident => {
87
+ // dlog(`--- considering ident ${identToString(ident)}`);
88
+ if (ident.kind === "ref") {
89
+ if (!ident.refersTo && !ident.std) {
90
+ if (stdWgsl(ident.originalName)) {
91
+ ident.std = true;
92
+ } else {
93
+ let foundDecl =
94
+ findDeclInModule(ident.scope, ident) ??
95
+ findDeclImport(ident, parsed);
96
+
97
+ if (foundDecl) {
98
+ ident.refersTo = foundDecl;
99
+ if (!knownDecls.has(foundDecl)) {
100
+ knownDecls.add(foundDecl);
101
+ setDisplayName(ident.originalName, foundDecl, globalNames);
102
+ if (foundDecl.declElem && isGlobal(foundDecl.declElem)) {
103
+ newDecls.push(foundDecl);
104
+ }
105
+ // dlog(` > found new decl: ${identToString(foundDecl)}`);
106
+ }
107
+ } else {
108
+ const { refIdentElem } = ident;
109
+ if (refIdentElem) {
110
+ const { srcModule, start, end } = refIdentElem;
111
+ const { filePath } = srcModule;
112
+ const msg = `unresolved identifier in file: ${filePath}`;
113
+ srcLog(srcModule.src, [start, end], msg);
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+ });
120
+
121
+ // follow references from child scopes
122
+ const newFromChildren = scope.children.flatMap(child => {
123
+ // dlog("to newFromChildren", { childScope: scopeIdentTree(child) });
124
+ return bindIdentsRecursive(child, bindContext);
125
+ });
126
+ // console.log(
127
+ // "new from children",
128
+ // newFromChildren.map(d => identToString(d)),
129
+ // );
130
+
131
+ // follow references from referenced declarations
132
+ const newFromRefs = newDecls.flatMap(decl => {
133
+ // dlog("to newFromRefs", {
134
+ // decl: identToString(decl),
135
+ // declScope: scopeIdentTree(decl.scope),
136
+ // });
137
+ if (debugNames && !decl.scope) {
138
+ console.log(`--- decl ${identToString(decl)} has no scope`);
139
+ return [];
140
+ }
141
+ return bindIdentsRecursive(decl.scope, bindContext);
142
+ });
143
+ // console.log(
144
+ // "new from refs",
145
+ // newFromRefs.map(d => identToString(d)),
146
+ // );
147
+
148
+ return [newDecls, newFromChildren, newFromRefs].flat();
149
+ }
150
+
151
+ function setDisplayName(
152
+ proposedName: string,
153
+ decl: DeclIdent,
154
+ globalNames: Set<string>,
155
+ ): void {
156
+ if (!decl.mangledName) {
157
+ // if (!decl.declElem) {
158
+ // console.log(
159
+ // `--- decl ident ${identToString(decl)} has no declElem attached`,)
160
+ // } else
161
+ if (decl.declElem && isGlobal(decl.declElem)) {
162
+ decl.mangledName = declUniqueName(proposedName, globalNames);
163
+ // dlog(` > mangle global decl: ${identToString(decl)}`);
164
+ } else {
165
+ // dlog(` > no-mangle local decl: ${identToString(decl)}`);
166
+ decl.mangledName = decl.originalName;
167
+ }
168
+ }
169
+ }
170
+
171
+ function stdWgsl(name: string): boolean {
172
+ return stdType(name) || stdFn(name);
173
+ }
174
+
175
+ /** search earlier in the scope and in parent scopes to find a matching decl ident */
176
+ function findDeclInModule(
177
+ scope: Scope,
178
+ ident: RefIdent,
179
+ ): DeclIdent | undefined {
180
+ const { parent } = scope;
181
+ const { originalName } = ident;
182
+
183
+ const found = scope.idents.find(
184
+ i => i.kind === "decl" && i.originalName === originalName,
185
+ );
186
+ if (found) return found as DeclIdent;
187
+
188
+ // recurse to check all idents in parent scope
189
+ if (parent) {
190
+ // dlog("checking parent scope", {
191
+ // ident: identToString(ident),
192
+ // });
193
+ // console.log(scopeIdentTree(parent));
194
+ return findDeclInModule(parent, ident);
195
+ }
196
+ }
197
+
198
+ /** Match a reference identifier to a declaration in
199
+ * another module via an import statement */
200
+ function findDeclImport(
201
+ refIdent: RefIdent,
202
+ parsed: ParsedRegistry,
203
+ ): DeclIdent | undefined {
204
+ // dlog(identToString(refIdent), { ast: !!refIdent.ast });
205
+ const flatImps = flatImports(refIdent.ast);
206
+
207
+ // find module path by combining identifer reference with import statement
208
+ const modulePathParts = matchingImport(refIdent, flatImps); // module path in array form
209
+
210
+ if (modulePathParts) {
211
+ return findExport(modulePathParts, parsed);
212
+ }
213
+ }
214
+
215
+ /** using the flattened import array, find an import that matches a provided identifier */
216
+ function matchingImport(
217
+ ident: RefIdent,
218
+ flatImports: FlatImport[],
219
+ ): string[] | undefined {
220
+ const identParts = ident.originalName.split("::");
221
+ for (const flat of flatImports) {
222
+ const impTail = overlapTail(flat.importPath, identParts);
223
+ if (impTail) {
224
+ return [...flat.modulePath, ...impTail];
225
+ }
226
+ }
227
+ }
228
+
229
+ /** @return an exported root element for the provided path */
230
+ function findExport(
231
+ modulePathParts: string[],
232
+ parsed: ParsedRegistry,
233
+ ): DeclIdent | undefined {
234
+ const legacyConvert = modulePathParts.map(p => (p === "." ? "package" : p)); // TODO rm after we update grammar
235
+ const modulePath = legacyConvert.slice(0, -1).join("::");
236
+ const module = parsed.modules[modulePath];
237
+ if (!module) {
238
+ // TODO show error with source location
239
+ console.log(
240
+ `ident ${modulePathParts.join("::")} in import statement, but module not found`,
241
+ );
242
+ }
243
+
244
+ return exportDecl(module.rootScope, last(modulePathParts)!);
245
+ }
246
+
247
+ /** return mangled name for decl ident,
248
+ * mutating the Ident to remember mangled name if it hasn't yet been determined */
249
+ export function declUniqueName(
250
+ proposedName: string,
251
+ rootNames: Set<string>,
252
+ ): string {
253
+ const displayName = uniquifyName(proposedName, rootNames);
254
+ rootNames.add(displayName);
255
+
256
+ return displayName;
257
+ }
258
+
259
+ /** construct global unique name for use in the output */
260
+ function uniquifyName(proposedName: string, rootNames: Set<string>): string {
261
+ let renamed = proposedName;
262
+ let conflicts = 0;
263
+
264
+ // create a unique name
265
+ while (rootNames.has(renamed)) {
266
+ renamed = proposedName + conflicts++;
267
+ }
268
+
269
+ // dlog({ proposedName, renamed });
270
+ return renamed;
271
+ }
272
+
273
+ export function isGlobal(elem: DeclarationElem): boolean {
274
+ return ["alias", "const", "override", "fn", "struct", "gvar"].includes(
275
+ elem.kind,
276
+ );
277
+ }
@@ -0,0 +1,44 @@
1
+ import {
2
+ any,
3
+ anyNot,
4
+ anyThrough,
5
+ disablePreParse,
6
+ makeEolf,
7
+ or,
8
+ Parser,
9
+ repeat,
10
+ req,
11
+ resultLog,
12
+ seq,
13
+ tokens,
14
+ } from "mini-parse";
15
+ import { argsTokens, lineCommentTokens, mainTokens } from "./WESLTokens.ts";
16
+
17
+ /* Basic parsing functions for comment handling . */
18
+
19
+ /** currently unused */
20
+ export const unknown = any().map(r => {
21
+ const { kind, text } = r.value;
22
+ const deepName = r.ctx._debugNames.join(" > ");
23
+
24
+ resultLog(r, `??? ${kind}: '${text}' ${deepName}`);
25
+ // throw new Error("Fail fast");
26
+ });
27
+
28
+ const eolf: Parser<any> = disablePreParse(
29
+ makeEolf(argsTokens, argsTokens.ws),
30
+ );
31
+
32
+ const skipToEol = tokens(lineCommentTokens, anyThrough(eolf));
33
+
34
+ export const blockComment: Parser<any> = seq(
35
+ "/*",
36
+ repeat(or(() => blockComment, anyNot("*/"))),
37
+ req("*/"),
38
+ );
39
+
40
+ export const lineComment = seq(tokens(mainTokens, "//"), skipToEol);
41
+
42
+ export const comment = or(() => lineComment, blockComment).trace({
43
+ hide: true,
44
+ });
@@ -0,0 +1,59 @@
1
+ import { tracing } from "mini-parse";
2
+ import {
3
+ ImportTree,
4
+ PathSegment,
5
+ SegmentList,
6
+ SimpleSegment,
7
+ } from "./ImportTree.js";
8
+
9
+ export interface FlatImport {
10
+ importPath: string[];
11
+ modulePath: string[];
12
+ }
13
+
14
+ /**
15
+ * Simplify importTree into a flattened map from import paths to module paths.
16
+ *
17
+ * @return map from import path (with 'as' renaming) to module Path
18
+ */
19
+ export function flattenTreeImport(imp: ImportTree): FlatImport[] {
20
+ return recursiveResolve([], [], imp.segments);
21
+
22
+ /** recurse through segments of path, producing */
23
+ function recursiveResolve(
24
+ resolvedImportPath: string[],
25
+ resolvedExportPath: string[],
26
+ remainingPath: PathSegment[],
27
+ ): FlatImport[] {
28
+ const [segment, ...rest] = remainingPath;
29
+ if (segment === undefined) {
30
+ throw new Error(`undefined segment ${imp.segments}`);
31
+ }
32
+ if (segment instanceof SimpleSegment) {
33
+ const importPath = [...resolvedImportPath, segment.as || segment.name];
34
+ const modulePath = [...resolvedExportPath, segment.name];
35
+ if (rest.length) {
36
+ // we're in the middle of the path so keep recursing
37
+ return recursiveResolve(importPath, modulePath, rest);
38
+ } else {
39
+ return [{ importPath, modulePath }];
40
+ }
41
+ }
42
+ if (segment instanceof SegmentList) {
43
+ // resolve path with each element in the list
44
+ return segment.list.flatMap(elem => {
45
+ const rPath = [elem, ...rest];
46
+ return recursiveResolve(resolvedImportPath, resolvedExportPath, rPath);
47
+ });
48
+ } else if (segment instanceof ImportTree) {
49
+ return recursiveResolve(
50
+ resolvedImportPath,
51
+ resolvedExportPath,
52
+ segment.segments,
53
+ );
54
+ }
55
+
56
+ if (tracing) console.log("unknown segment type", segment); // should be impossible
57
+ return [];
58
+ }
59
+ }
@@ -0,0 +1,142 @@
1
+ import {
2
+ disablePreParse,
3
+ kind,
4
+ makeEolf,
5
+ matchOneOf,
6
+ NoTags,
7
+ opt,
8
+ or,
9
+ Parser,
10
+ repeat,
11
+ repeatPlus,
12
+ seq,
13
+ setTraceName,
14
+ TagRecord,
15
+ tagScope,
16
+ tokenMatcher,
17
+ tokens,
18
+ tokenSkipSet,
19
+ tracing,
20
+ withSepPlus
21
+ } from "mini-parse";
22
+ import {
23
+ importElem,
24
+ importList,
25
+ importSegment,
26
+ importTree,
27
+ } from "./WESLCollect.js";
28
+ import { digits, eol, ident, } from "./WESLTokens.js";
29
+
30
+
31
+ // TODO now that ';' is required, special ws and eol handling is probably not needed.
32
+ const skipWsSet = new Set(["ws"]);
33
+ function skipWs<V, T extends TagRecord>(p: Parser<V, T>): Parser<V, T> {
34
+ return tokenSkipSet(skipWsSet, p);
35
+ }
36
+ function noSkipWs<V, T extends TagRecord>(p: Parser<V, T>): Parser<V, T> {
37
+ return tokenSkipSet(null, p);
38
+ }
39
+
40
+ const importSymbolSet = "/ { } , ( ) .. . * ; @ #"; // Had to add @ and # here to get the parsing tests to work. Weird.
41
+ const importSymbol = matchOneOf(importSymbolSet);
42
+
43
+ // TODO reconsider whether we need a separate token set for import statements vs wgsl/wesl
44
+ export const importTokens = tokenMatcher({
45
+ ws: /\s+/,
46
+ importSymbol,
47
+ ident, // TODO allow '-' in pkg names?
48
+ digits,
49
+ });
50
+
51
+ export const eolTokens = tokenMatcher({
52
+ ws: /[ \t]+/, // don't include \n, for eolf
53
+ eol,
54
+ });
55
+
56
+ const eolf = disablePreParse(makeEolf(eolTokens, importTokens.ws));
57
+ const wordToken = kind(importTokens.ident);
58
+
59
+ // forward references for mutual recursion
60
+ let packagePath: Parser<any, NoTags> = null as any;
61
+
62
+ const simpleSegment = tagScope(
63
+ wordToken.ptag("segment").collect(importSegment),
64
+ );
65
+
66
+ /** last simple segment is allowed to have an 'as' rename */
67
+ const lastSimpleSegment = tagScope(
68
+ seq(
69
+ wordToken.ptag("segment"),
70
+ skipWs(opt(seq("as", wordToken.ptag("as")))),
71
+ ).collect(importSegment),
72
+ );
73
+
74
+ /** an item an a collection list {a, b} */
75
+ const collectionItem = or(
76
+ tagScope(or(() => packagePath).collect(importTree)),
77
+ lastSimpleSegment,
78
+ );
79
+
80
+ const importCollection = tagScope(
81
+ seq(
82
+ "{",
83
+ skipWs(
84
+ seq(
85
+ withSepPlus(",", () => collectionItem.ctag("list")),
86
+ "}",
87
+ ),
88
+ ),
89
+ ).collect(importList),
90
+ );
91
+
92
+ /** a relative path element like "./" or "../" */
93
+ const relativeSegment = tagScope(
94
+ seq(or(".", "..").ptag("segment"), "/").collect(importSegment),
95
+ ).ctag("p");
96
+
97
+ const lastSegment = or(lastSimpleSegment, importCollection);
98
+
99
+ const packageTail = seq(
100
+ repeat(seq(simpleSegment.ctag("p"), "/")),
101
+ lastSegment.ctag("p"),
102
+ );
103
+
104
+ /** a module path starting with ../ or ./ */
105
+ const relativePath = seq(repeatPlus(relativeSegment), packageTail);
106
+
107
+ const packagePrefix = tagScope(
108
+ seq(wordToken.ptag("segment"), "/").collect(importSegment),
109
+ ).ctag("p");
110
+
111
+ /** a module path, starting with a simple element */
112
+ packagePath = seq(packagePrefix, packageTail);
113
+
114
+ const fullPath = noSkipWs(
115
+ seq(kind(importTokens.ws), or(relativePath, packagePath)),
116
+ );
117
+
118
+ /** parse a WESL style wgsl import statement. */
119
+ export const weslImport = tagScope(
120
+ tokens(
121
+ importTokens,
122
+ seq("import", fullPath, opt(";"), eolf).collect(importElem()),
123
+ ),
124
+ );
125
+
126
+ if (tracing) {
127
+ const names: Record<string, Parser<unknown, TagRecord>> = {
128
+ simpleSegment,
129
+ lastSimpleSegment,
130
+ importCollection,
131
+ relativeSegment,
132
+ relativePath,
133
+ packagePrefix,
134
+ packagePath,
135
+ fullPath,
136
+ weslImport,
137
+ };
138
+
139
+ Object.entries(names).forEach(([name, parser]) => {
140
+ setTraceName(parser, name);
141
+ });
142
+ }
@@ -0,0 +1,19 @@
1
+ export class ImportTree {
2
+ /** segments in path order */
3
+ constructor(public segments: PathSegment[]) {}
4
+ }
5
+
6
+ export type PathSegment = SimpleSegment | ImportTree | SegmentList;
7
+
8
+ export class SimpleSegment {
9
+ constructor(
10
+ public name: string,
11
+ public as?: string,
12
+ public args?: string[], // generic args (only allowed on final segment). TODO drop
13
+ ) {}
14
+ }
15
+
16
+ /** or choices for this path segment */
17
+ export class SegmentList {
18
+ constructor(public list: PathSegment[]) {}
19
+ }