wesl 0.6.0-pre10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +31 -0
  2. package/dist/index.js +4468 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/minified.js +3426 -0
  5. package/dist/minified.js.map +1 -0
  6. package/dist/tools/packages/wesl/src/AbstractElems.d.ts +322 -0
  7. package/dist/tools/packages/wesl/src/Assertions.d.ts +27 -0
  8. package/dist/tools/packages/wesl/src/BindIdents.d.ts +70 -0
  9. package/dist/tools/packages/wesl/src/Conditions.d.ts +6 -0
  10. package/dist/tools/packages/wesl/src/FlattenTreeImport.d.ts +11 -0
  11. package/dist/tools/packages/wesl/src/LinkedWesl.d.ts +50 -0
  12. package/dist/tools/packages/wesl/src/Linker.d.ts +87 -0
  13. package/dist/tools/packages/wesl/src/LinkerUtil.d.ts +3 -0
  14. package/dist/tools/packages/wesl/src/LiveDeclarations.d.ts +12 -0
  15. package/dist/tools/packages/wesl/src/LowerAndEmit.d.ts +31 -0
  16. package/dist/tools/packages/wesl/src/Mangler.d.ts +39 -0
  17. package/dist/tools/packages/wesl/src/ParseWESL.d.ts +60 -0
  18. package/dist/tools/packages/wesl/src/ParsedRegistry.d.ts +29 -0
  19. package/dist/tools/packages/wesl/src/PathUtil.d.ts +6 -0
  20. package/dist/tools/packages/wesl/src/RawEmit.d.ts +6 -0
  21. package/dist/tools/packages/wesl/src/Reflection.d.ts +45 -0
  22. package/dist/tools/packages/wesl/src/Scope.d.ts +81 -0
  23. package/dist/tools/packages/wesl/src/StandardTypes.d.ts +13 -0
  24. package/dist/tools/packages/wesl/src/TransformBindingStructs.d.ts +52 -0
  25. package/dist/tools/packages/wesl/src/Util.d.ts +43 -0
  26. package/dist/tools/packages/wesl/src/WESLCollect.d.ts +94 -0
  27. package/dist/tools/packages/wesl/src/WeslBundle.d.ts +13 -0
  28. package/dist/tools/packages/wesl/src/WeslDevice.d.ts +25 -0
  29. package/dist/tools/packages/wesl/src/debug/ASTtoString.d.ts +5 -0
  30. package/dist/tools/packages/wesl/src/debug/ImportToString.d.ts +2 -0
  31. package/dist/tools/packages/wesl/src/debug/LineWrapper.d.ts +21 -0
  32. package/dist/tools/packages/wesl/src/debug/ScopeToString.d.ts +6 -0
  33. package/dist/tools/packages/wesl/src/index.d.ts +11 -0
  34. package/dist/tools/packages/wesl/src/parse/ImportGrammar.d.ts +5 -0
  35. package/dist/tools/packages/wesl/src/parse/Keywords.d.ts +4 -0
  36. package/dist/tools/packages/wesl/src/parse/WeslBaseGrammar.d.ts +5 -0
  37. package/dist/tools/packages/wesl/src/parse/WeslExpression.d.ts +13 -0
  38. package/dist/tools/packages/wesl/src/parse/WeslGrammar.d.ts +80 -0
  39. package/dist/tools/packages/wesl/src/parse/WeslStream.d.ts +44 -0
  40. package/dist/tools/packages/wesl/src/test/BindWESL.test.d.ts +1 -0
  41. package/dist/tools/packages/wesl/src/test/ConditionLinking.test.d.ts +1 -0
  42. package/dist/tools/packages/wesl/src/test/ConditionalTranslationCases.test.d.ts +1 -0
  43. package/dist/tools/packages/wesl/src/test/ErrorLogging.test.d.ts +1 -0
  44. package/dist/tools/packages/wesl/src/test/Expression.test.d.ts +1 -0
  45. package/dist/tools/packages/wesl/src/test/FlattenTreeImport.test.d.ts +1 -0
  46. package/dist/tools/packages/wesl/src/test/ImportCases.test.d.ts +1 -0
  47. package/dist/tools/packages/wesl/src/test/ImportSyntaxCases.test.d.ts +1 -0
  48. package/dist/tools/packages/wesl/src/test/LinkGlob.test.d.ts +1 -0
  49. package/dist/tools/packages/wesl/src/test/LinkPackage.test.d.ts +1 -0
  50. package/dist/tools/packages/wesl/src/test/Linker.test.d.ts +1 -0
  51. package/dist/tools/packages/wesl/src/test/Mangling.test.d.ts +1 -0
  52. package/dist/tools/packages/wesl/src/test/ParseComments.test.d.ts +1 -0
  53. package/dist/tools/packages/wesl/src/test/ParseConditions.test.d.ts +1 -0
  54. package/dist/tools/packages/wesl/src/test/ParseError.test.d.ts +1 -0
  55. package/dist/tools/packages/wesl/src/test/ParseWESL.test.d.ts +1 -0
  56. package/dist/tools/packages/wesl/src/test/PathUtil.test.d.ts +1 -0
  57. package/dist/tools/packages/wesl/src/test/PrettyGrammar.test.d.ts +1 -0
  58. package/dist/tools/packages/wesl/src/test/Reflection.test.d.ts +1 -0
  59. package/dist/tools/packages/wesl/src/test/ScopeWESL.test.d.ts +1 -0
  60. package/dist/tools/packages/wesl/src/test/TestLink.d.ts +21 -0
  61. package/dist/tools/packages/wesl/src/test/TestSetup.d.ts +1 -0
  62. package/dist/tools/packages/wesl/src/test/TestUtil.d.ts +40 -0
  63. package/dist/tools/packages/wesl/src/test/Tokenizer.test.d.ts +1 -0
  64. package/dist/tools/packages/wesl/src/test/TransformBindingStructs.test.d.ts +1 -0
  65. package/dist/tools/packages/wesl/src/test/Util.test.d.ts +1 -0
  66. package/dist/tools/packages/wesl/src/test/VirtualModules.test.d.ts +1 -0
  67. package/dist/tools/packages/wesl/src/test/WeslDevice.test.d.ts +1 -0
  68. package/dist/tools/packages/wesl/src/test/WgslTests.d.ts +0 -0
  69. package/dist/tools/packages/wesl/src/vlq/vlq.d.ts +11 -0
  70. package/package.json +46 -0
  71. package/src/AbstractElems.ts +446 -0
  72. package/src/Assertions.ts +51 -0
  73. package/src/BindIdents.ts +523 -0
  74. package/src/Conditions.ts +74 -0
  75. package/src/FlattenTreeImport.ts +55 -0
  76. package/src/LinkedWesl.ts +184 -0
  77. package/src/Linker.ts +284 -0
  78. package/src/LinkerUtil.ts +29 -0
  79. package/src/LiveDeclarations.ts +31 -0
  80. package/src/LowerAndEmit.ts +413 -0
  81. package/src/Mangler.ts +94 -0
  82. package/src/ParseWESL.ts +157 -0
  83. package/src/ParsedRegistry.ts +120 -0
  84. package/src/PathUtil.ts +31 -0
  85. package/src/RawEmit.ts +102 -0
  86. package/src/Reflection.ts +334 -0
  87. package/src/Scope.ts +162 -0
  88. package/src/StandardTypes.ts +97 -0
  89. package/src/TransformBindingStructs.ts +319 -0
  90. package/src/Util.ts +194 -0
  91. package/src/WESLCollect.ts +614 -0
  92. package/src/WeslBundle.ts +16 -0
  93. package/src/WeslDevice.ts +209 -0
  94. package/src/debug/ASTtoString.ts +290 -0
  95. package/src/debug/ImportToString.ts +29 -0
  96. package/src/debug/LineWrapper.ts +70 -0
  97. package/src/debug/ScopeToString.ts +79 -0
  98. package/src/index.ts +11 -0
  99. package/src/parse/ImportGrammar.ts +157 -0
  100. package/src/parse/Keywords.ts +26 -0
  101. package/src/parse/WeslBaseGrammar.ts +8 -0
  102. package/src/parse/WeslExpression.ts +207 -0
  103. package/src/parse/WeslGrammar.ts +856 -0
  104. package/src/parse/WeslStream.ts +279 -0
  105. package/src/test/BindWESL.test.ts +57 -0
  106. package/src/test/ConditionLinking.test.ts +91 -0
  107. package/src/test/ConditionalTranslationCases.test.ts +56 -0
  108. package/src/test/ErrorLogging.test.ts +30 -0
  109. package/src/test/Expression.test.ts +22 -0
  110. package/src/test/FlattenTreeImport.test.ts +74 -0
  111. package/src/test/ImportCases.test.ts +56 -0
  112. package/src/test/ImportSyntaxCases.test.ts +24 -0
  113. package/src/test/LinkGlob.test.ts +25 -0
  114. package/src/test/LinkPackage.test.ts +26 -0
  115. package/src/test/Linker.test.ts +125 -0
  116. package/src/test/Mangling.test.ts +45 -0
  117. package/src/test/ParseComments.test.ts +36 -0
  118. package/src/test/ParseConditions.test.ts +183 -0
  119. package/src/test/ParseError.test.ts +36 -0
  120. package/src/test/ParseWESL.test.ts +1572 -0
  121. package/src/test/PathUtil.test.ts +34 -0
  122. package/src/test/PrettyGrammar.test.ts +20 -0
  123. package/src/test/Reflection.test.ts +172 -0
  124. package/src/test/ScopeWESL.test.ts +462 -0
  125. package/src/test/TestLink.ts +82 -0
  126. package/src/test/TestSetup.ts +4 -0
  127. package/src/test/TestUtil.ts +126 -0
  128. package/src/test/Tokenizer.test.ts +135 -0
  129. package/src/test/TransformBindingStructs.test.ts +230 -0
  130. package/src/test/Util.test.ts +22 -0
  131. package/src/test/VirtualModules.test.ts +37 -0
  132. package/src/test/WeslDevice.test.ts +265 -0
  133. package/src/test/WgslTests.ts +0 -0
  134. package/src/test/__snapshots__/ParseDirectives.test.ts.snap +25 -0
  135. package/src/test/__snapshots__/ParseWESL.test.ts.snap +119 -0
  136. package/src/test/__snapshots__/RustDirective.test.ts.snap +359 -0
  137. package/src/test/wgsl_1/main.wgsl +3 -0
  138. package/src/test/wgsl_1/util.wgsl +1 -0
  139. package/src/test/wgsl_2/main2.wgsl +3 -0
  140. package/src/test/wgsl_2/util2.wgsl +1 -0
  141. package/src/vlq/vlq.ts +94 -0
