wesl 0.6.48 → 0.7.0
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 +295 -214
- package/dist/index.js +2947 -1550
- package/package.json +6 -8
- package/src/AbstractElems.ts +81 -81
- package/src/Assertions.ts +5 -5
- package/src/BindIdents.ts +193 -319
- 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/ModulePathUtil.ts +59 -0
- package/src/ModuleResolver.ts +26 -62
- 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 +5 -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/LowerAndEmit.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { SrcMapBuilder } from "mini-parse";
|
|
2
1
|
import type {
|
|
3
2
|
AbstractElem,
|
|
4
3
|
AttributeElem,
|
|
@@ -14,11 +13,11 @@ import type {
|
|
|
14
13
|
TextElem,
|
|
15
14
|
} from "./AbstractElems.ts";
|
|
16
15
|
import { assertThatDebug, assertUnreachable } from "./Assertions.ts";
|
|
17
|
-
import { isGlobal } from "./BindIdents.ts";
|
|
18
16
|
import { failIdentElem } from "./ClickableError.ts";
|
|
19
17
|
import { filterValidElements } from "./Conditions.ts";
|
|
20
18
|
import { identToString } from "./debug/ScopeToString.ts";
|
|
21
19
|
import type { Conditions, DeclIdent, Ident } from "./Scope.ts";
|
|
20
|
+
import type { SrcMapBuilder } from "./SrcMap.ts";
|
|
22
21
|
|
|
23
22
|
export interface EmitParams {
|
|
24
23
|
srcBuilder: SrcMapBuilder;
|
|
@@ -30,14 +29,14 @@ export interface EmitParams {
|
|
|
30
29
|
skipConditionalFiltering?: boolean;
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
/**
|
|
32
|
+
/** Passed to the emitters. */
|
|
34
33
|
interface EmitContext {
|
|
35
34
|
srcBuilder: SrcMapBuilder;
|
|
36
35
|
conditions: Conditions;
|
|
37
36
|
extracting: boolean;
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
/**
|
|
39
|
+
/** Traverse the AST, starting from root elements, emitting WGSL for each. */
|
|
41
40
|
export function lowerAndEmit(params: EmitParams): void {
|
|
42
41
|
const { srcBuilder, rootElems, conditions } = params;
|
|
43
42
|
const { extracting = true, skipConditionalFiltering = false } = params;
|
|
@@ -51,13 +50,11 @@ export function lowerAndEmit(params: EmitParams): void {
|
|
|
51
50
|
});
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
function lowerAndEmitElem(e: AbstractElem, ctx: EmitContext): void {
|
|
55
54
|
switch (e.kind) {
|
|
56
|
-
// import statements are dropped from from emitted text
|
|
57
55
|
case "import":
|
|
58
|
-
return;
|
|
56
|
+
return; // import statements are dropped from emitted text
|
|
59
57
|
|
|
60
|
-
// terminal elements copy strings to the output
|
|
61
58
|
case "text":
|
|
62
59
|
emitText(e, ctx);
|
|
63
60
|
return;
|
|
@@ -68,7 +65,6 @@ export function lowerAndEmitElem(e: AbstractElem, ctx: EmitContext): void {
|
|
|
68
65
|
emitSynthetic(e, ctx);
|
|
69
66
|
return;
|
|
70
67
|
|
|
71
|
-
// identifiers are copied to the output, but with potentially mangled names
|
|
72
68
|
case "ref":
|
|
73
69
|
emitRefIdent(e, ctx);
|
|
74
70
|
return;
|
|
@@ -76,30 +72,49 @@ export function lowerAndEmitElem(e: AbstractElem, ctx: EmitContext): void {
|
|
|
76
72
|
emitDeclIdent(e, ctx);
|
|
77
73
|
return;
|
|
78
74
|
|
|
79
|
-
|
|
75
|
+
case "literal":
|
|
76
|
+
case "binary-expression":
|
|
77
|
+
case "unary-expression":
|
|
78
|
+
case "call-expression":
|
|
79
|
+
case "parenthesized-expression":
|
|
80
|
+
case "component-expression":
|
|
81
|
+
case "component-member-expression":
|
|
82
|
+
emitExpression(e, ctx);
|
|
83
|
+
return;
|
|
84
|
+
|
|
80
85
|
case "param":
|
|
81
|
-
case "var":
|
|
82
86
|
case "typeDecl":
|
|
83
|
-
case "let":
|
|
84
|
-
case "module":
|
|
85
87
|
case "member":
|
|
86
88
|
case "memberRef":
|
|
87
89
|
case "expression":
|
|
88
90
|
case "type":
|
|
89
|
-
case "statement":
|
|
90
|
-
case "stuff":
|
|
91
91
|
case "switch-clause":
|
|
92
92
|
emitContents(e, ctx);
|
|
93
93
|
return;
|
|
94
94
|
|
|
95
|
-
//
|
|
95
|
+
// "stuff" elements (compound statements) need trimming for proper formatting
|
|
96
|
+
// LATER get rid of "stuff" elements
|
|
97
|
+
case "stuff":
|
|
98
|
+
emitStuff(e, ctx);
|
|
99
|
+
return;
|
|
100
|
+
|
|
101
|
+
case "module":
|
|
102
|
+
emitModule(e, ctx);
|
|
103
|
+
return;
|
|
104
|
+
|
|
105
|
+
case "var":
|
|
106
|
+
case "let":
|
|
107
|
+
case "statement":
|
|
108
|
+
case "continuing":
|
|
109
|
+
emitStatement(e, ctx);
|
|
110
|
+
return;
|
|
111
|
+
|
|
96
112
|
case "override":
|
|
97
113
|
case "const":
|
|
98
114
|
case "assert":
|
|
99
115
|
case "alias":
|
|
100
116
|
case "gvar":
|
|
101
|
-
|
|
102
|
-
emitContents(e, ctx);
|
|
117
|
+
emitRootDecl(e, ctx);
|
|
103
118
|
return;
|
|
104
119
|
|
|
105
120
|
case "fn":
|
|
@@ -125,24 +140,70 @@ export function lowerAndEmitElem(e: AbstractElem, ctx: EmitContext): void {
|
|
|
125
140
|
}
|
|
126
141
|
}
|
|
127
142
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
function emitStuff(e: ContainerElem, ctx: EmitContext): void {
|
|
144
|
+
emitContentsWithTrimming(e, ctx);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function emitModule(e: ContainerElem, ctx: EmitContext): void {
|
|
148
|
+
// Skip whitespace-only text elements at module level
|
|
149
|
+
const validElements = filterValidElements(e.contents, ctx.conditions);
|
|
150
|
+
for (const child of validElements) {
|
|
151
|
+
if (child.kind === "text") {
|
|
152
|
+
const text = child.srcModule.src.slice(child.start, child.end);
|
|
153
|
+
if (text.trim() === "") continue;
|
|
154
|
+
}
|
|
155
|
+
lowerAndEmitElem(child, ctx);
|
|
133
156
|
}
|
|
134
157
|
}
|
|
135
158
|
|
|
136
|
-
|
|
159
|
+
function emitStatement(
|
|
160
|
+
e: Extract<
|
|
161
|
+
ContainerElem,
|
|
162
|
+
{ kind: "var" | "let" | "statement" | "continuing" }
|
|
163
|
+
>,
|
|
164
|
+
ctx: EmitContext,
|
|
165
|
+
): void {
|
|
166
|
+
const attrsInContents =
|
|
167
|
+
e.contents.length > 0 && e.contents[0].kind === "attribute";
|
|
168
|
+
if (!attrsInContents) {
|
|
169
|
+
emitAttributes(e.attributes, ctx);
|
|
170
|
+
}
|
|
171
|
+
emitContents(e, ctx);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function emitRootDecl(
|
|
175
|
+
e: Extract<
|
|
176
|
+
ContainerElem,
|
|
177
|
+
{ kind: "override" | "const" | "assert" | "alias" | "gvar" }
|
|
178
|
+
>,
|
|
179
|
+
ctx: EmitContext,
|
|
180
|
+
): void {
|
|
181
|
+
emitRootElemNl(ctx);
|
|
182
|
+
const attrsInContents =
|
|
183
|
+
e.contents.length > 0 && e.contents[0].kind === "attribute";
|
|
184
|
+
if (!attrsInContents) {
|
|
185
|
+
emitAttributes(e.attributes, ctx);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
emitContentsWithTrimming(e, ctx);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Emit newlines between root elements. */
|
|
192
|
+
function emitRootElemNl(ctx: EmitContext): void {
|
|
193
|
+
ctx.srcBuilder.addNl();
|
|
194
|
+
ctx.srcBuilder.addNl();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function emitText(e: TextElem, ctx: EmitContext): void {
|
|
137
198
|
ctx.srcBuilder.addCopy(e.start, e.end);
|
|
138
199
|
}
|
|
139
200
|
|
|
140
|
-
|
|
201
|
+
function emitName(e: NameElem, ctx: EmitContext): void {
|
|
141
202
|
ctx.srcBuilder.add(e.name, e.start, e.end);
|
|
142
203
|
}
|
|
143
204
|
|
|
144
|
-
/**
|
|
145
|
-
|
|
205
|
+
/** Emit function explicitly to control commas between conditional parameters. */
|
|
206
|
+
function emitFn(e: FnElem, ctx: EmitContext): void {
|
|
146
207
|
const { attributes, name, params, returnAttributes, returnType, body } = e;
|
|
147
208
|
const { conditions, srcBuilder: builder } = ctx;
|
|
148
209
|
|
|
@@ -154,6 +215,13 @@ export function emitFn(e: FnElem, ctx: EmitContext): void {
|
|
|
154
215
|
builder.appendNext("(");
|
|
155
216
|
const validParams = filterValidElements(params, conditions);
|
|
156
217
|
validParams.forEach((p, i) => {
|
|
218
|
+
// Emit attributes separately only if not already in contents
|
|
219
|
+
// LATER stop including attributes in contents when we emit from ast
|
|
220
|
+
const attrsInContents =
|
|
221
|
+
p.contents.length > 0 && p.contents[0].kind === "attribute";
|
|
222
|
+
if (!attrsInContents) {
|
|
223
|
+
emitAttributes(p.attributes, ctx);
|
|
224
|
+
}
|
|
157
225
|
emitContentsNoWs(p as ContainerElem, ctx);
|
|
158
226
|
if (i < validParams.length - 1) {
|
|
159
227
|
builder.appendNext(", ");
|
|
@@ -164,7 +232,7 @@ export function emitFn(e: FnElem, ctx: EmitContext): void {
|
|
|
164
232
|
if (returnType) {
|
|
165
233
|
builder.appendNext("-> ");
|
|
166
234
|
emitAttributes(returnAttributes, ctx);
|
|
167
|
-
|
|
235
|
+
emitContentsNoWs(returnType, ctx);
|
|
168
236
|
builder.appendNext(" ");
|
|
169
237
|
}
|
|
170
238
|
|
|
@@ -176,14 +244,16 @@ function emitAttributes(
|
|
|
176
244
|
ctx: EmitContext,
|
|
177
245
|
): void {
|
|
178
246
|
attributes?.forEach(a => {
|
|
179
|
-
emitAttribute(a, ctx);
|
|
180
|
-
|
|
247
|
+
const emitted = emitAttribute(a, ctx);
|
|
248
|
+
if (emitted) {
|
|
249
|
+
ctx.srcBuilder.add(" ", a.start, a.end);
|
|
250
|
+
}
|
|
181
251
|
});
|
|
182
252
|
}
|
|
183
253
|
|
|
184
|
-
/**
|
|
185
|
-
|
|
186
|
-
const { name, members, start
|
|
254
|
+
/** Emit structs explicitly to control commas between conditional members. */
|
|
255
|
+
function emitStruct(e: StructElem, ctx: EmitContext): void {
|
|
256
|
+
const { attributes, name, members, start } = e;
|
|
187
257
|
const { srcBuilder, conditions } = ctx;
|
|
188
258
|
|
|
189
259
|
const validMembers = filterValidElements(members, conditions);
|
|
@@ -194,24 +264,28 @@ export function emitStruct(e: StructElem, ctx: EmitContext): void {
|
|
|
194
264
|
return;
|
|
195
265
|
}
|
|
196
266
|
|
|
267
|
+
emitAttributes(attributes, ctx);
|
|
197
268
|
srcBuilder.add("struct ", start, name.start);
|
|
198
269
|
emitDeclIdent(name, ctx);
|
|
199
270
|
|
|
200
271
|
if (validLength === 1) {
|
|
201
|
-
srcBuilder.
|
|
202
|
-
|
|
203
|
-
srcBuilder.
|
|
272
|
+
srcBuilder.appendNext(" { ");
|
|
273
|
+
emitContentsWithTrimming(validMembers[0] as ContainerElem, ctx);
|
|
274
|
+
srcBuilder.appendNext(" }");
|
|
275
|
+
srcBuilder.addNl();
|
|
204
276
|
} else {
|
|
205
|
-
srcBuilder.
|
|
277
|
+
srcBuilder.appendNext(" {");
|
|
278
|
+
srcBuilder.addNl();
|
|
206
279
|
|
|
207
280
|
validMembers.forEach(m => {
|
|
208
|
-
srcBuilder.
|
|
281
|
+
srcBuilder.appendNext(" ");
|
|
209
282
|
emitContentsNoWs(m as ContainerElem, ctx);
|
|
210
|
-
srcBuilder.
|
|
283
|
+
srcBuilder.appendNext(",");
|
|
211
284
|
srcBuilder.addNl();
|
|
212
285
|
});
|
|
213
286
|
|
|
214
|
-
srcBuilder.
|
|
287
|
+
srcBuilder.appendNext("}");
|
|
288
|
+
srcBuilder.addNl();
|
|
215
289
|
}
|
|
216
290
|
}
|
|
217
291
|
|
|
@@ -222,21 +296,45 @@ function warnEmptyStruct(e: StructElem): void {
|
|
|
222
296
|
failIdentElem(name, message);
|
|
223
297
|
}
|
|
224
298
|
|
|
225
|
-
|
|
299
|
+
function emitSynthetic(e: SyntheticElem, ctx: EmitContext): void {
|
|
226
300
|
const { text } = e;
|
|
227
301
|
ctx.srcBuilder.addSynthetic(text, text, 0, text.length);
|
|
228
302
|
}
|
|
229
303
|
|
|
230
|
-
|
|
231
|
-
// elem.contents.forEach(e => console.log("orig", astToString(e)));
|
|
304
|
+
function emitContents(elem: ContainerElem, ctx: EmitContext): void {
|
|
232
305
|
const validElements = filterValidElements(elem.contents, ctx.conditions);
|
|
233
|
-
// validElements.forEach(e => console.log("valid", astToString(e)));
|
|
234
306
|
validElements.forEach(e => {
|
|
235
307
|
lowerAndEmitElem(e, ctx);
|
|
236
308
|
});
|
|
237
309
|
}
|
|
238
310
|
|
|
239
|
-
/**
|
|
311
|
+
/** Emit contents with leading/trailing whitespace trimming (V2 parser). */
|
|
312
|
+
function emitContentsWithTrimming(elem: ContainerElem, ctx: EmitContext): void {
|
|
313
|
+
const validElements = filterValidElements(elem.contents, ctx.conditions);
|
|
314
|
+
|
|
315
|
+
// Find first/last non-conditional-attribute indices for trimming
|
|
316
|
+
const firstEmit = validElements.findIndex(e => !isConditionalAttr(e));
|
|
317
|
+
const lastEmit = validElements.findLastIndex(e => !isConditionalAttr(e));
|
|
318
|
+
|
|
319
|
+
validElements.forEach((elem, i) => {
|
|
320
|
+
if (elem.kind === "text") {
|
|
321
|
+
let text = elem.srcModule.src.slice(elem.start, elem.end);
|
|
322
|
+
if (i === firstEmit) text = text.trimStart();
|
|
323
|
+
if (i === lastEmit) text = text.trimEnd();
|
|
324
|
+
if (text) ctx.srcBuilder.add(text, elem.start, elem.end);
|
|
325
|
+
} else {
|
|
326
|
+
lowerAndEmitElem(elem, ctx);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function isConditionalAttr(e: AbstractElem): boolean {
|
|
332
|
+
if (e.kind !== "attribute") return false;
|
|
333
|
+
const { kind } = e.attribute;
|
|
334
|
+
return kind === "@if" || kind === "@elif" || kind === "@else";
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Emit contents without whitespace. */
|
|
240
338
|
function emitContentsNoWs(elem: ContainerElem, ctx: EmitContext): void {
|
|
241
339
|
const validElements = filterValidElements(elem.contents, ctx.conditions);
|
|
242
340
|
validElements.forEach(e => {
|
|
@@ -251,7 +349,7 @@ function emitContentsNoWs(elem: ContainerElem, ctx: EmitContext): void {
|
|
|
251
349
|
});
|
|
252
350
|
}
|
|
253
351
|
|
|
254
|
-
|
|
352
|
+
function emitRefIdent(e: RefIdentElem, ctx: EmitContext): void {
|
|
255
353
|
if (e.ident.std) {
|
|
256
354
|
ctx.srcBuilder.add(e.ident.originalName, e.start, e.end);
|
|
257
355
|
} else {
|
|
@@ -261,61 +359,138 @@ export function emitRefIdent(e: RefIdentElem, ctx: EmitContext): void {
|
|
|
261
359
|
}
|
|
262
360
|
}
|
|
263
361
|
|
|
264
|
-
|
|
362
|
+
function emitDeclIdent(e: DeclIdentElem, ctx: EmitContext): void {
|
|
265
363
|
const mangledName = displayName(e.ident);
|
|
266
364
|
ctx.srcBuilder.add(mangledName!, e.start, e.end);
|
|
267
365
|
}
|
|
268
366
|
|
|
269
|
-
function
|
|
270
|
-
const { kind } = e
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
ctx.srcBuilder.add(")", params[params.length - 1].end, e.end);
|
|
290
|
-
}
|
|
291
|
-
} else if (kind === "@builtin") {
|
|
367
|
+
function emitExpression(e: ExpressionElem, ctx: EmitContext): void {
|
|
368
|
+
const { kind } = e;
|
|
369
|
+
|
|
370
|
+
if (kind === "literal") {
|
|
371
|
+
ctx.srcBuilder.add(e.value, e.start, e.end);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (kind === "ref") {
|
|
376
|
+
emitRefIdent(e, ctx);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (kind === "type") {
|
|
381
|
+
emitContents(e, ctx);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (kind === "binary-expression") {
|
|
386
|
+
emitExpression(e.left, ctx);
|
|
292
387
|
ctx.srcBuilder.add(
|
|
293
|
-
|
|
294
|
-
e.
|
|
295
|
-
e.
|
|
388
|
+
` ${e.operator.value} `,
|
|
389
|
+
e.operator.span[0],
|
|
390
|
+
e.operator.span[1],
|
|
296
391
|
);
|
|
297
|
-
|
|
392
|
+
emitExpression(e.right, ctx);
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (kind === "unary-expression") {
|
|
298
397
|
ctx.srcBuilder.add(
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
e.
|
|
302
|
-
e.end,
|
|
398
|
+
e.operator.value,
|
|
399
|
+
e.operator.span[0],
|
|
400
|
+
e.operator.span[1],
|
|
303
401
|
);
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
402
|
+
emitExpression(e.expression, ctx);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (kind === "parenthesized-expression") {
|
|
407
|
+
emitExpression(e.expression, ctx);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (kind === "call-expression") {
|
|
412
|
+
emitExpression(e.function, ctx);
|
|
413
|
+
if (e.templateArgs) {
|
|
414
|
+
for (const targ of e.templateArgs) lowerAndEmitElem(targ, ctx);
|
|
415
|
+
}
|
|
416
|
+
for (const arg of e.arguments) {
|
|
417
|
+
emitExpression(arg, ctx);
|
|
418
|
+
}
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (kind === "component-expression") {
|
|
423
|
+
emitExpression(e.base, ctx);
|
|
424
|
+
emitExpression(e.access, ctx);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (kind === "component-member-expression") {
|
|
429
|
+
emitExpression(e.base, ctx);
|
|
430
|
+
if (e.access.kind === "name") {
|
|
431
|
+
ctx.srcBuilder.add(e.access.name, e.access.start, e.access.end);
|
|
432
|
+
}
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
assertUnreachable(kind);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function emitAttribute(e: AttributeElem, ctx: EmitContext): boolean {
|
|
440
|
+
const { kind } = e.attribute;
|
|
441
|
+
|
|
442
|
+
if (kind === "@if" || kind === "@elif" || kind === "@else") {
|
|
443
|
+
return false; // WESL-only, dropped from WGSL
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (kind === "@attribute") {
|
|
447
|
+
emitStandardAttribute(e, ctx);
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (kind === "@builtin") {
|
|
307
452
|
ctx.srcBuilder.add(
|
|
308
|
-
|
|
453
|
+
"@builtin(" + e.attribute.param.name + ")",
|
|
309
454
|
e.start,
|
|
310
455
|
e.end,
|
|
311
456
|
);
|
|
312
|
-
|
|
313
|
-
// @elif is wesl only, dropped from wgsl
|
|
314
|
-
} else if (kind === "@else") {
|
|
315
|
-
// @else is wesl only, dropped from wgsl
|
|
316
|
-
} else {
|
|
317
|
-
assertUnreachable(kind);
|
|
457
|
+
return true;
|
|
318
458
|
}
|
|
459
|
+
|
|
460
|
+
if (kind === "@diagnostic") {
|
|
461
|
+
const diagStr =
|
|
462
|
+
"@diagnostic" +
|
|
463
|
+
diagnosticControlToString(e.attribute.severity, e.attribute.rule);
|
|
464
|
+
ctx.srcBuilder.add(diagStr, e.start, e.end);
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (kind === "@interpolate") {
|
|
469
|
+
const params = e.attribute.params.map(v => v.name).join(", ");
|
|
470
|
+
ctx.srcBuilder.add(`@interpolate(${params})`, e.start, e.end);
|
|
471
|
+
return true;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
assertUnreachable(kind);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function emitStandardAttribute(e: AttributeElem, ctx: EmitContext): void {
|
|
478
|
+
if (e.attribute.kind !== "@attribute") return;
|
|
479
|
+
|
|
480
|
+
const { params } = e.attribute;
|
|
481
|
+
if (!params || params.length === 0) {
|
|
482
|
+
ctx.srcBuilder.add("@" + e.attribute.name, e.start, e.end);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
ctx.srcBuilder.add("@" + e.attribute.name + "(", e.start, params[0].start);
|
|
487
|
+
for (let i = 0; i < params.length; i++) {
|
|
488
|
+
ctx.srcBuilder.addCopy(params[i].start, params[i].end);
|
|
489
|
+
if (i < params.length - 1) {
|
|
490
|
+
ctx.srcBuilder.add(",", params[i].end, params[i + 1].start);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
ctx.srcBuilder.add(")", params[params.length - 1].end, e.end);
|
|
319
494
|
}
|
|
320
495
|
|
|
321
496
|
export function diagnosticControlToString(
|
|
@@ -336,8 +511,6 @@ export function expressionToString(elem: ExpressionElem): string {
|
|
|
336
511
|
return elem.ident.originalName;
|
|
337
512
|
} else if (kind === "literal") {
|
|
338
513
|
return elem.value;
|
|
339
|
-
} else if (kind === "translate-time-feature") {
|
|
340
|
-
return elem.name;
|
|
341
514
|
} else if (kind === "parenthesized-expression") {
|
|
342
515
|
return `(${expressionToString(elem.expression)})`;
|
|
343
516
|
} else if (kind === "component-expression") {
|
|
@@ -345,7 +518,13 @@ export function expressionToString(elem: ExpressionElem): string {
|
|
|
345
518
|
} else if (kind === "component-member-expression") {
|
|
346
519
|
return `${expressionToString(elem.base)}.${elem.access}`;
|
|
347
520
|
} else if (kind === "call-expression") {
|
|
348
|
-
|
|
521
|
+
const fn = elem.function;
|
|
522
|
+
const name =
|
|
523
|
+
fn.kind === "ref" ? fn.ident.originalName : fn.name.originalName;
|
|
524
|
+
const targs = elem.templateArgs ? `<...>` : "";
|
|
525
|
+
return `${name}${targs}(${elem.arguments.map(expressionToString).join(", ")})`;
|
|
526
|
+
} else if (kind === "type") {
|
|
527
|
+
return elem.name.originalName;
|
|
349
528
|
} else {
|
|
350
529
|
assertUnreachable(kind);
|
|
351
530
|
}
|
|
@@ -355,30 +534,21 @@ function emitDirective(e: DirectiveElem, ctx: EmitContext): void {
|
|
|
355
534
|
const { directive } = e;
|
|
356
535
|
const { kind } = directive;
|
|
357
536
|
if (kind === "diagnostic") {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
e.start,
|
|
361
|
-
e.end,
|
|
362
|
-
);
|
|
537
|
+
const diagStr = `diagnostic${diagnosticControlToString(directive.severity, directive.rule)};`;
|
|
538
|
+
ctx.srcBuilder.add(diagStr, e.start, e.end);
|
|
363
539
|
} else if (kind === "enable") {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
e.start,
|
|
367
|
-
e.end,
|
|
368
|
-
);
|
|
540
|
+
const exts = directive.extensions.map(v => v.name).join(", ");
|
|
541
|
+
ctx.srcBuilder.add(`enable ${exts};`, e.start, e.end);
|
|
369
542
|
} else if (kind === "requires") {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
e.start,
|
|
373
|
-
e.end,
|
|
374
|
-
);
|
|
543
|
+
const exts = directive.extensions.map(v => v.name).join(", ");
|
|
544
|
+
ctx.srcBuilder.add(`requires ${exts};`, e.start, e.end);
|
|
375
545
|
} else {
|
|
376
546
|
assertUnreachable(kind);
|
|
377
547
|
}
|
|
378
548
|
}
|
|
379
549
|
|
|
380
550
|
function displayName(declIdent: DeclIdent): string {
|
|
381
|
-
if (isGlobal
|
|
551
|
+
if (declIdent.isGlobal) {
|
|
382
552
|
assertThatDebug(
|
|
383
553
|
declIdent.mangledName,
|
|
384
554
|
`ERR: mangled name not found for decl ident ${identToString(declIdent)}`,
|
|
@@ -390,9 +560,7 @@ function displayName(declIdent: DeclIdent): string {
|
|
|
390
560
|
return declIdent.mangledName || declIdent.originalName;
|
|
391
561
|
}
|
|
392
562
|
|
|
393
|
-
/**
|
|
394
|
-
* expects that bindIdents has filled in all refersTo: links
|
|
395
|
-
*/
|
|
563
|
+
/** Trace through refersTo links until we find the declaration. */
|
|
396
564
|
export function findDecl(ident: Ident): DeclIdent {
|
|
397
565
|
let i: Ident | undefined = ident;
|
|
398
566
|
do {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** WESL module path utilities for converting between :: and / separators. */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convert module path segments to relative file path.
|
|
5
|
+
* Handles package/packageName prefixes and super:: resolution.
|
|
6
|
+
*
|
|
7
|
+
* @param parts - module path as array e.g., ["package", "utils", "helper"]
|
|
8
|
+
* @param packageName - the current package's name (required)
|
|
9
|
+
* @param srcModuleParts - source module path for super:: resolution (optional)
|
|
10
|
+
* @returns relative file path e.g., "utils/helper", or undefined if external
|
|
11
|
+
*/
|
|
12
|
+
export function modulePartsToRelativePath(
|
|
13
|
+
parts: string[],
|
|
14
|
+
packageName: string,
|
|
15
|
+
srcModuleParts?: string[],
|
|
16
|
+
): string | undefined {
|
|
17
|
+
const resolved = srcModuleParts ? resolveSuper(parts, srcModuleParts) : parts;
|
|
18
|
+
|
|
19
|
+
const rootSegment = resolved[0];
|
|
20
|
+
if (rootSegment === "package" || rootSegment === packageName) {
|
|
21
|
+
return resolved.slice(1).join("/");
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** String variant of modulePartsToRelativePath. */
|
|
27
|
+
export function moduleToRelativePath(
|
|
28
|
+
modulePath: string,
|
|
29
|
+
packageName: string,
|
|
30
|
+
srcModulePath?: string,
|
|
31
|
+
): string | undefined {
|
|
32
|
+
const srcParts = srcModulePath?.split("::");
|
|
33
|
+
const parts = modulePath.split("::");
|
|
34
|
+
return modulePartsToRelativePath(parts, packageName, srcParts);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Resolve super:: elements to absolute module path.
|
|
39
|
+
*
|
|
40
|
+
* @param parts - module path with potential super:: elements
|
|
41
|
+
* @param srcModuleParts - source module path for context
|
|
42
|
+
* @returns absolute module path parts (no super:: elements)
|
|
43
|
+
*/
|
|
44
|
+
export function resolveSuper(
|
|
45
|
+
parts: string[],
|
|
46
|
+
srcModuleParts: string[],
|
|
47
|
+
): string[] {
|
|
48
|
+
const lastSuper = parts.lastIndexOf("super");
|
|
49
|
+
if (lastSuper === -1) return parts;
|
|
50
|
+
const base = srcModuleParts.slice(0, -(lastSuper + 1));
|
|
51
|
+
return [...base, ...parts.slice(lastSuper + 1)];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Normalize debug root to end with / or be empty. */
|
|
55
|
+
export function normalizeDebugRoot(root?: string): string {
|
|
56
|
+
if (root === undefined) return "./";
|
|
57
|
+
if (root === "") return "";
|
|
58
|
+
return root.endsWith("/") ? root : root + "/";
|
|
59
|
+
}
|