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
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { linkTest } from "./TestUtil.ts";
|
|
3
|
+
import { expectTrimmedMatch } from "./TrimmedMatch.ts";
|
|
4
|
+
|
|
5
|
+
// Statements are emitted structurally from typed AST fields (not by copying
|
|
6
|
+
// source text), so these tests lock the canonical layout and confirm that
|
|
7
|
+
// comments attached to statements survive linking.
|
|
8
|
+
|
|
9
|
+
test("linked WGSL keeps leading and trailing body comments", async () => {
|
|
10
|
+
const src = `
|
|
11
|
+
fn main() {
|
|
12
|
+
// leading comment
|
|
13
|
+
let x = 1; // trailing comment
|
|
14
|
+
let y = x;
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
const result = await linkTest(src);
|
|
18
|
+
expect(result).toContain("// leading comment");
|
|
19
|
+
expect(result).toContain("// trailing comment");
|
|
20
|
+
// leading comment on its own line above the statement, trailing kept inline
|
|
21
|
+
expectTrimmedMatch(result, src);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("linked WGSL keeps a comment in an otherwise empty block", async () => {
|
|
25
|
+
const src = `
|
|
26
|
+
fn foo() {
|
|
27
|
+
// fooImpl
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
const result = await linkTest(src);
|
|
31
|
+
expect(result).toContain("// fooImpl");
|
|
32
|
+
expectTrimmedMatch(result, src);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("keeps attributes on compound statement bodies", async () => {
|
|
36
|
+
const src = `
|
|
37
|
+
fn main() {
|
|
38
|
+
for (var i = 0; i < 4; i++) @diagnostic(off, derivative_uniformity) {
|
|
39
|
+
let x = i;
|
|
40
|
+
}
|
|
41
|
+
switch 0 @diagnostic(off, derivative_uniformity) {
|
|
42
|
+
default: {
|
|
43
|
+
let y = 1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
const result = await linkTest(src);
|
|
49
|
+
expectTrimmedMatch(result, src);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("structural emit of control flow", async () => {
|
|
53
|
+
const src = `
|
|
54
|
+
fn main() {
|
|
55
|
+
var i = 0;
|
|
56
|
+
if i < 10 {
|
|
57
|
+
i = i + 1;
|
|
58
|
+
} else if i < 20 {
|
|
59
|
+
i += 2;
|
|
60
|
+
} else {
|
|
61
|
+
i = 0;
|
|
62
|
+
}
|
|
63
|
+
for (var j = 0; j < 4; j++) {
|
|
64
|
+
i = i + j;
|
|
65
|
+
}
|
|
66
|
+
while i > 0 {
|
|
67
|
+
i = i - 1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
const result = await linkTest(src);
|
|
72
|
+
expectTrimmedMatch(result, src);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("structural emit of loop, continuing, and switch", async () => {
|
|
76
|
+
const src = `
|
|
77
|
+
fn main() {
|
|
78
|
+
var i = 0;
|
|
79
|
+
loop {
|
|
80
|
+
i = i + 1;
|
|
81
|
+
continuing {
|
|
82
|
+
break if i > 100;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
switch i {
|
|
86
|
+
case 0, 1: {
|
|
87
|
+
i = 5;
|
|
88
|
+
}
|
|
89
|
+
default: {
|
|
90
|
+
i = 9;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
const result = await linkTest(src);
|
|
96
|
+
expectTrimmedMatch(result, src);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("linked WGSL keeps an inline comment before a call argument", async () => {
|
|
100
|
+
const src = `
|
|
101
|
+
fn main() {
|
|
102
|
+
let y = max(1, /* big */ 2);
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
const result = await linkTest(src);
|
|
106
|
+
expect(result).toContain("/* big */");
|
|
107
|
+
expectTrimmedMatch(result, src);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("linked WGSL keeps an inline comment after a call argument", async () => {
|
|
111
|
+
const src = `
|
|
112
|
+
fn main() {
|
|
113
|
+
let y = max(1 /* big */, 2);
|
|
114
|
+
}
|
|
115
|
+
`;
|
|
116
|
+
const result = await linkTest(src);
|
|
117
|
+
expectTrimmedMatch(result, src);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("linked WGSL keeps an inline comment between binary operands", async () => {
|
|
121
|
+
const src = `
|
|
122
|
+
fn main() {
|
|
123
|
+
let y = 1 + /* mid */ 2;
|
|
124
|
+
}
|
|
125
|
+
`;
|
|
126
|
+
const result = await linkTest(src);
|
|
127
|
+
expectTrimmedMatch(result, src);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("a line comment inside an expression stays on its own line", async () => {
|
|
131
|
+
const src = `
|
|
132
|
+
fn main() {
|
|
133
|
+
let y = max(1, // big
|
|
134
|
+
2);
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
const result = await linkTest(src);
|
|
138
|
+
expect(result).toContain("// big");
|
|
139
|
+
// the line comment must be followed by a newline, or it would swallow the
|
|
140
|
+
// rest of the expression (`2);`) into the comment
|
|
141
|
+
expect(result).not.toContain("// big 2");
|
|
142
|
+
expect(result).toContain("2)");
|
|
143
|
+
});
|
package/src/test/StripWesl.ts
CHANGED
|
@@ -6,29 +6,50 @@ import { WeslStream } from "../parse/WeslStream.ts";
|
|
|
6
6
|
* . extra whitespace,
|
|
7
7
|
* . comments,
|
|
8
8
|
* . trailing commas in brackets, paren, and array containers
|
|
9
|
+
* . redundant module-scope `;` (an empty global declaration, which structural
|
|
10
|
+
* emit canonicalizes away)
|
|
9
11
|
*/
|
|
10
12
|
export function stripWesl(text: string): string {
|
|
11
13
|
const stream = new WeslStream(text);
|
|
12
14
|
const firstToken = stream.nextToken();
|
|
13
15
|
if (firstToken === null) return "";
|
|
14
16
|
|
|
17
|
+
let depth = firstToken.text === "{" ? 1 : 0;
|
|
18
|
+
let prev = firstToken.text;
|
|
15
19
|
let result = firstToken.text;
|
|
16
20
|
while (true) {
|
|
17
21
|
const token = stream.nextToken();
|
|
18
22
|
if (token === null) return result;
|
|
19
23
|
|
|
24
|
+
// A `;` at module scope following a `}` (or another such `;`) is an empty
|
|
25
|
+
// global declaration; the structural emitter drops it, so drop it here too.
|
|
26
|
+
if (token.text === ";" && depth === 0 && (prev === "}" || prev === ";")) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
|
|
20
30
|
if (token.text === ",") {
|
|
21
31
|
const nextToken = stream.nextToken();
|
|
22
32
|
const nextText = nextToken?.text;
|
|
23
33
|
if (nextText === "}" || nextText === "]" || nextText === ")") {
|
|
24
34
|
// Ignore trailing comma
|
|
25
|
-
result += " ";
|
|
26
|
-
result += nextText;
|
|
35
|
+
result += " " + nextText;
|
|
27
36
|
} else {
|
|
28
|
-
result += ", " + (
|
|
37
|
+
result += ", " + (nextText ?? "");
|
|
38
|
+
}
|
|
39
|
+
if (nextText !== undefined) {
|
|
40
|
+
depth += braceDelta(nextText);
|
|
41
|
+
prev = nextText;
|
|
29
42
|
}
|
|
30
43
|
} else {
|
|
31
44
|
result += " " + token.text;
|
|
45
|
+
depth += braceDelta(token.text);
|
|
46
|
+
prev = token.text;
|
|
32
47
|
}
|
|
33
48
|
}
|
|
34
49
|
}
|
|
50
|
+
|
|
51
|
+
function braceDelta(text: string): number {
|
|
52
|
+
if (text === "{") return 1;
|
|
53
|
+
if (text === "}") return -1;
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
package/src/test/TestLink.ts
CHANGED
|
@@ -3,9 +3,15 @@ import type { WgslTestSrc } from "wesl-testsuite";
|
|
|
3
3
|
import { link } from "../Linker.ts";
|
|
4
4
|
import { type ManglerFn, underscoreMangle } from "../Mangler.ts";
|
|
5
5
|
import { mapValues } from "../Util.ts";
|
|
6
|
-
import {
|
|
6
|
+
import { expectTokenMatch } from "./TestUtil.ts";
|
|
7
|
+
import { trimSrc } from "./TrimmedMatch.ts";
|
|
7
8
|
|
|
8
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Link wesl sources and compare the linked wgsl against expectations.
|
|
11
|
+
* These cases verify linking (imports, mangling, conditional compilation,
|
|
12
|
+
* tree-shaking), not layout, so the comparison is token-based: whitespace and
|
|
13
|
+
* comments are ignored, leaving line layout to the emit-specific tests.
|
|
14
|
+
*/
|
|
9
15
|
export async function testLink(
|
|
10
16
|
weslSrc: Record<string, string>,
|
|
11
17
|
rootModuleName: string,
|
|
@@ -13,7 +19,7 @@ export async function testLink(
|
|
|
13
19
|
mangler?: ManglerFn,
|
|
14
20
|
): Promise<void> {
|
|
15
21
|
const resultMap = await link({ weslSrc, rootModuleName, mangler });
|
|
16
|
-
|
|
22
|
+
expectTokenMatch(resultMap.dest, expectedWgsl);
|
|
17
23
|
}
|
|
18
24
|
|
|
19
25
|
type CaseMap = Map<string, WgslTestSrc>;
|
|
@@ -43,6 +49,16 @@ const knownFormattingDifferences: Record<string, string> = {
|
|
|
43
49
|
"@if on break statement": `
|
|
44
50
|
fn foo() { while true { break; } }
|
|
45
51
|
fn bar() { while true { } }`,
|
|
52
|
+
"@if on switch statement": `
|
|
53
|
+
fn func() {
|
|
54
|
+
switch 0 { default: { let foo = 10; } }
|
|
55
|
+
}`,
|
|
56
|
+
"@if on switch clause": `
|
|
57
|
+
fn func() {
|
|
58
|
+
switch 0 {
|
|
59
|
+
default: { let foo = 10; }
|
|
60
|
+
}
|
|
61
|
+
}`,
|
|
46
62
|
};
|
|
47
63
|
|
|
48
64
|
/** Test linking a single case from a shared test suite (ImportCases, etc.) */
|
package/src/test/TestUtil.ts
CHANGED
|
@@ -2,13 +2,23 @@ import { expect } from "vitest";
|
|
|
2
2
|
import { type BoundAndTransformed, RecordResolver, type SrcModule } from "wesl";
|
|
3
3
|
import { bindAndTransform, type LinkParams, link } from "../Linker.ts";
|
|
4
4
|
import { withLoggerAsync } from "../Logging.ts";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type ParseOptions,
|
|
7
|
+
parseSrcModule,
|
|
8
|
+
type WeslAST,
|
|
9
|
+
} from "../ParseWESL.ts";
|
|
6
10
|
import { expectNoLog, logCatch } from "./LogCatcher.ts";
|
|
7
11
|
import { stripWesl } from "./StripWesl.ts";
|
|
8
12
|
|
|
9
13
|
export type LinkTestOpts = Pick<
|
|
10
14
|
LinkParams,
|
|
11
|
-
|
|
15
|
+
| "conditions"
|
|
16
|
+
| "libs"
|
|
17
|
+
| "config"
|
|
18
|
+
| "virtualLibs"
|
|
19
|
+
| "constants"
|
|
20
|
+
| "mangler"
|
|
21
|
+
| "weslExtensions"
|
|
12
22
|
>;
|
|
13
23
|
|
|
14
24
|
interface BindTestResult {
|
|
@@ -22,13 +32,13 @@ export function expectTokenMatch(actual: string, expected: string): void {
|
|
|
22
32
|
}
|
|
23
33
|
|
|
24
34
|
/** Parse a single wesl file. */
|
|
25
|
-
export function parseWESL(src: string): WeslAST {
|
|
35
|
+
export function parseWESL(src: string, options?: ParseOptions): WeslAST {
|
|
26
36
|
const srcModule: SrcModule = {
|
|
27
37
|
modulePath: "package::test",
|
|
28
38
|
debugFilePath: "./test.wesl",
|
|
29
39
|
src,
|
|
30
40
|
};
|
|
31
|
-
return parseSrcModule(srcModule);
|
|
41
|
+
return parseSrcModule(srcModule, options);
|
|
32
42
|
}
|
|
33
43
|
|
|
34
44
|
/** Link wesl for tests. First module is ./test.wesl, rest are ./file1.wesl, etc. */
|
|
@@ -74,13 +84,13 @@ async function linkWithLogInternal(
|
|
|
74
84
|
}
|
|
75
85
|
|
|
76
86
|
/** Parse wesl for testing, ensuring no logged warnings. */
|
|
77
|
-
export function parseTest(src: string): WeslAST {
|
|
78
|
-
return expectNoLog(() => parseWESL(src));
|
|
87
|
+
export function parseTest(src: string, options?: ParseOptions): WeslAST {
|
|
88
|
+
return expectNoLog(() => parseWESL(src, options));
|
|
79
89
|
}
|
|
80
90
|
|
|
81
91
|
/** Parse wesl without log collection (for debugging). */
|
|
82
|
-
export function parseTestRaw(src: string): WeslAST {
|
|
83
|
-
return parseWESL(src);
|
|
92
|
+
export function parseTestRaw(src: string, options?: ParseOptions): WeslAST {
|
|
93
|
+
return parseWESL(src, options);
|
|
84
94
|
}
|
|
85
95
|
|
|
86
96
|
/** Parse and bind wesl source for testing. Returns bound result and resolver. */
|
|
@@ -107,6 +107,24 @@ test("parse >> as template", () => {
|
|
|
107
107
|
expect(tokenizer.nextToken()).toBe(null);
|
|
108
108
|
});
|
|
109
109
|
|
|
110
|
+
test("template discovery ignores > inside comments", () => {
|
|
111
|
+
const src = "a < b /* > */ ;";
|
|
112
|
+
const tokenizer = new WeslStream(src);
|
|
113
|
+
expect(tokenizer.nextToken()?.text).toBe("a");
|
|
114
|
+
expect(tokenizer.nextTemplateStartToken()).toBe(null);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("template discovery sees comments inside template lists", () => {
|
|
118
|
+
const src = "array< /* len */ f32, 4 >";
|
|
119
|
+
const tokenizer = new WeslStream(src);
|
|
120
|
+
expect(tokenizer.nextToken()?.text).toBe("array");
|
|
121
|
+
expect(tokenizer.nextTemplateStartToken()).toEqual({
|
|
122
|
+
kind: "symbol",
|
|
123
|
+
text: "<",
|
|
124
|
+
span: [5, 6],
|
|
125
|
+
} as WeslToken);
|
|
126
|
+
});
|
|
127
|
+
|
|
110
128
|
test("parse skip block comment", () => {
|
|
111
129
|
const src = "/* /* // */ */vec3<f32>";
|
|
112
130
|
const tokenizer = new WeslStream(src);
|
|
@@ -133,3 +151,81 @@ test("parse skip line without newline", () => {
|
|
|
133
151
|
expect(tokenizer.nextToken()).toBe(null);
|
|
134
152
|
expect(tokenizer.checkpoint()).toBe(src.length);
|
|
135
153
|
});
|
|
154
|
+
|
|
155
|
+
test("unicode mid-word falls back with correct span", () => {
|
|
156
|
+
const src = "réflexion x";
|
|
157
|
+
const tokenizer = new WeslStream(src);
|
|
158
|
+
expect(tokenizer.nextToken()).toEqual({
|
|
159
|
+
kind: "word",
|
|
160
|
+
text: "réflexion",
|
|
161
|
+
span: [0, 9],
|
|
162
|
+
} as WeslToken);
|
|
163
|
+
expect(tokenizer.nextToken()?.span).toEqual([10, 11]);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("surrogate-pair ident spans count UTF-16 units", () => {
|
|
167
|
+
const src = "𐰓𐰏𐰇 x";
|
|
168
|
+
const tokenizer = new WeslStream(src);
|
|
169
|
+
expect(tokenizer.nextToken()).toEqual({
|
|
170
|
+
kind: "word",
|
|
171
|
+
text: "𐰓𐰏𐰇",
|
|
172
|
+
span: [0, 6],
|
|
173
|
+
} as WeslToken);
|
|
174
|
+
expect(tokenizer.nextToken()?.span).toEqual([7, 8]);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("underscore boundary: _é word, _x word, bare _ symbol", () => {
|
|
178
|
+
const under = new WeslStream("_é _x _ x");
|
|
179
|
+
expect(under.nextToken()).toEqual({
|
|
180
|
+
kind: "word",
|
|
181
|
+
text: "_é",
|
|
182
|
+
span: [0, 2],
|
|
183
|
+
} as WeslToken);
|
|
184
|
+
expect(under.nextToken()).toEqual({
|
|
185
|
+
kind: "word",
|
|
186
|
+
text: "_x",
|
|
187
|
+
span: [3, 5],
|
|
188
|
+
} as WeslToken);
|
|
189
|
+
expect(under.nextToken()).toEqual({
|
|
190
|
+
kind: "symbol",
|
|
191
|
+
text: "_",
|
|
192
|
+
span: [6, 7],
|
|
193
|
+
} as WeslToken);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("unicode blankspace separates tokens", () => {
|
|
197
|
+
const src = "a\u{2028}b\u{0085}c";
|
|
198
|
+
const tokenizer = new WeslStream(src);
|
|
199
|
+
expect(tokenizer.nextToken()?.span).toEqual([0, 1]);
|
|
200
|
+
expect(tokenizer.nextToken()?.span).toEqual([2, 3]);
|
|
201
|
+
expect(tokenizer.nextToken()?.span).toEqual([4, 5]);
|
|
202
|
+
expect(tokenizer.nextToken()).toBe(null);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("leading-dot float and 3-char symbols", () => {
|
|
206
|
+
const src = "x >>= .5 <<= 0x1p4";
|
|
207
|
+
const tokenizer = new WeslStream(src);
|
|
208
|
+
tokenizer.nextToken(); // x
|
|
209
|
+
expect(tokenizer.nextToken()).toEqual({
|
|
210
|
+
kind: "symbol",
|
|
211
|
+
text: ">>=",
|
|
212
|
+
span: [2, 5],
|
|
213
|
+
} as WeslToken);
|
|
214
|
+
expect(tokenizer.nextToken()).toEqual({
|
|
215
|
+
kind: "number",
|
|
216
|
+
text: ".5",
|
|
217
|
+
span: [6, 8],
|
|
218
|
+
} as WeslToken);
|
|
219
|
+
expect(tokenizer.nextToken()?.text).toBe("<<=");
|
|
220
|
+
expect(tokenizer.nextToken()).toEqual({
|
|
221
|
+
kind: "number",
|
|
222
|
+
text: "0x1p4",
|
|
223
|
+
span: [13, 18],
|
|
224
|
+
} as WeslToken);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("invalid character throws", () => {
|
|
228
|
+
const tokenizer = new WeslStream("a # b");
|
|
229
|
+
tokenizer.nextToken();
|
|
230
|
+
expect(() => tokenizer.nextToken()).toThrow(/Invalid token #/);
|
|
231
|
+
});
|
|
@@ -2,66 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`parse unicode ident 1`] = `
|
|
4
4
|
"module
|
|
5
|
-
text '
|
|
6
|
-
'
|
|
7
5
|
fn Δέλτα()
|
|
8
6
|
decl %Δέλτα
|
|
9
|
-
|
|
10
|
-
text '{}'
|
|
11
|
-
text '
|
|
12
|
-
'
|
|
7
|
+
block
|
|
13
8
|
fn réflexion()
|
|
14
9
|
decl %réflexion
|
|
15
|
-
|
|
16
|
-
text '{}'
|
|
17
|
-
text '
|
|
18
|
-
'
|
|
10
|
+
block
|
|
19
11
|
fn Кызыл()
|
|
20
12
|
decl %Кызыл
|
|
21
|
-
|
|
22
|
-
text '{}'
|
|
23
|
-
text '
|
|
24
|
-
'
|
|
13
|
+
block
|
|
25
14
|
fn 𐰓𐰏𐰇()
|
|
26
15
|
decl %𐰓𐰏𐰇
|
|
27
|
-
|
|
28
|
-
text '{}'
|
|
29
|
-
text '
|
|
30
|
-
'
|
|
16
|
+
block
|
|
31
17
|
fn 朝焼け()
|
|
32
18
|
decl %朝焼け
|
|
33
|
-
|
|
34
|
-
text '{}'
|
|
35
|
-
text '
|
|
36
|
-
'
|
|
19
|
+
block
|
|
37
20
|
fn سلام()
|
|
38
21
|
decl %سلام
|
|
39
|
-
|
|
40
|
-
text '{}'
|
|
41
|
-
text '
|
|
42
|
-
'
|
|
22
|
+
block
|
|
43
23
|
fn 검정()
|
|
44
24
|
decl %검정
|
|
45
|
-
|
|
46
|
-
text '{}'
|
|
47
|
-
text '
|
|
48
|
-
'
|
|
25
|
+
block
|
|
49
26
|
fn שָׁלוֹם()
|
|
50
27
|
decl %שָׁלוֹם
|
|
51
|
-
|
|
52
|
-
text '{}'
|
|
53
|
-
text '
|
|
54
|
-
'
|
|
28
|
+
block
|
|
55
29
|
fn गुलाबी()
|
|
56
30
|
decl %गुलाबी
|
|
57
|
-
|
|
58
|
-
text '{}'
|
|
59
|
-
text '
|
|
60
|
-
'
|
|
31
|
+
block
|
|
61
32
|
fn փիրուզ()
|
|
62
33
|
decl %փիրուզ
|
|
63
|
-
|
|
64
|
-
text '{}'
|
|
65
|
-
text '
|
|
66
|
-
'"
|
|
34
|
+
block"
|
|
67
35
|
`;
|
package/src/RawEmit.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AttributeElem,
|
|
3
|
-
NameElem,
|
|
4
|
-
StuffElem,
|
|
5
|
-
TranslateTimeExpressionElem,
|
|
6
|
-
TypeRefElem,
|
|
7
|
-
TypeTemplateParameter,
|
|
8
|
-
UnknownExpressionElem,
|
|
9
|
-
} from "./AbstractElems.ts";
|
|
10
|
-
import { assertUnreachable } from "./Assertions.ts";
|
|
11
|
-
import {
|
|
12
|
-
diagnosticControlToString,
|
|
13
|
-
expressionToString,
|
|
14
|
-
findDecl,
|
|
15
|
-
} from "./LowerAndEmit.ts";
|
|
16
|
-
import type { RefIdent } from "./Scope.ts";
|
|
17
|
-
|
|
18
|
-
// LATER DRY emitting elements like this with LowerAndEmit?
|
|
19
|
-
|
|
20
|
-
export function attributeToString(e: AttributeElem): string {
|
|
21
|
-
const { kind } = e.attribute;
|
|
22
|
-
// LATER emit more precise source map info by making use of all the spans
|
|
23
|
-
// Like the first case does
|
|
24
|
-
if (kind === "@attribute") {
|
|
25
|
-
const { params } = e.attribute;
|
|
26
|
-
if (params === undefined || params.length === 0) {
|
|
27
|
-
return "@" + e.attribute.name;
|
|
28
|
-
} else {
|
|
29
|
-
return `@${e.attribute.name}(${params
|
|
30
|
-
.map(param => contentsToString(param))
|
|
31
|
-
.join(", ")})`;
|
|
32
|
-
}
|
|
33
|
-
} else if (kind === "@builtin") {
|
|
34
|
-
return "@builtin(" + e.attribute.param.name + ")";
|
|
35
|
-
} else if (kind === "@diagnostic") {
|
|
36
|
-
return (
|
|
37
|
-
"@diagnostic" +
|
|
38
|
-
diagnosticControlToString(e.attribute.severity, e.attribute.rule)
|
|
39
|
-
);
|
|
40
|
-
} else if (kind === "@if") {
|
|
41
|
-
return `@if(${expressionToString(e.attribute.param.expression)})`;
|
|
42
|
-
} else if (kind === "@elif") {
|
|
43
|
-
return `@elif(${expressionToString(e.attribute.param.expression)})`;
|
|
44
|
-
} else if (kind === "@else") {
|
|
45
|
-
return "@else";
|
|
46
|
-
} else if (kind === "@interpolate") {
|
|
47
|
-
return `@interpolate(${e.attribute.params.map(v => v.name).join(", ")})`;
|
|
48
|
-
} else {
|
|
49
|
-
assertUnreachable(kind);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function typeListToString(params: TypeTemplateParameter[]): string {
|
|
54
|
-
return `<${params.map(typeParamToString).join(", ")}>`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function typeParamToString(param?: TypeTemplateParameter): string {
|
|
58
|
-
if (param === undefined) return "?";
|
|
59
|
-
if (param.kind === "type") return typeRefToString(param);
|
|
60
|
-
return expressionToString(param);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function typeRefToString(t?: TypeRefElem): string {
|
|
64
|
-
if (!t) return "?";
|
|
65
|
-
const { name, templateParams } = t;
|
|
66
|
-
const params = templateParams ? typeListToString(templateParams) : "";
|
|
67
|
-
return `${refToString(name)}${params}`;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function refToString(ref: RefIdent | string): string {
|
|
71
|
-
if (typeof ref === "string") return ref;
|
|
72
|
-
if (ref.std) return ref.originalName;
|
|
73
|
-
const decl = findDecl(ref);
|
|
74
|
-
return decl.mangledName || decl.originalName;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function contentsToString(
|
|
78
|
-
elem:
|
|
79
|
-
| TranslateTimeExpressionElem
|
|
80
|
-
| UnknownExpressionElem
|
|
81
|
-
| NameElem
|
|
82
|
-
| StuffElem,
|
|
83
|
-
): string {
|
|
84
|
-
if (elem.kind === "translate-time-expression") {
|
|
85
|
-
throw new Error("Not supported");
|
|
86
|
-
} else if (elem.kind === "expression" || elem.kind === "stuff") {
|
|
87
|
-
const parts = elem.contents.map(c => {
|
|
88
|
-
const { kind } = c;
|
|
89
|
-
if (kind === "text") {
|
|
90
|
-
return c.srcModule.src.slice(c.start, c.end);
|
|
91
|
-
} else if (kind === "ref") {
|
|
92
|
-
return refToString(c.ident);
|
|
93
|
-
} else {
|
|
94
|
-
return `?${c.kind}?`;
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
return parts.join(" ");
|
|
98
|
-
} else if (elem.kind === "name") {
|
|
99
|
-
return elem.name;
|
|
100
|
-
} else {
|
|
101
|
-
assertUnreachable(elem);
|
|
102
|
-
}
|
|
103
|
-
}
|