@@ -0,0 +1,279 @@
1
+ import {
2
+ CachingStream,
3
+ MatchersStream,
4
+ matchOneOf,
5
+ ParseError,
6
+ RegexMatchers,
7
+ Stream,
8
+ TypedToken,
9
+ withStreamAction,
10
+ } from "mini-parse";
11
+ import { keywords, reservedWords } from "./Keywords";
12
+ export type WeslTokenKind = "word" | "keyword" | "number" | "symbol";
13
+
14
+ export type WeslToken<Kind extends WeslTokenKind = WeslTokenKind> =
15
+ TypedToken<Kind>;
16
+
17
+ // https://www.w3.org/TR/WGSL/#blankspace-and-line-breaks
18
+ /** Whitespaces including new lines */
19
+ const blankspaces = /[ \t\n\v\f\r\u{0085}\u{200E}\u{200F}\u{2028}\u{2029}]+/u;
20
+ const symbolSet =
21
+ "& && -> @ / ! [ ] { } :: : , == = != >>= >> >= > <<= << <= < % - --" +
22
+ " . + ++ | || ( ) ; * ~ ^ // /* */ += -= *= /= %= &= |= ^=" +
23
+ // For the _ = expr; syntax
24
+ " _";
25
+
26
+ const ident =
27
+ /(?:(?:[_\p{XID_Start}][\p{XID_Continue}]+)|(?:[\p{XID_Start}]))/u;
28
+
29
+ /** Checks if a word is a valid WGSL ident, and not a keyword */
30
+ export function isIdent(text: string): boolean {
31
+ if (text.match(ident)?.[0] !== text) {
32
+ return false;
33
+ }
34
+ if (keywordOrReserved.has(text)) {
35
+ return false;
36
+ }
37
+ return true;
38
+ }
39
+
40
+ const keywordOrReserved = new Set(keywords.concat(reservedWords));
41
+
42
+ const digits = new RegExp(
43
+ // decimal_float_literal
44
+ /(?:0[fh])|(?:[1-9][0-9]*[fh])/.source +
45
+ /|(?:[0-9]*\.[0-9]+(?:[eE][+-]?[0-9]+)?[fh]?)/.source +
46
+ /|(?:[0-9]+\.[0-9]*(?:[eE][+-]?[0-9]+)?[fh]?)/.source +
47
+ /|(?:[0-9]+[eE][+-]?[0-9]+[fh]?)/.source +
48
+ // hex_float_literal
49
+ /|(?:0[xX][0-9a-fA-F]*\.[0-9a-fA-F]+(?:[pP][+-]?[0-9]+[fh]?)?)/.source +
50
+ /|(?:0[xX][0-9a-fA-F]+\.[0-9a-fA-F]*(?:[pP][+-]?[0-9]+[fh]?)?)/.source +
51
+ /|(?:0[xX][0-9a-fA-F]+[pP][+-]?[0-9]+[fh]?)/.source +
52
+ // hex_int_literal
53
+ /|(?:0[xX][0-9a-fA-F]+[iu]?)/.source +
54
+ // decimal_int_literal
55
+ /|(?:0[iu]?)|(?:[1-9][0-9]*[iu]?)/.source,
56
+ );
57
+
58
+ const commentStart = /\/\/|\/\*/;
59
+
60
+ type InternalTokenKind =
61
+ | "word"
62
+ | "number"
63
+ | "blankspaces"
64
+ | "commentStart"
65
+ | "symbol"
66
+ | "invalid";
67
+ const weslMatcher = new RegexMatchers<InternalTokenKind>({
68
+ word: ident,
69
+ number: digits,
70
+ blankspaces,
71
+ commentStart,
72
+ symbol: matchOneOf(symbolSet),
73
+ invalid: /[^]/,
74
+ });
75
+
76
+ /** To mark parts of the grammar implementation that are WESL specific extensions */
77
+ export function weslExtension<T>(combinator: T): T {
78
+ return combinator;
79
+ }
80
+
81
+ export class WeslStream implements Stream<WeslToken> {
82
+ private stream: Stream<TypedToken<InternalTokenKind>>;
83
+ /** New line */
84
+ private eolPattern = /[\n\v\f\u{0085}\u{2028}\u{2029}]|\r\n?/gu;
85
+ /** Block comments */
86
+ private blockCommentPattern = /\/\*|\*\//g;
87
+ constructor(public src: string) {
88
+ this.stream = new CachingStream(new MatchersStream(src, weslMatcher));
89
+ }
90
+ checkpoint(): number {
91
+ return this.stream.checkpoint();
92
+ }
93
+ reset(position: number): void {
94
+ this.stream.reset(position);
95
+ }
96
+ nextToken(): WeslToken | null {
97
+ while (true) {
98
+ const token = this.stream.nextToken();
99
+ if (token === null) return null;
100
+
101
+ const kind = token.kind;
102
+ if (kind === "blankspaces") {
103
+ continue;
104
+ } else if (kind === "commentStart") {
105
+ // SAFETY: The underlying streams can be seeked to any position
106
+ if (token.text === "//") {
107
+ this.stream.reset(this.skipToEol(token.span[1]));
108
+ } else {
109
+ this.stream.reset(this.skipBlockComment(token.span[1]));
110
+ }
111
+ } else if (kind === "word") {
112
+ let returnToken = token as TypedToken<typeof kind | "keyword">;
113
+ if (keywordOrReserved.has(token.text)) {
114
+ returnToken.kind = "keyword";
115
+ }
116
+ return returnToken;
117
+ } else if (kind === "invalid") {
118
+ throw new ParseError(
119
+ "Invalid token " + token.text,
120
+ this.stream.checkpoint(),
121
+ );
122
+ } else {
123
+ return token as TypedToken<typeof kind>;
124
+ }
125
+ }
126
+ }
127
+
128
+ private skipToEol(position: number): number {
129
+ this.eolPattern.lastIndex = position;
130
+ const result = this.eolPattern.exec(this.src);
131
+ if (result === null) {
132
+ // We reached the end of the file
133
+ return this.src.length;
134
+ } else {
135
+ // Move forward
136
+ return this.eolPattern.lastIndex;
137
+ }
138
+ }
139
+
140
+ private skipBlockComment(position: number): number {
141
+ while (true) {
142
+ this.blockCommentPattern.lastIndex = position;
143
+ const result = this.blockCommentPattern.exec(this.src);
144
+ if (result === null) {
145
+ throw new ParseError("Unclosed block comment!", position);
146
+ } else if (result[0] === "*/") {
147
+ // Close block
148
+ return this.blockCommentPattern.lastIndex;
149
+ } else if (result[0] === "/*") {
150
+ // Open block
151
+ position = this.skipBlockComment(this.blockCommentPattern.lastIndex);
152
+ } else {
153
+ throw new Error("Unreachable, invalid block comment pattern");
154
+ }
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Only matches the `<` token if it is a template
160
+ * Precondition: An ident was parsed right before this.
161
+ * Runs the [template list discovery algorithm](https://www.w3.org/TR/WGSL/#template-list-discovery).
162
+ */
163
+ nextTemplateStartToken(): (WeslToken & { kind: "symbol" }) | null {
164
+ const startPosition = this.stream.checkpoint();
165
+ const token: WeslToken | null = this.nextToken();
166
+ this.stream.reset(startPosition);
167
+ if (token === null) return null;
168
+
169
+ if (token.kind !== "symbol") {
170
+ return null;
171
+ }
172
+
173
+ //<<= << <= cannot be templates, so we match the entire token text
174
+ if (token.text === "<") {
175
+ if (this.isTemplateStart(token.span[1])) {
176
+ this.stream.reset(token.span[1]);
177
+ return token as WeslToken & { kind: typeof token.kind };
178
+ } else {
179
+ this.stream.reset(startPosition);
180
+ return null;
181
+ }
182
+ } else {
183
+ return null;
184
+ }
185
+ }
186
+
187
+ nextTemplateEndToken(): (WeslToken & { kind: "symbol" }) | null {
188
+ const startPosition = this.stream.checkpoint();
189
+ const token: WeslToken | null = this.nextToken();
190
+ this.stream.reset(startPosition);
191
+ if (token === null) return null;
192
+
193
+ // template closing can also match a >= or >>, so we split the token
194
+ if (token.kind === "symbol" && token.text[0] === ">") {
195
+ // SAFETY: The underlying streams implementations can be reset to any position.
196
+ const tokenPosition = token.span[0];
197
+ this.stream.reset(tokenPosition + 1);
198
+ return {
199
+ kind: "symbol",
200
+ span: [tokenPosition, tokenPosition + 1],
201
+ text: ">",
202
+ };
203
+ } else {
204
+ return null;
205
+ }
206
+ }
207
+
208
+ isTemplateStart(afterToken: number): boolean {
209
+ // Skip over <
210
+ this.stream.reset(afterToken);
211
+ // We start with a < token
212
+ let pendingCounter = 1;
213
+ while (true) {
214
+ const nextToken = this.stream.nextToken();
215
+ if (nextToken === null) return false;
216
+ if (nextToken.kind !== "symbol") continue;
217
+ if (nextToken.text === "<") {
218
+ // Start a nested template
219
+ pendingCounter += 1;
220
+ } else if (nextToken.text[0] === ">") {
221
+ if (nextToken.text === ">" || nextToken.text == ">=") {
222
+ pendingCounter -= 1;
223
+ } else if (nextToken.text === ">>=" || nextToken.text === ">>") {
224
+ pendingCounter -= 2;
225
+ } else {
226
+ throw new Error(
227
+ "This case should never be reached, looks like we forgot one of the tokens that start with >",
228
+ );
229
+ }
230
+ if (pendingCounter <= 0) {
231
+ return true;
232
+ }
233
+ } else if (nextToken.text === "(") {
234
+ this.skipBracketsTo(")");
235
+ } else if (nextToken.text === "[") {
236
+ this.skipBracketsTo("]");
237
+ } else if (
238
+ nextToken.text === "==" ||
239
+ nextToken.text === "!=" ||
240
+ nextToken.text === ";" ||
241
+ nextToken.text === "{" ||
242
+ nextToken.text === ":" ||
243
+ nextToken.text === "&&" ||
244
+ nextToken.text === "||"
245
+ ) {
246
+ return false;
247
+ }
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Call this after consuming an opening bracket.
253
+ * Skips until a closing bracket. This also consumes the closing bracket.
254
+ */
255
+ skipBracketsTo(closingBracket: string) {
256
+ while (true) {
257
+ const nextToken = this.stream.nextToken();
258
+ if (nextToken === null)
259
+ throw new ParseError("Unclosed bracket!", this.stream.checkpoint());
260
+
261
+ if (nextToken.kind !== "symbol") continue;
262
+ if (nextToken.text === "(") {
263
+ this.skipBracketsTo(")");
264
+ } else if (nextToken.text === "[") {
265
+ this.skipBracketsTo("]");
266
+ } else if (nextToken.text === closingBracket) {
267
+ // We're done!
268
+ return;
269
+ }
270
+ }
271
+ }
272
+ }
273
+
274
+ export const templateOpen = withStreamAction(stream => {
275
+ return (stream as WeslStream).nextTemplateStartToken();
276
+ });
277
+ export const templateClose = withStreamAction(stream => {
278
+ return (stream as WeslStream).nextTemplateEndToken();
279
+ });
@@ -0,0 +1,57 @@
1
+ import { expect, test } from "vitest";
2
+ import { scopeToStringLong } from "../debug/ScopeToString.ts";
3
+ import { bindTest } from "./TestUtil.ts";
4
+
5
+ test("nested scopes binding", () => {
6
+ const src = `
7
+ fn main() {
8
+ let bar = 72;
9
+ if (true) {
10
+ if (true) {
11
+ let new_bar = bar; // Should be 72!
12
+ }
13
+ let bar = 5;
14
+ }
15
+ }
16
+ `;
17
+
18
+ const { registry } = bindTest(src);
19
+ const ast = registry.modules["package::test"];
20
+
21
+ expect(scopeToStringLong(ast.rootScope)).toMatchInlineSnapshot(`
22
+ "{
23
+ -{ %main(main) #1
24
+ { %bar(bar) #3
25
+ {
26
+ { %new_bar #5 bar #7 -> %bar(bar) #3 } #5
27
+ %bar #9
28
+ } #4
29
+ } #2
30
+ } #1
31
+ } #0"
32
+ `);
33
+ });
34
+
35
+ test("@location attribute const", () => {
36
+ const src = `
37
+ const pos = 0;
38
+
39
+ @fragment
40
+ fn fragmentMain(@location(0) pos : vec3f) -> @location(pos) vec4f {
41
+ let x = pos;
42
+ }
43
+ `;
44
+ const { registry } = bindTest(src);
45
+ const ast = registry.modules["package::test"];
46
+ expect(scopeToStringLong(ast.rootScope)).toMatchInlineSnapshot(`
47
+ "{ %pos(pos) #1
48
+ { } #1
49
+ -{ %fragmentMain(fragmentMain) #3 pos #9 -> %pos(pos) #1
50
+
51
+ { %pos(pos) #5 vec3f #7 -> undefined
52
+ vec4f #11 -> undefined %x #13 pos #15 -> %pos(pos) #5 }
53
+ #3
54
+ } #2
55
+ } #0"
56
+ `);
57
+ });
@@ -0,0 +1,91 @@
1
+ import { test } from "vitest";
2
+ import { testLink } from "./TestLink.ts";
3
+
4
+ test("conditional statement", async () => {
5
+ const app = `
6
+ fn main() {
7
+ @if(false) let x = 1;
8
+ }
9
+ `;
10
+
11
+ const expectWgsl = `
12
+ fn main() {
13
+ }
14
+ `;
15
+ await testLink({ app: app }, "app", expectWgsl);
16
+ });
17
+
18
+ test("conditional compound statement", async () => {
19
+ const app = `
20
+ fn main() {
21
+ @if(false) {
22
+ let x = 1;
23
+ }
24
+ }
25
+ `;
26
+
27
+ const expectWgsl = `
28
+ fn main() {
29
+ }
30
+ `;
31
+ await testLink({ app: app }, "app", expectWgsl);
32
+ });
33
+
34
+ test("conditional local variables", async () => {
35
+ const app = `
36
+ fn main() {
37
+ @if(true) var x = 1;
38
+ @if(false) var x = 2 ;
39
+ }
40
+ `;
41
+
42
+ const expectWgsl = `
43
+ fn main() {
44
+ var x = 1;
45
+ }
46
+ `;
47
+ await testLink({ app: app }, "app", expectWgsl);
48
+ });
49
+
50
+ test("conditional binding ", async () => {
51
+ const app = `
52
+ fn main() {
53
+ @if(true) var x = 1;
54
+ @if(false) var x = 2;
55
+ x += 1;
56
+ }
57
+ `;
58
+
59
+ const expectWgsl = `
60
+ fn main() {
61
+ var x = 1;
62
+ x += 1;
63
+ }
64
+ `;
65
+ await testLink({ app: app }, "app", expectWgsl);
66
+ });
67
+
68
+ test("conditional binding references", async () => {
69
+ const app = `
70
+ import package::file1::b;
71
+
72
+ fn main() {
73
+ @if(true) var x = b;
74
+ @if(false) var x = 2;
75
+ x += 1;
76
+ }
77
+ `;
78
+
79
+ const file1 = `
80
+ const b = 9;
81
+ `;
82
+
83
+ const expectWgsl = `
84
+ fn main() {
85
+ var x = b;
86
+ x += 1;
87
+ }
88
+ const b = 9;
89
+ `;
90
+ await testLink({ app, file1 }, "app", expectWgsl);
91
+ });
@@ -0,0 +1,56 @@
1
+ import { afterAll, test, TestContext } from "vitest";
2
+ import { conditionalTranslationCases } from "wesl-testsuite";
3
+ import { testFromCase, verifyCaseCoverage } from "./TestLink.js";
4
+
5
+ const examplesByName = new Map(
6
+ conditionalTranslationCases.map(t => [t.name, t]),
7
+ );
8
+
9
+ async function caseTest(ctx: TestContext): Promise<void> {
10
+ return testFromCase(ctx.task.name, examplesByName);
11
+ }
12
+
13
+ // requires code stripping to be disabled (which wesl-js doesn't support)
14
+ const weslJsTests = conditionalTranslationCases.filter(
15
+ c => c.name !== "conditional declaration shadowing",
16
+ );
17
+ afterAll(verifyCaseCoverage(weslJsTests));
18
+
19
+ test("@if on diagnostic directive", ctx => caseTest(ctx));
20
+ test("@if on enable directive", ctx => caseTest(ctx));
21
+ test("@if on requires directive", ctx => caseTest(ctx));
22
+ test("@if on global const declaration", ctx => caseTest(ctx));
23
+ test("@if on global override declaration", ctx => caseTest(ctx));
24
+ test("@if on global variable declaration", ctx => caseTest(ctx));
25
+ test("@if on type alias", ctx => caseTest(ctx));
26
+ test("@if on module scope const_assert", ctx => caseTest(ctx));
27
+ test("@if on function declaration", ctx => caseTest(ctx));
28
+ test("@if on function formal parameter", ctx => caseTest(ctx));
29
+ test("@if on structure declaration", ctx => caseTest(ctx));
30
+ test("@if on structure member", ctx => caseTest(ctx));
31
+ test("@if on compound statement", ctx => caseTest(ctx));
32
+ test("@if on if statement", ctx => caseTest(ctx));
33
+ test("@if on switch statement", ctx => caseTest(ctx));
34
+ test("@if on switch clause", ctx => caseTest(ctx));
35
+ test("@if on loop statement", ctx => caseTest(ctx));
36
+ test("@if on for statement", ctx => caseTest(ctx));
37
+ test("@if on while statement", ctx => caseTest(ctx));
38
+ test("@if on break statement", ctx => caseTest(ctx));
39
+ test("@if on break-if statement", ctx => caseTest(ctx));
40
+ test("@if on continue statement", ctx => caseTest(ctx));
41
+ test("@if on continuing statement", ctx => caseTest(ctx));
42
+ test("@if on return statement", ctx => caseTest(ctx));
43
+ test("@if on discard statement", ctx => caseTest(ctx));
44
+ test("@if on call statement", ctx => caseTest(ctx));
45
+ test("@if on function-scope const_assert", ctx => caseTest(ctx));
46
+ test("@if short-circuiting OR", ctx => caseTest(ctx));
47
+ test("@if short-circuiting AND", ctx => caseTest(ctx));
48
+ test("@if logical NOT", ctx => caseTest(ctx));
49
+ test("@if parentheses", ctx => caseTest(ctx));
50
+
51
+ test("declaration shadowing", ctx => caseTest(ctx));
52
+
53
+ test("conditional import of const_assert", ctx => caseTest(ctx));
54
+ test("double conditional import of const_assert", ctx => caseTest(ctx));
55
+ test("conditional transitive const", ctx => caseTest(ctx));
56
+ test("conditional transitive fn", ctx => caseTest(ctx));
@@ -0,0 +1,30 @@
1
+ import { expect, test } from "vitest";
2
+ import { linkWithLogQuietly } from "./TestUtil.ts";
3
+
4
+ test("unresolved identifier", async () => {
5
+ const src = `
6
+ fn main() { x = 7; }
7
+ `;
8
+ const { log } = await linkWithLogQuietly(src);
9
+ expect(log).toMatchInlineSnapshot(`
10
+ "unresolved identifier 'x' in file: ./test.wesl
11
+ fn main() { x = 7; } Ln 2
12
+ ^^"
13
+ `);
14
+ });
15
+
16
+ test("conditionally empty struct", async () => {
17
+ const src = `
18
+ struct Empty {
19
+ @if(false) u: u32,
20
+ }
21
+ `;
22
+ const { log } = await linkWithLogQuietly(src);
23
+ expect(log).toMatchInlineSnapshot(
24
+ `
25
+ "struct Empty in ./test.wesl has no members (with current conditions)
26
+ struct Empty { Ln 2
27
+ ^"
28
+ `,
29
+ );
30
+ });
@@ -0,0 +1,22 @@
1
+ import { eof, seq } from "mini-parse";
2
+ import { expect, test } from "vitest";
3
+ import { expression } from "../parse/WeslExpression.ts";
4
+ import { testAppParse } from "./TestUtil.ts";
5
+
6
+ test("parse number", () => {
7
+ const src = `3`;
8
+ const { parsed } = testAppParse(seq(expression, eof), src);
9
+ expect(parsed).not.toBeNull();
10
+ });
11
+
12
+ test("parse comparisons with && ||", () => {
13
+ const src = `a<3 && 4>(5)`;
14
+ const { parsed } = testAppParse(seq(expression, eof), src);
15
+ expect(parsed).not.toBeNull();
16
+ });
17
+
18
+ test("parse vec templated type", () => {
19
+ const src = `vec2<f32>`;
20
+ const { parsed } = testAppParse(seq(expression, eof), src);
21
+ expect(parsed).not.toBeNull();
22
+ });
@@ -0,0 +1,74 @@
1
+ import { expect, test } from "vitest";
2
+ import { ImportCollection, ImportStatement } from "../AbstractElems.ts";
3
+ import { flattenTreeImport } from "../FlattenTreeImport.ts";
4
+
5
+ test("complex tree import", () => {
6
+ const list: ImportCollection = {
7
+ kind: "import-collection",
8
+ subtrees: [
9
+ {
10
+ kind: "import-statement",
11
+ segments: [],
12
+ finalSegment: { kind: "import-item", name: "foo", as: "bar" },
13
+ },
14
+ {
15
+ kind: "import-statement",
16
+ segments: [],
17
+ finalSegment: { kind: "import-item", name: "doh" },
18
+ },
19
+ {
20
+ kind: "import-statement",
21
+ segments: [{ kind: "import-segment", name: "bib" }],
22
+ finalSegment: { kind: "import-item", name: "bog" },
23
+ },
24
+ ],
25
+ };
26
+
27
+ const tree: ImportStatement = {
28
+ kind: "import-statement",
29
+ segments: [
30
+ {
31
+ kind: "import-segment",
32
+ name: "zap",
33
+ },
34
+ ],
35
+ finalSegment: list,
36
+ };
37
+ const flattened = flattenTreeImport(tree);
38
+ expect(flattened).toMatchInlineSnapshot(`
39
+ [
40
+ {
41
+ "importPath": [
42
+ "zap",
43
+ "bar",
44
+ ],
45
+ "modulePath": [
46
+ "zap",
47
+ "foo",
48
+ ],
49
+ },
50
+ {
51
+ "importPath": [
52
+ "zap",
53
+ "doh",
54
+ ],
55
+ "modulePath": [
56
+ "zap",
57
+ "doh",
58
+ ],
59
+ },
60
+ {
61
+ "importPath": [
62
+ "zap",
63
+ "bib",
64
+ "bog",
65
+ ],
66
+ "modulePath": [
67
+ "zap",
68
+ "bib",
69
+ "bog",
70
+ ],
71
+ },
72
+ ]
73
+ `);
74
+ });
@@ -0,0 +1,56 @@
1
+ import { afterAll, test, TestContext } from "vitest";
2
+ import { importCases } from "wesl-testsuite";
3
+ import { testFromCase, verifyCaseCoverage } from "./TestLink.js";
4
+
5
+ // wgsl example src, indexed by name
6
+ const examplesByName = new Map(importCases.map(t => [t.name, t]));
7
+
8
+ async function caseTest(ctx: TestContext): Promise<void> {
9
+ return testFromCase(ctx.task.name, examplesByName);
10
+ }
11
+
12
+ afterAll(verifyCaseCoverage(importCases));
13
+
14
+ test("import package::bar::foo;", ctx => caseTest(ctx));
15
+ test("main has other root elements", ctx => caseTest(ctx));
16
+ test("import foo as bar", ctx => caseTest(ctx));
17
+ test("import twice doesn't get two copies", ctx => caseTest(ctx));
18
+ test("imported fn calls support fn with root conflict", ctx => caseTest(ctx));
19
+ test("import twice with two as names", ctx => caseTest(ctx));
20
+ test("import transitive conflicts with main", ctx => caseTest(ctx));
21
+ test("multiple exports from the same module", ctx => caseTest(ctx));
22
+ test("import and resolve conflicting support function", ctx => caseTest(ctx));
23
+ test("import support fn that references another import", ctx => caseTest(ctx));
24
+ test("import support fn from two exports", ctx => caseTest(ctx));
25
+ test("import a struct", ctx => caseTest(ctx));
26
+ test("struct referenced by a fn param", ctx => caseTest(ctx));
27
+ test("import fn with support struct constructor", ctx => caseTest(ctx));
28
+ test("import a transitive struct", ctx => caseTest(ctx));
29
+ test("'import as' a struct", ctx => caseTest(ctx));
30
+ test("import a struct with name conflicting support struct", ctx =>
31
+ caseTest(ctx));
32
+ test("copy alias to output", ctx => caseTest(ctx));
33
+ test("copy diagnostics to output", ctx => caseTest(ctx));
34
+ test("const referenced by imported fn", ctx => caseTest(ctx));
35
+ test("fn call with a separator", ctx => caseTest(ctx));
36
+ test("local var to struct", ctx => caseTest(ctx));
37
+ test("global var to struct", ctx => caseTest(ctx));
38
+ test("return type of function", ctx => caseTest(ctx));
39
+ test("import a const", ctx => caseTest(ctx));
40
+ test("import an alias", ctx => caseTest(ctx));
41
+ test("alias f32", ctx => caseTest(ctx));
42
+ test("fn f32()", ctx => caseTest(ctx));
43
+ test("circular import", ctx => caseTest(ctx));
44
+ test("inline package reference", ctx => caseTest(ctx));
45
+ test("inline super:: reference", ctx => caseTest(ctx));
46
+ test("import super::file1", ctx => caseTest(ctx));
47
+ test("declaration after subscope", ctx => caseTest(ctx));
48
+
49
+ // test(, ctx =>
50
+ // linkTest2(ctx.task.name, {
51
+ // linked: `
52
+ // `,
53
+ // });
54
+ // });
55
+
56
+ // TODO add case for diagnostic in non-root module (should fail?)
@@ -0,0 +1,24 @@
1
+ import { withLogSpy } from "mini-parse/test-util";
2
+ import { expect, test } from "vitest";
3
+ import { importSyntaxCases } from "wesl-testsuite";
4
+ import { weslImports } from "../parse/ImportGrammar.js";
5
+ import { testAppParse } from "./TestUtil.js";
6
+
7
+ function expectParseFail(src: string): void {
8
+ withLogSpy(() => {
9
+ expect(() => testAppParse(weslImports, src)).toThrow();
10
+ });
11
+ }
12
+
13
+ function expectParses(src: string): void {
14
+ const result = testAppParse(weslImports, src);
15
+ expect(result.stable.imports.length).toBeGreaterThan(0);
16
+ }
17
+
18
+ importSyntaxCases.forEach(c => {
19
+ if (c.fails) {
20
+ test(c.src, () => expectParseFail(c.src));
21
+ } else {
22
+ test(c.src, () => expectParses(c.src));
23
+ }
24
+ });