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.
- package/README.md +31 -0
- package/dist/index.js +4468 -0
- package/dist/index.js.map +1 -0
- package/dist/minified.js +3426 -0
- package/dist/minified.js.map +1 -0
- package/dist/tools/packages/wesl/src/AbstractElems.d.ts +322 -0
- package/dist/tools/packages/wesl/src/Assertions.d.ts +27 -0
- package/dist/tools/packages/wesl/src/BindIdents.d.ts +70 -0
- package/dist/tools/packages/wesl/src/Conditions.d.ts +6 -0
- package/dist/tools/packages/wesl/src/FlattenTreeImport.d.ts +11 -0
- package/dist/tools/packages/wesl/src/LinkedWesl.d.ts +50 -0
- package/dist/tools/packages/wesl/src/Linker.d.ts +87 -0
- package/dist/tools/packages/wesl/src/LinkerUtil.d.ts +3 -0
- package/dist/tools/packages/wesl/src/LiveDeclarations.d.ts +12 -0
- package/dist/tools/packages/wesl/src/LowerAndEmit.d.ts +31 -0
- package/dist/tools/packages/wesl/src/Mangler.d.ts +39 -0
- package/dist/tools/packages/wesl/src/ParseWESL.d.ts +60 -0
- package/dist/tools/packages/wesl/src/ParsedRegistry.d.ts +29 -0
- package/dist/tools/packages/wesl/src/PathUtil.d.ts +6 -0
- package/dist/tools/packages/wesl/src/RawEmit.d.ts +6 -0
- package/dist/tools/packages/wesl/src/Reflection.d.ts +45 -0
- package/dist/tools/packages/wesl/src/Scope.d.ts +81 -0
- package/dist/tools/packages/wesl/src/StandardTypes.d.ts +13 -0
- package/dist/tools/packages/wesl/src/TransformBindingStructs.d.ts +52 -0
- package/dist/tools/packages/wesl/src/Util.d.ts +43 -0
- package/dist/tools/packages/wesl/src/WESLCollect.d.ts +94 -0
- package/dist/tools/packages/wesl/src/WeslBundle.d.ts +13 -0
- package/dist/tools/packages/wesl/src/WeslDevice.d.ts +25 -0
- package/dist/tools/packages/wesl/src/debug/ASTtoString.d.ts +5 -0
- package/dist/tools/packages/wesl/src/debug/ImportToString.d.ts +2 -0
- package/dist/tools/packages/wesl/src/debug/LineWrapper.d.ts +21 -0
- package/dist/tools/packages/wesl/src/debug/ScopeToString.d.ts +6 -0
- package/dist/tools/packages/wesl/src/index.d.ts +11 -0
- package/dist/tools/packages/wesl/src/parse/ImportGrammar.d.ts +5 -0
- package/dist/tools/packages/wesl/src/parse/Keywords.d.ts +4 -0
- package/dist/tools/packages/wesl/src/parse/WeslBaseGrammar.d.ts +5 -0
- package/dist/tools/packages/wesl/src/parse/WeslExpression.d.ts +13 -0
- package/dist/tools/packages/wesl/src/parse/WeslGrammar.d.ts +80 -0
- package/dist/tools/packages/wesl/src/parse/WeslStream.d.ts +44 -0
- package/dist/tools/packages/wesl/src/test/BindWESL.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ConditionLinking.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ConditionalTranslationCases.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ErrorLogging.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/Expression.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/FlattenTreeImport.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ImportCases.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ImportSyntaxCases.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/LinkGlob.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/LinkPackage.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/Linker.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/Mangling.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ParseComments.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ParseConditions.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ParseError.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ParseWESL.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/PathUtil.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/PrettyGrammar.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/Reflection.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/ScopeWESL.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/TestLink.d.ts +21 -0
- package/dist/tools/packages/wesl/src/test/TestSetup.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/TestUtil.d.ts +40 -0
- package/dist/tools/packages/wesl/src/test/Tokenizer.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/TransformBindingStructs.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/Util.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/VirtualModules.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/WeslDevice.test.d.ts +1 -0
- package/dist/tools/packages/wesl/src/test/WgslTests.d.ts +0 -0
- package/dist/tools/packages/wesl/src/vlq/vlq.d.ts +11 -0
- package/package.json +46 -0
- package/src/AbstractElems.ts +446 -0
- package/src/Assertions.ts +51 -0
- package/src/BindIdents.ts +523 -0
- package/src/Conditions.ts +74 -0
- package/src/FlattenTreeImport.ts +55 -0
- package/src/LinkedWesl.ts +184 -0
- package/src/Linker.ts +284 -0
- package/src/LinkerUtil.ts +29 -0
- package/src/LiveDeclarations.ts +31 -0
- package/src/LowerAndEmit.ts +413 -0
- package/src/Mangler.ts +94 -0
- package/src/ParseWESL.ts +157 -0
- package/src/ParsedRegistry.ts +120 -0
- package/src/PathUtil.ts +31 -0
- package/src/RawEmit.ts +102 -0
- package/src/Reflection.ts +334 -0
- package/src/Scope.ts +162 -0
- package/src/StandardTypes.ts +97 -0
- package/src/TransformBindingStructs.ts +319 -0
- package/src/Util.ts +194 -0
- package/src/WESLCollect.ts +614 -0
- package/src/WeslBundle.ts +16 -0
- package/src/WeslDevice.ts +209 -0
- package/src/debug/ASTtoString.ts +290 -0
- package/src/debug/ImportToString.ts +29 -0
- package/src/debug/LineWrapper.ts +70 -0
- package/src/debug/ScopeToString.ts +79 -0
- package/src/index.ts +11 -0
- package/src/parse/ImportGrammar.ts +157 -0
- package/src/parse/Keywords.ts +26 -0
- package/src/parse/WeslBaseGrammar.ts +8 -0
- package/src/parse/WeslExpression.ts +207 -0
- package/src/parse/WeslGrammar.ts +856 -0
- package/src/parse/WeslStream.ts +279 -0
- package/src/test/BindWESL.test.ts +57 -0
- package/src/test/ConditionLinking.test.ts +91 -0
- package/src/test/ConditionalTranslationCases.test.ts +56 -0
- package/src/test/ErrorLogging.test.ts +30 -0
- package/src/test/Expression.test.ts +22 -0
- package/src/test/FlattenTreeImport.test.ts +74 -0
- package/src/test/ImportCases.test.ts +56 -0
- package/src/test/ImportSyntaxCases.test.ts +24 -0
- package/src/test/LinkGlob.test.ts +25 -0
- package/src/test/LinkPackage.test.ts +26 -0
- package/src/test/Linker.test.ts +125 -0
- package/src/test/Mangling.test.ts +45 -0
- package/src/test/ParseComments.test.ts +36 -0
- package/src/test/ParseConditions.test.ts +183 -0
- package/src/test/ParseError.test.ts +36 -0
- package/src/test/ParseWESL.test.ts +1572 -0
- package/src/test/PathUtil.test.ts +34 -0
- package/src/test/PrettyGrammar.test.ts +20 -0
- package/src/test/Reflection.test.ts +172 -0
- package/src/test/ScopeWESL.test.ts +462 -0
- package/src/test/TestLink.ts +82 -0
- package/src/test/TestSetup.ts +4 -0
- package/src/test/TestUtil.ts +126 -0
- package/src/test/Tokenizer.test.ts +135 -0
- package/src/test/TransformBindingStructs.test.ts +230 -0
- package/src/test/Util.test.ts +22 -0
- package/src/test/VirtualModules.test.ts +37 -0
- package/src/test/WeslDevice.test.ts +265 -0
- package/src/test/WgslTests.ts +0 -0
- package/src/test/__snapshots__/ParseDirectives.test.ts.snap +25 -0
- package/src/test/__snapshots__/ParseWESL.test.ts.snap +119 -0
- package/src/test/__snapshots__/RustDirective.test.ts.snap +359 -0
- package/src/test/wgsl_1/main.wgsl +3 -0
- package/src/test/wgsl_1/util.wgsl +1 -0
- package/src/test/wgsl_2/main2.wgsl +3 -0
- package/src/test/wgsl_2/util2.wgsl +1 -0
- package/src/vlq/vlq.ts +94 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { expectTrimmedMatch, trimSrc } from "mini-parse/vitest-util";
|
|
2
|
+
import { expect, RunnerTestSuite } from "vitest";
|
|
3
|
+
import { WgslTestSrc } from "wesl-testsuite";
|
|
4
|
+
import { link } from "../Linker.js";
|
|
5
|
+
import { ManglerFn, underscoreMangle } from "../Mangler.ts";
|
|
6
|
+
import { resetScopeIds } from "../Scope.ts";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Link wesl sources and compare the linked wgsl vs expectations.
|
|
10
|
+
* Ignores blank lines and initial blank columns.
|
|
11
|
+
*
|
|
12
|
+
* (for tests)
|
|
13
|
+
*/
|
|
14
|
+
export async function testLink(
|
|
15
|
+
weslSrc: Record<string, string>,
|
|
16
|
+
rootModuleName: string,
|
|
17
|
+
expectedWgsl: string,
|
|
18
|
+
mangler?: ManglerFn,
|
|
19
|
+
): Promise<void> {
|
|
20
|
+
resetScopeIds();
|
|
21
|
+
/* -- link -- */
|
|
22
|
+
const stdResultMap = await link({
|
|
23
|
+
weslSrc,
|
|
24
|
+
rootModuleName,
|
|
25
|
+
mangler,
|
|
26
|
+
});
|
|
27
|
+
const stdResult = stdResultMap.dest;
|
|
28
|
+
|
|
29
|
+
/* -- trim and verify results line by line -- */
|
|
30
|
+
expectTrimmedMatch(stdResult, expectedWgsl);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type TestCaseMap = Map<string, WgslTestSrc>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Test link one test case from one a shared test suite
|
|
37
|
+
* (ImportCases, ConditionalTranslationCases, etc.)
|
|
38
|
+
*/
|
|
39
|
+
export async function testFromCase(
|
|
40
|
+
name: string,
|
|
41
|
+
cases: TestCaseMap,
|
|
42
|
+
): Promise<void> {
|
|
43
|
+
/* -- find and trim source texts -- */
|
|
44
|
+
const caseFound = cases.get(name);
|
|
45
|
+
if (!caseFound) {
|
|
46
|
+
throw new Error(`Skipping test "${name}"\nNo example found.`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const {
|
|
50
|
+
weslSrc,
|
|
51
|
+
expectedWgsl = "",
|
|
52
|
+
underscoreWgsl = expectedWgsl,
|
|
53
|
+
} = caseFound;
|
|
54
|
+
|
|
55
|
+
const srcEntries = Object.entries(weslSrc).map(([name, wgsl]) => {
|
|
56
|
+
const trimmedSrc = trimSrc(wgsl);
|
|
57
|
+
return [name, trimmedSrc] as [string, string];
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const trimmedWesl = Object.fromEntries(srcEntries);
|
|
61
|
+
|
|
62
|
+
const rootName = srcEntries[0][0];
|
|
63
|
+
await testLink(trimmedWesl, rootName, expectedWgsl);
|
|
64
|
+
await testLink(trimmedWesl, rootName, underscoreWgsl, underscoreMangle);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* for afterAll(), to verify that all cases are covered from one of the shared test suites
|
|
69
|
+
*/
|
|
70
|
+
export function verifyCaseCoverage(
|
|
71
|
+
caseList: WgslTestSrc[],
|
|
72
|
+
): (suite: RunnerTestSuite) => void {
|
|
73
|
+
return function verifyCases(suite: RunnerTestSuite) {
|
|
74
|
+
const testNameSet = new Set(suite.tasks.map(t => t.name));
|
|
75
|
+
const caseNames = caseList.map(c => c.name);
|
|
76
|
+
const missing = caseNames.filter(name => !testNameSet.has(name));
|
|
77
|
+
if (missing.length) {
|
|
78
|
+
console.error("Missing tests for cases:", missing);
|
|
79
|
+
expect("missing test: " + missing.toString()).toBe("");
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Parser, Stream, withLoggerAsync } from "mini-parse";
|
|
2
|
+
import {
|
|
3
|
+
expectNoLog,
|
|
4
|
+
logCatch,
|
|
5
|
+
TestParseResult,
|
|
6
|
+
testParseWithStream,
|
|
7
|
+
} from "mini-parse/test-util";
|
|
8
|
+
import {
|
|
9
|
+
BoundAndTransformed,
|
|
10
|
+
ParsedRegistry,
|
|
11
|
+
parsedRegistry,
|
|
12
|
+
parseIntoRegistry,
|
|
13
|
+
} from "wesl";
|
|
14
|
+
import { bindAndTransform, link, LinkParams } from "../Linker.js";
|
|
15
|
+
import { WeslStream, WeslToken } from "../parse/WeslStream.js";
|
|
16
|
+
import { parseWESL, syntheticWeslParseState, WeslAST } from "../ParseWESL.js";
|
|
17
|
+
import { resetScopeIds } from "../Scope.js";
|
|
18
|
+
|
|
19
|
+
export function testAppParse<T>(
|
|
20
|
+
parser: Parser<Stream<WeslToken>, T>,
|
|
21
|
+
src: string,
|
|
22
|
+
): TestParseResult<T, WeslAST> {
|
|
23
|
+
const appState = syntheticWeslParseState();
|
|
24
|
+
const stream = new WeslStream(src);
|
|
25
|
+
return testParseWithStream(parser, stream, appState);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Convenience wrapper to link wgsl for tests.
|
|
29
|
+
* The first module is named "./test.wesl",
|
|
30
|
+
* subsequent modules are named "./file1.wesl", "./file2.wesl", etc.
|
|
31
|
+
*/
|
|
32
|
+
export async function linkTest(...rawWgsl: string[]): Promise<string> {
|
|
33
|
+
return linkTestOpts({}, ...rawWgsl);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type LinkTestOpts = Pick<
|
|
37
|
+
LinkParams,
|
|
38
|
+
"conditions" | "libs" | "config" | "virtualLibs" | "constants" | "mangler"
|
|
39
|
+
>;
|
|
40
|
+
|
|
41
|
+
export async function linkTestOpts(
|
|
42
|
+
opts: LinkTestOpts,
|
|
43
|
+
...rawWgsl: string[]
|
|
44
|
+
): Promise<string> {
|
|
45
|
+
const weslSrc = makeTestBundle(rawWgsl);
|
|
46
|
+
|
|
47
|
+
const rootModuleName = "test";
|
|
48
|
+
const srcMap = await link({ weslSrc, rootModuleName, ...opts });
|
|
49
|
+
return srcMap.dest;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Link wesl for tests, and return the console log as well */
|
|
53
|
+
export async function linkWithLog(...rawWgsl: string[]): Promise<{
|
|
54
|
+
log: string;
|
|
55
|
+
result: string;
|
|
56
|
+
}> {
|
|
57
|
+
return linkWithLogInternal(rawWgsl);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Link wesl for tests, and return the console log as well.
|
|
61
|
+
* Quietly swallow any exceptions thrown */
|
|
62
|
+
export async function linkWithLogQuietly(...rawWgsl: string[]): Promise<{
|
|
63
|
+
log: string;
|
|
64
|
+
result: string;
|
|
65
|
+
}> {
|
|
66
|
+
return linkWithLogInternal(rawWgsl, true);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** link wesl for tests */
|
|
70
|
+
async function linkWithLogInternal(
|
|
71
|
+
rawWgsl: string[],
|
|
72
|
+
quiet = false,
|
|
73
|
+
): Promise<{
|
|
74
|
+
log: string;
|
|
75
|
+
result: string;
|
|
76
|
+
}> {
|
|
77
|
+
const { log, logged } = logCatch();
|
|
78
|
+
let result = "??";
|
|
79
|
+
try {
|
|
80
|
+
result = await withLoggerAsync(log, async () => linkTest(...rawWgsl));
|
|
81
|
+
} catch (e) {
|
|
82
|
+
if (!quiet) console.error(e);
|
|
83
|
+
}
|
|
84
|
+
return { result, log: logged() };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** parse wesl for testing, and return the AST */
|
|
88
|
+
export function parseTest(src: string): WeslAST {
|
|
89
|
+
return expectNoLog(() => parseTestRaw(src));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** test w/o any log collection, to not confuse debugging */
|
|
93
|
+
export function parseTestRaw(src: string) {
|
|
94
|
+
return parseWESL(src, undefined);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface BindTestResult {
|
|
98
|
+
bound: BoundAndTransformed;
|
|
99
|
+
registry: ParsedRegistry;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Test parsing and binding some wesl src.
|
|
104
|
+
* @return both the bound result and the internal registry.
|
|
105
|
+
* (since binding mutates the AST, it's useful for tests to review)
|
|
106
|
+
*/
|
|
107
|
+
export function bindTest(...rawWesl: string[]): BindTestResult {
|
|
108
|
+
resetScopeIds();
|
|
109
|
+
const weslSrc = makeTestBundle(rawWesl);
|
|
110
|
+
|
|
111
|
+
const registry = parsedRegistry();
|
|
112
|
+
parseIntoRegistry(weslSrc, registry, "package", "test");
|
|
113
|
+
const bound = bindAndTransform({ rootModuleName: "test", registry });
|
|
114
|
+
return { bound, registry };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** @return a weslSrc record for tests by synthesizing file names
|
|
118
|
+
* The root module is named ./test.wesl
|
|
119
|
+
*/
|
|
120
|
+
function makeTestBundle(rawWgsl: string[]): Record<string, string> {
|
|
121
|
+
const [root, ...rest] = rawWgsl;
|
|
122
|
+
const restWgsl = Object.fromEntries(
|
|
123
|
+
rest.map((src, i) => [`./file${i + 1}.wesl`, src]),
|
|
124
|
+
);
|
|
125
|
+
return { "./test.wesl": root, ...restWgsl };
|
|
126
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { WeslStream, WeslToken } from "../parse/WeslStream";
|
|
3
|
+
|
|
4
|
+
test("tokenize empty string", () => {
|
|
5
|
+
const tokenizer = new WeslStream("");
|
|
6
|
+
expect(tokenizer.nextToken()).toEqual(null);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test("parse fn foo() { }", () => {
|
|
10
|
+
const src = "fn foo() { }";
|
|
11
|
+
const tokenizer = new WeslStream(src);
|
|
12
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
13
|
+
kind: "keyword",
|
|
14
|
+
text: "fn",
|
|
15
|
+
span: [0, 2],
|
|
16
|
+
});
|
|
17
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
18
|
+
kind: "word",
|
|
19
|
+
text: "foo",
|
|
20
|
+
span: [3, 6],
|
|
21
|
+
});
|
|
22
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
23
|
+
kind: "symbol",
|
|
24
|
+
text: "(",
|
|
25
|
+
span: [6, 7],
|
|
26
|
+
});
|
|
27
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
28
|
+
kind: "symbol",
|
|
29
|
+
text: ")",
|
|
30
|
+
span: [7, 8],
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("parse var<storage> lights : vec3<f32>", () => {
|
|
35
|
+
const src = "var<storage> lights : vec3<f32>";
|
|
36
|
+
const tokenizer = new WeslStream(src);
|
|
37
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
38
|
+
kind: "keyword",
|
|
39
|
+
text: "var",
|
|
40
|
+
span: [0, 3],
|
|
41
|
+
});
|
|
42
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
43
|
+
kind: "symbol",
|
|
44
|
+
text: "<",
|
|
45
|
+
span: [3, 4],
|
|
46
|
+
});
|
|
47
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
48
|
+
kind: "word",
|
|
49
|
+
text: "storage",
|
|
50
|
+
span: [4, 11],
|
|
51
|
+
});
|
|
52
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
53
|
+
kind: "symbol",
|
|
54
|
+
text: ">",
|
|
55
|
+
span: [11, 12],
|
|
56
|
+
});
|
|
57
|
+
expect(tokenizer.nextToken()?.text).toEqual("lights");
|
|
58
|
+
expect(tokenizer.nextToken()?.text).toEqual(":");
|
|
59
|
+
expect(tokenizer.nextToken()?.text).toEqual("vec3");
|
|
60
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
61
|
+
kind: "symbol",
|
|
62
|
+
text: "<",
|
|
63
|
+
span: [26, 27],
|
|
64
|
+
});
|
|
65
|
+
expect(tokenizer.nextToken()?.text).toEqual("f32");
|
|
66
|
+
expect(tokenizer.nextToken()?.text).toEqual(">");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("parse >>", () => {
|
|
70
|
+
const src = ">>";
|
|
71
|
+
const tokenizer = new WeslStream(src);
|
|
72
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
73
|
+
kind: "symbol",
|
|
74
|
+
text: ">>",
|
|
75
|
+
span: [0, 2],
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("parse >> as template", () => {
|
|
80
|
+
const src = "array<foo >>";
|
|
81
|
+
const tokenizer = new WeslStream(src);
|
|
82
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
83
|
+
kind: "word",
|
|
84
|
+
text: "array",
|
|
85
|
+
span: [0, 5],
|
|
86
|
+
});
|
|
87
|
+
expect(tokenizer.nextTemplateStartToken()).toEqual(<WeslToken>{
|
|
88
|
+
kind: "symbol",
|
|
89
|
+
text: "<",
|
|
90
|
+
span: [5, 6],
|
|
91
|
+
});
|
|
92
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
93
|
+
kind: "word",
|
|
94
|
+
text: "foo",
|
|
95
|
+
span: [6, 9],
|
|
96
|
+
});
|
|
97
|
+
expect(tokenizer.nextTemplateEndToken()).toEqual(<WeslToken>{
|
|
98
|
+
kind: "symbol",
|
|
99
|
+
text: ">",
|
|
100
|
+
span: [10, 11],
|
|
101
|
+
});
|
|
102
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
103
|
+
kind: "symbol",
|
|
104
|
+
text: ">",
|
|
105
|
+
span: [11, 12],
|
|
106
|
+
});
|
|
107
|
+
expect(tokenizer.nextToken()).toBe(null);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("parse skip block comment", () => {
|
|
111
|
+
const src = "/* /* // */ */vec3<f32>";
|
|
112
|
+
const tokenizer = new WeslStream(src);
|
|
113
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
114
|
+
kind: "word",
|
|
115
|
+
text: "vec3",
|
|
116
|
+
span: [14, 18],
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("parse skip line comment", () => {
|
|
121
|
+
const src = "// vec3<f32> */ a\nvec3";
|
|
122
|
+
const tokenizer = new WeslStream(src);
|
|
123
|
+
expect(tokenizer.nextToken()).toEqual(<WeslToken>{
|
|
124
|
+
kind: "word",
|
|
125
|
+
text: "vec3",
|
|
126
|
+
span: [18, 22],
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("parse skip line without newline", () => {
|
|
131
|
+
const src = "// foo bar";
|
|
132
|
+
const tokenizer = new WeslStream(src);
|
|
133
|
+
expect(tokenizer.nextToken()).toBe(null);
|
|
134
|
+
expect(tokenizer.checkpoint()).toBe(src.length);
|
|
135
|
+
});
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { SrcMapBuilder } from "mini-parse";
|
|
2
|
+
import { expectTrimmedMatch } from "mini-parse/vitest-util";
|
|
3
|
+
import { expect, test } from "vitest";
|
|
4
|
+
import { bindIdents } from "../BindIdents.ts";
|
|
5
|
+
import { astToString } from "../debug/ASTtoString.ts";
|
|
6
|
+
import { lowerAndEmit } from "../LowerAndEmit.ts";
|
|
7
|
+
import { parsedRegistry } from "../ParsedRegistry.ts";
|
|
8
|
+
import {
|
|
9
|
+
bindingStructsPlugin,
|
|
10
|
+
findRefsToBindingStructs,
|
|
11
|
+
lowerBindingStructs,
|
|
12
|
+
markBindingStructs,
|
|
13
|
+
transformBindingReference,
|
|
14
|
+
transformBindingStruct,
|
|
15
|
+
} from "../TransformBindingStructs.ts";
|
|
16
|
+
import { linkTestOpts, parseTest } from "./TestUtil.ts";
|
|
17
|
+
|
|
18
|
+
test("markBindingStructs true", () => {
|
|
19
|
+
const src = `
|
|
20
|
+
struct Bindings {
|
|
21
|
+
@group(0) @binding(0) particles: ptr<storage, array<f32>, read_write>,
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
const ast = parseTest(src);
|
|
26
|
+
const structs = markBindingStructs(ast.moduleElem);
|
|
27
|
+
expect(structs.length).toBe(1);
|
|
28
|
+
expect(structs[0].bindingStruct).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("markBindingStructs false", () => {
|
|
32
|
+
const src = `
|
|
33
|
+
struct Bindings {
|
|
34
|
+
particles: ptr<storage, array<f32>, read_write>,
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const ast = parseTest(src);
|
|
39
|
+
const structs = markBindingStructs(ast.moduleElem);
|
|
40
|
+
expect(structs.length).toBe(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("transformBindingStruct", () => {
|
|
44
|
+
const src = `
|
|
45
|
+
struct Bindings {
|
|
46
|
+
@group(0) @binding(0) particles: ptr<storage, array<f32>, read_write>,
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
const rootAst = parseTest(src);
|
|
51
|
+
bindIdents({ rootAst, registry: parsedRegistry() });
|
|
52
|
+
const bindingStruct = markBindingStructs(rootAst.moduleElem)[0];
|
|
53
|
+
const newVars = transformBindingStruct(bindingStruct, new Set());
|
|
54
|
+
|
|
55
|
+
const srcBuilder = new SrcMapBuilder({ text: rootAst.srcModule.src });
|
|
56
|
+
lowerAndEmit(srcBuilder, newVars, {});
|
|
57
|
+
const linked = SrcMapBuilder.build([srcBuilder]).dest.text;
|
|
58
|
+
expect(linked).toMatchInlineSnapshot(
|
|
59
|
+
`
|
|
60
|
+
"@group(0) @binding(0) var<storage, read_write> particles : array<f32>;
|
|
61
|
+
"
|
|
62
|
+
`,
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("findRefsToBindingStructs", () => {
|
|
67
|
+
const src = `
|
|
68
|
+
struct Bindings {
|
|
69
|
+
@group(0) @binding(0) particles: ptr<storage, array<f32>, read_write>,
|
|
70
|
+
}
|
|
71
|
+
struct NotBindings { a: i32 }
|
|
72
|
+
var y: NotBindings;
|
|
73
|
+
|
|
74
|
+
fn main(b: Bindings) {
|
|
75
|
+
let x = b.particles;
|
|
76
|
+
let z = y.a;
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
const rootAst = parseTest(src);
|
|
81
|
+
bindIdents({ rootAst, registry: parsedRegistry() });
|
|
82
|
+
markBindingStructs(rootAst.moduleElem)[0];
|
|
83
|
+
const found = findRefsToBindingStructs(rootAst.moduleElem);
|
|
84
|
+
expect(found.length).toBe(1);
|
|
85
|
+
const foundAst = astToString(found[0].memberRef);
|
|
86
|
+
expect(foundAst).toMatchInlineSnapshot(`
|
|
87
|
+
"memberRef b.particles
|
|
88
|
+
ref b
|
|
89
|
+
text '.'
|
|
90
|
+
name particles"
|
|
91
|
+
`);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("transformBindingReference", () => {
|
|
95
|
+
const src = `
|
|
96
|
+
struct Bindings {
|
|
97
|
+
@group(0) @binding(0) particles: ptr<storage, array<f32>, read_write>,
|
|
98
|
+
}
|
|
99
|
+
fn main(b: Bindings) {
|
|
100
|
+
let x = b.particles;
|
|
101
|
+
}
|
|
102
|
+
`;
|
|
103
|
+
|
|
104
|
+
const rootAst = parseTest(src);
|
|
105
|
+
bindIdents({ rootAst, registry: parsedRegistry() });
|
|
106
|
+
const bindingStruct = markBindingStructs(rootAst.moduleElem)[0];
|
|
107
|
+
transformBindingStruct(bindingStruct, new Set());
|
|
108
|
+
const found = findRefsToBindingStructs(rootAst.moduleElem);
|
|
109
|
+
expect(found.length).toBe(1);
|
|
110
|
+
const { memberRef, struct } = found[0];
|
|
111
|
+
const synthElem = transformBindingReference(memberRef, struct);
|
|
112
|
+
const synthAst = astToString(synthElem);
|
|
113
|
+
expect(synthAst).toMatchInlineSnapshot(`"synthetic 'particles'"`);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("lower binding structs", () => {
|
|
117
|
+
const src = `
|
|
118
|
+
struct Bindings {
|
|
119
|
+
@group(0) @binding(0) particles: ptr<storage, array<f32>, read_write>,
|
|
120
|
+
}
|
|
121
|
+
fn main(b: Bindings) {
|
|
122
|
+
let x = b.particles;
|
|
123
|
+
}
|
|
124
|
+
`;
|
|
125
|
+
|
|
126
|
+
const expected = `
|
|
127
|
+
@group(0) @binding(0) var<storage, read_write> particles : array<f32>;
|
|
128
|
+
|
|
129
|
+
fn main() {
|
|
130
|
+
let x = particles;
|
|
131
|
+
}
|
|
132
|
+
`;
|
|
133
|
+
const rootAst = parseTest(src);
|
|
134
|
+
const { globalNames } = bindIdents({ rootAst, registry: parsedRegistry() });
|
|
135
|
+
const tAst = { ...rootAst, globalNames, notableElems: {} };
|
|
136
|
+
const lowered = lowerBindingStructs(tAst);
|
|
137
|
+
|
|
138
|
+
const loweredAst = astToString(lowered.moduleElem);
|
|
139
|
+
expect(loweredAst).toMatchInlineSnapshot(`
|
|
140
|
+
"module
|
|
141
|
+
synthetic '@group(0) @binding(0) var<storage, read_write> particles : array<f32>;
|
|
142
|
+
'
|
|
143
|
+
text '
|
|
144
|
+
'
|
|
145
|
+
text '
|
|
146
|
+
'
|
|
147
|
+
fn main(b: Bindings)
|
|
148
|
+
decl %main
|
|
149
|
+
param
|
|
150
|
+
statement
|
|
151
|
+
text '{
|
|
152
|
+
let '
|
|
153
|
+
typeDecl %x
|
|
154
|
+
decl %x
|
|
155
|
+
text ' = '
|
|
156
|
+
memberRef b.particles
|
|
157
|
+
synthetic 'particles'
|
|
158
|
+
text ';
|
|
159
|
+
}'
|
|
160
|
+
text '
|
|
161
|
+
'"
|
|
162
|
+
`);
|
|
163
|
+
|
|
164
|
+
const srcBuilder = new SrcMapBuilder({ text: lowered.srcModule.src });
|
|
165
|
+
lowerAndEmit(srcBuilder, [lowered.moduleElem], {}, false);
|
|
166
|
+
const linked = SrcMapBuilder.build([srcBuilder]).dest.text;
|
|
167
|
+
expectTrimmedMatch(linked, expected);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("lower binding structs with conflicting root name", async () => {
|
|
171
|
+
const src = `
|
|
172
|
+
struct Bindings {
|
|
173
|
+
@group(0) @binding(0) particles: ptr<storage, array<f32>, read_write>,
|
|
174
|
+
}
|
|
175
|
+
const particles = 7;
|
|
176
|
+
fn main(b: Bindings) {
|
|
177
|
+
let x = b.particles;
|
|
178
|
+
}
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
const expected = `
|
|
182
|
+
@group(0) @binding(0) var<storage, read_write> particles0 : array<f32>;
|
|
183
|
+
|
|
184
|
+
const particles = 7;
|
|
185
|
+
fn main() {
|
|
186
|
+
let x = particles0;
|
|
187
|
+
}
|
|
188
|
+
`;
|
|
189
|
+
|
|
190
|
+
const opts = { config: { plugins: [bindingStructsPlugin()] } };
|
|
191
|
+
const linked = await linkTestOpts(opts, src);
|
|
192
|
+
expectTrimmedMatch(linked, expected);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("lower 5 bindings", async () => {
|
|
196
|
+
const src = `
|
|
197
|
+
struct Uniforms {
|
|
198
|
+
foo: u32
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
struct MyBindings {
|
|
202
|
+
@group(0) @binding(0) particles: ptr<storage, array<u32>, read_write>,
|
|
203
|
+
@group(0) @binding(1) uniforms: ptr<uniform, Uniforms>,
|
|
204
|
+
@group(0) @binding(2) tex: texture_2d<rgba8unorm>,
|
|
205
|
+
@group(0) @binding(3) samp: sampler,
|
|
206
|
+
@group(0) @binding(4) stTex: texture_storage_2d<rgba8unorm, read>,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
@compute fn main(b: MyBindings) {
|
|
210
|
+
b.particles[0] = b.uniforms.foo;
|
|
211
|
+
}
|
|
212
|
+
`;
|
|
213
|
+
|
|
214
|
+
const expected = `
|
|
215
|
+
@group(0) @binding(0) var<storage, read_write> particles : array<u32>;
|
|
216
|
+
@group(0) @binding(1) var<uniform> uniforms : Uniforms;
|
|
217
|
+
@group(0) @binding(2) var tex : texture_2d<rgba8unorm>;
|
|
218
|
+
@group(0) @binding(3) var samp : sampler;
|
|
219
|
+
@group(0) @binding(4) var stTex : texture_storage_2d<rgba8unorm, read>;
|
|
220
|
+
|
|
221
|
+
struct Uniforms { foo: u32 }
|
|
222
|
+
@compute fn main() {
|
|
223
|
+
particles[0] = uniforms.foo;
|
|
224
|
+
}
|
|
225
|
+
`;
|
|
226
|
+
|
|
227
|
+
const opts = { config: { plugins: [bindingStructsPlugin()] } };
|
|
228
|
+
const linked = await linkTestOpts(opts, src);
|
|
229
|
+
expectTrimmedMatch(linked, expected);
|
|
230
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { overlapTail, scan } from "../Util.js";
|
|
3
|
+
|
|
4
|
+
test("scan", () => {
|
|
5
|
+
const result = scan([1, 2, 1], (a, b: string) => b.slice(a), "foobar");
|
|
6
|
+
expect(result).toEqual(["foobar", "oobar", "bar", "ar"]);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test("overlap 0", () => {
|
|
10
|
+
const result = overlapTail([2, 3], [4, 5]);
|
|
11
|
+
expect(result).toBeUndefined();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("overlap 1", () => {
|
|
15
|
+
const result = overlapTail([2, 3], [3, 4, 5]);
|
|
16
|
+
expect(result).toEqual([4, 5]);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("overlap 2", () => {
|
|
20
|
+
const result = overlapTail([2, 3], [2, 3]);
|
|
21
|
+
expect(result).toEqual([]);
|
|
22
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { expectTrimmedMatch } from "mini-parse/vitest-util";
|
|
2
|
+
import { test } from "vitest";
|
|
3
|
+
import { linkTestOpts } from "./TestUtil.ts";
|
|
4
|
+
|
|
5
|
+
test("simple virtual module", async () => {
|
|
6
|
+
const src = `
|
|
7
|
+
import virt::Uniforms;
|
|
8
|
+
@binding(0) @group(0) var<uniform> u: Uniforms;
|
|
9
|
+
`;
|
|
10
|
+
const result = await linkTestOpts(
|
|
11
|
+
{ virtualLibs: { virt: () => "struct Uniforms { foo: u32 }" } },
|
|
12
|
+
src,
|
|
13
|
+
);
|
|
14
|
+
const expected = `
|
|
15
|
+
@binding(0) @group(0) var<uniform> u: Uniforms;
|
|
16
|
+
struct Uniforms { foo: u32 }
|
|
17
|
+
`;
|
|
18
|
+
expectTrimmedMatch(result, expected);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("virtual constants", async () => {
|
|
22
|
+
const src = `
|
|
23
|
+
import constants::num_lights;
|
|
24
|
+
fn main() {
|
|
25
|
+
for (var i = 0; i < num_lights; i++) { }
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
const result = await linkTestOpts({ constants: { num_lights: 4 } }, src);
|
|
29
|
+
const expected = `
|
|
30
|
+
fn main() {
|
|
31
|
+
for (var i = 0; i < num_lights; i++) { }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const num_lights = 4;
|
|
35
|
+
`;
|
|
36
|
+
expectTrimmedMatch(result, expected);
|
|
37
|
+
});
|