wesl 0.7.25 → 0.7.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +29 -46
- package/dist/index.js +50 -66
- package/package.json +2 -2
- package/src/AbstractElems.ts +1 -1
- package/src/BindIdents.ts +31 -51
- package/src/ClickableError.ts +8 -1
- package/src/Linker.ts +70 -84
- package/src/Mangler.ts +1 -1
- package/src/StandardTypes.ts +1 -1
- package/src/WeslDevice.ts +1 -0
- package/src/discovery/FindUnboundIdents.ts +1 -0
- package/src/parse/Keywords.ts +1 -1
- package/src/parse/ParseStatement.ts +1 -1
- package/src/test/BevyLink.test.ts +1 -1
package/README.md
CHANGED
|
@@ -41,5 +41,5 @@ fn random_color(uv: vec2u) -> vec3f {
|
|
|
41
41
|
</pre>
|
|
42
42
|
|
|
43
43
|
[wesl plugin]: https://www.npmjs.com/package/wesl-plugin
|
|
44
|
-
[examples]: https://github.com/
|
|
44
|
+
[examples]: https://github.com/webgpu-tools/wesl-js/tree/main/examples
|
|
45
45
|
[wesl]: https://www.npmjs.com/package/wesl
|
package/dist/index.d.ts
CHANGED
|
@@ -279,7 +279,7 @@ type ElemKindMap = {
|
|
|
279
279
|
type: TypeRefElem;
|
|
280
280
|
var: VarElem;
|
|
281
281
|
};
|
|
282
|
-
/** Inspired by https://github.com/
|
|
282
|
+
/** Inspired by https://github.com/webgpu-tools/wesl-rs/blob/3b2434eac1b2ebda9eb8bfb25f43d8600d819872/crates/wgsl-parse/src/syntax.rs#L364 */
|
|
283
283
|
type ExpressionElem = Literal | RefIdentElem | TypeRefElem | ParenthesizedExpression | ComponentExpression | ComponentMemberExpression | UnaryExpression | BinaryExpression | FunctionCallExpression;
|
|
284
284
|
type TerminalElem = DirectiveElem | DeclIdentElem | NameElem | RefIdentElem | TextElem | ImportElem;
|
|
285
285
|
type GlobalDeclarationElem = AliasElem | ConstElem | FnElem | GlobalVarElem | OverrideElem | StructElem;
|
|
@@ -737,7 +737,7 @@ globalNames: Set<string>) => string;
|
|
|
737
737
|
/**
|
|
738
738
|
* Construct a globally unique name based on the declaration
|
|
739
739
|
* module path separated by underscores.
|
|
740
|
-
* Corresponds to "Underscore-count mangling" from [NameMangling.md](https://github.com/
|
|
740
|
+
* Corresponds to "Underscore-count mangling" from [NameMangling.md](https://github.com/webgpu-tools/wesl-spec/blob/main/NameMangling.md)
|
|
741
741
|
*/
|
|
742
742
|
declare function underscoreMangle(decl: DeclIdent, srcModule: SrcModule): string;
|
|
743
743
|
/**
|
|
@@ -844,27 +844,15 @@ interface LinkConfig {
|
|
|
844
844
|
plugins?: WeslJsPlugin[];
|
|
845
845
|
}
|
|
846
846
|
interface LinkParams {
|
|
847
|
-
/** Module resolver for lazy loading
|
|
848
|
-
* Replaces weslSrc for lazy module loading.
|
|
849
|
-
* If provided, weslSrc will be ignored. */
|
|
847
|
+
/** Module resolver for lazy loading. If provided, weslSrc is ignored. */
|
|
850
848
|
resolver?: ModuleResolver;
|
|
851
|
-
/**
|
|
852
|
-
*
|
|
853
|
-
*
|
|
854
|
-
* value is wesl src
|
|
855
|
-
*
|
|
856
|
-
*
|
|
857
|
-
* Only accepts unix-style, relative filesystem paths that are valid WGSL identifiers
|
|
858
|
-
* - Unix-style: Slashes as separators.
|
|
859
|
-
* - Valid WGSL identifiers: No backslashes, no `..`, or other non-identifier symbols.
|
|
860
|
-
* - Relative paths: They have to be relative to the wesl root.
|
|
861
|
-
*/
|
|
849
|
+
/** Record of module sources keyed by module path (`package::foo::bar`)
|
|
850
|
+
* or relative file path (`./foo/bar.wesl`). Paths must be unix-style,
|
|
851
|
+
* relative to the wesl root, and valid WGSL identifiers. */
|
|
862
852
|
weslSrc?: Record<string, string>;
|
|
863
|
-
/** name
|
|
864
|
-
*
|
|
865
|
-
*
|
|
866
|
-
* can be specified as file path (./main.wesl), a module path (package::main), or just a module name (main)
|
|
867
|
-
*/
|
|
853
|
+
/** Root module name: file path (`./main.wesl`), module path (`package::main`), or bare name (`main`).
|
|
854
|
+
* For apps, the root module contains entry points (`@compute`, `@vertex`, `@fragment`).
|
|
855
|
+
* For libraries, it defines the public API. */
|
|
868
856
|
rootModuleName?: string;
|
|
869
857
|
/** For debug logging. Will be prepended to file paths. */
|
|
870
858
|
debugWeslRoot?: string;
|
|
@@ -888,9 +876,17 @@ interface LinkParams {
|
|
|
888
876
|
mangler?: ManglerFn;
|
|
889
877
|
}
|
|
890
878
|
/** Project config for web components and tools. */
|
|
891
|
-
type WeslProject = Pick<LinkParams, "weslSrc" | "rootModuleName" | "conditions" | "constants" | "libs" | "packageName"
|
|
892
|
-
/**
|
|
893
|
-
|
|
879
|
+
type WeslProject = Pick<LinkParams, "weslSrc" | "rootModuleName" | "conditions" | "constants" | "libs" | "packageName"> & {
|
|
880
|
+
/** Shader directory relative to project root (set by ?link from wesl.toml). */shaderRoot?: string;
|
|
881
|
+
};
|
|
882
|
+
/** Context passed to virtual library generators. */
|
|
883
|
+
interface VirtualLibContext {
|
|
884
|
+
conditions: Conditions;
|
|
885
|
+
rootModulePath: string;
|
|
886
|
+
packageName: string;
|
|
887
|
+
}
|
|
888
|
+
/** Generate a virtual WESL module. */
|
|
889
|
+
type VirtualLibraryFn = (ctx: VirtualLibContext) => string;
|
|
894
890
|
/**
|
|
895
891
|
* Link a set of WESL source modules (typically the text from .wesl files) into a single WGSL string.
|
|
896
892
|
* Linking starts with a specified 'root' source module, and recursively incorporates code
|
|
@@ -927,23 +923,12 @@ declare function normalizeModuleName(name: string): string;
|
|
|
927
923
|
//#endregion
|
|
928
924
|
//#region src/BindIdents.d.ts
|
|
929
925
|
/**
|
|
930
|
-
* BindIdents pass:
|
|
931
|
-
*
|
|
932
|
-
* Goals:
|
|
933
|
-
* - Link reference idents to declaration idents
|
|
934
|
-
* - Collect used declarations (to emit in link)
|
|
935
|
-
* - Create mangled names for globals to avoid conflicts
|
|
936
|
-
*
|
|
937
|
-
* Algorithm:
|
|
938
|
-
* - Recursive depth-first walk of scope tree (not syntax tree)
|
|
939
|
-
* - For each ref: search current scope, then up to parent scopes
|
|
940
|
-
* - If no local match: check import statements for external matches
|
|
941
|
-
* - For found global decls: mangle name to be unique, collect for emission
|
|
926
|
+
* BindIdents pass: depth-first walk of the scope tree (not syntax tree),
|
|
927
|
+
* linking ref idents to declarations and mangling global names.
|
|
942
928
|
*
|
|
943
|
-
*
|
|
944
|
-
*
|
|
945
|
-
* @if/@else:
|
|
946
|
-
* mirroring the emission phase's filterValidElements but on scopes.
|
|
929
|
+
* For each ref: search current scope upward, then check imports for external matches.
|
|
930
|
+
* LiveDecls tracks visible declarations with parent links.
|
|
931
|
+
* @if/@else: mirrors filterValidElements but on scopes.
|
|
947
932
|
*/
|
|
948
933
|
/** Results returned from binding pass. */
|
|
949
934
|
interface BindResults {
|
|
@@ -1014,6 +999,8 @@ interface BindContext {
|
|
|
1014
999
|
virtuals?: VirtualLibrarySet;
|
|
1015
1000
|
/** Host package name for resolving package:: in virtual modules. */
|
|
1016
1001
|
packageName: string;
|
|
1002
|
+
/** Root module path (e.g., "package::main"). */
|
|
1003
|
+
rootModulePath: string;
|
|
1017
1004
|
/** Unbound identifiers if accumulateUnbound is true. */
|
|
1018
1005
|
unbound?: UnboundRef[];
|
|
1019
1006
|
/** Don't follow references from declarations (for library dependency detection). */
|
|
@@ -1021,11 +1008,7 @@ interface BindContext {
|
|
|
1021
1008
|
/** Visit all conditional branches (for dependency discovery). */
|
|
1022
1009
|
discoveryMode?: boolean;
|
|
1023
1010
|
}
|
|
1024
|
-
/**
|
|
1025
|
-
* Recursively bind references to declarations in this scope and child scopes.
|
|
1026
|
-
* Tracks @else state to ensure filtered @else blocks don't pull in unused declarations.
|
|
1027
|
-
* @return new declarations found
|
|
1028
|
-
*/
|
|
1011
|
+
/** Recursively bind refs to decls in this scope and children. @return new declarations found */
|
|
1029
1012
|
declare function bindIdentsRecursive(scope: Scope, bindContext: BindContext, liveDecls: LiveDecls): DeclIdent[];
|
|
1030
1013
|
//#endregion
|
|
1031
1014
|
//#region src/Conditions.d.ts
|
|
@@ -1284,4 +1267,4 @@ declare function offsetToLineNumber(offset: number, text: string): [lineNum: num
|
|
|
1284
1267
|
*/
|
|
1285
1268
|
declare function errorHighlight(source: string, span: Span): [string, string];
|
|
1286
1269
|
//#endregion
|
|
1287
|
-
export { AbstractElem, AbstractElemBase, AliasElem, Attribute, AttributeElem, BatchModuleResolver, BinaryExpression, BinaryOperator, BindIdentsParams, BindResults, BindingAST, BindingStructElem, BlockStatement, BoundAndTransformed, BuiltinAttribute, BundleResolver, ComponentExpression, ComponentMemberExpression, CompositeResolver, ConditionalAttribute, Conditions, ConstAssertElem, ConstElem, ContainerElem, ContinuingElem, DeclIdent, DeclIdentElem, DeclarationElem, DiagnosticAttribute, DiagnosticDirective, DiagnosticRule, DirectiveElem, DirectiveVariant, ElemKindMap, ElemWithAttributes, ElemWithContentsBase, ElifAttribute, ElseAttribute, EmittableElem, EnableDirective, ExpressionElem, ExtendedGPUValidationError, FnElem, FnParamElem, FunctionCallExpression, GlobalDeclarationElem, GlobalVarElem, GrammarElem, HasAttributes, Ident, IfAttribute, ImportCollection, ImportElem, ImportItem, ImportSegment, ImportStatement, InterpolateAttribute, LetElem, LexicalScope, LinkConfig, LinkParams, LinkRegistryParams, LinkedWesl, LinkerTransform, Literal, LiveDecls, ManglerFn, ModuleElem, ModuleResolver, NameElem, OpenElem, OverrideElem, ParenthesizedExpression, ParseError, type ParseOptions, PartialScope, RecordResolver, RecordResolverOptions, RefIdent, RefIdentElem, RequiresDirective, Scope, ScopeItem, SimpleMemberRef, Span, SrcMap, SrcMapBuilder, SrcMapEntry, SrcModule, SrcPosition, SrcWithPath, StableState, StandardAttribute, StatementElem, StructElem, StructMemberElem, StuffElem, SwitchClauseElem, SyntheticElem, TerminalElem, TextElem, TrackingResolver, TransformedAST, TranslateTimeExpressionElem, TypeRefElem, TypeTemplateParameter, TypedDeclElem, UnaryExpression, UnaryOperator, UnboundRef, UnknownExpressionElem, VarElem, VirtualLibrary, VirtualLibraryFn, VirtualLibrarySet, WeslAST, WeslBundle, WeslDevice, WeslGPUCompilationInfo, WeslGPUCompilationMessage, WeslJsPlugin, WeslParseContext, WeslParseError, WeslParseState, WeslProject, WeslStream, _linkSync, astToString, attributeToString, bindAndTransform, bindIdents, bindIdentsRecursive, bindingStructsPlugin, childIdent, childScope, containsScope, debug, debugContentsToString, discoverModules, emptyScope, errorHighlight, fileToModulePath, filterMap, filterValidElements, findAllRootDecls, findMap, findRefsToBindingStructs, findUnboundIdents, findUnboundRefs, findValidRootDecls, flatImports, freshResolver, groupBy, grouped, identToString, last, lengthPrefixMangle, link, linkRegistry, liveDeclsToString, log, lowerBindingStructs, makeLiveDecls, makeWeslDevice, mapForward, mapValues, markBindingStructs, markEntryTypes, mergeScope, minimalMangle, minimallyMangledName, modulePartsToRelativePath, moduleToRelativePath, multiKeySet, multisampledTextureTypes, nextIdentId, noSuffix, normalize, normalizeDebugRoot, normalizeModuleName, npmNameVariations, offsetToLineNumber, overlapTail, parseSrcModule, partition, publicDecl, replaceWords, requestWeslDevice, resetScopeIds, resolveModulePath, sampledTextureTypes, sanitizePackageName, scan, scopeToString, scopeToStringLong, srcLog, stdEnumerant, stdEnumerants, stdFn, stdFns, stdType, stdTypes, textureStorageTypes, transformBindingReference, transformBindingStruct, underscoreMangle, validation, wgslStandardAttributes, withLoggerAsync };
|
|
1270
|
+
export { AbstractElem, AbstractElemBase, AliasElem, Attribute, AttributeElem, BatchModuleResolver, BinaryExpression, BinaryOperator, BindIdentsParams, BindResults, BindingAST, BindingStructElem, BlockStatement, BoundAndTransformed, BuiltinAttribute, BundleResolver, ComponentExpression, ComponentMemberExpression, CompositeResolver, ConditionalAttribute, Conditions, ConstAssertElem, ConstElem, ContainerElem, ContinuingElem, DeclIdent, DeclIdentElem, DeclarationElem, DiagnosticAttribute, DiagnosticDirective, DiagnosticRule, DirectiveElem, DirectiveVariant, ElemKindMap, ElemWithAttributes, ElemWithContentsBase, ElifAttribute, ElseAttribute, EmittableElem, EnableDirective, ExpressionElem, ExtendedGPUValidationError, FnElem, FnParamElem, FunctionCallExpression, GlobalDeclarationElem, GlobalVarElem, GrammarElem, HasAttributes, Ident, IfAttribute, ImportCollection, ImportElem, ImportItem, ImportSegment, ImportStatement, InterpolateAttribute, LetElem, LexicalScope, LinkConfig, LinkParams, LinkRegistryParams, LinkedWesl, LinkerTransform, Literal, LiveDecls, ManglerFn, ModuleElem, ModuleResolver, NameElem, OpenElem, OverrideElem, ParenthesizedExpression, ParseError, type ParseOptions, PartialScope, RecordResolver, RecordResolverOptions, RefIdent, RefIdentElem, RequiresDirective, Scope, ScopeItem, SimpleMemberRef, Span, SrcMap, SrcMapBuilder, SrcMapEntry, SrcModule, SrcPosition, SrcWithPath, StableState, StandardAttribute, StatementElem, StructElem, StructMemberElem, StuffElem, SwitchClauseElem, SyntheticElem, TerminalElem, TextElem, TrackingResolver, TransformedAST, TranslateTimeExpressionElem, TypeRefElem, TypeTemplateParameter, TypedDeclElem, UnaryExpression, UnaryOperator, UnboundRef, UnknownExpressionElem, VarElem, VirtualLibContext, VirtualLibrary, VirtualLibraryFn, VirtualLibrarySet, WeslAST, WeslBundle, WeslDevice, WeslGPUCompilationInfo, WeslGPUCompilationMessage, WeslJsPlugin, WeslParseContext, WeslParseError, WeslParseState, WeslProject, WeslStream, _linkSync, astToString, attributeToString, bindAndTransform, bindIdents, bindIdentsRecursive, bindingStructsPlugin, childIdent, childScope, containsScope, debug, debugContentsToString, discoverModules, emptyScope, errorHighlight, fileToModulePath, filterMap, filterValidElements, findAllRootDecls, findMap, findRefsToBindingStructs, findUnboundIdents, findUnboundRefs, findValidRootDecls, flatImports, freshResolver, groupBy, grouped, identToString, last, lengthPrefixMangle, link, linkRegistry, liveDeclsToString, log, lowerBindingStructs, makeLiveDecls, makeWeslDevice, mapForward, mapValues, markBindingStructs, markEntryTypes, mergeScope, minimalMangle, minimallyMangledName, modulePartsToRelativePath, moduleToRelativePath, multiKeySet, multisampledTextureTypes, nextIdentId, noSuffix, normalize, normalizeDebugRoot, normalizeModuleName, npmNameVariations, offsetToLineNumber, overlapTail, parseSrcModule, partition, publicDecl, replaceWords, requestWeslDevice, resetScopeIds, resolveModulePath, sampledTextureTypes, sanitizePackageName, scan, scopeToString, scopeToStringLong, srcLog, stdEnumerant, stdEnumerants, stdFn, stdFns, stdType, stdTypes, textureStorageTypes, transformBindingReference, transformBindingStruct, underscoreMangle, validation, wgslStandardAttributes, withLoggerAsync };
|
package/dist/index.js
CHANGED
|
@@ -283,12 +283,13 @@ const isBrowser = "document" in globalThis;
|
|
|
283
283
|
/** Throw an error with an embedded source map so that browser users can
|
|
284
284
|
* click on the error in the browser debug console and see the wesl source code. */
|
|
285
285
|
function throwClickableError(params) {
|
|
286
|
-
const { url, text, lineNumber, lineColumn, length, error } = params;
|
|
286
|
+
const { url, text, lineNumber, lineColumn, length, offset, error } = params;
|
|
287
287
|
const weslLocation = {
|
|
288
288
|
file: url,
|
|
289
289
|
line: lineNumber,
|
|
290
290
|
column: lineColumn,
|
|
291
|
-
length
|
|
291
|
+
length,
|
|
292
|
+
offset
|
|
292
293
|
};
|
|
293
294
|
error.weslLocation = weslLocation;
|
|
294
295
|
if (!isBrowser) throw error;
|
|
@@ -349,6 +350,7 @@ function failIdentElem(identElem, msg = "") {
|
|
|
349
350
|
lineNumber,
|
|
350
351
|
lineColumn,
|
|
351
352
|
length: end - start,
|
|
353
|
+
offset: start,
|
|
352
354
|
error: new Error(detailedMessage)
|
|
353
355
|
});
|
|
354
356
|
}
|
|
@@ -656,7 +658,7 @@ function lowerAndEmitElem(e, ctx) {
|
|
|
656
658
|
case "let":
|
|
657
659
|
case "statement":
|
|
658
660
|
case "continuing":
|
|
659
|
-
emitStatement
|
|
661
|
+
emitStatement(e, ctx);
|
|
660
662
|
return;
|
|
661
663
|
case "override":
|
|
662
664
|
case "const":
|
|
@@ -694,7 +696,7 @@ function emitModule(e, ctx) {
|
|
|
694
696
|
lowerAndEmitElem(child, ctx);
|
|
695
697
|
}
|
|
696
698
|
}
|
|
697
|
-
function emitStatement
|
|
699
|
+
function emitStatement(e, ctx) {
|
|
698
700
|
if (!(e.contents.length > 0 && e.contents[0].kind === "attribute")) emitAttributes(e.attributes, ctx);
|
|
699
701
|
emitContents(e, ctx);
|
|
700
702
|
}
|
|
@@ -1257,7 +1259,7 @@ function liveDeclsToString(liveDecls) {
|
|
|
1257
1259
|
/**
|
|
1258
1260
|
* Construct a globally unique name based on the declaration
|
|
1259
1261
|
* module path separated by underscores.
|
|
1260
|
-
* Corresponds to "Underscore-count mangling" from [NameMangling.md](https://github.com/
|
|
1262
|
+
* Corresponds to "Underscore-count mangling" from [NameMangling.md](https://github.com/webgpu-tools/wesl-spec/blob/main/NameMangling.md)
|
|
1261
1263
|
*/
|
|
1262
1264
|
function underscoreMangle(decl, srcModule) {
|
|
1263
1265
|
const { modulePath } = srcModule;
|
|
@@ -3633,9 +3635,8 @@ function flatImports(ast, conditions) {
|
|
|
3633
3635
|
//#region src/BindIdents.ts
|
|
3634
3636
|
/** Bind ref idents to declarations and mangle global declaration names. */
|
|
3635
3637
|
function bindIdents(params) {
|
|
3636
|
-
const { rootAst, resolver, virtuals, accumulateUnbound } = params;
|
|
3638
|
+
const { rootAst, resolver, virtuals, accumulateUnbound, discoveryMode } = params;
|
|
3637
3639
|
const { conditions = {}, mangler = minimalMangle } = params;
|
|
3638
|
-
const { discoveryMode } = params;
|
|
3639
3640
|
const packageName = rootAst.srcModule.modulePath.split("::")[0];
|
|
3640
3641
|
const rootDecls = discoveryMode ? findAllRootDecls(rootAst.rootScope) : findValidRootDecls(rootAst.rootScope, conditions);
|
|
3641
3642
|
const { globalNames, knownDecls } = initRootDecls(rootDecls);
|
|
@@ -3646,6 +3647,7 @@ function bindIdents(params) {
|
|
|
3646
3647
|
virtuals,
|
|
3647
3648
|
mangler,
|
|
3648
3649
|
packageName,
|
|
3650
|
+
rootModulePath: rootAst.srcModule.modulePath,
|
|
3649
3651
|
foundScopes: /* @__PURE__ */ new Set(),
|
|
3650
3652
|
globalNames,
|
|
3651
3653
|
globalStatements: /* @__PURE__ */ new Map(),
|
|
@@ -3656,8 +3658,9 @@ function bindIdents(params) {
|
|
|
3656
3658
|
decls: new Map(rootDecls.map((d) => [d.originalName, d])),
|
|
3657
3659
|
parent: null
|
|
3658
3660
|
};
|
|
3659
|
-
const fromRootDecls = rootDecls.flatMap((
|
|
3660
|
-
const
|
|
3661
|
+
const fromRootDecls = rootDecls.flatMap((d) => processDependentScope(d, bindContext));
|
|
3662
|
+
const { rootScope } = rootAst;
|
|
3663
|
+
const fromRefs = bindIdentsRecursive(rootScope, bindContext, liveDecls);
|
|
3661
3664
|
const newStatements = [...bindContext.globalStatements.values()];
|
|
3662
3665
|
return {
|
|
3663
3666
|
decls: [...fromRootDecls, ...fromRefs],
|
|
@@ -3702,11 +3705,7 @@ function findAllRootDecls(rootScope) {
|
|
|
3702
3705
|
function publicDecl(scope, name, conditions) {
|
|
3703
3706
|
return getValidRootDecls(scope, conditions).find((d) => d.originalName === name);
|
|
3704
3707
|
}
|
|
3705
|
-
/**
|
|
3706
|
-
* Recursively bind references to declarations in this scope and child scopes.
|
|
3707
|
-
* Tracks @else state to ensure filtered @else blocks don't pull in unused declarations.
|
|
3708
|
-
* @return new declarations found
|
|
3709
|
-
*/
|
|
3708
|
+
/** Recursively bind refs to decls in this scope and children. @return new declarations found */
|
|
3710
3709
|
function bindIdentsRecursive(scope, bindContext, liveDecls) {
|
|
3711
3710
|
const { dontFollowDecls, foundScopes } = bindContext;
|
|
3712
3711
|
if (foundScopes.has(scope)) return [];
|
|
@@ -3744,11 +3743,7 @@ function processDependentScope(decl, ctx) {
|
|
|
3744
3743
|
if (!rootDecls) return [];
|
|
3745
3744
|
return bindIdentsRecursive(dependentScope, ctx, makeLiveDecls(rootDecls));
|
|
3746
3745
|
}
|
|
3747
|
-
/**
|
|
3748
|
-
* Trace references to their declarations.
|
|
3749
|
-
* Mutates to mangle declarations and mark std references.
|
|
3750
|
-
* @return found declaration, or undefined if already processed
|
|
3751
|
-
*/
|
|
3746
|
+
/** Resolve a ref to its declaration, mangling globals and marking std refs. */
|
|
3752
3747
|
function handleRef(ident, liveDecls, bindContext) {
|
|
3753
3748
|
if (ident.refersTo || ident.std) return;
|
|
3754
3749
|
if (ident.conditionRef) return;
|
|
@@ -3828,7 +3823,8 @@ function findExport(pathParts, srcModule, ctx) {
|
|
|
3828
3823
|
const modulePath = resolveModulePath(pathParts, srcModule.modulePath.split("::")).slice(0, -1).join("::");
|
|
3829
3824
|
const moduleAst = ctx.resolver.resolveModule(modulePath) ?? virtualModule(pathParts[0], ctx);
|
|
3830
3825
|
if (!moduleAst) return void 0;
|
|
3831
|
-
const
|
|
3826
|
+
const name = last(pathParts);
|
|
3827
|
+
const decl = publicDecl(moduleAst.rootScope, name, ctx.conditions);
|
|
3832
3828
|
if (decl) return {
|
|
3833
3829
|
decl,
|
|
3834
3830
|
moduleAst
|
|
@@ -3839,9 +3835,14 @@ function virtualModule(moduleName, ctx) {
|
|
|
3839
3835
|
const found = ctx.virtuals?.[moduleName];
|
|
3840
3836
|
if (!found) return void 0;
|
|
3841
3837
|
if (found.ast) return found.ast;
|
|
3842
|
-
const
|
|
3838
|
+
const { conditions, rootModulePath, packageName } = ctx;
|
|
3839
|
+
const src = found.fn({
|
|
3840
|
+
conditions,
|
|
3841
|
+
rootModulePath,
|
|
3842
|
+
packageName
|
|
3843
|
+
});
|
|
3843
3844
|
found.ast = parseSrcModule({
|
|
3844
|
-
modulePath:
|
|
3845
|
+
modulePath: packageName + "::" + moduleName,
|
|
3845
3846
|
debugFilePath: moduleName,
|
|
3846
3847
|
src
|
|
3847
3848
|
});
|
|
@@ -3861,8 +3862,8 @@ function rootLiveDecls(decl, conditions) {
|
|
|
3861
3862
|
assertThatDebug(scope.kind === "scope");
|
|
3862
3863
|
const root = scope;
|
|
3863
3864
|
if (!root._scopeDecls) {
|
|
3864
|
-
const
|
|
3865
|
-
root._scopeDecls = { decls: new Map(
|
|
3865
|
+
const decls = findValidRootDecls(scope, conditions);
|
|
3866
|
+
root._scopeDecls = { decls: new Map(decls.map((d) => [d.originalName, d])) };
|
|
3866
3867
|
}
|
|
3867
3868
|
return root._scopeDecls;
|
|
3868
3869
|
}
|
|
@@ -4083,6 +4084,7 @@ function findUnboundRefs(resolver) {
|
|
|
4083
4084
|
globalStatements: /* @__PURE__ */ new Map(),
|
|
4084
4085
|
mangler: minimalMangle,
|
|
4085
4086
|
packageName: "package",
|
|
4087
|
+
rootModulePath: "package::main",
|
|
4086
4088
|
unbound: [],
|
|
4087
4089
|
dontFollowDecls: true,
|
|
4088
4090
|
discoveryMode: true
|
|
@@ -4468,20 +4470,13 @@ async function link(params) {
|
|
|
4468
4470
|
}
|
|
4469
4471
|
/** linker api for benchmarking */
|
|
4470
4472
|
function _linkSync(params) {
|
|
4471
|
-
const { weslSrc, libs = [], packageName, debugWeslRoot } = params;
|
|
4472
|
-
|
|
4473
|
-
const
|
|
4474
|
-
if (resolver) resolvers.push(resolver);
|
|
4475
|
-
else if (weslSrc) resolvers.push(new RecordResolver(weslSrc, {
|
|
4473
|
+
const { weslSrc, libs = [], packageName, debugWeslRoot, resolver } = params;
|
|
4474
|
+
if (!resolver && !weslSrc) throw new Error("Either resolver or weslSrc must be provided");
|
|
4475
|
+
const allResolvers = [resolver ?? new RecordResolver(weslSrc, {
|
|
4476
4476
|
packageName,
|
|
4477
4477
|
debugWeslRoot
|
|
4478
|
-
}));
|
|
4479
|
-
|
|
4480
|
-
if (libs.length > 0) {
|
|
4481
|
-
const libResolvers = createLibraryResolvers(libs, debugWeslRoot);
|
|
4482
|
-
resolvers.push(...libResolvers);
|
|
4483
|
-
}
|
|
4484
|
-
const finalResolver = resolvers.length === 1 ? resolvers[0] : new CompositeResolver(resolvers);
|
|
4478
|
+
}), ...createLibraryResolvers(libs, debugWeslRoot)];
|
|
4479
|
+
const finalResolver = allResolvers.length === 1 ? allResolvers[0] : new CompositeResolver(allResolvers);
|
|
4485
4480
|
return linkRegistry({
|
|
4486
4481
|
...params,
|
|
4487
4482
|
resolver: finalResolver
|
|
@@ -4515,8 +4510,9 @@ function flattenLibraryTree(libs) {
|
|
|
4515
4510
|
* that share some sources.)
|
|
4516
4511
|
*/
|
|
4517
4512
|
function linkRegistry(params) {
|
|
4518
|
-
const { transformedAst, newDecls, newStatements } = bindAndTransform(params);
|
|
4519
|
-
|
|
4513
|
+
const { transformedAst: ast, newDecls, newStatements } = bindAndTransform(params);
|
|
4514
|
+
const builders = emitWgsl(ast.moduleElem, ast.srcModule, newDecls, newStatements, params.conditions);
|
|
4515
|
+
return SrcMapBuilder.build(builders);
|
|
4520
4516
|
}
|
|
4521
4517
|
/** Bind identifiers and apply transform plugins */
|
|
4522
4518
|
function bindAndTransform(params) {
|
|
@@ -4564,6 +4560,7 @@ function setupVirtualLibs(virtualLibs, constants) {
|
|
|
4564
4560
|
}
|
|
4565
4561
|
return libs && mapValues(libs, (fn) => ({ fn }));
|
|
4566
4562
|
}
|
|
4563
|
+
/** Run registered transform plugins over the bound AST. */
|
|
4567
4564
|
function applyTransformPlugins(rootModule, globalNames, config) {
|
|
4568
4565
|
const { moduleElem, srcModule } = rootModule;
|
|
4569
4566
|
const startAst = {
|
|
@@ -4574,54 +4571,40 @@ function applyTransformPlugins(rootModule, globalNames, config) {
|
|
|
4574
4571
|
};
|
|
4575
4572
|
return filterMap(config?.plugins ?? [], (plugin) => plugin.transform).reduce((ast, transform) => transform(ast), startAst);
|
|
4576
4573
|
}
|
|
4574
|
+
/** Assemble WGSL output from prologue statements, root module, and imported declarations. */
|
|
4577
4575
|
function emitWgsl(rootModuleElem, srcModule, newDecls, newStatements, conditions = {}) {
|
|
4578
|
-
const prologueBuilders = newStatements.map((s) =>
|
|
4579
|
-
const rootBuilder =
|
|
4580
|
-
text: srcModule.src,
|
|
4581
|
-
path: srcModule.debugFilePath
|
|
4582
|
-
});
|
|
4576
|
+
const prologueBuilders = newStatements.map((s) => emitElem(s.srcModule, s.elem, conditions, { addNl: true }));
|
|
4577
|
+
const rootBuilder = builderFromModule(srcModule);
|
|
4583
4578
|
lowerAndEmit({
|
|
4584
4579
|
srcBuilder: rootBuilder,
|
|
4585
4580
|
rootElems: [rootModuleElem],
|
|
4586
4581
|
conditions,
|
|
4587
4582
|
extracting: false
|
|
4588
4583
|
});
|
|
4589
|
-
const declBuilders = newDecls.map((decl) =>
|
|
4584
|
+
const declBuilders = newDecls.map((decl) => emitElem(decl.srcModule, decl.declElem, conditions, { skipConditionalFiltering: true }));
|
|
4590
4585
|
return [
|
|
4591
4586
|
...prologueBuilders,
|
|
4592
4587
|
rootBuilder,
|
|
4593
4588
|
...declBuilders
|
|
4594
4589
|
];
|
|
4595
4590
|
}
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
const
|
|
4599
|
-
const builder = new SrcMapBuilder({
|
|
4600
|
-
text,
|
|
4601
|
-
path
|
|
4602
|
-
});
|
|
4591
|
+
/** Emit a single element (prologue statement or imported declaration) into a SrcMapBuilder. */
|
|
4592
|
+
function emitElem(srcModule, elem, conditions, opts = {}) {
|
|
4593
|
+
const builder = builderFromModule(srcModule);
|
|
4603
4594
|
lowerAndEmit({
|
|
4604
4595
|
srcBuilder: builder,
|
|
4605
4596
|
rootElems: [elem],
|
|
4606
|
-
conditions
|
|
4597
|
+
conditions,
|
|
4598
|
+
skipConditionalFiltering: opts.skipConditionalFiltering
|
|
4607
4599
|
});
|
|
4608
|
-
builder.addNl();
|
|
4600
|
+
if (opts.addNl) builder.addNl();
|
|
4609
4601
|
return builder;
|
|
4610
4602
|
}
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
text,
|
|
4616
|
-
path
|
|
4617
|
-
});
|
|
4618
|
-
lowerAndEmit({
|
|
4619
|
-
srcBuilder: builder,
|
|
4620
|
-
rootElems: [decl.declElem],
|
|
4621
|
-
conditions,
|
|
4622
|
-
skipConditionalFiltering: true
|
|
4603
|
+
function builderFromModule(srcModule) {
|
|
4604
|
+
return new SrcMapBuilder({
|
|
4605
|
+
text: srcModule.src,
|
|
4606
|
+
path: srcModule.debugFilePath
|
|
4623
4607
|
});
|
|
4624
|
-
return builder;
|
|
4625
4608
|
}
|
|
4626
4609
|
//#endregion
|
|
4627
4610
|
//#region src/LinkerUtil.ts
|
|
@@ -4922,6 +4905,7 @@ function makeWeslDevice(device) {
|
|
|
4922
4905
|
lineNumber: message.lineNum,
|
|
4923
4906
|
lineColumn: message.linePos,
|
|
4924
4907
|
length: message.length,
|
|
4908
|
+
offset: message.offset,
|
|
4925
4909
|
error: /* @__PURE__ */ new Error(message.type + ": " + message.message)
|
|
4926
4910
|
});
|
|
4927
4911
|
else console.error(ev.error.message);
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wesl",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.27",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
7
7
|
"src"
|
|
8
8
|
],
|
|
9
|
-
"repository": "github:
|
|
9
|
+
"repository": "github:webgpu-tools/wesl-js",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
12
|
"types": "./dist/index.d.ts",
|
package/src/AbstractElems.ts
CHANGED
|
@@ -52,7 +52,7 @@ export type ElemKindMap = {
|
|
|
52
52
|
var: VarElem;
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
-
/** Inspired by https://github.com/
|
|
55
|
+
/** Inspired by https://github.com/webgpu-tools/wesl-rs/blob/3b2434eac1b2ebda9eb8bfb25f43d8600d819872/crates/wgsl-parse/src/syntax.rs#L364 */
|
|
56
56
|
export type ExpressionElem =
|
|
57
57
|
| Literal
|
|
58
58
|
| RefIdentElem
|
package/src/BindIdents.ts
CHANGED
|
@@ -28,23 +28,12 @@ import {
|
|
|
28
28
|
import { last } from "./Util.ts";
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* BindIdents pass:
|
|
31
|
+
* BindIdents pass: depth-first walk of the scope tree (not syntax tree),
|
|
32
|
+
* linking ref idents to declarations and mangling global names.
|
|
32
33
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* - Create mangled names for globals to avoid conflicts
|
|
37
|
-
*
|
|
38
|
-
* Algorithm:
|
|
39
|
-
* - Recursive depth-first walk of scope tree (not syntax tree)
|
|
40
|
-
* - For each ref: search current scope, then up to parent scopes
|
|
41
|
-
* - If no local match: check import statements for external matches
|
|
42
|
-
* - For found global decls: mangle name to be unique, collect for emission
|
|
43
|
-
*
|
|
44
|
-
* LiveDecls: tracks visible declarations at the current position, with parent links.
|
|
45
|
-
*
|
|
46
|
-
* @if/@else: respects conditional compilation by tracking @else validity state,
|
|
47
|
-
* mirroring the emission phase's filterValidElements but on scopes.
|
|
34
|
+
* For each ref: search current scope upward, then check imports for external matches.
|
|
35
|
+
* LiveDecls tracks visible declarations with parent links.
|
|
36
|
+
* @if/@else: mirrors filterValidElements but on scopes.
|
|
48
37
|
*/
|
|
49
38
|
|
|
50
39
|
/** Results returned from binding pass. */
|
|
@@ -107,9 +96,9 @@ export interface BindIdentsParams
|
|
|
107
96
|
|
|
108
97
|
/** Bind ref idents to declarations and mangle global declaration names. */
|
|
109
98
|
export function bindIdents(params: BindIdentsParams): BindResults {
|
|
110
|
-
const { rootAst, resolver, virtuals, accumulateUnbound } =
|
|
99
|
+
const { rootAst, resolver, virtuals, accumulateUnbound, discoveryMode } =
|
|
100
|
+
params;
|
|
111
101
|
const { conditions = {}, mangler = minimalMangle } = params;
|
|
112
|
-
const { discoveryMode } = params;
|
|
113
102
|
const packageName = rootAst.srcModule.modulePath.split("::")[0];
|
|
114
103
|
|
|
115
104
|
const rootDecls = discoveryMode
|
|
@@ -117,6 +106,7 @@ export function bindIdents(params: BindIdentsParams): BindResults {
|
|
|
117
106
|
: findValidRootDecls(rootAst.rootScope, conditions);
|
|
118
107
|
const { globalNames, knownDecls } = initRootDecls(rootDecls);
|
|
119
108
|
|
|
109
|
+
const rootModulePath = rootAst.srcModule.modulePath;
|
|
120
110
|
const bindContext = {
|
|
121
111
|
resolver,
|
|
122
112
|
conditions,
|
|
@@ -124,6 +114,7 @@ export function bindIdents(params: BindIdentsParams): BindResults {
|
|
|
124
114
|
virtuals,
|
|
125
115
|
mangler,
|
|
126
116
|
packageName,
|
|
117
|
+
rootModulePath,
|
|
127
118
|
foundScopes: new Set<Scope>(),
|
|
128
119
|
globalNames,
|
|
129
120
|
globalStatements: new Map<AbstractElem, EmittableElem>(),
|
|
@@ -134,15 +125,11 @@ export function bindIdents(params: BindIdentsParams): BindResults {
|
|
|
134
125
|
const decls = new Map(rootDecls.map(d => [d.originalName, d] as const));
|
|
135
126
|
const liveDecls: LiveDecls = { decls, parent: null };
|
|
136
127
|
|
|
137
|
-
const fromRootDecls = rootDecls.flatMap(
|
|
138
|
-
processDependentScope(
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
const fromRefs = bindIdentsRecursive(
|
|
142
|
-
rootAst.rootScope,
|
|
143
|
-
bindContext,
|
|
144
|
-
liveDecls,
|
|
128
|
+
const fromRootDecls = rootDecls.flatMap(d =>
|
|
129
|
+
processDependentScope(d, bindContext),
|
|
145
130
|
);
|
|
131
|
+
const { rootScope } = rootAst;
|
|
132
|
+
const fromRefs = bindIdentsRecursive(rootScope, bindContext, liveDecls);
|
|
146
133
|
const newStatements = [...bindContext.globalStatements.values()];
|
|
147
134
|
return {
|
|
148
135
|
decls: [...fromRootDecls, ...fromRefs],
|
|
@@ -233,6 +220,9 @@ interface BindContext {
|
|
|
233
220
|
/** Host package name for resolving package:: in virtual modules. */
|
|
234
221
|
packageName: string;
|
|
235
222
|
|
|
223
|
+
/** Root module path (e.g., "package::main"). */
|
|
224
|
+
rootModulePath: string;
|
|
225
|
+
|
|
236
226
|
/** Unbound identifiers if accumulateUnbound is true. */
|
|
237
227
|
unbound?: UnboundRef[];
|
|
238
228
|
|
|
@@ -243,11 +233,7 @@ interface BindContext {
|
|
|
243
233
|
discoveryMode?: boolean;
|
|
244
234
|
}
|
|
245
235
|
|
|
246
|
-
/**
|
|
247
|
-
* Recursively bind references to declarations in this scope and child scopes.
|
|
248
|
-
* Tracks @else state to ensure filtered @else blocks don't pull in unused declarations.
|
|
249
|
-
* @return new declarations found
|
|
250
|
-
*/
|
|
236
|
+
/** Recursively bind refs to decls in this scope and children. @return new declarations found */
|
|
251
237
|
export function bindIdentsRecursive(
|
|
252
238
|
scope: Scope,
|
|
253
239
|
bindContext: BindContext,
|
|
@@ -257,8 +243,11 @@ export function bindIdentsRecursive(
|
|
|
257
243
|
if (foundScopes.has(scope)) return [];
|
|
258
244
|
foundScopes.add(scope);
|
|
259
245
|
|
|
260
|
-
const
|
|
261
|
-
|
|
246
|
+
const { newGlobals, newFromChildren } = processScope(
|
|
247
|
+
scope,
|
|
248
|
+
bindContext,
|
|
249
|
+
liveDecls,
|
|
250
|
+
);
|
|
262
251
|
const newFromRefs = dontFollowDecls
|
|
263
252
|
? []
|
|
264
253
|
: handleDecls(newGlobals, bindContext);
|
|
@@ -301,11 +290,7 @@ function processDependentScope(decl: DeclIdent, ctx: BindContext): DeclIdent[] {
|
|
|
301
290
|
return bindIdentsRecursive(dependentScope, ctx, makeLiveDecls(rootDecls));
|
|
302
291
|
}
|
|
303
292
|
|
|
304
|
-
/**
|
|
305
|
-
* Trace references to their declarations.
|
|
306
|
-
* Mutates to mangle declarations and mark std references.
|
|
307
|
-
* @return found declaration, or undefined if already processed
|
|
308
|
-
*/
|
|
293
|
+
/** Resolve a ref to its declaration, mangling globals and marking std refs. */
|
|
309
294
|
function handleRef(
|
|
310
295
|
ident: RefIdent,
|
|
311
296
|
liveDecls: LiveDecls,
|
|
@@ -443,11 +428,8 @@ function findExport(
|
|
|
443
428
|
ctx.resolver.resolveModule(modulePath) ?? virtualModule(pathParts[0], ctx);
|
|
444
429
|
if (!moduleAst) return undefined;
|
|
445
430
|
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
last(pathParts)!,
|
|
449
|
-
ctx.conditions,
|
|
450
|
-
);
|
|
431
|
+
const name = last(pathParts)!;
|
|
432
|
+
const decl = publicDecl(moduleAst.rootScope, name, ctx.conditions);
|
|
451
433
|
if (decl) return { decl, moduleAst };
|
|
452
434
|
}
|
|
453
435
|
|
|
@@ -460,10 +442,10 @@ function virtualModule(
|
|
|
460
442
|
if (!found) return undefined;
|
|
461
443
|
if (found.ast) return found.ast;
|
|
462
444
|
|
|
463
|
-
const
|
|
464
|
-
const
|
|
465
|
-
const
|
|
466
|
-
found.ast = parseSrcModule({ modulePath, debugFilePath, src });
|
|
445
|
+
const { conditions, rootModulePath, packageName } = ctx;
|
|
446
|
+
const src = found.fn({ conditions, rootModulePath, packageName });
|
|
447
|
+
const modulePath = packageName + "::" + moduleName;
|
|
448
|
+
found.ast = parseSrcModule({ modulePath, debugFilePath: moduleName, src });
|
|
467
449
|
return found.ast;
|
|
468
450
|
}
|
|
469
451
|
|
|
@@ -490,10 +472,8 @@ function rootLiveDecls(
|
|
|
490
472
|
|
|
491
473
|
const root = scope as LexicalScope;
|
|
492
474
|
if (!root._scopeDecls) {
|
|
493
|
-
const
|
|
494
|
-
root._scopeDecls = {
|
|
495
|
-
decls: new Map(rootDecls.map(d => [d.originalName, d])),
|
|
496
|
-
};
|
|
475
|
+
const decls = findValidRootDecls(scope, conditions);
|
|
476
|
+
root._scopeDecls = { decls: new Map(decls.map(d => [d.originalName, d])) };
|
|
497
477
|
}
|
|
498
478
|
return root._scopeDecls;
|
|
499
479
|
}
|
package/src/ClickableError.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface WeslErrorLocation {
|
|
|
9
9
|
line: number;
|
|
10
10
|
column: number;
|
|
11
11
|
length: number;
|
|
12
|
+
/** byte offset into the source file */
|
|
13
|
+
offset: number;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export interface ClickableErrorParams {
|
|
@@ -27,6 +29,9 @@ export interface ClickableErrorParams {
|
|
|
27
29
|
/** number of characters in the error section */
|
|
28
30
|
length: number;
|
|
29
31
|
|
|
32
|
+
/** byte offset of the error section within the source text */
|
|
33
|
+
offset: number;
|
|
34
|
+
|
|
30
35
|
/** the original error */
|
|
31
36
|
error: Error;
|
|
32
37
|
}
|
|
@@ -36,12 +41,13 @@ const isBrowser = "document" in globalThis;
|
|
|
36
41
|
/** Throw an error with an embedded source map so that browser users can
|
|
37
42
|
* click on the error in the browser debug console and see the wesl source code. */
|
|
38
43
|
export function throwClickableError(params: ClickableErrorParams): void {
|
|
39
|
-
const { url, text, lineNumber, lineColumn, length, error } = params;
|
|
44
|
+
const { url, text, lineNumber, lineColumn, length, offset, error } = params;
|
|
40
45
|
const weslLocation: WeslErrorLocation = {
|
|
41
46
|
file: url,
|
|
42
47
|
line: lineNumber,
|
|
43
48
|
column: lineColumn,
|
|
44
49
|
length,
|
|
50
|
+
offset,
|
|
45
51
|
};
|
|
46
52
|
(error as any).weslLocation = weslLocation;
|
|
47
53
|
if (!debug || !isBrowser) throw error;
|
|
@@ -142,6 +148,7 @@ export function failIdentElem(
|
|
|
142
148
|
lineNumber,
|
|
143
149
|
lineColumn,
|
|
144
150
|
length,
|
|
151
|
+
offset: start,
|
|
145
152
|
error: new Error(detailedMessage),
|
|
146
153
|
});
|
|
147
154
|
}
|
package/src/Linker.ts
CHANGED
|
@@ -37,29 +37,17 @@ export interface LinkConfig {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export interface LinkParams {
|
|
40
|
-
/** Module resolver for lazy loading
|
|
41
|
-
* Replaces weslSrc for lazy module loading.
|
|
42
|
-
* If provided, weslSrc will be ignored. */
|
|
40
|
+
/** Module resolver for lazy loading. If provided, weslSrc is ignored. */
|
|
43
41
|
resolver?: ModuleResolver;
|
|
44
42
|
|
|
45
|
-
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* value is wesl src
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* Only accepts unix-style, relative filesystem paths that are valid WGSL identifiers
|
|
52
|
-
* - Unix-style: Slashes as separators.
|
|
53
|
-
* - Valid WGSL identifiers: No backslashes, no `..`, or other non-identifier symbols.
|
|
54
|
-
* - Relative paths: They have to be relative to the wesl root.
|
|
55
|
-
*/
|
|
43
|
+
/** Record of module sources keyed by module path (`package::foo::bar`)
|
|
44
|
+
* or relative file path (`./foo/bar.wesl`). Paths must be unix-style,
|
|
45
|
+
* relative to the wesl root, and valid WGSL identifiers. */
|
|
56
46
|
weslSrc?: Record<string, string>;
|
|
57
47
|
|
|
58
|
-
/** name
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* can be specified as file path (./main.wesl), a module path (package::main), or just a module name (main)
|
|
62
|
-
*/
|
|
48
|
+
/** Root module name: file path (`./main.wesl`), module path (`package::main`), or bare name (`main`).
|
|
49
|
+
* For apps, the root module contains entry points (`@compute`, `@vertex`, `@fragment`).
|
|
50
|
+
* For libraries, it defines the public API. */
|
|
63
51
|
rootModuleName?: string;
|
|
64
52
|
|
|
65
53
|
/** For debug logging. Will be prepended to file paths. */
|
|
@@ -100,10 +88,20 @@ export type WeslProject = Pick<
|
|
|
100
88
|
| "constants"
|
|
101
89
|
| "libs"
|
|
102
90
|
| "packageName"
|
|
103
|
-
|
|
91
|
+
> & {
|
|
92
|
+
/** Shader directory relative to project root (set by ?link from wesl.toml). */
|
|
93
|
+
shaderRoot?: string;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/** Context passed to virtual library generators. */
|
|
97
|
+
export interface VirtualLibContext {
|
|
98
|
+
conditions: Conditions;
|
|
99
|
+
rootModulePath: string;
|
|
100
|
+
packageName: string;
|
|
101
|
+
}
|
|
104
102
|
|
|
105
|
-
/** Generate a virtual WESL module
|
|
106
|
-
export type VirtualLibraryFn = (
|
|
103
|
+
/** Generate a virtual WESL module. */
|
|
104
|
+
export type VirtualLibraryFn = (ctx: VirtualLibContext) => string;
|
|
107
105
|
|
|
108
106
|
/**
|
|
109
107
|
* Link a set of WESL source modules (typically the text from .wesl files) into a single WGSL string.
|
|
@@ -120,26 +118,20 @@ export async function link(params: LinkParams): Promise<LinkedWesl> {
|
|
|
120
118
|
|
|
121
119
|
/** linker api for benchmarking */
|
|
122
120
|
export function _linkSync(params: LinkParams): SrcMap {
|
|
123
|
-
const { weslSrc, libs = [], packageName, debugWeslRoot } = params;
|
|
124
|
-
const { resolver } = params;
|
|
125
|
-
|
|
126
|
-
const resolvers: ModuleResolver[] = [];
|
|
121
|
+
const { weslSrc, libs = [], packageName, debugWeslRoot, resolver } = params;
|
|
127
122
|
|
|
128
|
-
if (resolver) {
|
|
129
|
-
resolvers.push(resolver);
|
|
130
|
-
} else if (weslSrc) {
|
|
131
|
-
resolvers.push(new RecordResolver(weslSrc, { packageName, debugWeslRoot }));
|
|
132
|
-
} else {
|
|
123
|
+
if (!resolver && !weslSrc) {
|
|
133
124
|
throw new Error("Either resolver or weslSrc must be provided");
|
|
134
125
|
}
|
|
126
|
+
const primaryResolver =
|
|
127
|
+
resolver ?? new RecordResolver(weslSrc!, { packageName, debugWeslRoot });
|
|
135
128
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
resolvers.push(...libResolvers);
|
|
139
|
-
}
|
|
140
|
-
|
|
129
|
+
const libResolvers = createLibraryResolvers(libs, debugWeslRoot);
|
|
130
|
+
const allResolvers = [primaryResolver, ...libResolvers];
|
|
141
131
|
const finalResolver =
|
|
142
|
-
|
|
132
|
+
allResolvers.length === 1
|
|
133
|
+
? allResolvers[0]
|
|
134
|
+
: new CompositeResolver(allResolvers);
|
|
143
135
|
|
|
144
136
|
return linkRegistry({ ...params, resolver: finalResolver });
|
|
145
137
|
}
|
|
@@ -194,17 +186,15 @@ export interface LinkRegistryParams
|
|
|
194
186
|
*/
|
|
195
187
|
export function linkRegistry(params: LinkRegistryParams): SrcMap {
|
|
196
188
|
const bound = bindAndTransform(params);
|
|
197
|
-
const { transformedAst, newDecls, newStatements } = bound;
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
newStatements,
|
|
205
|
-
params.conditions,
|
|
206
|
-
),
|
|
189
|
+
const { transformedAst: ast, newDecls, newStatements } = bound;
|
|
190
|
+
const builders = emitWgsl(
|
|
191
|
+
ast.moduleElem,
|
|
192
|
+
ast.srcModule,
|
|
193
|
+
newDecls,
|
|
194
|
+
newStatements,
|
|
195
|
+
params.conditions,
|
|
207
196
|
);
|
|
197
|
+
return SrcMapBuilder.build(builders);
|
|
208
198
|
}
|
|
209
199
|
|
|
210
200
|
export interface BoundAndTransformed {
|
|
@@ -222,18 +212,16 @@ export function bindAndTransform(
|
|
|
222
212
|
|
|
223
213
|
const modulePath = normalizeModuleName(rootModuleName);
|
|
224
214
|
const rootAst = getRootModule(resolver, modulePath, rootModuleName);
|
|
225
|
-
|
|
226
215
|
const virtuals = setupVirtualLibs(params.virtualLibs, constants);
|
|
227
216
|
|
|
228
|
-
const
|
|
217
|
+
const bound = bindIdents({
|
|
229
218
|
rootAst,
|
|
230
219
|
resolver,
|
|
231
220
|
conditions,
|
|
232
221
|
virtuals,
|
|
233
222
|
mangler,
|
|
234
|
-
};
|
|
235
|
-
const
|
|
236
|
-
const { globalNames, decls: newDecls, newStatements } = bindResults;
|
|
223
|
+
});
|
|
224
|
+
const { globalNames, decls: newDecls, newStatements } = bound;
|
|
237
225
|
|
|
238
226
|
const transformedAst = applyTransformPlugins(rootAst, globalNames, config);
|
|
239
227
|
return { transformedAst, newDecls, newStatements };
|
|
@@ -275,7 +263,7 @@ function setupVirtualLibs(
|
|
|
275
263
|
): VirtualLibrarySet | undefined {
|
|
276
264
|
let libs = virtualLibs;
|
|
277
265
|
if (constants) {
|
|
278
|
-
const constantsGen = () =>
|
|
266
|
+
const constantsGen: VirtualLibraryFn = () =>
|
|
279
267
|
Object.entries(constants)
|
|
280
268
|
.map(([name, value]) => `const ${name} = ${value};`)
|
|
281
269
|
.join("\n");
|
|
@@ -284,25 +272,21 @@ function setupVirtualLibs(
|
|
|
284
272
|
return libs && mapValues(libs, fn => ({ fn }));
|
|
285
273
|
}
|
|
286
274
|
|
|
275
|
+
/** Run registered transform plugins over the bound AST. */
|
|
287
276
|
function applyTransformPlugins(
|
|
288
277
|
rootModule: WeslAST,
|
|
289
278
|
globalNames: Set<string>,
|
|
290
279
|
config?: LinkConfig,
|
|
291
280
|
): TransformedAST {
|
|
292
|
-
const { moduleElem, srcModule } = rootModule;
|
|
293
|
-
|
|
294
281
|
// for now only transform the root module
|
|
282
|
+
const { moduleElem, srcModule } = rootModule;
|
|
295
283
|
const startAst = { moduleElem, srcModule, globalNames, notableElems: {} };
|
|
296
284
|
const plugins = config?.plugins ?? [];
|
|
297
285
|
const transforms = filterMap(plugins, plugin => plugin.transform);
|
|
298
|
-
|
|
299
|
-
(ast, transform) => transform(ast),
|
|
300
|
-
startAst,
|
|
301
|
-
);
|
|
302
|
-
|
|
303
|
-
return transformedAst;
|
|
286
|
+
return transforms.reduce((ast, transform) => transform(ast), startAst);
|
|
304
287
|
}
|
|
305
288
|
|
|
289
|
+
/** Assemble WGSL output from prologue statements, root module, and imported declarations. */
|
|
306
290
|
function emitWgsl(
|
|
307
291
|
rootModuleElem: ModuleElem,
|
|
308
292
|
srcModule: SrcModule,
|
|
@@ -310,12 +294,11 @@ function emitWgsl(
|
|
|
310
294
|
newStatements: EmittableElem[],
|
|
311
295
|
conditions: Conditions = {},
|
|
312
296
|
): SrcMapBuilder[] {
|
|
313
|
-
const prologueBuilders = newStatements.map(s =>
|
|
297
|
+
const prologueBuilders = newStatements.map(s =>
|
|
298
|
+
emitElem(s.srcModule, s.elem, conditions, { addNl: true }),
|
|
299
|
+
);
|
|
314
300
|
|
|
315
|
-
const rootBuilder =
|
|
316
|
-
text: srcModule.src,
|
|
317
|
-
path: srcModule.debugFilePath,
|
|
318
|
-
});
|
|
301
|
+
const rootBuilder = builderFromModule(srcModule);
|
|
319
302
|
lowerAndEmit({
|
|
320
303
|
srcBuilder: rootBuilder,
|
|
321
304
|
rootElems: [rootModuleElem],
|
|
@@ -323,37 +306,40 @@ function emitWgsl(
|
|
|
323
306
|
extracting: false,
|
|
324
307
|
});
|
|
325
308
|
|
|
326
|
-
const declBuilders = newDecls.map(decl =>
|
|
309
|
+
const declBuilders = newDecls.map(decl =>
|
|
310
|
+
emitElem(decl.srcModule, decl.declElem!, conditions, {
|
|
311
|
+
skipConditionalFiltering: true,
|
|
312
|
+
}),
|
|
313
|
+
);
|
|
327
314
|
|
|
328
315
|
return [...prologueBuilders, rootBuilder, ...declBuilders];
|
|
329
316
|
}
|
|
330
317
|
|
|
331
|
-
|
|
332
|
-
|
|
318
|
+
/** Emit a single element (prologue statement or imported declaration) into a SrcMapBuilder. */
|
|
319
|
+
function emitElem(
|
|
320
|
+
srcModule: SrcModule,
|
|
321
|
+
elem: AbstractElem,
|
|
333
322
|
conditions: Conditions,
|
|
323
|
+
opts: { addNl?: boolean; skipConditionalFiltering?: boolean } = {},
|
|
334
324
|
): SrcMapBuilder {
|
|
335
|
-
const
|
|
336
|
-
const { src: text, debugFilePath: path } = srcModule;
|
|
337
|
-
const builder = new SrcMapBuilder({ text, path });
|
|
338
|
-
lowerAndEmit({ srcBuilder: builder, rootElems: [elem], conditions });
|
|
339
|
-
builder.addNl();
|
|
340
|
-
return builder;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/** Skip conditional filtering because findValidRootDecls already validated these declarations */
|
|
344
|
-
function emitDecl(decl: DeclIdent, conditions: Conditions): SrcMapBuilder {
|
|
345
|
-
const { src: text, debugFilePath: path } = decl.srcModule;
|
|
346
|
-
const builder = new SrcMapBuilder({ text, path });
|
|
325
|
+
const builder = builderFromModule(srcModule);
|
|
347
326
|
lowerAndEmit({
|
|
348
327
|
srcBuilder: builder,
|
|
349
|
-
rootElems: [
|
|
328
|
+
rootElems: [elem],
|
|
350
329
|
conditions,
|
|
351
|
-
skipConditionalFiltering:
|
|
330
|
+
skipConditionalFiltering: opts.skipConditionalFiltering,
|
|
352
331
|
});
|
|
332
|
+
if (opts.addNl) builder.addNl();
|
|
353
333
|
return builder;
|
|
354
334
|
}
|
|
355
335
|
|
|
356
|
-
|
|
336
|
+
function builderFromModule(srcModule: SrcModule): SrcMapBuilder {
|
|
337
|
+
return new SrcMapBuilder({
|
|
338
|
+
text: srcModule.src,
|
|
339
|
+
path: srcModule.debugFilePath,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
357
343
|
/*
|
|
358
344
|
|
|
359
345
|
LATER
|
package/src/Mangler.ts
CHANGED
|
@@ -25,7 +25,7 @@ export type ManglerFn = (
|
|
|
25
25
|
/**
|
|
26
26
|
* Construct a globally unique name based on the declaration
|
|
27
27
|
* module path separated by underscores.
|
|
28
|
-
* Corresponds to "Underscore-count mangling" from [NameMangling.md](https://github.com/
|
|
28
|
+
* Corresponds to "Underscore-count mangling" from [NameMangling.md](https://github.com/webgpu-tools/wesl-spec/blob/main/NameMangling.md)
|
|
29
29
|
*/
|
|
30
30
|
export function underscoreMangle(
|
|
31
31
|
decl: DeclIdent,
|
package/src/StandardTypes.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// From https://www.w3.org/TR/WGSL/#predeclared
|
|
2
|
-
// Use https://github.com/
|
|
2
|
+
// Use https://github.com/webgpu-tools/wgsl-spec to regenerate these list in the future
|
|
3
3
|
|
|
4
4
|
export const stdFns = `bitcast all any select arrayLength
|
|
5
5
|
abs acos acosh asin asinh atan atanh atan2 ceil clamp cos cosh
|
package/src/WeslDevice.ts
CHANGED
|
@@ -42,6 +42,7 @@ export function findUnboundRefs(resolver: BatchModuleResolver): UnboundRef[] {
|
|
|
42
42
|
globalStatements: new Map<AbstractElem, EmittableElem>(),
|
|
43
43
|
mangler: minimalMangle,
|
|
44
44
|
packageName: "package",
|
|
45
|
+
rootModulePath: "package::main",
|
|
45
46
|
unbound: [] as UnboundRef[],
|
|
46
47
|
dontFollowDecls: true,
|
|
47
48
|
discoveryMode: true,
|
package/src/parse/Keywords.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Use https://github.com/
|
|
1
|
+
// Use https://github.com/webgpu-tools/wgsl-spec to check this list in the future
|
|
2
2
|
// I recommend checking whether a new list and the current list are equal
|
|
3
3
|
|
|
4
4
|
/** https://www.w3.org/TR/WGSL/#keyword-summary */
|
|
@@ -39,7 +39,7 @@ interface CompoundOptions {
|
|
|
39
39
|
|
|
40
40
|
// Experimental: declarations in conditional blocks visible in outer scope.
|
|
41
41
|
// e.g. @if(X) { let y = 1; } makes y visible outside the block.
|
|
42
|
-
// see https://github.com/
|
|
42
|
+
// see https://github.com/webgpu-tools/wesl-spec/issues/158
|
|
43
43
|
const conditionalBlockFeature = true;
|
|
44
44
|
|
|
45
45
|
/** Function bodies share scope with parameters (per WGSL spec). */
|
|
@@ -11,7 +11,7 @@ const bevyBulkTest = {
|
|
|
11
11
|
name: "Bevy",
|
|
12
12
|
baseDir: "bevy-wgsl",
|
|
13
13
|
git: {
|
|
14
|
-
url: "https://github.com/
|
|
14
|
+
url: "https://github.com/webgpu-tools/bevy-wgsl.git",
|
|
15
15
|
revision: "84977ff025eaf8d92e56a9c35b815fae70eb4af0",
|
|
16
16
|
},
|
|
17
17
|
};
|