shaderkit 0.6.4 → 0.8.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/LICENSE +21 -21
- package/README.md +781 -781
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/package.json +46 -40
- package/src/ast.ts +425 -425
- package/src/constants.ts +1005 -1005
- package/src/generator.ts +128 -128
- package/src/hoister.ts +171 -0
- package/src/index.ts +7 -7
- package/src/minifier.ts +445 -159
- package/src/parser.ts +878 -802
- package/src/tokenizer.ts +105 -89
- package/src/visitor.ts +131 -131
package/src/minifier.ts
CHANGED
|
@@ -1,159 +1,445 @@
|
|
|
1
|
-
import { type Token, tokenize } from './tokenizer.js'
|
|
2
|
-
import { GLSL_KEYWORDS, WGSL_KEYWORDS } from './constants.js'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/** Whether to rename
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
(tokens[i -
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
1
|
+
import { type Token, tokenize } from './tokenizer.js'
|
|
2
|
+
import { GLSL_KEYWORDS, WGSL_KEYWORDS } from './constants.js'
|
|
3
|
+
import { parse } from './parser.js'
|
|
4
|
+
import { generate } from './generator.js'
|
|
5
|
+
import { visit } from './visitor.js'
|
|
6
|
+
import { ArraySpecifier } from './ast.js'
|
|
7
|
+
|
|
8
|
+
export type MangleMatcher = (token: Token, index: number, tokens: Token[]) => boolean
|
|
9
|
+
|
|
10
|
+
export interface MinifyOptions {
|
|
11
|
+
/** Whether to rename variables. Will call a {@link MangleMatcher} if specified. Default is `false`. */
|
|
12
|
+
mangle: boolean | MangleMatcher
|
|
13
|
+
/** A map to read and write renamed variables to when mangling. */
|
|
14
|
+
mangleMap: Map<string, string>
|
|
15
|
+
/** Whether to rename external variables such as uniforms or varyings. Default is `false`. */
|
|
16
|
+
mangleExternals: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const isWord = /* @__PURE__ */ RegExp.prototype.test.bind(/^\w/)
|
|
20
|
+
const isName = /* @__PURE__ */ RegExp.prototype.test.bind(/^[_A-Za-z]/)
|
|
21
|
+
const isScoped = /* @__PURE__ */ RegExp.prototype.test.bind(/[;{}\\@]/)
|
|
22
|
+
const isStorage = /* @__PURE__ */ RegExp.prototype.test.bind(
|
|
23
|
+
/^(binding|group|layout|uniform|in|out|attribute|varying)$/,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
// Checks for WGSL-specific `fn foo(`, `var bar =`, `let baz =`, `const qux =`
|
|
27
|
+
const WGSL_REGEX = /\bfn\s+\w+\s*\(|\b(var|let|const)\s+\w+\s*[:=]/
|
|
28
|
+
|
|
29
|
+
function minifyLegacy(
|
|
30
|
+
code: string,
|
|
31
|
+
{ mangle = false, mangleMap = new Map(), mangleExternals = false }: Partial<MinifyOptions> = {},
|
|
32
|
+
): string {
|
|
33
|
+
const mangleCache = new Map<string, string>()
|
|
34
|
+
const tokens: Token[] = tokenize(code).filter((token) => token.type !== 'whitespace' && token.type !== 'comment')
|
|
35
|
+
|
|
36
|
+
let mangleIndex: number = -1
|
|
37
|
+
let lineIndex: number = -1
|
|
38
|
+
let blockIndex: number = -1
|
|
39
|
+
let minified: string = ''
|
|
40
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
41
|
+
const token = tokens[i]
|
|
42
|
+
|
|
43
|
+
// Track possibly external scopes
|
|
44
|
+
if (isStorage(token.value) || isScoped(tokens[i - 1]?.value)) lineIndex = i
|
|
45
|
+
|
|
46
|
+
// Mark enter/leave block-scope
|
|
47
|
+
if (token.value === '{' && isName(tokens[i - 1]?.value)) blockIndex = i - 1
|
|
48
|
+
else if (token.value === '}') blockIndex = -1
|
|
49
|
+
|
|
50
|
+
// Pad alphanumeric tokens
|
|
51
|
+
if (isWord(token.value) && isWord(tokens[i - 1]?.value)) minified += ' '
|
|
52
|
+
|
|
53
|
+
let prefix = token.value
|
|
54
|
+
if (tokens[i - 1]?.value === '.') {
|
|
55
|
+
prefix = `${tokens[i - 2]?.value}.` + prefix
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Mangle declarations and their references
|
|
59
|
+
if (token.type === 'identifier' && (typeof mangle === 'boolean' ? mangle : mangle(token, i, tokens))) {
|
|
60
|
+
const namespace = tokens[i - 1]?.value === '}' && tokens[i + 1]?.value === ';'
|
|
61
|
+
const storage = isStorage(tokens[lineIndex]?.value)
|
|
62
|
+
const list = storage && tokens[i - 1]?.value === ','
|
|
63
|
+
let renamed = mangleMap.get(prefix) ?? mangleCache.get(prefix)
|
|
64
|
+
if (
|
|
65
|
+
// no-op
|
|
66
|
+
!renamed &&
|
|
67
|
+
// Skip struct properties
|
|
68
|
+
blockIndex === -1 &&
|
|
69
|
+
// Is declaration, reference, namespace, or comma-separated list
|
|
70
|
+
(isName(tokens[i - 1]?.value) ||
|
|
71
|
+
// uniform Type { ... } name;
|
|
72
|
+
namespace ||
|
|
73
|
+
// uniform float foo, bar;
|
|
74
|
+
list ||
|
|
75
|
+
// fn (arg: type) -> void
|
|
76
|
+
(tokens[i - 1]?.type === 'symbol' && tokens[i + 1]?.value === ':')) &&
|
|
77
|
+
// Skip shader externals when disabled
|
|
78
|
+
(mangleExternals || !storage)
|
|
79
|
+
) {
|
|
80
|
+
// Write shader externals and preprocessor defines to mangleMap for multiple passes
|
|
81
|
+
// TODO: do so via scope tracking
|
|
82
|
+
const isExternal =
|
|
83
|
+
// Shader externals
|
|
84
|
+
(mangleExternals && storage) ||
|
|
85
|
+
// Defines
|
|
86
|
+
tokens[i - 2]?.value === '#' ||
|
|
87
|
+
// Namespaced uniform structs
|
|
88
|
+
namespace ||
|
|
89
|
+
// Comma-separated list of uniforms
|
|
90
|
+
list ||
|
|
91
|
+
// WGSL entrypoints via @stage or @workgroup_size(...)
|
|
92
|
+
(tokens[i - 1]?.value === 'fn' && (tokens[i - 2]?.value === ')' || tokens[i - 3]?.value === '@'))
|
|
93
|
+
const cache = isExternal ? mangleMap : mangleCache
|
|
94
|
+
|
|
95
|
+
while (!renamed || cache.has(renamed) || WGSL_KEYWORDS.includes(renamed)) {
|
|
96
|
+
renamed = ''
|
|
97
|
+
mangleIndex++
|
|
98
|
+
|
|
99
|
+
let j = mangleIndex
|
|
100
|
+
while (j > 0) {
|
|
101
|
+
renamed = String.fromCharCode(97 + ((j - 1) % 26)) + renamed
|
|
102
|
+
j = Math.floor(j / 26)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
cache.set(prefix, renamed)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
minified += renamed ?? token.value
|
|
110
|
+
} else {
|
|
111
|
+
if (token.value === '\\') minified += '\n'
|
|
112
|
+
else minified += token.value
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return minified.trim()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface Scope {
|
|
120
|
+
// types: Map<string, string>
|
|
121
|
+
values: Map<string, string>
|
|
122
|
+
references: Map<string, string>
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Minifies a string of GLSL or WGSL code.
|
|
127
|
+
*/
|
|
128
|
+
export function minify(
|
|
129
|
+
code: string,
|
|
130
|
+
{ mangle = false, mangleMap = new Map(), mangleExternals = false }: Partial<MinifyOptions> = {},
|
|
131
|
+
): string {
|
|
132
|
+
const isWGSL = WGSL_REGEX.test(code)
|
|
133
|
+
const KEYWORDS = isWGSL ? WGSL_KEYWORDS : GLSL_KEYWORDS
|
|
134
|
+
|
|
135
|
+
// TODO: remove when WGSL is better supported
|
|
136
|
+
if (isWGSL) return minifyLegacy(code, { mangle, mangleMap, mangleExternals })
|
|
137
|
+
|
|
138
|
+
const program = parse(code)
|
|
139
|
+
|
|
140
|
+
if (mangle) {
|
|
141
|
+
const scopes: Scope[] = []
|
|
142
|
+
|
|
143
|
+
function pushScope(): void {
|
|
144
|
+
scopes.push({ values: new Map(), references: new Map() })
|
|
145
|
+
}
|
|
146
|
+
function popScope(): void {
|
|
147
|
+
scopes.length -= 1
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function getScopedType(name: string): string | null {
|
|
151
|
+
for (let i = scopes.length - 1; i >= 0; i--) {
|
|
152
|
+
const type = scopes[i].references.get(name)
|
|
153
|
+
if (type) return type
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const typeScopes = new Map<string, Scope>()
|
|
160
|
+
const types: (string | null)[] = []
|
|
161
|
+
|
|
162
|
+
function getScopedName(name: string): string | null {
|
|
163
|
+
if (types.length === 0 && mangleMap.has(name)) {
|
|
164
|
+
return mangleMap.get(name)!
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (types[0] != null && typeScopes.has(types[0])) {
|
|
168
|
+
const scope = typeScopes.get(types[0])!
|
|
169
|
+
const renamed = scope.values.get(name)
|
|
170
|
+
if (renamed) return renamed
|
|
171
|
+
} else {
|
|
172
|
+
for (let i = scopes.length - 1; i >= 0; i--) {
|
|
173
|
+
const renamed = scopes[i].values.get(name)
|
|
174
|
+
if (renamed) return renamed
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return null
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let mangleIndex: number = -1
|
|
182
|
+
function mangleName(name: string, isExternal: boolean): string {
|
|
183
|
+
let renamed = (isExternal && mangleMap.get(name)) || getScopedName(name)
|
|
184
|
+
|
|
185
|
+
while (
|
|
186
|
+
!renamed ||
|
|
187
|
+
getScopedName(renamed) !== null ||
|
|
188
|
+
(isExternal && mangleMap.has(renamed)) ||
|
|
189
|
+
KEYWORDS.includes(renamed)
|
|
190
|
+
) {
|
|
191
|
+
renamed = ''
|
|
192
|
+
mangleIndex++
|
|
193
|
+
|
|
194
|
+
let j = mangleIndex
|
|
195
|
+
while (j > 0) {
|
|
196
|
+
renamed = String.fromCharCode(97 + ((j - 1) % 26)) + renamed
|
|
197
|
+
j = Math.floor(j / 26)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
scopes.at(-1)!.values.set(name, renamed)
|
|
202
|
+
if (isExternal) {
|
|
203
|
+
if (types[0] != null) mangleMap.set(types[0] + '.' + name, renamed)
|
|
204
|
+
else mangleMap.set(name, renamed)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return renamed
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const structs = new Set<string>()
|
|
211
|
+
const externals = new Set<string>()
|
|
212
|
+
const externalTypes = new Set<string>()
|
|
213
|
+
|
|
214
|
+
// Top-level pass for externals and type definitions
|
|
215
|
+
for (const statement of program.body) {
|
|
216
|
+
if (statement.type === 'StructDeclaration') {
|
|
217
|
+
structs.add(statement.id.name)
|
|
218
|
+
} else if (statement.type === 'StructuredBufferDeclaration') {
|
|
219
|
+
const isExternal = statement.qualifiers.some(isStorage)
|
|
220
|
+
|
|
221
|
+
if (statement.typeSpecifier.type === 'Identifier') {
|
|
222
|
+
structs.add(statement.typeSpecifier.name)
|
|
223
|
+
if (isExternal) externalTypes.add(statement.typeSpecifier.name)
|
|
224
|
+
} else if (statement.typeSpecifier.type === 'ArraySpecifier') {
|
|
225
|
+
structs.add(statement.typeSpecifier.typeSpecifier.name)
|
|
226
|
+
if (isExternal) externalTypes.add(statement.typeSpecifier.typeSpecifier.name)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (isExternal) {
|
|
230
|
+
if (statement.id) {
|
|
231
|
+
externals.add(statement.id.name)
|
|
232
|
+
} else {
|
|
233
|
+
for (const member of statement.members) {
|
|
234
|
+
if (member.type !== 'VariableDeclaration') continue
|
|
235
|
+
|
|
236
|
+
for (const decl of member.declarations) {
|
|
237
|
+
if (decl.id.type === 'Identifier') {
|
|
238
|
+
externals.add(decl.id.name)
|
|
239
|
+
} else if (decl.id.type === 'ArraySpecifier') {
|
|
240
|
+
externals.add((decl.id as unknown as ArraySpecifier).typeSpecifier.name)
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
} else if (statement.type === 'VariableDeclaration') {
|
|
247
|
+
for (const decl of statement.declarations) {
|
|
248
|
+
const isExternal = decl.qualifiers.some(isStorage)
|
|
249
|
+
if (isExternal) {
|
|
250
|
+
if (decl.id.type === 'Identifier') {
|
|
251
|
+
externals.add(decl.id.name)
|
|
252
|
+
} else if (decl.id.type === 'ArraySpecifier') {
|
|
253
|
+
externals.add((decl.id as unknown as ArraySpecifier).typeSpecifier.name)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
visit(program, {
|
|
261
|
+
Program: {
|
|
262
|
+
enter() {
|
|
263
|
+
pushScope()
|
|
264
|
+
},
|
|
265
|
+
exit() {
|
|
266
|
+
popScope()
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
BlockStatement: {
|
|
270
|
+
enter() {
|
|
271
|
+
pushScope()
|
|
272
|
+
},
|
|
273
|
+
exit() {
|
|
274
|
+
popScope()
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
FunctionDeclaration: {
|
|
278
|
+
enter(node) {
|
|
279
|
+
const name = node.id.name
|
|
280
|
+
|
|
281
|
+
// TODO: this might be external in the case of WGSL entrypoints
|
|
282
|
+
if (name !== 'main') node.id.name = mangleName(name, false)
|
|
283
|
+
|
|
284
|
+
const scope = scopes.at(-1)!
|
|
285
|
+
if (node.typeSpecifier.type === 'Identifier') {
|
|
286
|
+
scope.references.set(name, node.typeSpecifier.name)
|
|
287
|
+
} else if (node.typeSpecifier.type === 'ArraySpecifier') {
|
|
288
|
+
scope.references.set(name, node.typeSpecifier.typeSpecifier.name)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
pushScope()
|
|
292
|
+
|
|
293
|
+
for (const param of node.params) {
|
|
294
|
+
if (param.id) param.id.name = mangleName(param.id.name, false)
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
exit() {
|
|
298
|
+
popScope()
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
StructDeclaration: {
|
|
302
|
+
enter(node) {
|
|
303
|
+
const name = node.id.name
|
|
304
|
+
|
|
305
|
+
const isExternal = externalTypes.has(node.id.name)
|
|
306
|
+
if (!isExternal || mangleExternals) {
|
|
307
|
+
node.id.name = mangleName(name, isExternal)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
pushScope()
|
|
311
|
+
typeScopes.set(name, scopes.at(-1)!)
|
|
312
|
+
types.push(name)
|
|
313
|
+
},
|
|
314
|
+
exit() {
|
|
315
|
+
types.length -= 1
|
|
316
|
+
popScope()
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
StructuredBufferDeclaration: {
|
|
320
|
+
enter(node) {
|
|
321
|
+
if (node.typeSpecifier.type !== 'Identifier') return
|
|
322
|
+
|
|
323
|
+
// When an instance name is not defined, the type specifier can be used as an external reference
|
|
324
|
+
const typeName = node.typeSpecifier.name
|
|
325
|
+
if (node.id || mangleExternals) {
|
|
326
|
+
node.typeSpecifier.name = mangleName(typeName, false)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (!node.id) return
|
|
330
|
+
|
|
331
|
+
const name = node.id.name
|
|
332
|
+
|
|
333
|
+
const isExternal = externalTypes.has(typeName)
|
|
334
|
+
if (!isExternal || mangleExternals) {
|
|
335
|
+
node.id.name = mangleName(name, isExternal)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const scope = scopes.at(-1)!
|
|
339
|
+
scope.references.set(name, typeName)
|
|
340
|
+
types.push(typeName)
|
|
341
|
+
|
|
342
|
+
pushScope()
|
|
343
|
+
typeScopes.set(name, scopes.at(-1)!)
|
|
344
|
+
},
|
|
345
|
+
exit(node) {
|
|
346
|
+
if (node.id) {
|
|
347
|
+
types.length -= 1
|
|
348
|
+
popScope()
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
VariableDeclaration(node, ancestors) {
|
|
353
|
+
// TODO: ensure uniform decl lists work
|
|
354
|
+
const parent = ancestors.at(-1) // Container -> VariableDecl
|
|
355
|
+
const isParentExternal =
|
|
356
|
+
parent?.type === 'StructDeclaration' ||
|
|
357
|
+
(parent?.type === 'StructuredBufferDeclaration' && parent.qualifiers.some(isStorage))
|
|
358
|
+
|
|
359
|
+
for (const decl of node.declarations) {
|
|
360
|
+
// Skip preprocessor
|
|
361
|
+
if (decl.type !== 'VariableDeclarator') continue
|
|
362
|
+
|
|
363
|
+
let name: string = ''
|
|
364
|
+
if (decl.id.type === 'Identifier') {
|
|
365
|
+
name = decl.id.name
|
|
366
|
+
} else if (decl.id.type === 'ArraySpecifier') {
|
|
367
|
+
name = (decl.id as unknown as ArraySpecifier).typeSpecifier.name
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const scope = scopes.at(-1)!
|
|
371
|
+
if (decl.typeSpecifier.type === 'Identifier') {
|
|
372
|
+
scope.references.set(name, decl.typeSpecifier.name)
|
|
373
|
+
} else if (decl.typeSpecifier.type === 'ArraySpecifier') {
|
|
374
|
+
scope.references.set(name, decl.typeSpecifier.typeSpecifier.name)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const isExternal = isParentExternal || decl.qualifiers.some(isStorage)
|
|
378
|
+
if (!isExternal || mangleExternals) {
|
|
379
|
+
mangleName(name, isExternal)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
PreprocessorStatement(node) {
|
|
384
|
+
if (node.name === 'define' && node.value) {
|
|
385
|
+
const [name, value] = node.value
|
|
386
|
+
|
|
387
|
+
let isExternal = false
|
|
388
|
+
|
|
389
|
+
if (value) {
|
|
390
|
+
if (value.type === 'Identifier') {
|
|
391
|
+
isExternal ||= externals.has(value.name) || externalTypes.has(value.name)
|
|
392
|
+
if (!isExternal || mangleExternals) value.name = mangleName(value.name, isExternal)
|
|
393
|
+
} else if (value.type === 'MemberExpression') {
|
|
394
|
+
// TODO: this needs to be more robust to handle string replacement
|
|
395
|
+
} else if (value.type === 'CallExpression' && value.callee.type === 'Identifier') {
|
|
396
|
+
isExternal ||= externals.has(value.callee.name)
|
|
397
|
+
if (!isExternal || mangleExternals) value.callee.name = mangleName(value.callee.name, isExternal)
|
|
398
|
+
// TODO: locally mangle arguments
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (name.type === 'Identifier') {
|
|
403
|
+
isExternal ||= externals.has(name.name) || externalTypes.has(name.name)
|
|
404
|
+
if (!isExternal || mangleExternals) name.name = mangleName(name.name, isExternal)
|
|
405
|
+
} else if (name.type === 'MemberExpression') {
|
|
406
|
+
// TODO: this needs to be more robust to handle string replacement
|
|
407
|
+
} else if (name.type === 'CallExpression' && name.callee.type === 'Identifier') {
|
|
408
|
+
isExternal ||= externals.has(name.callee.name)
|
|
409
|
+
if (!isExternal || mangleExternals) name.callee.name = mangleName(name.callee.name, isExternal)
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
MemberExpression: {
|
|
414
|
+
enter(node) {
|
|
415
|
+
let type: string | null = ''
|
|
416
|
+
|
|
417
|
+
if (node.object.type === 'CallExpression' && node.object.callee.type === 'Identifier') {
|
|
418
|
+
// TODO: length() should be mangled whereas array.length() should not
|
|
419
|
+
type = getScopedType(node.object.callee.name)
|
|
420
|
+
} else if (node.object.type === 'MemberExpression' && node.object.object.type === 'Identifier') {
|
|
421
|
+
// Only computed member expressions can be parsed this way (e.g., (array[2]).position)
|
|
422
|
+
type = getScopedType(node.object.object.name)
|
|
423
|
+
const renamed = getScopedName(node.object.object.name)
|
|
424
|
+
if (renamed !== null) node.object.object.name = renamed
|
|
425
|
+
} else if (node.object.type === 'Identifier') {
|
|
426
|
+
type = getScopedType(node.object.name)
|
|
427
|
+
const renamed = getScopedName(node.object.name)
|
|
428
|
+
if (renamed !== null) node.object.name = renamed
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
types.push(type)
|
|
432
|
+
},
|
|
433
|
+
exit() {
|
|
434
|
+
types.length -= 1
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
Identifier(node) {
|
|
438
|
+
const renamed = getScopedName(node.name)
|
|
439
|
+
if (renamed !== null) node.name = renamed
|
|
440
|
+
},
|
|
441
|
+
})
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return generate(program, { target: 'GLSL' })
|
|
445
|
+
}
|