wesl 0.6.48 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +295 -214
- package/dist/index.js +2947 -1550
- package/package.json +6 -8
- package/src/AbstractElems.ts +81 -81
- package/src/Assertions.ts +5 -5
- package/src/BindIdents.ts +193 -319
- package/src/ClickableError.ts +3 -2
- package/src/Conditions.ts +2 -2
- package/src/LinkedWesl.ts +1 -1
- package/src/Linker.ts +4 -3
- package/src/LinkerUtil.ts +1 -1
- package/src/Logging.ts +165 -0
- package/src/LowerAndEmit.ts +278 -110
- package/src/ModulePathUtil.ts +59 -0
- package/src/ModuleResolver.ts +26 -62
- package/src/ParseError.ts +9 -0
- package/src/ParseWESL.ts +30 -94
- package/src/RawEmit.ts +1 -4
- package/src/Reflection.ts +1 -1
- package/src/Scope.ts +3 -0
- package/src/Span.ts +2 -0
- package/src/SrcMap.ts +208 -0
- package/src/Stream.ts +30 -0
- package/src/TransformBindingStructs.ts +2 -2
- package/src/Util.ts +1 -1
- package/src/debug/ASTtoString.ts +84 -135
- package/src/discovery/FindUnboundIdents.ts +14 -5
- package/src/index.ts +5 -0
- package/src/parse/ContentsHelpers.ts +70 -0
- package/src/parse/ExpressionUtil.ts +121 -0
- package/src/parse/Keywords.ts +12 -12
- package/src/parse/OperatorBinding.ts +146 -0
- package/src/parse/ParseAttribute.ts +272 -0
- package/src/parse/ParseCall.ts +77 -0
- package/src/parse/ParseControlFlow.ts +129 -0
- package/src/parse/ParseDirective.ts +105 -0
- package/src/parse/ParseExpression.ts +288 -0
- package/src/parse/ParseFn.ts +151 -0
- package/src/parse/ParseGlobalVar.ts +131 -0
- package/src/parse/ParseIdent.ts +77 -0
- package/src/parse/ParseImport.ts +160 -0
- package/src/parse/ParseLocalVar.ts +69 -0
- package/src/parse/ParseLoop.ts +112 -0
- package/src/parse/ParseModule.ts +116 -0
- package/src/parse/ParseSimpleStatement.ts +162 -0
- package/src/parse/ParseStatement.ts +215 -0
- package/src/parse/ParseStruct.ts +89 -0
- package/src/parse/ParseType.ts +71 -0
- package/src/parse/ParseUtil.ts +174 -0
- package/src/parse/ParseValueDeclaration.ts +130 -0
- package/src/parse/ParseWesl.ts +51 -0
- package/src/parse/ParsingContext.ts +93 -0
- package/src/parse/WeslStream.ts +63 -20
- package/src/parse/stream/CachingStream.ts +48 -0
- package/src/parse/stream/MatchersStream.ts +85 -0
- package/src/parse/stream/RegexHelpers.ts +38 -0
- package/src/test/BevyLink.test.ts +100 -0
- package/src/test/BindStdTypes.test.ts +110 -0
- package/src/test/{BindWESL.test.ts → BindWESLV2.test.ts} +21 -22
- package/src/test/BulkTests.test.ts +11 -12
- package/src/test/ConditionLinking.test.ts +107 -0
- package/src/test/ConditionalElif.test.ts +1 -13
- package/src/test/ConditionalTranslationCases.test.ts +5 -0
- package/src/test/ErrorLogging.test.ts +2 -2
- package/src/test/ImportCasesV2.test.ts +63 -0
- package/src/test/LinkFails.test.ts +69 -0
- package/src/test/LinkPackage.test.ts +1 -1
- package/src/test/Linker.test.ts +75 -2
- package/src/test/LogCatcher.ts +53 -0
- package/src/test/Mangling.test.ts +1 -1
- package/src/test/ParseComments.test.ts +1 -2
- package/src/test/{ParseConditions.test.ts → ParseConditionsV2.test.ts} +57 -49
- package/src/test/ParseErrorV2.test.ts +73 -0
- package/src/test/{ParseWESL.test.ts → ParseWeslV2.test.ts} +288 -370
- package/src/test/{ScopeWESL.test.ts → ScopeWESLV2.test.ts} +205 -176
- package/src/test/TestLink.ts +51 -51
- package/src/test/TestSetup.ts +9 -3
- package/src/test/TestUtil.ts +47 -77
- package/src/test/TrimmedMatch.ts +40 -0
- package/src/test/VirtualModules.test.ts +33 -2
- package/src/test/WeslDevice.test.ts +9 -2
- package/src/test/__snapshots__/ParseWeslV2.test.ts.snap +67 -0
- package/src/test-util.ts +7 -0
- package/src/WESLCollect.ts +0 -656
- package/src/parse/AttributeGrammar.ts +0 -232
- package/src/parse/ImportGrammar.ts +0 -195
- package/src/parse/WeslBaseGrammar.ts +0 -11
- package/src/parse/WeslExpression.ts +0 -231
- package/src/parse/WeslGrammar.ts +0 -739
- package/src/test/Expression.test.ts +0 -22
- package/src/test/ImportSyntaxCases.test.ts +0 -24
- package/src/test/ParseError.test.ts +0 -45
- package/src/test/Reflection.test.ts +0 -176
- package/src/test/TransformBindingStructs.test.ts +0 -238
- /package/src/test/{ParseElif.test.ts → ParseElifV2.test.ts} +0 -0
package/src/ModuleResolver.ts
CHANGED
|
@@ -1,30 +1,17 @@
|
|
|
1
|
+
import { moduleToRelativePath, normalizeDebugRoot } from "./ModulePathUtil.ts";
|
|
1
2
|
import { parseSrcModule, type WeslAST } from "./ParseWESL.ts";
|
|
2
3
|
import { normalize, noSuffix } from "./PathUtil.ts";
|
|
3
4
|
import type { WeslBundle } from "./WeslBundle.ts";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Resolves module paths to parsed ASTs.
|
|
9
|
-
*
|
|
10
|
-
* Implementations may cache ASTs or load them lazily.
|
|
11
|
-
*/
|
|
6
|
+
/** Resolves module paths to parsed ASTs. Implementations may cache or load lazily. */
|
|
12
7
|
export interface ModuleResolver {
|
|
13
|
-
/**
|
|
14
|
-
* Resolve a module path to its parsed AST.
|
|
15
|
-
*
|
|
16
|
-
* @param modulePath - Module path in :: format (e.g., "package::foo::bar")
|
|
17
|
-
* @returns Parsed AST or undefined if module not found
|
|
18
|
-
*/
|
|
8
|
+
/** Resolve module path (e.g., "package::foo::bar") to AST, or undefined if not found. */
|
|
19
9
|
resolveModule(modulePath: string): WeslAST | undefined;
|
|
20
10
|
}
|
|
21
11
|
|
|
22
12
|
/** Module resolver that supports batch enumeration of all modules. */
|
|
23
13
|
export interface BatchModuleResolver extends ModuleResolver {
|
|
24
|
-
/**
|
|
25
|
-
* Return all modules, parsing them on-demand if needed.
|
|
26
|
-
* Implementations should ensure all sources are parsed before returning.
|
|
27
|
-
*/
|
|
14
|
+
/** Return all modules, parsing them on-demand if needed. */
|
|
28
15
|
allModules(): Iterable<[string, WeslAST]>;
|
|
29
16
|
}
|
|
30
17
|
|
|
@@ -35,6 +22,8 @@ export interface RecordResolverOptions {
|
|
|
35
22
|
debugWeslRoot?: string;
|
|
36
23
|
}
|
|
37
24
|
|
|
25
|
+
const libRegex = /^lib\.w[eg]sl$/i;
|
|
26
|
+
|
|
38
27
|
/** Module resolver for in-memory source records. Lazy by default. */
|
|
39
28
|
export class RecordResolver implements BatchModuleResolver {
|
|
40
29
|
readonly astCache = new Map<string, WeslAST>();
|
|
@@ -57,48 +46,40 @@ export class RecordResolver implements BatchModuleResolver {
|
|
|
57
46
|
if (cached) return cached;
|
|
58
47
|
|
|
59
48
|
const source = this.findSource(modulePath);
|
|
60
|
-
if (
|
|
49
|
+
if (source === undefined) return undefined;
|
|
61
50
|
|
|
62
51
|
const debugFilePath = this.modulePathToDebugPath(modulePath);
|
|
63
|
-
|
|
64
|
-
const ast = parseSrcModule({
|
|
65
|
-
modulePath,
|
|
66
|
-
debugFilePath,
|
|
67
|
-
src: source,
|
|
68
|
-
});
|
|
52
|
+
const ast = parseSrcModule({ modulePath, debugFilePath, src: source });
|
|
69
53
|
this.astCache.set(modulePath, ast);
|
|
70
54
|
return ast;
|
|
71
55
|
}
|
|
72
56
|
|
|
73
57
|
private findSource(modulePath: string): string | undefined {
|
|
74
|
-
if (this.sources[modulePath]) return this.sources[modulePath];
|
|
58
|
+
if (this.sources[modulePath] !== undefined) return this.sources[modulePath];
|
|
75
59
|
|
|
76
60
|
const filePath = this.moduleToFilePath(modulePath);
|
|
61
|
+
if (filePath === undefined) return undefined;
|
|
77
62
|
return findInVariants(this.sources, filePath);
|
|
78
63
|
}
|
|
79
64
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return modulePath;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const pathParts = parts.slice(1);
|
|
87
|
-
return pathParts.join("/");
|
|
65
|
+
/** Convert module path to file path, or undefined if not local. */
|
|
66
|
+
private moduleToFilePath(modulePath: string): string | undefined {
|
|
67
|
+
return moduleToRelativePath(modulePath, this.packageName);
|
|
88
68
|
}
|
|
89
69
|
|
|
90
70
|
private modulePathToDebugPath(modulePath: string): string {
|
|
91
|
-
const filePath = this.moduleToFilePath(modulePath);
|
|
71
|
+
const filePath = this.moduleToFilePath(modulePath) ?? modulePath;
|
|
92
72
|
return this.debugWeslRoot + filePath + ".wesl";
|
|
93
73
|
}
|
|
94
74
|
|
|
95
|
-
/**
|
|
75
|
+
/** Parse all modules and return entries. */
|
|
96
76
|
allModules(): Iterable<[string, WeslAST]> {
|
|
97
77
|
for (const filePath of Object.keys(this.sources)) {
|
|
78
|
+
const treatLibAsRoot = this.packageName !== "package";
|
|
98
79
|
const modulePath = fileToModulePath(
|
|
99
80
|
filePath,
|
|
100
81
|
this.packageName,
|
|
101
|
-
|
|
82
|
+
treatLibAsRoot,
|
|
102
83
|
);
|
|
103
84
|
this.resolveModule(modulePath);
|
|
104
85
|
}
|
|
@@ -137,10 +118,8 @@ export class BundleResolver implements ModuleResolver {
|
|
|
137
118
|
}
|
|
138
119
|
|
|
139
120
|
resolveModule(modulePath: string): WeslAST | undefined {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
!modulePath.startsWith(this.packageName + "::")
|
|
143
|
-
) {
|
|
121
|
+
const pkgPrefix = this.packageName + "::";
|
|
122
|
+
if (modulePath !== this.packageName && !modulePath.startsWith(pkgPrefix)) {
|
|
144
123
|
return undefined;
|
|
145
124
|
}
|
|
146
125
|
|
|
@@ -151,11 +130,7 @@ export class BundleResolver implements ModuleResolver {
|
|
|
151
130
|
if (!source) return undefined;
|
|
152
131
|
|
|
153
132
|
const debugFilePath = this.modulePathToDebugPath(modulePath);
|
|
154
|
-
const ast = parseSrcModule({
|
|
155
|
-
modulePath,
|
|
156
|
-
debugFilePath,
|
|
157
|
-
src: source,
|
|
158
|
-
});
|
|
133
|
+
const ast = parseSrcModule({ modulePath, debugFilePath, src: source });
|
|
159
134
|
this.astCache.set(modulePath, ast);
|
|
160
135
|
return ast;
|
|
161
136
|
}
|
|
@@ -173,11 +148,7 @@ export class BundleResolver implements ModuleResolver {
|
|
|
173
148
|
}
|
|
174
149
|
|
|
175
150
|
private moduleToFilePath(modulePath: string): string {
|
|
176
|
-
|
|
177
|
-
if (parts[0] !== this.packageName) {
|
|
178
|
-
return modulePath;
|
|
179
|
-
}
|
|
180
|
-
return parts.slice(1).join("/");
|
|
151
|
+
return moduleToRelativePath(modulePath, this.packageName) ?? modulePath;
|
|
181
152
|
}
|
|
182
153
|
|
|
183
154
|
private modulePathToDebugPath(modulePath: string): string {
|
|
@@ -186,8 +157,8 @@ export class BundleResolver implements ModuleResolver {
|
|
|
186
157
|
}
|
|
187
158
|
}
|
|
188
159
|
|
|
189
|
-
/** Convert file path to module path (e.g., "foo/bar.wesl"
|
|
190
|
-
function fileToModulePath(
|
|
160
|
+
/** Convert file path to module path (e.g., "foo/bar.wesl" to "package::foo::bar"). */
|
|
161
|
+
export function fileToModulePath(
|
|
191
162
|
filePath: string,
|
|
192
163
|
packageName: string,
|
|
193
164
|
treatLibAsRoot: boolean,
|
|
@@ -204,14 +175,7 @@ function fileToModulePath(
|
|
|
204
175
|
return packageName + "::" + moduleSuffix;
|
|
205
176
|
}
|
|
206
177
|
|
|
207
|
-
/**
|
|
208
|
-
function normalizeDebugRoot(debugWeslRoot?: string): string {
|
|
209
|
-
if (debugWeslRoot === undefined) return "./";
|
|
210
|
-
if (debugWeslRoot === "") return "";
|
|
211
|
-
return debugWeslRoot.endsWith("/") ? debugWeslRoot : debugWeslRoot + "/";
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/** Try path variants with and without ./ prefix and extension suffixes. */
|
|
178
|
+
/** Try path variants with/without ./ prefix and extension suffixes. */
|
|
215
179
|
function findInVariants(
|
|
216
180
|
sources: Record<string, string>,
|
|
217
181
|
basePath: string,
|
|
@@ -221,11 +185,11 @@ function findInVariants(
|
|
|
221
185
|
|
|
222
186
|
for (const prefix of prefixes) {
|
|
223
187
|
const path = prefix + basePath;
|
|
224
|
-
if (sources[path]) return sources[path];
|
|
188
|
+
if (sources[path] !== undefined) return sources[path];
|
|
225
189
|
|
|
226
190
|
for (const ext of extensions) {
|
|
227
191
|
const withExt = `${path}.${ext}`;
|
|
228
|
-
if (sources[withExt]) return sources[withExt];
|
|
192
|
+
if (sources[withExt] !== undefined) return sources[withExt];
|
|
229
193
|
}
|
|
230
194
|
}
|
|
231
195
|
|
package/src/ParseWESL.ts
CHANGED
|
@@ -1,80 +1,67 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type AppState,
|
|
3
|
-
ParseError,
|
|
4
|
-
type ParserInit,
|
|
5
|
-
type Span,
|
|
6
|
-
} from "mini-parse";
|
|
7
1
|
import type {
|
|
8
2
|
ConstAssertElem,
|
|
3
|
+
ContainerElem,
|
|
9
4
|
ImportElem,
|
|
10
5
|
ImportStatement,
|
|
11
6
|
ModuleElem,
|
|
12
7
|
} from "./AbstractElems.ts";
|
|
13
|
-
import { throwClickableError } from "./ClickableError.ts";
|
|
14
8
|
import { filterValidElements } from "./Conditions.ts";
|
|
15
9
|
import { type FlatImport, flattenTreeImport } from "./FlattenTreeImport.ts";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
emptyScope,
|
|
21
|
-
type Scope,
|
|
22
|
-
type SrcModule,
|
|
23
|
-
} from "./Scope.ts";
|
|
10
|
+
import type { ParseError } from "./ParseError.ts";
|
|
11
|
+
import { parseWesl } from "./parse/ParseWesl.ts";
|
|
12
|
+
import type { Conditions, Scope, SrcModule } from "./Scope.ts";
|
|
13
|
+
import type { Span } from "./Span.ts";
|
|
24
14
|
import { errorHighlight, offsetToLineNumber } from "./Util.ts";
|
|
25
|
-
import type { OpenElem } from "./WESLCollect.ts";
|
|
26
15
|
|
|
27
|
-
/**
|
|
16
|
+
/** Partial element being constructed during parsing. */
|
|
17
|
+
export type OpenElem<T extends ContainerElem = ContainerElem> = Pick<
|
|
18
|
+
T,
|
|
19
|
+
"kind" | "contents"
|
|
20
|
+
>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Result of parsing one WESL module (e.g., one .wesl file).
|
|
28
24
|
*
|
|
29
|
-
* The
|
|
30
|
-
* for convenient access by the binding stage.
|
|
25
|
+
* The AST is constructed into three sections for the binding stage:
|
|
31
26
|
* - import statements
|
|
32
27
|
* - language elements (fn, struct, etc)
|
|
33
28
|
* - scopes
|
|
34
|
-
*
|
|
35
29
|
*/
|
|
36
30
|
export interface WeslAST {
|
|
37
|
-
/**
|
|
31
|
+
/** Source text for this module. */
|
|
38
32
|
srcModule: SrcModule;
|
|
39
|
-
|
|
40
|
-
/** root module element */
|
|
33
|
+
/** Root module element. */
|
|
41
34
|
moduleElem: ModuleElem;
|
|
42
|
-
|
|
43
|
-
/** root scope for this module */
|
|
35
|
+
/** Root scope for this module. */
|
|
44
36
|
rootScope: Scope;
|
|
45
|
-
|
|
46
|
-
/** imports found in this module */
|
|
37
|
+
/** Imports found in this module. */
|
|
47
38
|
imports: ImportStatement[];
|
|
48
|
-
|
|
49
|
-
/** module level const_assert statements */
|
|
39
|
+
/** Module level const_assert statements. */
|
|
50
40
|
moduleAsserts?: ConstAssertElem[];
|
|
51
41
|
}
|
|
52
42
|
|
|
53
|
-
/**
|
|
43
|
+
/** Extended AST with cached flattened imports. */
|
|
54
44
|
export interface BindingAST extends WeslAST {
|
|
55
|
-
|
|
45
|
+
/** Flattened import statements (cached on demand). */
|
|
56
46
|
_flatImports?: FlatImport[];
|
|
57
47
|
}
|
|
58
48
|
|
|
59
|
-
/**
|
|
60
|
-
export interface WeslParseState
|
|
61
|
-
extends AppState<WeslParseContext, StableState> {
|
|
49
|
+
/** Stable and unstable state used during parsing. */
|
|
50
|
+
export interface WeslParseState {
|
|
62
51
|
context: WeslParseContext;
|
|
63
52
|
stable: StableState;
|
|
64
53
|
}
|
|
65
54
|
|
|
66
|
-
/**
|
|
55
|
+
/** Stable values used or accumulated during parsing. */
|
|
67
56
|
export type StableState = WeslAST;
|
|
68
57
|
|
|
69
|
-
/**
|
|
58
|
+
/** Unstable values used during parse collection. */
|
|
70
59
|
export interface WeslParseContext {
|
|
71
60
|
scope: Scope; // current scope (points somewhere in rootScope)
|
|
72
61
|
openElems: OpenElem[]; // elems that are collecting their contents
|
|
73
62
|
}
|
|
74
63
|
|
|
75
|
-
/**
|
|
76
|
-
* An error when parsing WESL fails. Designed to be human-readable.
|
|
77
|
-
*/
|
|
64
|
+
/** Human-readable error when parsing WESL fails. */
|
|
78
65
|
export class WeslParseError extends Error {
|
|
79
66
|
span: Span;
|
|
80
67
|
src: SrcModule;
|
|
@@ -84,69 +71,18 @@ export class WeslParseError extends Error {
|
|
|
84
71
|
let message = `${opts.src.debugFilePath}:${lineNum}:${linePos}`;
|
|
85
72
|
message += ` error: ${opts.cause.message}\n`;
|
|
86
73
|
message += errorHighlight(source, opts.cause.span).join("\n");
|
|
87
|
-
super(message, {
|
|
88
|
-
cause: opts.cause,
|
|
89
|
-
});
|
|
74
|
+
super(message, { cause: opts.cause });
|
|
90
75
|
this.span = opts.cause.span;
|
|
91
76
|
this.src = opts.src;
|
|
92
77
|
}
|
|
93
78
|
}
|
|
94
79
|
|
|
95
|
-
/** Parse a WESL file.
|
|
80
|
+
/** Parse a WESL file. */
|
|
96
81
|
export function parseSrcModule(srcModule: SrcModule): WeslAST {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const appState = blankWeslParseState(srcModule);
|
|
100
|
-
|
|
101
|
-
const init: ParserInit = { stream, appState };
|
|
102
|
-
try {
|
|
103
|
-
const parseResult = weslRoot.parse(init);
|
|
104
|
-
if (parseResult === null) {
|
|
105
|
-
throw new Error("parseWESL failed");
|
|
106
|
-
}
|
|
107
|
-
} catch (e) {
|
|
108
|
-
if (e instanceof ParseError) {
|
|
109
|
-
const [lineNumber, lineColumn] = offsetToLineNumber(
|
|
110
|
-
e.span[0],
|
|
111
|
-
srcModule.src,
|
|
112
|
-
);
|
|
113
|
-
const error = new WeslParseError({ cause: e, src: srcModule });
|
|
114
|
-
throwClickableError({
|
|
115
|
-
url: srcModule.debugFilePath,
|
|
116
|
-
text: srcModule.src,
|
|
117
|
-
error,
|
|
118
|
-
lineNumber,
|
|
119
|
-
lineColumn,
|
|
120
|
-
length: e.span[1] - e.span[0],
|
|
121
|
-
});
|
|
122
|
-
} else {
|
|
123
|
-
throw e;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return appState.stable as WeslAST;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export function blankWeslParseState(srcModule: SrcModule): WeslParseState {
|
|
131
|
-
const rootScope = emptyScope(null);
|
|
132
|
-
const moduleElem = null as any; // we'll fill this in later
|
|
133
|
-
return {
|
|
134
|
-
context: { scope: rootScope, openElems: [] },
|
|
135
|
-
stable: { srcModule, imports: [], rootScope, moduleElem },
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function syntheticWeslParseState(): WeslParseState {
|
|
140
|
-
const srcModule: SrcModule = {
|
|
141
|
-
modulePath: "package::test",
|
|
142
|
-
debugFilePath: "./test.wesl",
|
|
143
|
-
src: "",
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
return blankWeslParseState(srcModule);
|
|
82
|
+
return parseWesl(srcModule);
|
|
147
83
|
}
|
|
148
84
|
|
|
149
|
-
/** @return
|
|
85
|
+
/** @return flattened form of import tree for binding idents. */
|
|
150
86
|
export function flatImports(
|
|
151
87
|
ast: BindingAST,
|
|
152
88
|
conditions?: Conditions,
|
package/src/RawEmit.ts
CHANGED
|
@@ -56,11 +56,8 @@ export function typeListToString(params: TypeTemplateParameter[]): string {
|
|
|
56
56
|
|
|
57
57
|
export function typeParamToString(param?: TypeTemplateParameter): string {
|
|
58
58
|
if (param === undefined) return "?";
|
|
59
|
-
if (typeof param === "string") return param;
|
|
60
|
-
|
|
61
|
-
if (param.kind === "expression") return contentsToString(param);
|
|
62
59
|
if (param.kind === "type") return typeRefToString(param);
|
|
63
|
-
|
|
60
|
+
return expressionToString(param);
|
|
64
61
|
}
|
|
65
62
|
|
|
66
63
|
export function typeRefToString(t?: TypeRefElem): string {
|
package/src/Reflection.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { matchOneOf } from "mini-parse";
|
|
2
1
|
import type {
|
|
3
2
|
BindingStructElem,
|
|
4
3
|
NameElem,
|
|
@@ -11,6 +10,7 @@ import type {
|
|
|
11
10
|
import { assertThat } from "./Assertions.ts";
|
|
12
11
|
import type { TransformedAST, WeslJsPlugin } from "./Linker.ts";
|
|
13
12
|
import { identElemLog } from "./LinkerUtil.ts";
|
|
13
|
+
import { matchOneOf } from "./parse/stream/RegexHelpers.ts";
|
|
14
14
|
import type { RefIdent } from "./Scope.ts";
|
|
15
15
|
import {
|
|
16
16
|
multisampledTextureTypes,
|
package/src/Scope.ts
CHANGED
|
@@ -38,6 +38,9 @@ export interface RefIdent extends IdentBase {
|
|
|
38
38
|
refersTo?: Ident; // import or decl ident in scope to which this ident refers. undefined before binding
|
|
39
39
|
std?: true; // true if this is a standard wgsl identifier (like sin, or u32)
|
|
40
40
|
|
|
41
|
+
/** True for identifiers in @if/@elif conditions. Binding skips these (for now). */
|
|
42
|
+
conditionRef?: true;
|
|
43
|
+
|
|
41
44
|
// LATER consider tracking the current ast in BindIdents so that this field is unnecessary
|
|
42
45
|
ast: WeslAST; // AST from module that contains this ident (to find imports during decl binding)
|
|
43
46
|
|
package/src/Span.ts
ADDED
package/src/SrcMap.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/** A source map file, and a path for debug purposes. */
|
|
2
|
+
export interface SrcWithPath {
|
|
3
|
+
/** User friendly path */
|
|
4
|
+
path?: string;
|
|
5
|
+
text: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface SrcMapEntry {
|
|
9
|
+
src: SrcWithPath;
|
|
10
|
+
srcStart: number;
|
|
11
|
+
srcEnd: number;
|
|
12
|
+
destStart: number;
|
|
13
|
+
destEnd: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SrcPosition {
|
|
17
|
+
src: SrcWithPath;
|
|
18
|
+
position: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** map text ranges in multiple src texts to a single dest text */
|
|
22
|
+
export class SrcMap {
|
|
23
|
+
entries: SrcMapEntry[];
|
|
24
|
+
dest: SrcWithPath;
|
|
25
|
+
|
|
26
|
+
constructor(dest: SrcWithPath, entries: SrcMapEntry[] = []) {
|
|
27
|
+
this.dest = dest;
|
|
28
|
+
this.entries = entries;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** add a new mapping from src to dest ranges (must be non-overlapping in destination) */
|
|
32
|
+
addEntries(newEntries: SrcMapEntry[]): void {
|
|
33
|
+
this.entries.push(...newEntries);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** given positions in the dest string, return corresponding positions in the src strings */
|
|
37
|
+
mapPositions(...positions: number[]): SrcPosition[] {
|
|
38
|
+
return positions.map(p => this.destToSrc(p));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** internally compress adjacent entries where possible */
|
|
42
|
+
compact(): void {
|
|
43
|
+
if (!this.entries.length) return;
|
|
44
|
+
let prev = this.entries[0];
|
|
45
|
+
const newEntries: SrcMapEntry[] = [prev];
|
|
46
|
+
|
|
47
|
+
for (let i = 1; i < this.entries.length; i++) {
|
|
48
|
+
const e = this.entries[i];
|
|
49
|
+
if (
|
|
50
|
+
e.src.path === prev.src.path &&
|
|
51
|
+
e.src.text === prev.src.text &&
|
|
52
|
+
prev.destEnd === e.destStart &&
|
|
53
|
+
prev.srcEnd === e.srcStart
|
|
54
|
+
) {
|
|
55
|
+
// combine adjacent range entries into one
|
|
56
|
+
prev.destEnd = e.destEnd;
|
|
57
|
+
prev.srcEnd = e.srcEnd;
|
|
58
|
+
} else {
|
|
59
|
+
newEntries.push(e);
|
|
60
|
+
prev = e;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
this.entries = newEntries;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** sort in destination order */
|
|
67
|
+
sort(): void {
|
|
68
|
+
this.entries.sort((a, b) => a.destStart - b.destStart);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* This SrcMap's destination is a src for the other srcmap,
|
|
73
|
+
* so combine the two and return the result.
|
|
74
|
+
*/
|
|
75
|
+
merge(other: SrcMap): SrcMap {
|
|
76
|
+
if (other === this) return this;
|
|
77
|
+
|
|
78
|
+
const mappedEntries = other.entries.filter(
|
|
79
|
+
e => e.src.path === this.dest.path && e.src.text === this.dest.text,
|
|
80
|
+
);
|
|
81
|
+
if (mappedEntries.length === 0) {
|
|
82
|
+
console.log("other source map does not link to this one");
|
|
83
|
+
return other;
|
|
84
|
+
}
|
|
85
|
+
sortSrc(mappedEntries);
|
|
86
|
+
const newEntries = mappedEntries.map(e => {
|
|
87
|
+
const { src, position: srcStart } = this.destToSrc(e.srcStart);
|
|
88
|
+
const { src: endSrc, position: srcEnd } = this.destToSrc(e.srcEnd);
|
|
89
|
+
if (endSrc !== src) throw new Error("NYI, need to split");
|
|
90
|
+
const newEntry: SrcMapEntry = {
|
|
91
|
+
src,
|
|
92
|
+
srcStart,
|
|
93
|
+
srcEnd,
|
|
94
|
+
destStart: e.destStart,
|
|
95
|
+
destEnd: e.destEnd,
|
|
96
|
+
};
|
|
97
|
+
return newEntry;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const otherSources = other.entries.filter(
|
|
101
|
+
e => e.src.path !== this.dest.path || e.src.text !== this.dest.text,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const newMap = new SrcMap(other.dest, [...otherSources, ...newEntries]);
|
|
105
|
+
newMap.sort();
|
|
106
|
+
return newMap;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** @return the source position corresponding to a provided destination position */
|
|
110
|
+
destToSrc(destPos: number): SrcPosition {
|
|
111
|
+
const entry = this.entries.find(
|
|
112
|
+
e => e.destStart <= destPos && e.destEnd >= destPos,
|
|
113
|
+
);
|
|
114
|
+
if (!entry) {
|
|
115
|
+
return { src: this.dest, position: destPos };
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
src: entry.src,
|
|
119
|
+
position: entry.srcStart + destPos - entry.destStart,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** sort entries in place by src start position */
|
|
125
|
+
function sortSrc(entries: SrcMapEntry[]): void {
|
|
126
|
+
entries.sort((a, b) => a.srcStart - b.srcStart);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Incrementally append to a string, tracking source references */
|
|
130
|
+
export class SrcMapBuilder {
|
|
131
|
+
#fragments: string[] = [];
|
|
132
|
+
#destLength = 0;
|
|
133
|
+
#entries: SrcMapEntry[] = [];
|
|
134
|
+
source: SrcWithPath;
|
|
135
|
+
|
|
136
|
+
constructor(source: SrcWithPath) {
|
|
137
|
+
this.source = source;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** append a string fragment to the destination string */
|
|
141
|
+
add(fragment: string, srcStart: number, srcEnd: number): void {
|
|
142
|
+
const destStart = this.#destLength;
|
|
143
|
+
this.#destLength += fragment.length;
|
|
144
|
+
const destEnd = this.#destLength;
|
|
145
|
+
|
|
146
|
+
this.#fragments.push(fragment);
|
|
147
|
+
this.#entries.push({
|
|
148
|
+
src: this.source,
|
|
149
|
+
srcStart,
|
|
150
|
+
srcEnd,
|
|
151
|
+
destStart,
|
|
152
|
+
destEnd,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Append a fragment to the destination string,
|
|
158
|
+
* mapping source to the previous,
|
|
159
|
+
* and guessing that the source fragment is just as long as the dest fragment.
|
|
160
|
+
*/
|
|
161
|
+
appendNext(fragment: string): void {
|
|
162
|
+
const lastEnd = this.#entries.at(-1)?.destEnd ?? 0;
|
|
163
|
+
this.add(fragment, lastEnd, lastEnd + fragment.length);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
addSynthetic(
|
|
167
|
+
fragment: string,
|
|
168
|
+
syntheticSource: string,
|
|
169
|
+
srcStart: number,
|
|
170
|
+
srcEnd: number,
|
|
171
|
+
): void {
|
|
172
|
+
const destStart = this.#destLength;
|
|
173
|
+
this.#destLength += fragment.length;
|
|
174
|
+
const destEnd = this.#destLength;
|
|
175
|
+
|
|
176
|
+
this.#fragments.push(fragment);
|
|
177
|
+
this.#entries.push({
|
|
178
|
+
src: { text: syntheticSource },
|
|
179
|
+
srcStart,
|
|
180
|
+
srcEnd,
|
|
181
|
+
destStart,
|
|
182
|
+
destEnd,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** append a synthetic newline, mapped to previous source location */
|
|
187
|
+
addNl(): void {
|
|
188
|
+
const lastEntry = this.#entries.at(-1) ?? { srcStart: 0, srcEnd: 0 };
|
|
189
|
+
const { srcStart, srcEnd } = lastEntry;
|
|
190
|
+
this.add("\n", srcStart, srcEnd);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** copy a string fragment from the src to the destination string */
|
|
194
|
+
addCopy(srcStart: number, srcEnd: number): void {
|
|
195
|
+
const fragment = this.source.text.slice(srcStart, srcEnd);
|
|
196
|
+
this.add(fragment, srcStart, srcEnd);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** return a SrcMap */
|
|
200
|
+
static build(builders: SrcMapBuilder[]): SrcMap {
|
|
201
|
+
const map = new SrcMap(
|
|
202
|
+
{ text: builders.map(b => b.#fragments.join("")).join("") },
|
|
203
|
+
builders.flatMap(b => b.#entries),
|
|
204
|
+
);
|
|
205
|
+
map.compact();
|
|
206
|
+
return map;
|
|
207
|
+
}
|
|
208
|
+
}
|
package/src/Stream.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Span } from "./Span.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interface for a tokenizer. Returns a "next token", and can be reset to
|
|
5
|
+
* previously saved positions (checkpoints).
|
|
6
|
+
*/
|
|
7
|
+
export interface Stream<T extends Token> {
|
|
8
|
+
/** Returns the current position */
|
|
9
|
+
checkpoint(): number;
|
|
10
|
+
/** Restores a position */
|
|
11
|
+
reset(position: number): void;
|
|
12
|
+
/**
|
|
13
|
+
* Returns the next token, or `null` if the end of the stream has been reached.
|
|
14
|
+
* Always leaves `checkpoint` right after the token.
|
|
15
|
+
*/
|
|
16
|
+
nextToken(): T | null;
|
|
17
|
+
/** src text */
|
|
18
|
+
src: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** A text token */
|
|
22
|
+
export interface Token {
|
|
23
|
+
kind: string;
|
|
24
|
+
text: string;
|
|
25
|
+
span: Span;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TypedToken<Kind extends string> extends Token {
|
|
29
|
+
kind: Kind;
|
|
30
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { tracing } from "mini-parse";
|
|
2
1
|
import type {
|
|
3
2
|
AbstractElem,
|
|
4
3
|
AttributeElem,
|
|
@@ -13,6 +12,7 @@ import type {
|
|
|
13
12
|
} from "./AbstractElems.ts";
|
|
14
13
|
import type { TransformedAST, WeslJsPlugin } from "./Linker.ts";
|
|
15
14
|
import { visitAst } from "./LinkerUtil.ts";
|
|
15
|
+
import { debug } from "./Logging.ts";
|
|
16
16
|
import { findDecl } from "./LowerAndEmit.ts";
|
|
17
17
|
import { minimallyMangledName } from "./Mangler.ts";
|
|
18
18
|
import {
|
|
@@ -307,7 +307,7 @@ export function transformBindingReference(
|
|
|
307
307
|
const refName = memberRef.member.name;
|
|
308
308
|
const structMember = struct.members.find(m => m.name.name === refName)!;
|
|
309
309
|
if (!structMember || !structMember.mangledVarName) {
|
|
310
|
-
if (
|
|
310
|
+
if (debug) console.log(`missing mangledVarName for ${refName}`);
|
|
311
311
|
return { kind: "synthetic", text: refName };
|
|
312
312
|
}
|
|
313
313
|
const { extraComponents } = memberRef;
|
package/src/Util.ts
CHANGED