wesl 0.7.26 → 0.7.28
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 +1 -1
- package/dist/index.d.ts +283 -147
- package/dist/index.js +1765 -1143
- package/package.json +2 -2
- package/src/AbstractElems.ts +264 -82
- package/src/ClickableError.ts +8 -1
- package/src/Linker.ts +63 -67
- package/src/LinkerUtil.ts +141 -7
- package/src/LowerAndEmit.ts +660 -304
- package/src/Mangler.ts +1 -1
- package/src/ModuleResolver.ts +15 -4
- package/src/ParseWESL.ts +21 -33
- package/src/SrcMap.ts +7 -19
- package/src/StandardTypes.ts +1 -1
- package/src/WeslDevice.ts +1 -0
- package/src/debug/ASTtoString.ts +92 -76
- package/src/index.ts +1 -1
- package/src/parse/AttachComments.ts +289 -0
- package/src/parse/ExpressionUtil.ts +3 -3
- package/src/parse/Keywords.ts +1 -1
- package/src/parse/ParseAttribute.ts +49 -71
- package/src/parse/ParseCall.ts +3 -4
- package/src/parse/ParseControlFlow.ts +100 -56
- package/src/parse/ParseDirective.ts +9 -8
- package/src/parse/ParseDoBlock.ts +63 -0
- package/src/parse/ParseExpression.ts +11 -10
- package/src/parse/ParseFn.ts +11 -33
- package/src/parse/ParseGlobalVar.ts +36 -27
- package/src/parse/ParseIdent.ts +4 -5
- package/src/parse/ParseLocalVar.ts +16 -12
- package/src/parse/ParseLoop.ts +76 -39
- package/src/parse/ParseModule.ts +65 -19
- package/src/parse/ParseSimpleStatement.ts +112 -66
- package/src/parse/ParseStatement.ts +40 -67
- package/src/parse/ParseStruct.ts +8 -22
- package/src/parse/ParseType.ts +10 -13
- package/src/parse/ParseUtil.ts +26 -12
- package/src/parse/ParseValueDeclaration.ts +18 -31
- package/src/parse/ParseWesl.ts +11 -14
- package/src/parse/ParsingContext.ts +11 -9
- package/src/parse/WeslStream.ts +138 -121
- package/src/parse/stream/RegexMatchers.ts +45 -0
- package/src/parse/stream/WeslLexer.ts +249 -0
- package/src/test/BevyLink.test.ts +18 -11
- package/src/test/ConditionalElif.test.ts +4 -2
- package/src/test/DeclCommentEmit.test.ts +122 -0
- package/src/test/DoBlock.test.ts +164 -0
- package/src/test/FilterValidElements.test.ts +4 -6
- package/src/test/Linker.test.ts +23 -7
- package/src/test/Mangling.test.ts +8 -4
- package/src/test/ParseComments.test.ts +234 -6
- package/src/test/ParseConditionsV2.test.ts +47 -175
- package/src/test/ParseElifV2.test.ts +12 -33
- package/src/test/ParseErrorV2.test.ts +0 -0
- package/src/test/ParseWeslV2.test.ts +247 -626
- package/src/test/StatementEmit.test.ts +143 -0
- package/src/test/StripWesl.ts +24 -3
- package/src/test/TestLink.ts +19 -3
- package/src/test/TestUtil.ts +18 -8
- package/src/test/Tokenizer.test.ts +96 -0
- package/src/test/__snapshots__/ParseWeslV2.test.ts.snap +10 -42
- package/src/RawEmit.ts +0 -103
- package/src/Reflection.ts +0 -336
- package/src/TransformBindingStructs.ts +0 -320
- package/src/parse/ContentsHelpers.ts +0 -70
- package/src/parse/stream/CachingStream.ts +0 -48
- package/src/parse/stream/MatchersStream.ts +0 -85
|
@@ -11,13 +11,11 @@ const bevyBulkTest = {
|
|
|
11
11
|
name: "Bevy",
|
|
12
12
|
baseDir: "bevy-wgsl",
|
|
13
13
|
git: {
|
|
14
|
-
url: "https://github.com/
|
|
14
|
+
url: "https://github.com/webgpu-tools/bevy-wgsl.git",
|
|
15
15
|
revision: "84977ff025eaf8d92e56a9c35b815fae70eb4af0",
|
|
16
16
|
},
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
await fetchBulkTest(bevyBulkTest, fixturesDir);
|
|
20
|
-
|
|
21
19
|
// Constants needed by various Bevy modules
|
|
22
20
|
const bevyConstants = {
|
|
23
21
|
MAX_CASCADES_PER_LIGHT: 4,
|
|
@@ -36,6 +34,7 @@ const conditionalFiles: Record<string, Conditions> = {
|
|
|
36
34
|
"./pbr/decal/clustered.wesl": { CLUSTERED_DECALS_ARE_USABLE: true },
|
|
37
35
|
"./pbr/decal/forward.wesl": { DEPTH_PREPASS: true },
|
|
38
36
|
"./pbr/morph.wesl": { MORPH_TARGETS: true },
|
|
37
|
+
"./pbr/irradiance_volume.wesl": { IRRADIANCE_VOLUMES_ARE_USABLE: true },
|
|
39
38
|
};
|
|
40
39
|
|
|
41
40
|
// LATER: binding_array is a WGSL extension not yet supported
|
|
@@ -44,8 +43,21 @@ const skipFiles = [
|
|
|
44
43
|
"./pbr/decal/clustered.wesl", // imports mesh_view_bindings which uses binding_array
|
|
45
44
|
];
|
|
46
45
|
|
|
47
|
-
// Files that
|
|
48
|
-
|
|
46
|
+
// Files that produce empty WGSL with the default conditions: either pure
|
|
47
|
+
// re-export modules (imports only), or modules whose every top-level decl is
|
|
48
|
+
// behind an `@if` that is off by default (and whose only enabling condition
|
|
49
|
+
// pulls in an as-yet-unsupported WGSL extension, so we can't link it non-empty).
|
|
50
|
+
const emptyOutputFiles = [
|
|
51
|
+
"./sprite/mesh2d_view_types.wesl", // imports only
|
|
52
|
+
"./pbr/meshlet_visibility_buffer_resolve.wesl", // all decls behind @if(MESHLET_MESH_MATERIAL_PASS); enabling it needs binding_array (r64uint)
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
await fetchBulkTest(bevyBulkTest, fixturesDir);
|
|
56
|
+
|
|
57
|
+
const weslSrc = await loadBevyBundle();
|
|
58
|
+
const allFiles = Object.keys(weslSrc)
|
|
59
|
+
.filter(f => !skipFiles.includes(f))
|
|
60
|
+
.sort();
|
|
49
61
|
|
|
50
62
|
async function loadBevyBundle(): Promise<Record<string, string>> {
|
|
51
63
|
const bevyDir = new URL(
|
|
@@ -75,11 +87,6 @@ async function loadDir(
|
|
|
75
87
|
}
|
|
76
88
|
}
|
|
77
89
|
|
|
78
|
-
const weslSrc = await loadBevyBundle();
|
|
79
|
-
const allFiles = Object.keys(weslSrc)
|
|
80
|
-
.filter(f => !skipFiles.includes(f))
|
|
81
|
-
.sort();
|
|
82
|
-
|
|
83
90
|
allFiles.forEach(file => {
|
|
84
91
|
test(`bevy: link ${file}`, async () => {
|
|
85
92
|
const conditions = conditionalFiles[file] ?? {};
|
|
@@ -91,7 +98,7 @@ allFiles.forEach(file => {
|
|
|
91
98
|
);
|
|
92
99
|
|
|
93
100
|
const wgsl = result.dest;
|
|
94
|
-
if (
|
|
101
|
+
if (emptyOutputFiles.includes(file)) {
|
|
95
102
|
expect(wgsl).toBe("");
|
|
96
103
|
} else {
|
|
97
104
|
expect(wgsl.length).toBeGreaterThan(0);
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { linkTest } from "./TestUtil.ts";
|
|
3
|
+
import { expectTrimmedMatch } from "./TrimmedMatch.ts";
|
|
4
|
+
|
|
5
|
+
// Comments attached to declaration-level nodes (module decls, struct members,
|
|
6
|
+
// fn signatures) survive linking. Statement- and expression-level comments are
|
|
7
|
+
// covered in StatementEmit / ParseComments.
|
|
8
|
+
|
|
9
|
+
test("doc comment above a function", async () => {
|
|
10
|
+
const src = `
|
|
11
|
+
// describe foo
|
|
12
|
+
fn foo() { }
|
|
13
|
+
`;
|
|
14
|
+
const result = await linkTest(src);
|
|
15
|
+
expectTrimmedMatch(result, src);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("doc comment above a struct", async () => {
|
|
19
|
+
const src = `
|
|
20
|
+
// a point
|
|
21
|
+
struct Point { x: i32 }
|
|
22
|
+
`;
|
|
23
|
+
const result = await linkTest(src);
|
|
24
|
+
expect(result).toContain("// a point");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("doc comment above a global const", async () => {
|
|
28
|
+
const src = `
|
|
29
|
+
// the answer
|
|
30
|
+
const answer = 42;
|
|
31
|
+
`;
|
|
32
|
+
const result = await linkTest(src);
|
|
33
|
+
expectTrimmedMatch(result, src);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("trailing comment on a global const", async () => {
|
|
37
|
+
const src = `const answer = 42; // the answer`;
|
|
38
|
+
const result = await linkTest(src);
|
|
39
|
+
expectTrimmedMatch(result, src);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("leading comment on a struct member", async () => {
|
|
43
|
+
const src = `
|
|
44
|
+
struct S {
|
|
45
|
+
// the x field
|
|
46
|
+
x: i32,
|
|
47
|
+
y: i32,
|
|
48
|
+
}
|
|
49
|
+
`;
|
|
50
|
+
const result = await linkTest(src);
|
|
51
|
+
expectTrimmedMatch(result, src);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("trailing comment on a struct member", async () => {
|
|
55
|
+
const src = `
|
|
56
|
+
struct S {
|
|
57
|
+
x: i32, // horizontal
|
|
58
|
+
y: i32,
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
const result = await linkTest(src);
|
|
62
|
+
expect(result).toContain("// horizontal");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("comment on a sole struct member forces the multi-line form", async () => {
|
|
66
|
+
const src = `struct S { /* the field */ x: i32 }`;
|
|
67
|
+
const result = await linkTest(src);
|
|
68
|
+
expect(result).toContain("/* the field */");
|
|
69
|
+
expect(result).toContain("x: i32");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("comment leading a function parameter", async () => {
|
|
73
|
+
const src = `fn foo(/* count */ n: i32) { }`;
|
|
74
|
+
const result = await linkTest(src);
|
|
75
|
+
expectTrimmedMatch(result, src);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("comment trailing a function parameter", async () => {
|
|
79
|
+
const src = `fn foo(n: i32 /* count */) { }`;
|
|
80
|
+
const result = await linkTest(src);
|
|
81
|
+
expectTrimmedMatch(result, src);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("comment before a function name", async () => {
|
|
85
|
+
const src = `fn /* the entry point */ main() { }`;
|
|
86
|
+
const result = await linkTest(src);
|
|
87
|
+
expectTrimmedMatch(result, src);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("comment before a struct name", async () => {
|
|
91
|
+
const src = `struct /* a point */ Point { x: i32 }`;
|
|
92
|
+
const result = await linkTest(src);
|
|
93
|
+
expect(result).toContain("/* a point */");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("comment before an alias name", async () => {
|
|
97
|
+
const src = `alias /* small int */ Byte = i32;`;
|
|
98
|
+
const result = await linkTest(src);
|
|
99
|
+
expect(result).toContain("/* small int */");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("comment before a return type", async () => {
|
|
103
|
+
const src = `fn one() -> /* always one */ i32 { return 1; }`;
|
|
104
|
+
const result = await linkTest(src);
|
|
105
|
+
expect(result).toContain("/* always one */");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("comment trailing an attribute", async () => {
|
|
109
|
+
const src = `@compute /* on the gpu */ @workgroup_size(1) fn main() { }`;
|
|
110
|
+
const result = await linkTest(src);
|
|
111
|
+
expect(result).toContain("/* on the gpu */");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("comment before a local declaration name", async () => {
|
|
115
|
+
const src = `
|
|
116
|
+
fn main() {
|
|
117
|
+
let /* result */ x = 1;
|
|
118
|
+
}
|
|
119
|
+
`;
|
|
120
|
+
const result = await linkTest(src);
|
|
121
|
+
expect(result).toContain("/* result */");
|
|
122
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import type { CallElem, DoBlockElem } from "../AbstractElems.ts";
|
|
3
|
+
import { freshResolver, RecordResolver } from "../ModuleResolver.ts";
|
|
4
|
+
import { linkTestOpts, parseTest } from "./TestUtil.ts";
|
|
5
|
+
|
|
6
|
+
/** `do` blocks are an opt-in WESL extension (default off). */
|
|
7
|
+
const doExt = { weslExtensions: { doBlocks: true } } as const;
|
|
8
|
+
|
|
9
|
+
const example1 = `
|
|
10
|
+
@buffer var<storage, read_write> grid: array<f32, 64>;
|
|
11
|
+
|
|
12
|
+
@compute @workgroup_size(8)
|
|
13
|
+
fn jacobi_step(@builtin(global_invocation_id) id: vec3u) {
|
|
14
|
+
let i = id.x;
|
|
15
|
+
if (i > 0u && i < 63u) { grid[i] = (grid[i - 1u] + grid[i + 1u]) * 0.5; }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@test
|
|
19
|
+
do test_jacobi() {
|
|
20
|
+
for (var i = 0u; i < 100u; i++) { jacobi_step(8, 1, 1); }
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
const example2 = `
|
|
25
|
+
do reduce(count: u32) {
|
|
26
|
+
let groups = count / 256u;
|
|
27
|
+
if groups > 1u {
|
|
28
|
+
reduce_step(groups, 1, 1);
|
|
29
|
+
reduce(groups);
|
|
30
|
+
} else {
|
|
31
|
+
reduce_step(1, 1, 1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
do frame(u: Uniforms, @slider(1, 100) steps: u32) {
|
|
36
|
+
if u.frame == 0u { init_min_max(1, 1, 1); }
|
|
37
|
+
for (var i = 0u; i < steps; i++) {
|
|
38
|
+
step_sim(u.resolution.x / 8u, u.resolution.y / 8u, 1);
|
|
39
|
+
}
|
|
40
|
+
render();
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
function doBlocks(src: string): DoBlockElem[] {
|
|
45
|
+
const ast = parseTest(src, doExt);
|
|
46
|
+
return ast.moduleElem.decls.filter((e): e is DoBlockElem => e.kind === "do");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
test("parse do block: name, params, control flow body", () => {
|
|
50
|
+
const blocks = doBlocks(example1);
|
|
51
|
+
expect(blocks).toHaveLength(1);
|
|
52
|
+
const [block] = blocks;
|
|
53
|
+
expect(block.name.name).toBe("test_jacobi");
|
|
54
|
+
expect(block.params).toHaveLength(0);
|
|
55
|
+
expect(block.body.body.length).toBeGreaterThan(0);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("do body call-expressions expose structured .arguments", () => {
|
|
59
|
+
// Expressions are first-class AST nodes, so call args are typed ExpressionElem
|
|
60
|
+
// trees - the interpreter reads them directly.
|
|
61
|
+
const src = `
|
|
62
|
+
do tick() {
|
|
63
|
+
fill(1, 1, 1);
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
66
|
+
const [block] = doBlocks(src);
|
|
67
|
+
const stmt = block.body.body.find((c): c is CallElem => c.kind === "call");
|
|
68
|
+
expect(stmt).toBeDefined();
|
|
69
|
+
const call = stmt!.call;
|
|
70
|
+
expect(call.arguments).toHaveLength(3);
|
|
71
|
+
expect(call.arguments.every(a => a.kind === "literal")).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("parse do blocks: recursion, if/else, uniforms + slider params", () => {
|
|
75
|
+
const blocks = doBlocks(example2);
|
|
76
|
+
expect(blocks.map(b => b.name.name)).toEqual(["reduce", "frame"]);
|
|
77
|
+
|
|
78
|
+
const [reduce, frame] = blocks;
|
|
79
|
+
expect(reduce.params).toHaveLength(1);
|
|
80
|
+
expect(reduce.params[0].name.decl.ident.originalName).toBe("count");
|
|
81
|
+
|
|
82
|
+
expect(frame.params).toHaveLength(2);
|
|
83
|
+
expect(frame.params.map(p => p.name.decl.ident.originalName)).toEqual([
|
|
84
|
+
"u",
|
|
85
|
+
"steps",
|
|
86
|
+
]);
|
|
87
|
+
expect(frame.body.body.length).toBeGreaterThan(0);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("link drops the do block, keeps surrounding declarations", async () => {
|
|
91
|
+
const wgsl = await linkTestOpts(doExt, example1);
|
|
92
|
+
expect(wgsl).toContain("fn jacobi_step");
|
|
93
|
+
expect(wgsl).toContain("grid");
|
|
94
|
+
expect(wgsl).toContain("@compute");
|
|
95
|
+
expect(wgsl).not.toContain("test_jacobi");
|
|
96
|
+
expect(wgsl).not.toMatch(/(^|\s)do\s/);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("do block body links without spurious binding errors", async () => {
|
|
100
|
+
// example2 has no fn/global decls, only do blocks: a clean emit proves the
|
|
101
|
+
// do bodies (u.frame, steps, step_sim, render, reduce) bypass bindIdents.
|
|
102
|
+
const wgsl = await linkTestOpts(doExt, example2);
|
|
103
|
+
for (const name of [
|
|
104
|
+
"reduce",
|
|
105
|
+
"frame",
|
|
106
|
+
"step_sim",
|
|
107
|
+
"render",
|
|
108
|
+
"Uniforms",
|
|
109
|
+
"init_min_max",
|
|
110
|
+
]) {
|
|
111
|
+
expect(wgsl).not.toContain(name);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("error: fn and do block with the same name", () => {
|
|
116
|
+
const src = `
|
|
117
|
+
fn reduce() {}
|
|
118
|
+
do reduce() {}
|
|
119
|
+
`;
|
|
120
|
+
expect(() => parseTest(src, doExt)).toThrow(/declared as both fn and do/);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("error: two do blocks with the same name", () => {
|
|
124
|
+
// runners key do blocks by name, so a duplicate would silently shadow the
|
|
125
|
+
// first; reject it at parse time instead of running the wrong block.
|
|
126
|
+
const src = `
|
|
127
|
+
do same() {}
|
|
128
|
+
do same() {}
|
|
129
|
+
`;
|
|
130
|
+
expect(() => parseTest(src, doExt)).toThrow(/declared as do more than once/);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("default off: do block syntax is not recognized without the extension", () => {
|
|
134
|
+
// Without the extension, `do` stays a reserved word and module-level `do`
|
|
135
|
+
// fails to parse (no DoBlockElem is produced).
|
|
136
|
+
expect(() => parseTest(example1)).toThrow();
|
|
137
|
+
expect(() => parseTest("do tick() {}")).toThrow();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("freshResolver preserves weslExtensions across re-parse", () => {
|
|
141
|
+
// Resolver reuse re-parses via parseSrcModule(ast.srcModule, ast.parseOptions);
|
|
142
|
+
// the do-block extension must survive that round trip.
|
|
143
|
+
const src = "do tick() {}";
|
|
144
|
+
const inner = new RecordResolver({ "package::main": src }, doExt);
|
|
145
|
+
const wrapped = freshResolver(freshResolver(inner));
|
|
146
|
+
const ast = wrapped.resolveModule("package::main")!;
|
|
147
|
+
const blocks = ast.moduleElem.decls.filter(
|
|
148
|
+
(e): e is DoBlockElem => e.kind === "do",
|
|
149
|
+
);
|
|
150
|
+
expect(blocks.map(b => b.name.name)).toEqual(["tick"]);
|
|
151
|
+
|
|
152
|
+
// an optionless resolver stays OFF (fails to parse `do`, as before)
|
|
153
|
+
const off = freshResolver(new RecordResolver({ "package::main": src }));
|
|
154
|
+
expect(() => off.resolveModule("package::main")).toThrow();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("regression: a normal fn named do_something is unaffected", async () => {
|
|
158
|
+
const wgsl = await linkTestOpts({}, "fn do_something() {}");
|
|
159
|
+
expect(wgsl).toContain("fn do_something");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("regression: do as a reserved word still errors when the extension is off", () => {
|
|
163
|
+
expect(() => parseTest("fn f() { do }")).toThrow(/Expected ';'/);
|
|
164
|
+
});
|
|
@@ -21,7 +21,6 @@ function mockElem(testId: string, attributes?: AttributeElem[]): TestElem {
|
|
|
21
21
|
kind: "alias",
|
|
22
22
|
testId,
|
|
23
23
|
attributes,
|
|
24
|
-
contents: [],
|
|
25
24
|
start: 0,
|
|
26
25
|
end: 0,
|
|
27
26
|
} as TestElem;
|
|
@@ -31,13 +30,15 @@ function ifAttr(condition: boolean): AttributeElem {
|
|
|
31
30
|
const literal: Literal = {
|
|
32
31
|
kind: "literal",
|
|
33
32
|
value: condition ? "true" : "false",
|
|
34
|
-
|
|
33
|
+
start: 0,
|
|
34
|
+
end: 0,
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
const translateTimeExpr: TranslateTimeExpressionElem = {
|
|
38
38
|
kind: "translate-time-expression",
|
|
39
39
|
expression: literal,
|
|
40
|
-
|
|
40
|
+
start: 0,
|
|
41
|
+
end: 0,
|
|
41
42
|
};
|
|
42
43
|
|
|
43
44
|
const ifAttribute: IfAttribute = {
|
|
@@ -48,7 +49,6 @@ function ifAttr(condition: boolean): AttributeElem {
|
|
|
48
49
|
return {
|
|
49
50
|
kind: "attribute",
|
|
50
51
|
attribute: ifAttribute,
|
|
51
|
-
contents: [],
|
|
52
52
|
start: 0,
|
|
53
53
|
end: 0,
|
|
54
54
|
};
|
|
@@ -62,7 +62,6 @@ function elseAttr(): AttributeElem {
|
|
|
62
62
|
return {
|
|
63
63
|
kind: "attribute",
|
|
64
64
|
attribute: elseAttribute,
|
|
65
|
-
contents: [],
|
|
66
65
|
start: 0,
|
|
67
66
|
end: 0,
|
|
68
67
|
};
|
|
@@ -175,7 +174,6 @@ test("complex nested attributes", () => {
|
|
|
175
174
|
const standardAttr = (name: string): AttributeElem => ({
|
|
176
175
|
kind: "attribute",
|
|
177
176
|
attribute: { kind: "@attribute", name, params: [] },
|
|
178
|
-
contents: [],
|
|
179
177
|
start: 0,
|
|
180
178
|
end: 0,
|
|
181
179
|
});
|
package/src/test/Linker.test.ts
CHANGED
|
@@ -14,6 +14,12 @@ test("global diagnostic directive", async () => {
|
|
|
14
14
|
expectTrimmedMatch(result, src);
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
+
test("consecutive top-level directives stay on separate lines", async () => {
|
|
18
|
+
const src = `requires readonly_and_readwrite_storage_textures;\nenable f16;`;
|
|
19
|
+
const result = await linkTest(src);
|
|
20
|
+
expectTrimmedMatch(result, src);
|
|
21
|
+
});
|
|
22
|
+
|
|
17
23
|
test("@diagnostic attribute on statement", async () => {
|
|
18
24
|
const src = `fn foo() { @diagnostic(info, derivative_uniformity) if true { } }`;
|
|
19
25
|
const result = await linkTest(src);
|
|
@@ -54,7 +60,9 @@ test("link an alias", async () => {
|
|
|
54
60
|
const src = /* wgsl */ `
|
|
55
61
|
alias Num = f32;
|
|
56
62
|
|
|
57
|
-
fn main() {
|
|
63
|
+
fn main() {
|
|
64
|
+
Num(1.0);
|
|
65
|
+
}
|
|
58
66
|
`;
|
|
59
67
|
const result = await linkTest(src);
|
|
60
68
|
expectTrimmedMatch(result, src);
|
|
@@ -112,9 +120,9 @@ test("type inside fn with same name as fn", async () => {
|
|
|
112
120
|
// illegal but shouldn't hang
|
|
113
121
|
const src = `
|
|
114
122
|
fn foo() {
|
|
115
|
-
var a:foo;
|
|
123
|
+
var a: foo;
|
|
116
124
|
}
|
|
117
|
-
fn bar() {}
|
|
125
|
+
fn bar() { }
|
|
118
126
|
`;
|
|
119
127
|
const result = await linkTest(src);
|
|
120
128
|
expectTrimmedMatch(result, src);
|
|
@@ -183,8 +191,12 @@ test("cross-module @location attribute const", async () => {
|
|
|
183
191
|
`;
|
|
184
192
|
const result = await linkTest(main, file1);
|
|
185
193
|
const expected = `
|
|
186
|
-
fn main() {
|
|
187
|
-
|
|
194
|
+
fn main() {
|
|
195
|
+
let x = foo();
|
|
196
|
+
}
|
|
197
|
+
fn foo() -> @location(loc) vec4f {
|
|
198
|
+
return vec4f();
|
|
199
|
+
}
|
|
188
200
|
const loc = 0;
|
|
189
201
|
`;
|
|
190
202
|
expectTrimmedMatch(result, expected);
|
|
@@ -207,7 +219,9 @@ test("link with library bundle (tests CompositeResolver)", async () => {
|
|
|
207
219
|
];
|
|
208
220
|
const result = await linkTestOpts({ libs }, main);
|
|
209
221
|
const expected = `
|
|
210
|
-
fn main() {
|
|
222
|
+
fn main() {
|
|
223
|
+
helper();
|
|
224
|
+
}
|
|
211
225
|
fn helper() { }
|
|
212
226
|
`;
|
|
213
227
|
expectTrimmedMatch(result, expected);
|
|
@@ -221,7 +235,9 @@ test("inline ref in array size from another module", async () => {
|
|
|
221
235
|
const file1 = `const SIZE = 4;`;
|
|
222
236
|
const result = await linkTest(main, file1);
|
|
223
237
|
const expected = `
|
|
224
|
-
fn main() {
|
|
238
|
+
fn main() {
|
|
239
|
+
var a: array<f32, SIZE>;
|
|
240
|
+
}
|
|
225
241
|
const SIZE = 4;
|
|
226
242
|
`;
|
|
227
243
|
expectTrimmedMatch(result, expected);
|
|
@@ -15,8 +15,10 @@ fn main() { bar(); }
|
|
|
15
15
|
|
|
16
16
|
const linked = await linkTestOpts({ mangler: underscoreMangle }, main, file1);
|
|
17
17
|
const expected = `
|
|
18
|
-
fn main() {
|
|
19
|
-
|
|
18
|
+
fn main() {
|
|
19
|
+
package_file1_bar();
|
|
20
|
+
}
|
|
21
|
+
fn package_file1_bar() { }
|
|
20
22
|
`;
|
|
21
23
|
expectTrimmedMatch(linked, expected);
|
|
22
24
|
});
|
|
@@ -38,8 +40,10 @@ fn main() { bar(); }
|
|
|
38
40
|
});
|
|
39
41
|
|
|
40
42
|
const expected = `
|
|
41
|
-
fn main() {
|
|
42
|
-
|
|
43
|
+
fn main() {
|
|
44
|
+
package_container_file1_bar();
|
|
45
|
+
}
|
|
46
|
+
fn package_container_file1_bar() { }
|
|
43
47
|
`;
|
|
44
48
|
expectTrimmedMatch(linked.dest, expected);
|
|
45
49
|
});
|