wesl 0.6.49 → 0.7.1
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 +269 -215
- package/dist/index.js +2911 -1539
- package/package.json +6 -8
- package/src/AbstractElems.ts +81 -81
- package/src/Assertions.ts +5 -5
- package/src/BindIdents.ts +192 -306
- 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/ModuleResolver.ts +15 -25
- 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 +4 -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/BindIdents.ts
CHANGED
|
@@ -22,67 +22,57 @@ import { stdEnumerant, stdFn, stdType } from "./StandardTypes.ts";
|
|
|
22
22
|
import { last } from "./Util.ts";
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
@if/@else Handling:
|
|
47
|
-
The binding phase respects conditional compilation by tracking @else validity state as it
|
|
48
|
-
processes sibling scopes. This prevents references from within filtered @else blocks from
|
|
49
|
-
pulling in declarations that won't be emitted. The algorithm mirrors the emission phase's
|
|
50
|
-
filterValidElements approach but operates on scopes rather than elements.
|
|
51
|
-
*/
|
|
52
|
-
|
|
53
|
-
/** results returned from binding pass */
|
|
25
|
+
* BindIdents pass: link reference identifiers to declarations.
|
|
26
|
+
*
|
|
27
|
+
* Goals:
|
|
28
|
+
* - Link reference idents to declaration idents
|
|
29
|
+
* - Collect used declarations (to emit in link)
|
|
30
|
+
* - Create mangled names for globals to avoid conflicts
|
|
31
|
+
*
|
|
32
|
+
* Algorithm:
|
|
33
|
+
* - Recursive depth-first walk of scope tree (not syntax tree)
|
|
34
|
+
* - For each ref: search current scope, then up to parent scopes
|
|
35
|
+
* - If no local match: check import statements for external matches
|
|
36
|
+
* - For found global decls: mangle name to be unique, collect for emission
|
|
37
|
+
*
|
|
38
|
+
* LiveDecls: tracks visible declarations at the current position, with parent links.
|
|
39
|
+
*
|
|
40
|
+
* @if/@else: respects conditional compilation by tracking @else validity state,
|
|
41
|
+
* mirroring the emission phase's filterValidElements but on scopes.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/** Results returned from binding pass. */
|
|
54
45
|
export interface BindResults {
|
|
55
|
-
/**
|
|
46
|
+
/** Root level names (including mangled names from conflicts). */
|
|
56
47
|
globalNames: Set<string>;
|
|
57
48
|
|
|
58
|
-
/**
|
|
49
|
+
/** Global declarations referenced (to emit in link). */
|
|
59
50
|
decls: DeclIdent[];
|
|
60
51
|
|
|
61
|
-
/**
|
|
62
|
-
* (e.g. for adding module level const_assert statements) */
|
|
52
|
+
/** Additional global statements to emit (e.g., const_assert). */
|
|
63
53
|
newStatements: EmittableElem[];
|
|
64
54
|
|
|
65
|
-
/** if accumulateUnbound is true
|
|
55
|
+
/** Unbound module paths (only if accumulateUnbound is true). */
|
|
66
56
|
unbound?: string[][];
|
|
67
57
|
}
|
|
68
58
|
|
|
69
|
-
/**
|
|
59
|
+
/** An element that can be directly emitted into the linked result. */
|
|
70
60
|
export interface EmittableElem {
|
|
71
61
|
srcModule: SrcModule;
|
|
72
62
|
elem: AbstractElem;
|
|
73
63
|
}
|
|
74
64
|
|
|
75
|
-
/**
|
|
65
|
+
/** Virtual package generated by code generation function. */
|
|
76
66
|
export interface VirtualLibrary {
|
|
77
67
|
// LATER rename to VirtualPackage?
|
|
78
|
-
/**
|
|
68
|
+
/** Function to generate the module. */
|
|
79
69
|
fn: VirtualLibraryFn;
|
|
80
70
|
|
|
81
|
-
/**
|
|
71
|
+
/** Parsed AST for the module (constructed lazily). */
|
|
82
72
|
ast?: WeslAST;
|
|
83
73
|
}
|
|
84
74
|
|
|
85
|
-
/**
|
|
75
|
+
/** Key is virtual module name. */
|
|
86
76
|
export type VirtualLibrarySet = Record<string, VirtualLibrary>;
|
|
87
77
|
|
|
88
78
|
export interface BindIdentsParams
|
|
@@ -90,25 +80,17 @@ export interface BindIdentsParams
|
|
|
90
80
|
rootAst: WeslAST;
|
|
91
81
|
virtuals?: VirtualLibrarySet;
|
|
92
82
|
|
|
93
|
-
/**
|
|
94
|
-
* a BindResults.unbound. */
|
|
83
|
+
/** If true, accumulate unbound identifiers into BindResults.unbound instead of throwing. */
|
|
95
84
|
accumulateUnbound?: true;
|
|
96
85
|
}
|
|
97
86
|
|
|
98
|
-
/**
|
|
99
|
-
* Bind active reference idents to declaration Idents by mutating the refersTo: field
|
|
100
|
-
* Also in this pass, set the mangledName: field for all active global declaration idents.
|
|
101
|
-
*
|
|
102
|
-
* @param parsed
|
|
103
|
-
* @param conditions only bind to/from idents that are valid with the current condition set
|
|
104
|
-
* @return any new declaration elements found (they will need to be emitted)
|
|
105
|
-
*/
|
|
87
|
+
/** Bind ref idents to declarations and mangle global declaration names. */
|
|
106
88
|
export function bindIdents(params: BindIdentsParams): BindResults {
|
|
107
89
|
const { rootAst, resolver, virtuals, accumulateUnbound } = params;
|
|
108
90
|
const { conditions = {}, mangler = minimalMangle } = params;
|
|
109
91
|
|
|
110
92
|
const validRootDecls = findValidRootDecls(rootAst.rootScope, conditions);
|
|
111
|
-
const { globalNames, knownDecls } =
|
|
93
|
+
const { globalNames, knownDecls } = initRootDecls(validRootDecls);
|
|
112
94
|
|
|
113
95
|
const bindContext = {
|
|
114
96
|
resolver,
|
|
@@ -122,78 +104,74 @@ export function bindIdents(params: BindIdentsParams): BindResults {
|
|
|
122
104
|
unbound: accumulateUnbound ? [] : undefined,
|
|
123
105
|
};
|
|
124
106
|
|
|
125
|
-
const
|
|
126
|
-
const liveDecls: LiveDecls = { decls
|
|
107
|
+
const decls = new Map(validRootDecls.map(d => [d.originalName, d] as const));
|
|
108
|
+
const liveDecls: LiveDecls = { decls, parent: null };
|
|
127
109
|
|
|
128
|
-
|
|
110
|
+
// Process dependent scopes for all valid root decls (already filtered by conditions)
|
|
111
|
+
const fromRootDecls = validRootDecls.flatMap(decl =>
|
|
112
|
+
processDependentScope(decl, bindContext),
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const fromRefs = bindIdentsRecursive(
|
|
129
116
|
rootAst.rootScope,
|
|
130
117
|
bindContext,
|
|
131
118
|
liveDecls,
|
|
132
|
-
true,
|
|
133
119
|
);
|
|
134
120
|
const newStatements = [...bindContext.globalStatements.values()];
|
|
135
|
-
return {
|
|
121
|
+
return {
|
|
122
|
+
decls: [...fromRootDecls, ...fromRefs],
|
|
123
|
+
globalNames,
|
|
124
|
+
newStatements,
|
|
125
|
+
unbound: bindContext.unbound,
|
|
126
|
+
};
|
|
136
127
|
}
|
|
137
128
|
|
|
138
|
-
/** Initialize root declarations with mangled names and add to tracking sets */
|
|
139
|
-
function
|
|
140
|
-
|
|
141
|
-
knownDecls
|
|
142
|
-
|
|
143
|
-
const globalNames = new Set<string>();
|
|
144
|
-
const knownDecls = new Set<DeclIdent>();
|
|
145
|
-
validRootDecls.forEach(decl => {
|
|
146
|
-
decl.mangledName = decl.originalName;
|
|
147
|
-
globalNames.add(decl.originalName);
|
|
148
|
-
knownDecls.add(decl);
|
|
149
|
-
});
|
|
129
|
+
/** Initialize root declarations with mangled names and add to tracking sets. */
|
|
130
|
+
function initRootDecls(validRootDecls: DeclIdent[]) {
|
|
131
|
+
for (const d of validRootDecls) d.mangledName = d.originalName;
|
|
132
|
+
const knownDecls = new Set(validRootDecls);
|
|
133
|
+
const globalNames = new Set(validRootDecls.map(d => d.originalName));
|
|
150
134
|
return { globalNames, knownDecls };
|
|
151
135
|
}
|
|
152
136
|
|
|
153
|
-
/**
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
*/
|
|
137
|
+
/** Get conditional attribute from any scope item. */
|
|
138
|
+
function getCondAttr(
|
|
139
|
+
item: DeclIdent | RefIdent | Scope,
|
|
140
|
+
): Scope["condAttribute"] {
|
|
141
|
+
// Decls inside PartialScopes don't need their own conditional checked -
|
|
142
|
+
// the PartialScope.condAttribute handles filtering at the scope level.
|
|
143
|
+
if (item.kind === "decl" && item.containingScope.kind === "partial")
|
|
144
|
+
return undefined;
|
|
145
|
+
if (item.kind === "decl") return findConditional(item.declElem?.attributes);
|
|
146
|
+
if (item.kind === "partial" || item.kind === "scope")
|
|
147
|
+
return item.condAttribute;
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Iterate scope contents, yielding only conditionally valid items. */
|
|
152
|
+
function* validItems(scope: Scope, conditions: Conditions) {
|
|
153
|
+
let elseValid = false;
|
|
154
|
+
for (const item of scope.contents) {
|
|
155
|
+
const cond = validateConditional(getCondAttr(item), elseValid, conditions);
|
|
156
|
+
elseValid = cond.nextElseState;
|
|
157
|
+
if (cond.valid) yield item;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Find all conditionally valid declarations at the root level. */
|
|
168
162
|
export function findValidRootDecls(
|
|
169
163
|
rootScope: Scope,
|
|
170
164
|
conditions: Conditions,
|
|
171
165
|
): DeclIdent[] {
|
|
172
166
|
const found: DeclIdent[] = [];
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (item.kind === "decl") {
|
|
177
|
-
assertThatDebug(item.declElem);
|
|
178
|
-
const attr = findConditional(item.declElem.attributes);
|
|
179
|
-
const validation = validateConditional(attr, elseValid, conditions);
|
|
180
|
-
elseValid = validation.nextElseState;
|
|
181
|
-
if (validation.valid) found.push(item);
|
|
182
|
-
} else if (item.kind === "partial") {
|
|
183
|
-
const validation = validateConditional(
|
|
184
|
-
item.condAttribute,
|
|
185
|
-
elseValid,
|
|
186
|
-
conditions,
|
|
187
|
-
);
|
|
188
|
-
elseValid = validation.nextElseState;
|
|
189
|
-
if (validation.valid) collectDecls(item, found);
|
|
190
|
-
}
|
|
167
|
+
for (const item of validItems(rootScope, conditions)) {
|
|
168
|
+
if (item.kind === "decl") found.push(item);
|
|
169
|
+
else if (item.kind === "partial") collectDecls(item, found);
|
|
191
170
|
}
|
|
192
|
-
|
|
193
171
|
return found;
|
|
194
172
|
}
|
|
195
173
|
|
|
196
|
-
/** Find a public declaration with the given original name */
|
|
174
|
+
/** Find a public declaration with the given original name. */
|
|
197
175
|
export function publicDecl(
|
|
198
176
|
scope: Scope,
|
|
199
177
|
name: string,
|
|
@@ -203,212 +181,153 @@ export function publicDecl(
|
|
|
203
181
|
return validDecls.find(d => d.originalName === name);
|
|
204
182
|
}
|
|
205
183
|
|
|
206
|
-
/**
|
|
207
|
-
export function isGlobal(declIdent: DeclIdent): boolean {
|
|
208
|
-
const { declElem } = declIdent;
|
|
209
|
-
if (!declElem) return false;
|
|
210
|
-
|
|
211
|
-
return ["alias", "const", "override", "fn", "struct", "gvar"].includes(
|
|
212
|
-
declElem.kind,
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/** state used during the recursive scope tree walk to bind references to declarations */
|
|
184
|
+
/** State used during the recursive scope tree walk to bind references to declarations. */
|
|
217
185
|
interface BindContext {
|
|
218
186
|
resolver: ModuleResolver;
|
|
219
|
-
conditions: Record<string, any>;
|
|
220
187
|
|
|
221
|
-
|
|
188
|
+
conditions: Conditions;
|
|
189
|
+
|
|
190
|
+
/** Decl idents discovered so far (to avoid re-traversing). */
|
|
222
191
|
knownDecls: Set<DeclIdent>;
|
|
223
192
|
|
|
224
|
-
/**
|
|
193
|
+
/** Scopes already processed (avoid duplicate work). */
|
|
225
194
|
foundScopes: Set<Scope>;
|
|
226
195
|
|
|
227
|
-
/**
|
|
196
|
+
/** Root level names used so far (enables manglers to pick unique names). */
|
|
228
197
|
globalNames: Set<string>;
|
|
229
198
|
|
|
230
|
-
/**
|
|
199
|
+
/** Additional global statements to emit (indexed by elem for uniqueness). */
|
|
231
200
|
globalStatements: Map<AbstractElem, EmittableElem>;
|
|
232
201
|
|
|
233
|
-
/**
|
|
202
|
+
/** Construct unique identifier names for global declarations. */
|
|
234
203
|
mangler: ManglerFn;
|
|
235
204
|
|
|
236
205
|
virtuals?: VirtualLibrarySet;
|
|
237
206
|
|
|
238
|
-
/**
|
|
207
|
+
/** Unbound identifiers if accumulateUnbound is true. */
|
|
239
208
|
unbound?: string[][];
|
|
240
209
|
|
|
241
|
-
/**
|
|
210
|
+
/** Don't follow references from declarations (for library dependency detection). */
|
|
242
211
|
dontFollowDecls?: boolean;
|
|
243
212
|
}
|
|
244
213
|
|
|
245
214
|
/**
|
|
246
|
-
* Recursively bind references to declarations in this scope and
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
* IMPORTANT: This function tracks @else state while traversing sibling scopes.
|
|
251
|
-
* When an @if scope is processed, its validity determines whether the following
|
|
252
|
-
* @else scope should be considered valid. This ensures that references from
|
|
253
|
-
* filtered @else blocks don't pull in unused declarations.
|
|
254
|
-
*
|
|
255
|
-
* @return any new declarations found
|
|
256
|
-
* @param liveDecls current set of live declaration in this scope
|
|
257
|
-
* (empty when traversing to a new scope, possibly non-empty for a partial scope)
|
|
258
|
-
* @param isRoot liveDecls refers to a prepopulated root scope
|
|
259
|
-
* (root scope declarations may appear in any order)
|
|
215
|
+
* Recursively bind references to declarations in this scope and child scopes.
|
|
216
|
+
* Tracks @else state to ensure filtered @else blocks don't pull in unused declarations.
|
|
217
|
+
* @return new declarations found
|
|
260
218
|
*/
|
|
261
219
|
export function bindIdentsRecursive(
|
|
262
220
|
scope: Scope,
|
|
263
221
|
bindContext: BindContext,
|
|
264
222
|
liveDecls: LiveDecls,
|
|
265
|
-
isRoot = false,
|
|
266
223
|
): DeclIdent[] {
|
|
267
224
|
const { dontFollowDecls, foundScopes } = bindContext;
|
|
268
225
|
if (foundScopes.has(scope)) return [];
|
|
269
226
|
foundScopes.add(scope);
|
|
270
227
|
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
bindContext,
|
|
274
|
-
liveDecls,
|
|
275
|
-
isRoot,
|
|
276
|
-
);
|
|
277
|
-
|
|
228
|
+
const result = processScope(scope, bindContext, liveDecls);
|
|
229
|
+
const { newGlobals, newFromChildren } = result;
|
|
278
230
|
const newFromRefs = dontFollowDecls
|
|
279
231
|
? []
|
|
280
232
|
: handleDecls(newGlobals, bindContext);
|
|
281
|
-
|
|
282
233
|
return [newGlobals, newFromChildren, newFromRefs].flat();
|
|
283
234
|
}
|
|
284
235
|
|
|
285
|
-
/** Process all identifiers and subscopes in this scope */
|
|
236
|
+
/** Process all identifiers and subscopes in this scope. */
|
|
286
237
|
function processScope(
|
|
287
238
|
scope: Scope,
|
|
288
239
|
bindContext: BindContext,
|
|
289
240
|
liveDecls: LiveDecls,
|
|
290
|
-
isRoot: boolean,
|
|
291
241
|
): { newGlobals: DeclIdent[]; newFromChildren: DeclIdent[] } {
|
|
292
242
|
const newGlobals: DeclIdent[] = [];
|
|
293
243
|
const newFromChildren: DeclIdent[] = [];
|
|
294
|
-
let elseValid = false;
|
|
295
244
|
|
|
296
|
-
|
|
245
|
+
for (const child of validItems(scope, bindContext.conditions)) {
|
|
297
246
|
if (child.kind === "decl") {
|
|
298
|
-
|
|
247
|
+
liveDecls.decls.set(child.originalName, child);
|
|
299
248
|
} else if (child.kind === "ref") {
|
|
300
249
|
const newDecl = handleRef(child, liveDecls, bindContext);
|
|
301
250
|
if (newDecl) newGlobals.push(newDecl);
|
|
302
251
|
} else {
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
elseValid = fromScope.nextElseState;
|
|
307
|
-
}
|
|
252
|
+
const newLive =
|
|
253
|
+
child.kind === "scope" ? makeLiveDecls(liveDecls) : liveDecls;
|
|
254
|
+
newFromChildren.push(...bindIdentsRecursive(child, bindContext, newLive));
|
|
308
255
|
}
|
|
309
|
-
}
|
|
310
|
-
|
|
256
|
+
}
|
|
311
257
|
return { newGlobals, newFromChildren };
|
|
312
258
|
}
|
|
313
259
|
|
|
260
|
+
/** Process dependent scope for a single declaration. */
|
|
261
|
+
function processDependentScope(decl: DeclIdent, ctx: BindContext): DeclIdent[] {
|
|
262
|
+
const { dependentScope } = decl;
|
|
263
|
+
if (!dependentScope) return [];
|
|
264
|
+
const rootDecls = rootLiveDecls(decl, ctx.conditions);
|
|
265
|
+
if (!rootDecls) return [];
|
|
266
|
+
return bindIdentsRecursive(dependentScope, ctx, makeLiveDecls(rootDecls));
|
|
267
|
+
}
|
|
268
|
+
|
|
314
269
|
/**
|
|
315
270
|
* Trace references to their declarations.
|
|
316
271
|
* Mutates to mangle declarations and mark std references.
|
|
317
|
-
*
|
|
318
|
-
* @return the found declaration, or undefined if this ref has already been processed
|
|
272
|
+
* @return found declaration, or undefined if already processed
|
|
319
273
|
*/
|
|
320
274
|
function handleRef(
|
|
321
275
|
ident: RefIdent,
|
|
322
276
|
liveDecls: LiveDecls,
|
|
323
277
|
bindContext: BindContext,
|
|
324
278
|
): DeclIdent | undefined {
|
|
325
|
-
const { resolver, conditions, unbound, virtuals } = bindContext;
|
|
326
279
|
if (ident.refersTo || ident.std) return;
|
|
327
280
|
|
|
281
|
+
// Skip binding for condition refs - they resolve via Conditions map (for now)
|
|
282
|
+
if (ident.conditionRef) return;
|
|
283
|
+
|
|
328
284
|
const foundDecl =
|
|
329
285
|
findDeclInModule(ident, liveDecls) ??
|
|
330
|
-
findQualifiedImport(ident,
|
|
286
|
+
findQualifiedImport(ident, bindContext);
|
|
331
287
|
|
|
332
288
|
if (foundDecl) {
|
|
333
289
|
ident.refersTo = foundDecl.decl;
|
|
334
290
|
return handleNewDecl(ident, foundDecl, bindContext);
|
|
335
|
-
} else if (stdWgsl(ident.originalName)) {
|
|
336
|
-
ident.std = true;
|
|
337
|
-
} else if (!unbound) {
|
|
338
|
-
failIdent(ident, `unresolved identifier '${ident.originalName}'`);
|
|
339
291
|
}
|
|
340
|
-
}
|
|
341
292
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
*/
|
|
347
|
-
function handleScope(
|
|
348
|
-
childScope: Scope,
|
|
349
|
-
liveDecls: LiveDecls,
|
|
350
|
-
bindContext: BindContext,
|
|
351
|
-
elseValid: boolean,
|
|
352
|
-
): { decls: DeclIdent[]; nextElseState: boolean } | undefined {
|
|
353
|
-
const { valid, nextElseState } = validateConditional(
|
|
354
|
-
childScope.condAttribute,
|
|
355
|
-
elseValid,
|
|
356
|
-
bindContext.conditions,
|
|
357
|
-
);
|
|
358
|
-
if (!valid) return { decls: [], nextElseState };
|
|
293
|
+
if (stdWgsl(ident.originalName)) {
|
|
294
|
+
ident.std = true;
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
359
297
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
const decls = bindIdentsRecursive(childScope, bindContext, newLiveDecls);
|
|
363
|
-
return { decls, nextElseState };
|
|
298
|
+
if (!bindContext.unbound)
|
|
299
|
+
failIdent(ident, `unresolved identifier '${ident.originalName}'`);
|
|
364
300
|
}
|
|
365
301
|
|
|
366
302
|
function handleDecls(
|
|
367
303
|
newGlobals: DeclIdent[],
|
|
368
304
|
bindContext: BindContext,
|
|
369
305
|
): DeclIdent[] {
|
|
370
|
-
|
|
371
|
-
return newGlobals.flatMap(decl => {
|
|
372
|
-
const foundScope = decl.dependentScope;
|
|
373
|
-
if (!foundScope) return [];
|
|
374
|
-
const rootDecls = rootLiveDecls(decl, conditions);
|
|
375
|
-
if (!rootDecls) return [];
|
|
376
|
-
const rootLive = makeLiveDecls(rootDecls);
|
|
377
|
-
return bindIdentsRecursive(foundScope, bindContext, rootLive);
|
|
378
|
-
});
|
|
306
|
+
return newGlobals.flatMap(decl => processDependentScope(decl, bindContext));
|
|
379
307
|
}
|
|
380
308
|
|
|
381
|
-
/**
|
|
382
|
-
* If the found declaration is new, mangle its name and update the
|
|
383
|
-
* knownDecls and globalNames sets. Return it if it's a global declaration.
|
|
384
|
-
*/
|
|
309
|
+
/** If found declaration is new, mangle its name. Return if it's a global declaration. */
|
|
385
310
|
function handleNewDecl(
|
|
386
311
|
refIdent: RefIdent,
|
|
387
312
|
foundDecl: FoundDecl,
|
|
388
|
-
|
|
313
|
+
ctx: BindContext,
|
|
389
314
|
): DeclIdent | undefined {
|
|
390
315
|
const { decl, moduleAst } = foundDecl;
|
|
391
|
-
const { knownDecls, globalNames, mangler, globalStatements } =
|
|
316
|
+
const { knownDecls, globalNames, mangler, globalStatements } = ctx;
|
|
392
317
|
if (knownDecls.has(decl)) return;
|
|
393
318
|
|
|
394
319
|
knownDecls.add(decl);
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
globalNames,
|
|
399
|
-
decl.srcModule,
|
|
400
|
-
mangler,
|
|
401
|
-
);
|
|
320
|
+
const name = refIdent.originalName;
|
|
321
|
+
setMangledName(name, decl, globalNames, decl.srcModule, mangler);
|
|
322
|
+
if (!decl.isGlobal) return;
|
|
402
323
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
globalStatements.set(elem, { srcModule: decl.srcModule, elem });
|
|
406
|
-
});
|
|
407
|
-
return decl;
|
|
324
|
+
for (const elem of moduleAst.moduleAsserts ?? []) {
|
|
325
|
+
globalStatements.set(elem, { srcModule: decl.srcModule, elem });
|
|
408
326
|
}
|
|
327
|
+
return decl;
|
|
409
328
|
}
|
|
410
329
|
|
|
411
|
-
/** Search
|
|
330
|
+
/** Search current scope and parent scopes for a matching declaration. */
|
|
412
331
|
function findDeclInModule(
|
|
413
332
|
ident: RefIdent,
|
|
414
333
|
liveDecls: LiveDecls,
|
|
@@ -418,146 +337,119 @@ function findDeclInModule(
|
|
|
418
337
|
if (liveDecls.parent) return findDeclInModule(ident, liveDecls.parent);
|
|
419
338
|
}
|
|
420
339
|
|
|
421
|
-
/**
|
|
422
|
-
* Match a reference identifier to a declaration in another module via an import statement
|
|
423
|
-
* or via an inline qualified ident e.g. foo::bar().
|
|
424
|
-
*/
|
|
340
|
+
/** Match a ref ident to a declaration in another module via import or qualified ident. */
|
|
425
341
|
function findQualifiedImport(
|
|
426
342
|
refIdent: RefIdent,
|
|
427
|
-
|
|
428
|
-
conditions: Conditions,
|
|
429
|
-
virtuals: VirtualLibrarySet | undefined,
|
|
430
|
-
unbound: string[][] | undefined,
|
|
343
|
+
ctx: BindContext,
|
|
431
344
|
): FoundDecl | undefined {
|
|
345
|
+
const { conditions, unbound } = ctx;
|
|
432
346
|
const flatImps = flatImports(refIdent.ast, conditions);
|
|
433
347
|
const identParts = refIdent.originalName.split("::");
|
|
434
|
-
const
|
|
348
|
+
const pathParts =
|
|
435
349
|
matchingImport(identParts, flatImps) ?? qualifiedIdent(identParts);
|
|
436
350
|
|
|
437
|
-
if (!
|
|
351
|
+
if (!pathParts) {
|
|
438
352
|
if (unbound && !stdWgsl(refIdent.originalName)) unbound.push(identParts);
|
|
439
353
|
return undefined;
|
|
440
354
|
}
|
|
441
355
|
|
|
442
|
-
const result = findExport(
|
|
443
|
-
modulePathParts,
|
|
444
|
-
refIdent.ast.srcModule,
|
|
445
|
-
resolver,
|
|
446
|
-
conditions,
|
|
447
|
-
virtuals,
|
|
448
|
-
);
|
|
356
|
+
const result = findExport(pathParts, refIdent.ast.srcModule, ctx);
|
|
449
357
|
if (!result) {
|
|
450
|
-
if (unbound)
|
|
451
|
-
|
|
452
|
-
} else {
|
|
453
|
-
failIdent(
|
|
454
|
-
refIdent,
|
|
455
|
-
`module not found for '${modulePathParts.join("::")}'`,
|
|
456
|
-
);
|
|
457
|
-
}
|
|
358
|
+
if (unbound) unbound.push(pathParts);
|
|
359
|
+
else failIdent(refIdent, `module not found for '${pathParts.join("::")}'`);
|
|
458
360
|
}
|
|
459
361
|
return result;
|
|
460
362
|
}
|
|
461
363
|
|
|
462
|
-
/** Find an import statement that matches a provided identifier */
|
|
364
|
+
/** Find an import statement that matches a provided identifier. */
|
|
463
365
|
function matchingImport(
|
|
464
366
|
identParts: string[],
|
|
465
|
-
|
|
367
|
+
imports: FlatImport[],
|
|
466
368
|
): string[] | undefined {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
return [...flat.modulePath, ...identParts.slice(1)];
|
|
470
|
-
}
|
|
471
|
-
}
|
|
369
|
+
const flat = imports.find(f => f.importPath.at(-1) === identParts[0]);
|
|
370
|
+
if (flat) return [...flat.modulePath, ...identParts.slice(1)];
|
|
472
371
|
}
|
|
473
372
|
|
|
474
|
-
/**
|
|
373
|
+
/** Discovered declaration found during binding. */
|
|
475
374
|
interface FoundDecl {
|
|
476
375
|
decl: DeclIdent;
|
|
477
376
|
/** module containing the decl */
|
|
478
377
|
moduleAst: WeslAST;
|
|
479
378
|
}
|
|
480
379
|
|
|
481
|
-
/** @return an exported root declIdent for the provided path */
|
|
380
|
+
/** @return an exported root declIdent for the provided path. */
|
|
482
381
|
function findExport(
|
|
483
|
-
|
|
382
|
+
pathParts: string[],
|
|
484
383
|
srcModule: SrcModule,
|
|
485
|
-
|
|
486
|
-
conditions: Conditions = {},
|
|
487
|
-
virtuals?: VirtualLibrarySet,
|
|
384
|
+
ctx: BindContext,
|
|
488
385
|
): FoundDecl | undefined {
|
|
489
|
-
const
|
|
490
|
-
const
|
|
491
|
-
const
|
|
386
|
+
const { resolver, conditions, virtuals } = ctx;
|
|
387
|
+
const srcParts = srcModule.modulePath.split("::");
|
|
388
|
+
const fqParts = resolveSuper(pathParts, srcParts);
|
|
389
|
+
const modulePath = fqParts.slice(0, -1).join("::");
|
|
390
|
+
|
|
492
391
|
const moduleAst =
|
|
493
392
|
resolver.resolveModule(modulePath) ??
|
|
494
|
-
virtualModule(
|
|
393
|
+
virtualModule(pathParts[0], conditions, virtuals);
|
|
495
394
|
if (!moduleAst) return undefined;
|
|
496
395
|
|
|
497
|
-
const
|
|
498
|
-
const decl = publicDecl(moduleAst.rootScope, name, conditions);
|
|
396
|
+
const decl = publicDecl(moduleAst.rootScope, last(pathParts)!, conditions);
|
|
499
397
|
if (decl) return { decl, moduleAst };
|
|
500
398
|
}
|
|
501
399
|
|
|
502
|
-
/** @return AST for a virtual module */
|
|
400
|
+
/** @return AST for a virtual module. */
|
|
503
401
|
function virtualModule(
|
|
504
402
|
moduleName: string,
|
|
505
403
|
conditions: Conditions = {},
|
|
506
404
|
virtuals?: VirtualLibrarySet,
|
|
507
405
|
): WeslAST | undefined {
|
|
508
|
-
|
|
509
|
-
const found = virtuals[moduleName];
|
|
406
|
+
const found = virtuals?.[moduleName];
|
|
510
407
|
if (!found) return undefined;
|
|
511
|
-
|
|
512
408
|
if (found.ast) return found.ast;
|
|
409
|
+
|
|
513
410
|
const src = found.fn(conditions);
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
src,
|
|
518
|
-
};
|
|
519
|
-
found.ast = parseSrcModule(srcModule);
|
|
411
|
+
const modulePath = moduleName;
|
|
412
|
+
const debugFilePath = moduleName;
|
|
413
|
+
found.ast = parseSrcModule({ modulePath, debugFilePath, src });
|
|
520
414
|
return found.ast;
|
|
521
415
|
}
|
|
522
416
|
|
|
523
|
-
/** Get cached valid root declarations, computing on first access */
|
|
417
|
+
/** Get cached valid root declarations, computing on first access. */
|
|
524
418
|
function getValidRootDecls(
|
|
525
419
|
rootScope: Scope,
|
|
526
420
|
conditions: Conditions,
|
|
527
421
|
): DeclIdent[] {
|
|
528
422
|
const lexScope = rootScope as LexicalScope;
|
|
529
|
-
if (lexScope._validRootDecls)
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
lexScope._validRootDecls
|
|
533
|
-
return valid;
|
|
423
|
+
if (!lexScope._validRootDecls) {
|
|
424
|
+
lexScope._validRootDecls = findValidRootDecls(rootScope, conditions);
|
|
425
|
+
}
|
|
426
|
+
return lexScope._validRootDecls;
|
|
534
427
|
}
|
|
535
428
|
|
|
536
|
-
/** Given a global declIdent, return the liveDecls for its root scope */
|
|
429
|
+
/** Given a global declIdent, return the liveDecls for its root scope. */
|
|
537
430
|
function rootLiveDecls(
|
|
538
431
|
decl: DeclIdent,
|
|
539
432
|
conditions: Conditions,
|
|
540
433
|
): LiveDecls | undefined {
|
|
541
434
|
assertThatDebug(decl.isGlobal, identToString(decl));
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
435
|
+
|
|
436
|
+
let scope = decl.containingScope;
|
|
437
|
+
while (scope.parent) {
|
|
438
|
+
scope = scope.parent;
|
|
545
439
|
}
|
|
546
|
-
assertThatDebug(
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
440
|
+
assertThatDebug(scope.kind === "scope");
|
|
441
|
+
|
|
442
|
+
const root = scope as LexicalScope;
|
|
443
|
+
if (!root._scopeDecls) {
|
|
444
|
+
const rootDecls = findValidRootDecls(scope, conditions);
|
|
445
|
+
root._scopeDecls = {
|
|
446
|
+
decls: new Map(rootDecls.map(d => [d.originalName, d])),
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
return root._scopeDecls;
|
|
555
450
|
}
|
|
556
451
|
|
|
557
|
-
/**
|
|
558
|
-
* Set a globally unique mangled name for this declaration.
|
|
559
|
-
* Uses the mangler function to avoid name collisions.
|
|
560
|
-
*/
|
|
452
|
+
/** Set a globally unique mangled name for this declaration. */
|
|
561
453
|
function setMangledName(
|
|
562
454
|
proposedName: string,
|
|
563
455
|
decl: DeclIdent,
|
|
@@ -567,19 +459,16 @@ function setMangledName(
|
|
|
567
459
|
): void {
|
|
568
460
|
if (decl.mangledName) return;
|
|
569
461
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
} else {
|
|
576
|
-
mangledName = decl.originalName;
|
|
577
|
-
}
|
|
462
|
+
const sep = proposedName.lastIndexOf("::");
|
|
463
|
+
const name = sep === -1 ? proposedName : proposedName.slice(sep + 2);
|
|
464
|
+
const mangledName = decl.isGlobal
|
|
465
|
+
? mangler(decl, srcModule, name, globalNames)
|
|
466
|
+
: decl.originalName;
|
|
578
467
|
decl.mangledName = mangledName;
|
|
579
468
|
globalNames.add(mangledName);
|
|
580
469
|
}
|
|
581
470
|
|
|
582
|
-
/** @return true if ident is a standard
|
|
471
|
+
/** @return true if ident is a standard WGSL type, fn, or enumerant. */
|
|
583
472
|
function stdWgsl(name: string): boolean {
|
|
584
473
|
return stdType(name) || stdFn(name) || stdEnumerant(name); // TODO add tests for enumerants case (e.g. var x = read;)
|
|
585
474
|
}
|
|
@@ -588,13 +477,10 @@ function qualifiedIdent(identParts: string[]): string[] | undefined {
|
|
|
588
477
|
if (identParts.length > 1) return identParts;
|
|
589
478
|
}
|
|
590
479
|
|
|
591
|
-
/** Collect all declarations in a scope (used when scope is already validated) */
|
|
480
|
+
/** Collect all declarations in a scope (used when scope is already validated). */
|
|
592
481
|
function collectDecls(scope: Scope, found: DeclIdent[]): void {
|
|
593
482
|
for (const item of scope.contents) {
|
|
594
|
-
if (item.kind === "decl")
|
|
595
|
-
|
|
596
|
-
} else if (item.kind === "partial") {
|
|
597
|
-
collectDecls(item, found);
|
|
598
|
-
}
|
|
483
|
+
if (item.kind === "decl") found.push(item);
|
|
484
|
+
else if (item.kind === "partial") collectDecls(item, found);
|
|
599
485
|
}
|
|
600
486
|
}
|