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,413 @@
|
|
|
1
|
+
import { srcLog, SrcMapBuilder } from "mini-parse";
|
|
2
|
+
import {
|
|
3
|
+
AbstractElem,
|
|
4
|
+
AttributeElem,
|
|
5
|
+
ContainerElem,
|
|
6
|
+
DeclIdentElem,
|
|
7
|
+
DirectiveElem,
|
|
8
|
+
ElemWithAttributes,
|
|
9
|
+
ExpressionElem,
|
|
10
|
+
FnElem,
|
|
11
|
+
NameElem,
|
|
12
|
+
RefIdentElem,
|
|
13
|
+
StructElem,
|
|
14
|
+
SyntheticElem,
|
|
15
|
+
TextElem,
|
|
16
|
+
} from "./AbstractElems.ts";
|
|
17
|
+
import {
|
|
18
|
+
assertThatDebug,
|
|
19
|
+
assertUnreachable,
|
|
20
|
+
assertUnreachableSilent,
|
|
21
|
+
} from "./Assertions.ts";
|
|
22
|
+
import { isGlobal } from "./BindIdents.ts";
|
|
23
|
+
import { elementValid } from "./Conditions.ts";
|
|
24
|
+
import { identToString } from "./debug/ScopeToString.ts";
|
|
25
|
+
import { Conditions, DeclIdent, Ident } from "./Scope.ts";
|
|
26
|
+
|
|
27
|
+
/** passed to the emitters */
|
|
28
|
+
interface EmitContext {
|
|
29
|
+
srcBuilder: SrcMapBuilder; // constructing the linked output
|
|
30
|
+
conditions: Conditions; // settings for conditional compilation
|
|
31
|
+
extracting: boolean; // are we extracting or copying the root module
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** traverse the AST, starting from root elements, emitting wgsl for each */
|
|
35
|
+
export function lowerAndEmit(
|
|
36
|
+
srcBuilder: SrcMapBuilder,
|
|
37
|
+
rootElems: AbstractElem[],
|
|
38
|
+
conditions: Conditions,
|
|
39
|
+
extracting = true,
|
|
40
|
+
): void {
|
|
41
|
+
const emitContext: EmitContext = { conditions, srcBuilder, extracting };
|
|
42
|
+
// rootElems.forEach(r => console.log(astToString(r) + "\n"));
|
|
43
|
+
rootElems.forEach(e => lowerAndEmitElem(e, emitContext));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function lowerAndEmitElem(e: AbstractElem, ctx: EmitContext): void {
|
|
47
|
+
if (!conditionsValid(e, ctx.conditions)) return;
|
|
48
|
+
|
|
49
|
+
switch (e.kind) {
|
|
50
|
+
// import statements are dropped from from emitted text
|
|
51
|
+
case "import":
|
|
52
|
+
return;
|
|
53
|
+
|
|
54
|
+
// terminal elements copy strings to the output
|
|
55
|
+
case "text":
|
|
56
|
+
return emitText(e, ctx);
|
|
57
|
+
case "name":
|
|
58
|
+
return emitName(e, ctx);
|
|
59
|
+
case "synthetic":
|
|
60
|
+
return emitSynthetic(e, ctx);
|
|
61
|
+
|
|
62
|
+
// identifiers are copied to the output, but with potentially mangled names
|
|
63
|
+
case "ref":
|
|
64
|
+
return emitRefIdent(e, ctx);
|
|
65
|
+
case "decl":
|
|
66
|
+
return emitDeclIdent(e, ctx);
|
|
67
|
+
|
|
68
|
+
// container elements just emit their child elements
|
|
69
|
+
case "param":
|
|
70
|
+
case "var":
|
|
71
|
+
case "typeDecl":
|
|
72
|
+
case "let":
|
|
73
|
+
case "module":
|
|
74
|
+
case "member":
|
|
75
|
+
case "memberRef":
|
|
76
|
+
case "expression":
|
|
77
|
+
case "type":
|
|
78
|
+
case "statement":
|
|
79
|
+
case "stuff":
|
|
80
|
+
case "switch-clause":
|
|
81
|
+
return emitContents(e, ctx);
|
|
82
|
+
|
|
83
|
+
// root level container elements get some extra newlines to make the output prettier
|
|
84
|
+
case "override":
|
|
85
|
+
case "const":
|
|
86
|
+
case "assert":
|
|
87
|
+
case "alias":
|
|
88
|
+
case "gvar":
|
|
89
|
+
emitRootElemNl(ctx);
|
|
90
|
+
return emitContents(e, ctx);
|
|
91
|
+
|
|
92
|
+
case "fn":
|
|
93
|
+
emitRootElemNl(ctx);
|
|
94
|
+
return emitFn(e, ctx);
|
|
95
|
+
|
|
96
|
+
case "struct":
|
|
97
|
+
emitRootElemNl(ctx);
|
|
98
|
+
return emitStruct(e, ctx);
|
|
99
|
+
|
|
100
|
+
case "attribute":
|
|
101
|
+
return emitAttribute(e, ctx);
|
|
102
|
+
case "directive":
|
|
103
|
+
return emitDirective(e, ctx);
|
|
104
|
+
|
|
105
|
+
default:
|
|
106
|
+
assertUnreachable(e);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** emit root elems with a blank line inbetween */
|
|
111
|
+
function emitRootElemNl(ctx: EmitContext): void {
|
|
112
|
+
if (ctx.extracting) {
|
|
113
|
+
ctx.srcBuilder.addNl();
|
|
114
|
+
ctx.srcBuilder.addNl();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function emitText(e: TextElem, ctx: EmitContext): void {
|
|
119
|
+
ctx.srcBuilder.addCopy(e.start, e.end);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function emitName(e: NameElem, ctx: EmitContext): void {
|
|
123
|
+
ctx.srcBuilder.add(e.name, e.start, e.end);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** emit function explicitly so we can control commas between conditional parameters */
|
|
127
|
+
export function emitFn(e: FnElem, ctx: EmitContext): void {
|
|
128
|
+
const { attributes, name, params, returnAttributes, returnType, body } = e;
|
|
129
|
+
const { conditions, srcBuilder: builder } = ctx;
|
|
130
|
+
|
|
131
|
+
emitAttributes(attributes, ctx);
|
|
132
|
+
|
|
133
|
+
builder.add("fn ", name.start - 3, name.start);
|
|
134
|
+
emitDeclIdent(name, ctx);
|
|
135
|
+
|
|
136
|
+
builder.appendNext("(");
|
|
137
|
+
const validParams = params.filter(p => conditionsValid(p, conditions));
|
|
138
|
+
validParams.forEach((p, i) => {
|
|
139
|
+
emitContentsNoWs(p, ctx);
|
|
140
|
+
if (i < validParams.length - 1) {
|
|
141
|
+
builder.appendNext(", ");
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
builder.appendNext(") ");
|
|
145
|
+
|
|
146
|
+
if (returnType) {
|
|
147
|
+
builder.appendNext("-> ");
|
|
148
|
+
emitAttributes(returnAttributes, ctx);
|
|
149
|
+
emitContents(returnType, ctx);
|
|
150
|
+
builder.appendNext(" ");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
emitContents(body, ctx);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function emitAttributes(
|
|
157
|
+
attributes: AttributeElem[] | undefined,
|
|
158
|
+
ctx: EmitContext,
|
|
159
|
+
): void {
|
|
160
|
+
attributes?.forEach(a => {
|
|
161
|
+
emitAttribute(a, ctx);
|
|
162
|
+
ctx.srcBuilder.add(" ", a.start, a.end);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** emit structs explicitly so we can control commas between conditional members */
|
|
167
|
+
export function emitStruct(e: StructElem, ctx: EmitContext): void {
|
|
168
|
+
const { name, members, start, end } = e;
|
|
169
|
+
const { srcBuilder } = ctx;
|
|
170
|
+
|
|
171
|
+
const validMembers = members.filter(m => conditionsValid(m, ctx.conditions));
|
|
172
|
+
const validLength = validMembers.length;
|
|
173
|
+
|
|
174
|
+
if (validLength === 0) {
|
|
175
|
+
warnEmptyStruct(e);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
srcBuilder.add("struct ", start, name.start);
|
|
180
|
+
emitDeclIdent(name, ctx);
|
|
181
|
+
|
|
182
|
+
if (validLength === 1) {
|
|
183
|
+
srcBuilder.add(" { ", name.end, members[0].start);
|
|
184
|
+
emitContentsNoWs(validMembers[0], ctx);
|
|
185
|
+
srcBuilder.add(" }\n", end - 1, end);
|
|
186
|
+
} else {
|
|
187
|
+
srcBuilder.add(" {\n", name.end, members[0].start);
|
|
188
|
+
|
|
189
|
+
validMembers.forEach(m => {
|
|
190
|
+
srcBuilder.add(" ", m.start - 1, m.start);
|
|
191
|
+
emitContentsNoWs(m, ctx);
|
|
192
|
+
srcBuilder.add(",", m.end, m.end + 1);
|
|
193
|
+
srcBuilder.addNl();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
srcBuilder.add("}\n", end - 1, end);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function warnEmptyStruct(e: StructElem): void {
|
|
201
|
+
const { name, members } = e;
|
|
202
|
+
const condStr = members.length ? "(with current conditions)" : "";
|
|
203
|
+
const { debugFilePath: filePath } = name.srcModule;
|
|
204
|
+
srcLog(
|
|
205
|
+
name.srcModule.src,
|
|
206
|
+
e.start,
|
|
207
|
+
`struct ${name.ident.originalName} in ${filePath} has no members ${condStr}`,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function emitSynthetic(e: SyntheticElem, ctx: EmitContext): void {
|
|
212
|
+
const { text } = e;
|
|
213
|
+
ctx.srcBuilder.addSynthetic(text, text, 0, text.length);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function emitContents(elem: ContainerElem, ctx: EmitContext): void {
|
|
217
|
+
elem.contents.forEach(e => lowerAndEmitElem(e, ctx));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** emit contents w/o white space */
|
|
221
|
+
function emitContentsNoWs(elem: ContainerElem, ctx: EmitContext): void {
|
|
222
|
+
elem.contents.forEach(e => {
|
|
223
|
+
if (e.kind === "text") {
|
|
224
|
+
const { srcModule, start, end } = e;
|
|
225
|
+
const text = srcModule.src.slice(start, end);
|
|
226
|
+
if (text.trim() === "") {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
lowerAndEmitElem(e, ctx);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function emitRefIdent(e: RefIdentElem, ctx: EmitContext): void {
|
|
235
|
+
if (e.ident.std) {
|
|
236
|
+
ctx.srcBuilder.add(e.ident.originalName, e.start, e.end);
|
|
237
|
+
} else {
|
|
238
|
+
const declIdent = findDecl(e.ident);
|
|
239
|
+
const mangledName = displayName(declIdent);
|
|
240
|
+
ctx.srcBuilder.add(mangledName!, e.start, e.end);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function emitDeclIdent(e: DeclIdentElem, ctx: EmitContext): void {
|
|
245
|
+
const mangledName = displayName(e.ident);
|
|
246
|
+
ctx.srcBuilder.add(mangledName!, e.start, e.end);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function emitAttribute(e: AttributeElem, ctx: EmitContext): void {
|
|
250
|
+
const { kind } = e.attribute;
|
|
251
|
+
// LATER emit more precise source map info by making use of all the spans
|
|
252
|
+
// Like the first case does
|
|
253
|
+
if (kind === "@attribute") {
|
|
254
|
+
const { params } = e.attribute;
|
|
255
|
+
if (!params || params.length === 0) {
|
|
256
|
+
ctx.srcBuilder.add("@" + e.attribute.name, e.start, e.end);
|
|
257
|
+
} else {
|
|
258
|
+
ctx.srcBuilder.add(
|
|
259
|
+
"@" + e.attribute.name + "(",
|
|
260
|
+
e.start,
|
|
261
|
+
params[0].start,
|
|
262
|
+
);
|
|
263
|
+
for (let i = 0; i < params.length; i++) {
|
|
264
|
+
emitContents(params[i], ctx);
|
|
265
|
+
if (i < params.length - 1) {
|
|
266
|
+
ctx.srcBuilder.add(",", params[i].end, params[i + 1].start);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
ctx.srcBuilder.add(")", params[params.length - 1].end, e.end);
|
|
270
|
+
}
|
|
271
|
+
} else if (kind === "@builtin") {
|
|
272
|
+
ctx.srcBuilder.add(
|
|
273
|
+
"@builtin(" + e.attribute.param.name + ")",
|
|
274
|
+
e.start,
|
|
275
|
+
e.end,
|
|
276
|
+
);
|
|
277
|
+
} else if (kind === "@diagnostic") {
|
|
278
|
+
ctx.srcBuilder.add(
|
|
279
|
+
"@diagnostic" +
|
|
280
|
+
diagnosticControlToString(e.attribute.severity, e.attribute.rule),
|
|
281
|
+
e.start,
|
|
282
|
+
e.end,
|
|
283
|
+
);
|
|
284
|
+
} else if (kind === "@if") {
|
|
285
|
+
// (@if is wesl only, dropped from wgsl)
|
|
286
|
+
} else if (kind === "@interpolate") {
|
|
287
|
+
ctx.srcBuilder.add(
|
|
288
|
+
`@interpolate(${e.attribute.params.map(v => v.name).join(", ")})`,
|
|
289
|
+
e.start,
|
|
290
|
+
e.end,
|
|
291
|
+
);
|
|
292
|
+
} else {
|
|
293
|
+
assertUnreachable(kind);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function diagnosticControlToString(
|
|
298
|
+
severity: NameElem,
|
|
299
|
+
rule: [NameElem, NameElem | null],
|
|
300
|
+
): string {
|
|
301
|
+
const ruleStr = rule[0].name + (rule[1] !== null ? "." + rule[1].name : "");
|
|
302
|
+
return `(${severity.name}, ${ruleStr})`;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export function expressionToString(elem: ExpressionElem): string {
|
|
306
|
+
const { kind } = elem;
|
|
307
|
+
if (kind === "binary-expression") {
|
|
308
|
+
return `${expressionToString(elem.left)} ${elem.operator.value} ${expressionToString(elem.right)}`;
|
|
309
|
+
} else if (kind === "unary-expression") {
|
|
310
|
+
return `${elem.operator.value}${expressionToString(elem.expression)}`;
|
|
311
|
+
} else if (kind === "ref") {
|
|
312
|
+
return elem.ident.originalName;
|
|
313
|
+
} else if (kind === "literal") {
|
|
314
|
+
return elem.value;
|
|
315
|
+
} else if (kind === "translate-time-feature") {
|
|
316
|
+
return elem.name;
|
|
317
|
+
} else if (kind === "parenthesized-expression") {
|
|
318
|
+
return `(${expressionToString(elem.expression)})`;
|
|
319
|
+
} else if (kind === "component-expression") {
|
|
320
|
+
return `${expressionToString(elem.base)}[${elem.access}]`;
|
|
321
|
+
} else if (kind === "component-member-expression") {
|
|
322
|
+
return `${expressionToString(elem.base)}.${elem.access}`;
|
|
323
|
+
} else if (kind === "call-expression") {
|
|
324
|
+
return `${elem.function.ident.originalName}(${elem.arguments.map(expressionToString).join(", ")})`;
|
|
325
|
+
} else {
|
|
326
|
+
assertUnreachable(kind);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function emitDirective(e: DirectiveElem, ctx: EmitContext): void {
|
|
331
|
+
const { directive } = e;
|
|
332
|
+
const { kind } = directive;
|
|
333
|
+
if (kind === "diagnostic") {
|
|
334
|
+
ctx.srcBuilder.add(
|
|
335
|
+
`diagnostic${diagnosticControlToString(directive.severity, directive.rule)};`,
|
|
336
|
+
e.start,
|
|
337
|
+
e.end,
|
|
338
|
+
);
|
|
339
|
+
} else if (kind === "enable") {
|
|
340
|
+
ctx.srcBuilder.add(
|
|
341
|
+
`enable ${directive.extensions.map(v => v.name).join(", ")};`,
|
|
342
|
+
e.start,
|
|
343
|
+
e.end,
|
|
344
|
+
);
|
|
345
|
+
} else if (kind === "requires") {
|
|
346
|
+
ctx.srcBuilder.add(
|
|
347
|
+
`requires ${directive.extensions.map(v => v.name).join(", ")};`,
|
|
348
|
+
e.start,
|
|
349
|
+
e.end,
|
|
350
|
+
);
|
|
351
|
+
} else {
|
|
352
|
+
assertUnreachable(kind);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function displayName(declIdent: DeclIdent): string {
|
|
357
|
+
if (isGlobal(declIdent)) {
|
|
358
|
+
assertThatDebug(
|
|
359
|
+
declIdent.mangledName,
|
|
360
|
+
`ERR: mangled name not found for decl ident ${identToString(declIdent)}`,
|
|
361
|
+
);
|
|
362
|
+
// mangled name was set in binding step
|
|
363
|
+
return declIdent.mangledName!;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return declIdent.mangledName || declIdent.originalName;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** trace through refersTo links in reference Idents until we find the declaration
|
|
370
|
+
* expects that bindIdents has filled in all refersTo: links
|
|
371
|
+
*/
|
|
372
|
+
export function findDecl(ident: Ident): DeclIdent {
|
|
373
|
+
let i: Ident | undefined = ident;
|
|
374
|
+
do {
|
|
375
|
+
if (i.kind === "decl") {
|
|
376
|
+
return i;
|
|
377
|
+
}
|
|
378
|
+
i = i.refersTo;
|
|
379
|
+
} while (i);
|
|
380
|
+
|
|
381
|
+
// TODO show source position if this can happen in a non buggy linker.
|
|
382
|
+
throw new Error(`unresolved identifer: ${ident.originalName}`);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/** check if the element is visible with the current current conditional compilation settings */
|
|
386
|
+
export function conditionsValid(
|
|
387
|
+
elem: AbstractElem,
|
|
388
|
+
conditions: Conditions,
|
|
389
|
+
): true | false | undefined {
|
|
390
|
+
const attrElem = elem as ElemWithAttributes;
|
|
391
|
+
const { kind } = attrElem;
|
|
392
|
+
|
|
393
|
+
switch (kind) {
|
|
394
|
+
case "alias":
|
|
395
|
+
case "assert":
|
|
396
|
+
case "const":
|
|
397
|
+
case "directive":
|
|
398
|
+
case "member":
|
|
399
|
+
case "var":
|
|
400
|
+
case "let":
|
|
401
|
+
case "statement":
|
|
402
|
+
case "switch-clause":
|
|
403
|
+
case "override":
|
|
404
|
+
case "gvar":
|
|
405
|
+
case "fn":
|
|
406
|
+
case "struct":
|
|
407
|
+
case "param":
|
|
408
|
+
return elementValid(attrElem, conditions);
|
|
409
|
+
default:
|
|
410
|
+
assertUnreachableSilent(kind);
|
|
411
|
+
}
|
|
412
|
+
return true;
|
|
413
|
+
}
|
package/src/Mangler.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { DeclIdent, SrcModule } from "./Scope.ts";
|
|
2
|
+
/**
|
|
3
|
+
* A function for constructing a unique identifier name for a global declaration.
|
|
4
|
+
* Global names must be unique in the linked wgsl.
|
|
5
|
+
*
|
|
6
|
+
* Three manglers are currently available:
|
|
7
|
+
* . minimalMangle preserves original source names where possible
|
|
8
|
+
* . underscoreMangle constructs long but predictable names for every declaration
|
|
9
|
+
* . lengthPrefixMangle constructs long but predictable names for every declaration
|
|
10
|
+
*/
|
|
11
|
+
export type ManglerFn = (
|
|
12
|
+
/** global declaration that needs a name */
|
|
13
|
+
decl: DeclIdent,
|
|
14
|
+
|
|
15
|
+
/** module that contains the declaration */
|
|
16
|
+
srcModule: SrcModule,
|
|
17
|
+
|
|
18
|
+
/** name at use site (possibly import as renamed from declaration) */
|
|
19
|
+
proposedName: string,
|
|
20
|
+
|
|
21
|
+
/** current set of mangled root level names for the linked result (read only) */
|
|
22
|
+
globalNames: Set<string>,
|
|
23
|
+
) => string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Construct a globally unique name based on the declaration
|
|
27
|
+
* module path separated by underscores.
|
|
28
|
+
* Corresponds to "Underscore-count mangling" from [NameMangling.md](https://github.com/wgsl-tooling-wg/wesl-spec/blob/main/NameMangling.md)
|
|
29
|
+
*/
|
|
30
|
+
export function underscoreMangle(
|
|
31
|
+
decl: DeclIdent,
|
|
32
|
+
srcModule: SrcModule,
|
|
33
|
+
): string {
|
|
34
|
+
const { modulePath } = srcModule;
|
|
35
|
+
return [...modulePath.split("::"), decl.originalName]
|
|
36
|
+
.map(v => {
|
|
37
|
+
const underscoreCount = (v.match(/_/g) ?? []).length;
|
|
38
|
+
if (underscoreCount > 0) {
|
|
39
|
+
return "_" + underscoreCount + v;
|
|
40
|
+
} else {
|
|
41
|
+
return v;
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
.join("_");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Construct a globally unique name based on the declaration
|
|
49
|
+
*/
|
|
50
|
+
export function lengthPrefixMangle(
|
|
51
|
+
decl: DeclIdent,
|
|
52
|
+
srcModule: SrcModule,
|
|
53
|
+
): string {
|
|
54
|
+
function codepointCount(text: string): number {
|
|
55
|
+
return [...text].length;
|
|
56
|
+
}
|
|
57
|
+
const qualifiedIdent = [
|
|
58
|
+
...srcModule.modulePath.split("::"),
|
|
59
|
+
decl.originalName,
|
|
60
|
+
];
|
|
61
|
+
return "_" + qualifiedIdent.map(v => codepointCount(v) + v).join("");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* ManglerFn to construct a globally unique name
|
|
66
|
+
* using the requested name plus a uniquing number suffix if necessary
|
|
67
|
+
*/
|
|
68
|
+
export function minimalMangle(
|
|
69
|
+
_d: DeclIdent,
|
|
70
|
+
_s: SrcModule,
|
|
71
|
+
proposedName: string,
|
|
72
|
+
globalNames: Set<string>,
|
|
73
|
+
): string {
|
|
74
|
+
return minimallyMangledName(proposedName, globalNames);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Construct a globally unique name by using the requested name if possible
|
|
79
|
+
* and appending a number suffix necessary
|
|
80
|
+
*/
|
|
81
|
+
export function minimallyMangledName(
|
|
82
|
+
proposedName: string,
|
|
83
|
+
globalNames: Set<string>,
|
|
84
|
+
): string {
|
|
85
|
+
let renamed = proposedName;
|
|
86
|
+
let conflicts = 0;
|
|
87
|
+
|
|
88
|
+
// create a unique name
|
|
89
|
+
while (globalNames.has(renamed)) {
|
|
90
|
+
renamed = proposedName + conflicts++;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return renamed;
|
|
94
|
+
}
|
package/src/ParseWESL.ts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { AppState, ParseError, ParserInit, SrcMap } from "mini-parse";
|
|
2
|
+
import {
|
|
3
|
+
ConstAssertElem,
|
|
4
|
+
ImportStatement,
|
|
5
|
+
ModuleElem,
|
|
6
|
+
} from "./AbstractElems.ts";
|
|
7
|
+
import { FlatImport, flattenTreeImport } from "./FlattenTreeImport.ts";
|
|
8
|
+
import { weslRoot } from "./parse/WeslGrammar.ts";
|
|
9
|
+
import { WeslStream } from "./parse/WeslStream.ts";
|
|
10
|
+
import { emptyScope, Scope, SrcModule } from "./Scope.ts";
|
|
11
|
+
import { errorHighlight, offsetToLineNumber } from "./Util.ts";
|
|
12
|
+
import { OpenElem } from "./WESLCollect.ts";
|
|
13
|
+
import { throwClickableError } from "./WeslDevice.ts";
|
|
14
|
+
|
|
15
|
+
/** result of a parse for one wesl module (e.g. one .wesl file)
|
|
16
|
+
*
|
|
17
|
+
* The parser constructs the AST constructed into three sections
|
|
18
|
+
* for convenient access by the binding stage.
|
|
19
|
+
* - import statements
|
|
20
|
+
* - language elements (fn, struct, etc)
|
|
21
|
+
* - scopes
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
24
|
+
export interface WeslAST {
|
|
25
|
+
/** source text for this module */
|
|
26
|
+
srcModule: SrcModule;
|
|
27
|
+
|
|
28
|
+
/** root module element */
|
|
29
|
+
moduleElem: ModuleElem;
|
|
30
|
+
|
|
31
|
+
/** root scope for this module */
|
|
32
|
+
rootScope: Scope;
|
|
33
|
+
|
|
34
|
+
/** imports found in this module */
|
|
35
|
+
imports: ImportStatement[];
|
|
36
|
+
|
|
37
|
+
/** module level const_assert statements */
|
|
38
|
+
moduleAsserts?: ConstAssertElem[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** an extended version of the AST */
|
|
42
|
+
export interface BindingAST extends WeslAST {
|
|
43
|
+
/* a flattened version of the import statements constructed on demand from import trees, and cached here */
|
|
44
|
+
_flatImports?: FlatImport[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** stable and unstable state used during parsing */
|
|
48
|
+
export interface WeslParseState
|
|
49
|
+
extends AppState<WeslParseContext, StableState> {
|
|
50
|
+
context: WeslParseContext;
|
|
51
|
+
stable: StableState;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** stable values used or accumulated during parsing */
|
|
55
|
+
export type StableState = WeslAST;
|
|
56
|
+
|
|
57
|
+
/** unstable values used during parse collection */
|
|
58
|
+
export interface WeslParseContext {
|
|
59
|
+
scope: Scope; // current scope (points somewhere in rootScope)
|
|
60
|
+
openElems: OpenElem[]; // elems that are collecting their contents
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* An error when parsing WESL fails. Designed to be human-readable.
|
|
65
|
+
*/
|
|
66
|
+
export class WeslParseError extends Error {
|
|
67
|
+
position: number;
|
|
68
|
+
src: SrcModule;
|
|
69
|
+
constructor(opts: { cause: ParseError; src: SrcModule }) {
|
|
70
|
+
const source = opts.src.src;
|
|
71
|
+
const [lineNum, linePos] = offsetToLineNumber(opts.cause.position, source);
|
|
72
|
+
let message = `${opts.src.debugFilePath}:${lineNum}:${linePos}`;
|
|
73
|
+
message += ` error: ${opts.cause.message}\n`;
|
|
74
|
+
message += errorHighlight(source, [
|
|
75
|
+
opts.cause.position,
|
|
76
|
+
opts.cause.position + 1,
|
|
77
|
+
]).join("\n");
|
|
78
|
+
super(message, {
|
|
79
|
+
cause: opts.cause,
|
|
80
|
+
});
|
|
81
|
+
this.position = opts.cause.position;
|
|
82
|
+
this.src = opts.src;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Parse a WESL file. Throws on error. */
|
|
87
|
+
export function parseSrcModule(srcModule: SrcModule, srcMap?: SrcMap): WeslAST {
|
|
88
|
+
const stream = new WeslStream(srcModule.src);
|
|
89
|
+
|
|
90
|
+
const appState = blankWeslParseState(srcModule);
|
|
91
|
+
|
|
92
|
+
const init: ParserInit = { stream, appState };
|
|
93
|
+
try {
|
|
94
|
+
const parseResult = weslRoot.parse(init);
|
|
95
|
+
if (parseResult === null) {
|
|
96
|
+
throw new Error("parseWESL failed");
|
|
97
|
+
}
|
|
98
|
+
} catch (e) {
|
|
99
|
+
if (e instanceof ParseError) {
|
|
100
|
+
const [lineNumber, lineColumn] = offsetToLineNumber(
|
|
101
|
+
e.position,
|
|
102
|
+
srcModule.src,
|
|
103
|
+
);
|
|
104
|
+
const error = new WeslParseError({ cause: e, src: srcModule });
|
|
105
|
+
throwClickableError({
|
|
106
|
+
url: srcModule.debugFilePath,
|
|
107
|
+
text: srcModule.src,
|
|
108
|
+
error,
|
|
109
|
+
lineNumber,
|
|
110
|
+
lineColumn,
|
|
111
|
+
length: 1,
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
throw e;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return appState.stable as WeslAST;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function parseWESL(src: string, srcMap?: SrcMap): WeslAST {
|
|
122
|
+
const srcModule: SrcModule = {
|
|
123
|
+
modulePath: "package::test", // TODO this ought not be used outside of tests
|
|
124
|
+
debugFilePath: "./test.wesl",
|
|
125
|
+
src,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
return parseSrcModule(srcModule, srcMap);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function blankWeslParseState(srcModule: SrcModule): WeslParseState {
|
|
132
|
+
const rootScope = emptyScope(null);
|
|
133
|
+
const moduleElem = null as any; // we'll fill this in later
|
|
134
|
+
return {
|
|
135
|
+
context: { scope: rootScope, openElems: [] },
|
|
136
|
+
stable: { srcModule, imports: [], rootScope, moduleElem },
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function syntheticWeslParseState(): WeslParseState {
|
|
141
|
+
const srcModule: SrcModule = {
|
|
142
|
+
modulePath: "package::test",
|
|
143
|
+
debugFilePath: "./test.wesl",
|
|
144
|
+
src: "",
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
return blankWeslParseState(srcModule);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** @return a flattened form of the import tree for convenience in binding idents. */
|
|
151
|
+
export function flatImports(ast: BindingAST): FlatImport[] {
|
|
152
|
+
if (ast._flatImports) return ast._flatImports;
|
|
153
|
+
|
|
154
|
+
const flat = ast.imports.flatMap(flattenTreeImport);
|
|
155
|
+
ast._flatImports = flat;
|
|
156
|
+
return flat;
|
|
157
|
+
}
|