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.
- package/dist/index.d.ts +269 -215
- package/dist/index.js +2911 -1539
- package/package.json +6 -8
- package/src/AbstractElems.ts +81 -81
- package/src/Assertions.ts +5 -5
- package/src/BindIdents.ts +192 -306
- package/src/ClickableError.ts +3 -2
- package/src/Conditions.ts +2 -2
- package/src/LinkedWesl.ts +1 -1
- package/src/Linker.ts +4 -3
- package/src/LinkerUtil.ts +1 -1
- package/src/Logging.ts +165 -0
- package/src/LowerAndEmit.ts +278 -110
- package/src/ModuleResolver.ts +15 -25
- package/src/ParseError.ts +9 -0
- package/src/ParseWESL.ts +30 -94
- package/src/RawEmit.ts +1 -4
- package/src/Reflection.ts +1 -1
- package/src/Scope.ts +3 -0
- package/src/Span.ts +2 -0
- package/src/SrcMap.ts +208 -0
- package/src/Stream.ts +30 -0
- package/src/TransformBindingStructs.ts +2 -2
- package/src/Util.ts +1 -1
- package/src/debug/ASTtoString.ts +84 -135
- package/src/discovery/FindUnboundIdents.ts +14 -5
- package/src/index.ts +4 -0
- package/src/parse/ContentsHelpers.ts +70 -0
- package/src/parse/ExpressionUtil.ts +121 -0
- package/src/parse/Keywords.ts +12 -12
- package/src/parse/OperatorBinding.ts +146 -0
- package/src/parse/ParseAttribute.ts +272 -0
- package/src/parse/ParseCall.ts +77 -0
- package/src/parse/ParseControlFlow.ts +129 -0
- package/src/parse/ParseDirective.ts +105 -0
- package/src/parse/ParseExpression.ts +288 -0
- package/src/parse/ParseFn.ts +151 -0
- package/src/parse/ParseGlobalVar.ts +131 -0
- package/src/parse/ParseIdent.ts +77 -0
- package/src/parse/ParseImport.ts +160 -0
- package/src/parse/ParseLocalVar.ts +69 -0
- package/src/parse/ParseLoop.ts +112 -0
- package/src/parse/ParseModule.ts +116 -0
- package/src/parse/ParseSimpleStatement.ts +162 -0
- package/src/parse/ParseStatement.ts +215 -0
- package/src/parse/ParseStruct.ts +89 -0
- package/src/parse/ParseType.ts +71 -0
- package/src/parse/ParseUtil.ts +174 -0
- package/src/parse/ParseValueDeclaration.ts +130 -0
- package/src/parse/ParseWesl.ts +51 -0
- package/src/parse/ParsingContext.ts +93 -0
- package/src/parse/WeslStream.ts +63 -20
- package/src/parse/stream/CachingStream.ts +48 -0
- package/src/parse/stream/MatchersStream.ts +85 -0
- package/src/parse/stream/RegexHelpers.ts +38 -0
- package/src/test/BevyLink.test.ts +100 -0
- package/src/test/BindStdTypes.test.ts +110 -0
- package/src/test/{BindWESL.test.ts → BindWESLV2.test.ts} +21 -22
- package/src/test/BulkTests.test.ts +11 -12
- package/src/test/ConditionLinking.test.ts +107 -0
- package/src/test/ConditionalElif.test.ts +1 -13
- package/src/test/ConditionalTranslationCases.test.ts +5 -0
- package/src/test/ErrorLogging.test.ts +2 -2
- package/src/test/ImportCasesV2.test.ts +63 -0
- package/src/test/LinkFails.test.ts +69 -0
- package/src/test/LinkPackage.test.ts +1 -1
- package/src/test/Linker.test.ts +75 -2
- package/src/test/LogCatcher.ts +53 -0
- package/src/test/Mangling.test.ts +1 -1
- package/src/test/ParseComments.test.ts +1 -2
- package/src/test/{ParseConditions.test.ts → ParseConditionsV2.test.ts} +57 -49
- package/src/test/ParseErrorV2.test.ts +73 -0
- package/src/test/{ParseWESL.test.ts → ParseWeslV2.test.ts} +288 -370
- package/src/test/{ScopeWESL.test.ts → ScopeWESLV2.test.ts} +205 -176
- package/src/test/TestLink.ts +51 -51
- package/src/test/TestSetup.ts +9 -3
- package/src/test/TestUtil.ts +47 -77
- package/src/test/TrimmedMatch.ts +40 -0
- package/src/test/VirtualModules.test.ts +33 -2
- package/src/test/WeslDevice.test.ts +9 -2
- package/src/test/__snapshots__/ParseWeslV2.test.ts.snap +67 -0
- package/src/test-util.ts +7 -0
- package/src/WESLCollect.ts +0 -656
- package/src/parse/AttributeGrammar.ts +0 -232
- package/src/parse/ImportGrammar.ts +0 -195
- package/src/parse/WeslBaseGrammar.ts +0 -11
- package/src/parse/WeslExpression.ts +0 -231
- package/src/parse/WeslGrammar.ts +0 -739
- package/src/test/Expression.test.ts +0 -22
- package/src/test/ImportSyntaxCases.test.ts +0 -24
- package/src/test/ParseError.test.ts +0 -45
- package/src/test/Reflection.test.ts +0 -176
- package/src/test/TransformBindingStructs.test.ts +0 -238
- /package/src/test/{ParseElif.test.ts → ParseElifV2.test.ts} +0 -0
package/src/test/TestSetup.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { beforeEach } from "vitest";
|
|
2
|
+
import { resetScopeIds } from "wesl";
|
|
3
|
+
import { resetScopeIds as resetSourceScopeIds } from "../Scope.ts";
|
|
2
4
|
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
+
// Auto-reset scope IDs before each test for consistent output
|
|
6
|
+
// Reset both source and built counters (nodebug tests alias wesl to dist-nodebug)
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
resetScopeIds();
|
|
9
|
+
resetSourceScopeIds();
|
|
10
|
+
});
|
package/src/test/TestUtil.ts
CHANGED
|
@@ -1,134 +1,104 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
expectNoLog,
|
|
4
|
-
logCatch,
|
|
5
|
-
type TestParseResult,
|
|
6
|
-
testParseWithStream,
|
|
7
|
-
} from "mini-parse/test-util";
|
|
1
|
+
import { expect } from "vitest";
|
|
8
2
|
import { type BoundAndTransformed, RecordResolver, type SrcModule } from "wesl";
|
|
9
3
|
import { bindAndTransform, type LinkParams, link } from "../Linker.ts";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from "../ParseWESL.ts";
|
|
15
|
-
import { WeslStream, type WeslToken } from "../parse/WeslStream.ts";
|
|
16
|
-
import { resetScopeIds } from "../Scope.ts";
|
|
4
|
+
import { withLoggerAsync } from "../Logging.ts";
|
|
5
|
+
import { parseSrcModule, type WeslAST } from "../ParseWESL.ts";
|
|
6
|
+
import { expectNoLog, logCatch } from "./LogCatcher.ts";
|
|
7
|
+
import { stripWesl } from "./StripWesl.ts";
|
|
17
8
|
|
|
18
|
-
|
|
9
|
+
export type LinkTestOpts = Pick<
|
|
10
|
+
LinkParams,
|
|
11
|
+
"conditions" | "libs" | "config" | "virtualLibs" | "constants" | "mangler"
|
|
12
|
+
>;
|
|
13
|
+
|
|
14
|
+
interface BindTestResult {
|
|
15
|
+
bound: BoundAndTransformed;
|
|
16
|
+
resolver: RecordResolver;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Compare WGSL/WESL by token sequence, ignoring whitespace. */
|
|
20
|
+
export function expectTokenMatch(actual: string, expected: string): void {
|
|
21
|
+
expect(stripWesl(actual)).toBe(stripWesl(expected));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Parse a single wesl file. */
|
|
19
25
|
export function parseWESL(src: string): WeslAST {
|
|
20
26
|
const srcModule: SrcModule = {
|
|
21
|
-
modulePath: "package::test",
|
|
27
|
+
modulePath: "package::test",
|
|
22
28
|
debugFilePath: "./test.wesl",
|
|
23
29
|
src,
|
|
24
30
|
};
|
|
25
|
-
|
|
26
31
|
return parseSrcModule(srcModule);
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
/**
|
|
30
|
-
export function testAppParse<T>(
|
|
31
|
-
parser: Parser<Stream<WeslToken>, T>,
|
|
32
|
-
src: string,
|
|
33
|
-
): TestParseResult<T, WeslAST> {
|
|
34
|
-
const appState = syntheticWeslParseState();
|
|
35
|
-
const stream = new WeslStream(src);
|
|
36
|
-
return testParseWithStream(parser, stream, appState);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** Convenience wrapper to link wgsl for tests.
|
|
40
|
-
* The first module is named "./test.wesl",
|
|
41
|
-
* subsequent modules are named "./file1.wesl", "./file2.wesl", etc.
|
|
42
|
-
*/
|
|
34
|
+
/** Link wesl for tests. First module is ./test.wesl, rest are ./file1.wesl, etc. */
|
|
43
35
|
export async function linkTest(...rawWgsl: string[]): Promise<string> {
|
|
44
36
|
return linkTestOpts({}, ...rawWgsl);
|
|
45
37
|
}
|
|
46
38
|
|
|
47
|
-
export
|
|
48
|
-
LinkParams,
|
|
49
|
-
"conditions" | "libs" | "config" | "virtualLibs" | "constants" | "mangler"
|
|
50
|
-
>;
|
|
51
|
-
|
|
52
|
-
export async function linkTestOpts(
|
|
53
|
-
opts: LinkTestOpts,
|
|
54
|
-
...rawWgsl: string[]
|
|
55
|
-
): Promise<string> {
|
|
39
|
+
export async function linkTestOpts(opts: LinkTestOpts, ...rawWgsl: string[]) {
|
|
56
40
|
const weslSrc = makeTestBundle(rawWgsl);
|
|
57
|
-
|
|
58
|
-
const rootModuleName = "test";
|
|
59
|
-
const srcMap = await link({ weslSrc, rootModuleName, ...opts });
|
|
41
|
+
const srcMap = await link({ weslSrc, rootModuleName: "test", ...opts });
|
|
60
42
|
return srcMap.dest;
|
|
61
43
|
}
|
|
62
44
|
|
|
63
|
-
|
|
64
|
-
export async function linkWithLog(...rawWgsl: string[]): Promise<{
|
|
45
|
+
interface LogResult {
|
|
65
46
|
log: string;
|
|
66
47
|
result: string;
|
|
67
|
-
}> {
|
|
68
|
-
return linkWithLogInternal(rawWgsl);
|
|
69
48
|
}
|
|
70
49
|
|
|
71
|
-
/** Link wesl for tests, capturing console output
|
|
72
|
-
export async function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
50
|
+
/** Link wesl for tests, capturing console output. */
|
|
51
|
+
export async function linkWithLog(...rawWgsl: string[]): Promise<LogResult> {
|
|
52
|
+
return linkWithLogInternal(rawWgsl, false);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Link wesl for tests, capturing console output and swallowing exceptions. */
|
|
56
|
+
export async function linkWithLogQuietly(
|
|
57
|
+
...rawWgsl: string[]
|
|
58
|
+
): Promise<LogResult> {
|
|
76
59
|
return linkWithLogInternal(rawWgsl, true);
|
|
77
60
|
}
|
|
78
61
|
|
|
79
62
|
async function linkWithLogInternal(
|
|
80
63
|
rawWgsl: string[],
|
|
81
|
-
quiet
|
|
82
|
-
): Promise<{
|
|
83
|
-
log: string;
|
|
84
|
-
result: string;
|
|
85
|
-
}> {
|
|
64
|
+
quiet: boolean,
|
|
65
|
+
): Promise<LogResult> {
|
|
86
66
|
const { log, logged } = logCatch();
|
|
87
67
|
let result = "??";
|
|
88
68
|
try {
|
|
89
|
-
result = await withLoggerAsync(log,
|
|
69
|
+
result = await withLoggerAsync(log, () => linkTest(...rawWgsl));
|
|
90
70
|
} catch (e) {
|
|
91
71
|
if (!quiet) console.error(e);
|
|
92
72
|
}
|
|
93
73
|
return { result, log: logged() };
|
|
94
74
|
}
|
|
95
75
|
|
|
96
|
-
/** Parse wesl for testing, ensuring no logged warnings */
|
|
76
|
+
/** Parse wesl for testing, ensuring no logged warnings. */
|
|
97
77
|
export function parseTest(src: string): WeslAST {
|
|
98
|
-
return expectNoLog(() =>
|
|
78
|
+
return expectNoLog(() => parseWESL(src));
|
|
99
79
|
}
|
|
100
80
|
|
|
101
|
-
/** Parse wesl without log collection (for debugging) */
|
|
102
|
-
export function parseTestRaw(src: string) {
|
|
81
|
+
/** Parse wesl without log collection (for debugging). */
|
|
82
|
+
export function parseTestRaw(src: string): WeslAST {
|
|
103
83
|
return parseWESL(src);
|
|
104
84
|
}
|
|
105
85
|
|
|
106
|
-
|
|
107
|
-
bound: BoundAndTransformed;
|
|
108
|
-
resolver: RecordResolver;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** Parse and bind wesl source for testing. Returns bound result and resolver for inspection. */
|
|
86
|
+
/** Parse and bind wesl source for testing. Returns bound result and resolver. */
|
|
112
87
|
export function bindTest(...rawWesl: string[]): BindTestResult {
|
|
113
|
-
resetScopeIds();
|
|
114
88
|
const weslSrc = makeTestBundle(rawWesl);
|
|
115
|
-
|
|
116
89
|
const resolver = new RecordResolver(weslSrc, {
|
|
117
90
|
packageName: "package",
|
|
118
91
|
debugWeslRoot: "test",
|
|
119
92
|
});
|
|
120
|
-
const bound = bindAndTransform({
|
|
121
|
-
rootModuleName: "test",
|
|
122
|
-
resolver,
|
|
123
|
-
});
|
|
93
|
+
const bound = bindAndTransform({ rootModuleName: "test", resolver });
|
|
124
94
|
return { bound, resolver };
|
|
125
95
|
}
|
|
126
96
|
|
|
127
|
-
/** Synthesize test file bundle
|
|
97
|
+
/** Synthesize test file bundle: ./test.wesl, ./file1.wesl, ./file2.wesl, etc. */
|
|
128
98
|
function makeTestBundle(rawWgsl: string[]): Record<string, string> {
|
|
129
99
|
const [root, ...rest] = rawWgsl;
|
|
130
|
-
const
|
|
100
|
+
const restFiles = Object.fromEntries(
|
|
131
101
|
rest.map((src, i) => [`./file${i + 1}.wesl`, src]),
|
|
132
102
|
);
|
|
133
|
-
return { "./test.wesl": root, ...
|
|
103
|
+
return { "./test.wesl": root, ...restFiles };
|
|
134
104
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { expect } from "vitest";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Trim source for test comparisons:
|
|
5
|
+
* - Remove blank lines
|
|
6
|
+
* - Remove leading and trailing white space
|
|
7
|
+
*/
|
|
8
|
+
export function trimSrc(src: string): string {
|
|
9
|
+
const rawLines = src.split("\n");
|
|
10
|
+
const trimmed = rawLines.map(l => l.trim());
|
|
11
|
+
const nonBlank = trimmed.filter(l => l !== "");
|
|
12
|
+
return nonBlank.join("\n");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Expect a match between two strings with blank lines and
|
|
17
|
+
* any consistent leading indent removed.
|
|
18
|
+
*/
|
|
19
|
+
export function expectTrimmedMatch(result: string, expected: string): void {
|
|
20
|
+
const resultTrimmed = trimSrc(result);
|
|
21
|
+
const expectTrimmed = trimSrc(expected);
|
|
22
|
+
if (resultTrimmed !== expectTrimmed) {
|
|
23
|
+
const expectLines = expectTrimmed.split("\n");
|
|
24
|
+
const resultLines = resultTrimmed.split("\n");
|
|
25
|
+
const len = Math.max(expectLines.length, resultLines.length);
|
|
26
|
+
for (let i = 0; i < len; i++) {
|
|
27
|
+
const diff = expectLines[i] !== resultLines[i];
|
|
28
|
+
if (diff) {
|
|
29
|
+
console.log(`...failed. Line ${i + 1} differs:
|
|
30
|
+
expected: ${expectLines[i]}
|
|
31
|
+
actual: ${resultLines[i] ?? ""}`);
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
console.log(
|
|
36
|
+
`\ntrimmed result:\n${resultTrimmed}\n\ntrimmed expected:\n${expectTrimmed}\n`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
expect(resultTrimmed).toBe(expectTrimmed);
|
|
40
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { link } from "../Linker.ts";
|
|
3
3
|
import { linkTestOpts } from "./TestUtil.ts";
|
|
4
|
+
import { expectTrimmedMatch } from "./TrimmedMatch.ts";
|
|
4
5
|
|
|
5
6
|
test("simple virtual module", async () => {
|
|
6
7
|
const src = `
|
|
@@ -35,3 +36,33 @@ test("virtual constants", async () => {
|
|
|
35
36
|
`;
|
|
36
37
|
expectTrimmedMatch(result, expected);
|
|
37
38
|
});
|
|
39
|
+
|
|
40
|
+
// WGSL reserved words (like 'common') are allowed in module paths per WESL spec.
|
|
41
|
+
test("inline reference to virtual module with reserved word name", async () => {
|
|
42
|
+
const result = await link({
|
|
43
|
+
weslSrc: { "./main.wesl": "fn main() { let x = common::value; }" },
|
|
44
|
+
rootModuleName: "main",
|
|
45
|
+
virtualLibs: { common: () => "const value = 42;" },
|
|
46
|
+
});
|
|
47
|
+
expect(result.dest).toContain("const value = 42");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("inline constant in array template param", async () => {
|
|
51
|
+
const src = `
|
|
52
|
+
fn main() {
|
|
53
|
+
var data: array<f32, constants::size>;
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
56
|
+
const result = await linkTestOpts({ constants: { size: 4 } }, src);
|
|
57
|
+
expect(result).toContain("const size = 4");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("function call with inline ref in template param", async () => {
|
|
61
|
+
const src = `
|
|
62
|
+
fn main() {
|
|
63
|
+
var data: array<f32, u32(constants::size)>;
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
66
|
+
const result = await linkTestOpts({ constants: { size: 4 } }, src);
|
|
67
|
+
expect(result).toContain("const size = 4");
|
|
68
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { setTimeout } from "node:timers";
|
|
2
|
-
import { SrcMap } from "mini-parse";
|
|
3
2
|
import { expect, test, vi } from "vitest";
|
|
4
3
|
import { LinkedWesl } from "../LinkedWesl";
|
|
4
|
+
import { SrcMap } from "../SrcMap.ts";
|
|
5
5
|
import { makeWeslDevice } from "../WeslDevice";
|
|
6
6
|
|
|
7
7
|
test("WeslDevice doesn't conflict with uncapturederror", async () => {
|
|
@@ -199,7 +199,14 @@ test("Invokes error throwing", async () => {
|
|
|
199
199
|
this.message = message;
|
|
200
200
|
});
|
|
201
201
|
vi.stubGlobal("GPUValidationError", GPUValidationErrorMock);
|
|
202
|
-
const GPUUncapturedErrorEventMock = vi.fn((
|
|
202
|
+
const GPUUncapturedErrorEventMock = vi.fn(function (
|
|
203
|
+
this: any,
|
|
204
|
+
type: string,
|
|
205
|
+
options: { error: GPUError },
|
|
206
|
+
) {
|
|
207
|
+
this.type = type;
|
|
208
|
+
this.error = options.error;
|
|
209
|
+
});
|
|
203
210
|
vi.stubGlobal("GPUUncapturedErrorEvent", GPUUncapturedErrorEventMock);
|
|
204
211
|
|
|
205
212
|
const dispatchEventPromise = Promise.withResolvers();
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`parse unicode ident 1`] = `
|
|
4
|
+
"module
|
|
5
|
+
text '
|
|
6
|
+
'
|
|
7
|
+
fn Δέλτα()
|
|
8
|
+
decl %Δέλτα
|
|
9
|
+
statement
|
|
10
|
+
text '{}'
|
|
11
|
+
text '
|
|
12
|
+
'
|
|
13
|
+
fn réflexion()
|
|
14
|
+
decl %réflexion
|
|
15
|
+
statement
|
|
16
|
+
text '{}'
|
|
17
|
+
text '
|
|
18
|
+
'
|
|
19
|
+
fn Кызыл()
|
|
20
|
+
decl %Кызыл
|
|
21
|
+
statement
|
|
22
|
+
text '{}'
|
|
23
|
+
text '
|
|
24
|
+
'
|
|
25
|
+
fn 𐰓𐰏𐰇()
|
|
26
|
+
decl %𐰓𐰏𐰇
|
|
27
|
+
statement
|
|
28
|
+
text '{}'
|
|
29
|
+
text '
|
|
30
|
+
'
|
|
31
|
+
fn 朝焼け()
|
|
32
|
+
decl %朝焼け
|
|
33
|
+
statement
|
|
34
|
+
text '{}'
|
|
35
|
+
text '
|
|
36
|
+
'
|
|
37
|
+
fn سلام()
|
|
38
|
+
decl %سلام
|
|
39
|
+
statement
|
|
40
|
+
text '{}'
|
|
41
|
+
text '
|
|
42
|
+
'
|
|
43
|
+
fn 검정()
|
|
44
|
+
decl %검정
|
|
45
|
+
statement
|
|
46
|
+
text '{}'
|
|
47
|
+
text '
|
|
48
|
+
'
|
|
49
|
+
fn שָׁלוֹם()
|
|
50
|
+
decl %שָׁלוֹם
|
|
51
|
+
statement
|
|
52
|
+
text '{}'
|
|
53
|
+
text '
|
|
54
|
+
'
|
|
55
|
+
fn गुलाबी()
|
|
56
|
+
decl %गुलाबी
|
|
57
|
+
statement
|
|
58
|
+
text '{}'
|
|
59
|
+
text '
|
|
60
|
+
'
|
|
61
|
+
fn փիրուզ()
|
|
62
|
+
decl %փիրուզ
|
|
63
|
+
statement
|
|
64
|
+
text '{}'
|
|
65
|
+
text '
|
|
66
|
+
'"
|
|
67
|
+
`;
|