shaderkit 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +523 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +523 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +37 -4
- package/src/constants.ts +411 -0
- package/src/index.ts +3 -0
- package/src/minifier.ts +121 -0
- package/src/tokenizer.ts +66 -0
package/src/constants.ts
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
// https://registry.khronos.org/OpenGL/specs/es/3.0/GLSL_ES_Specification_3.00.pdf
|
|
2
|
+
// NOTE: restrictions from 5.25-5.26 apply https://registry.khronos.org/webgl/specs/latest/2.0
|
|
3
|
+
export const GLSL_KEYWORDS = [
|
|
4
|
+
// 3.8 Keywords
|
|
5
|
+
'const',
|
|
6
|
+
'uniform',
|
|
7
|
+
'layout',
|
|
8
|
+
'centroid',
|
|
9
|
+
'flat',
|
|
10
|
+
'smooth',
|
|
11
|
+
'break',
|
|
12
|
+
'continue',
|
|
13
|
+
'do',
|
|
14
|
+
'for',
|
|
15
|
+
'while',
|
|
16
|
+
'switch',
|
|
17
|
+
'case',
|
|
18
|
+
'default',
|
|
19
|
+
'if',
|
|
20
|
+
'else',
|
|
21
|
+
'in',
|
|
22
|
+
'out',
|
|
23
|
+
'inout',
|
|
24
|
+
'float',
|
|
25
|
+
'int',
|
|
26
|
+
'void',
|
|
27
|
+
'bool',
|
|
28
|
+
'true',
|
|
29
|
+
'false',
|
|
30
|
+
'invariant',
|
|
31
|
+
'discard',
|
|
32
|
+
'return',
|
|
33
|
+
'mat2',
|
|
34
|
+
'mat3',
|
|
35
|
+
'mat4',
|
|
36
|
+
'mat2x2',
|
|
37
|
+
'mat2x3',
|
|
38
|
+
'mat2x4',
|
|
39
|
+
'mat3x2',
|
|
40
|
+
'mat3x3',
|
|
41
|
+
'mat3x4',
|
|
42
|
+
'mat4x2',
|
|
43
|
+
'mat4x3',
|
|
44
|
+
'mat4x4',
|
|
45
|
+
'vec2',
|
|
46
|
+
'vec3',
|
|
47
|
+
'vec4',
|
|
48
|
+
'ivec2',
|
|
49
|
+
'ivec3',
|
|
50
|
+
'ivec4',
|
|
51
|
+
'bvec2',
|
|
52
|
+
'bvec3',
|
|
53
|
+
'bvec4',
|
|
54
|
+
'uint',
|
|
55
|
+
'uvec2',
|
|
56
|
+
'uvec3',
|
|
57
|
+
'uvec4',
|
|
58
|
+
'lowp',
|
|
59
|
+
'mediump',
|
|
60
|
+
'highp',
|
|
61
|
+
'precision',
|
|
62
|
+
'sampler2D',
|
|
63
|
+
'sampler3D',
|
|
64
|
+
'samplerCube',
|
|
65
|
+
'sampler2DShadow',
|
|
66
|
+
'samplerCubeShadow',
|
|
67
|
+
'sampler2DArray',
|
|
68
|
+
'sampler2DArrayShadow',
|
|
69
|
+
'isampler2D',
|
|
70
|
+
'isampler3D',
|
|
71
|
+
'isamplerCube',
|
|
72
|
+
'isampler2DArray',
|
|
73
|
+
'usampler2D',
|
|
74
|
+
'usampler3D',
|
|
75
|
+
'usamplerCube',
|
|
76
|
+
'usampler2DArray',
|
|
77
|
+
'struct',
|
|
78
|
+
|
|
79
|
+
// 3.8 Keywords - Reserved for future use
|
|
80
|
+
'attribute',
|
|
81
|
+
'varying',
|
|
82
|
+
'coherent',
|
|
83
|
+
'volatile',
|
|
84
|
+
'restrict',
|
|
85
|
+
'readonly',
|
|
86
|
+
'writeonly',
|
|
87
|
+
'resource',
|
|
88
|
+
'atomic_uint',
|
|
89
|
+
'noperspective',
|
|
90
|
+
'patch',
|
|
91
|
+
'sample',
|
|
92
|
+
'subroutine',
|
|
93
|
+
'common',
|
|
94
|
+
'partition',
|
|
95
|
+
'active',
|
|
96
|
+
'asm',
|
|
97
|
+
'class',
|
|
98
|
+
'union',
|
|
99
|
+
'enum',
|
|
100
|
+
'typedef',
|
|
101
|
+
'template',
|
|
102
|
+
'this',
|
|
103
|
+
'goto',
|
|
104
|
+
'inline',
|
|
105
|
+
'noinline',
|
|
106
|
+
'volatile',
|
|
107
|
+
'public',
|
|
108
|
+
'static',
|
|
109
|
+
'extern',
|
|
110
|
+
'external',
|
|
111
|
+
'interface',
|
|
112
|
+
'long',
|
|
113
|
+
'short',
|
|
114
|
+
'double',
|
|
115
|
+
'half',
|
|
116
|
+
'fixed',
|
|
117
|
+
'unsigned',
|
|
118
|
+
'superp',
|
|
119
|
+
'input',
|
|
120
|
+
'output',
|
|
121
|
+
'hvec2',
|
|
122
|
+
'hvec3',
|
|
123
|
+
'hvec4',
|
|
124
|
+
'dvec2',
|
|
125
|
+
'dvec3',
|
|
126
|
+
'dvec4',
|
|
127
|
+
'fvec2',
|
|
128
|
+
'fvec3',
|
|
129
|
+
'fvec4',
|
|
130
|
+
'sampler3DRect',
|
|
131
|
+
'filter',
|
|
132
|
+
'image1D',
|
|
133
|
+
'image2D',
|
|
134
|
+
'image3D',
|
|
135
|
+
'imageCube',
|
|
136
|
+
'iimage1D',
|
|
137
|
+
'iimage2D',
|
|
138
|
+
'iimage3D',
|
|
139
|
+
'iimageCube',
|
|
140
|
+
'uimage1D',
|
|
141
|
+
'uimage2D',
|
|
142
|
+
'uimage3D',
|
|
143
|
+
'uimageCube',
|
|
144
|
+
'image1DArray',
|
|
145
|
+
'image2DArray',
|
|
146
|
+
'iimage1DArray',
|
|
147
|
+
'iimage2DArray',
|
|
148
|
+
'uimage1DArray',
|
|
149
|
+
'uimage2DArray',
|
|
150
|
+
'imageBuffer',
|
|
151
|
+
'iimageBuffer',
|
|
152
|
+
'uimageBuffer',
|
|
153
|
+
'sampler1D',
|
|
154
|
+
'sampler1DShadow',
|
|
155
|
+
'sampler1DArray',
|
|
156
|
+
'sampler1DArrayShadow',
|
|
157
|
+
'isampler1D',
|
|
158
|
+
'isampler1DArray',
|
|
159
|
+
'usampler1D',
|
|
160
|
+
'usampler1DArray',
|
|
161
|
+
'sampler2DRect',
|
|
162
|
+
'sampler2DRectShadow',
|
|
163
|
+
'isampler2DRect',
|
|
164
|
+
'usampler2DRect',
|
|
165
|
+
'samplerBuffer',
|
|
166
|
+
'isamplerBuffer',
|
|
167
|
+
'usamplerBuffer',
|
|
168
|
+
'sampler2DMS',
|
|
169
|
+
'isampler2DMS',
|
|
170
|
+
'usampler2DMS',
|
|
171
|
+
'sampler2DMSArray',
|
|
172
|
+
'isampler2DMSArray',
|
|
173
|
+
'usampler2DMSArray',
|
|
174
|
+
'sizeof',
|
|
175
|
+
'cast',
|
|
176
|
+
'namespace',
|
|
177
|
+
'using',
|
|
178
|
+
|
|
179
|
+
// 3.5 Preprocessor
|
|
180
|
+
'#define',
|
|
181
|
+
'#undef',
|
|
182
|
+
'#if',
|
|
183
|
+
'#ifdef',
|
|
184
|
+
'#ifndef',
|
|
185
|
+
'#else',
|
|
186
|
+
'#elif',
|
|
187
|
+
'#endif',
|
|
188
|
+
'#error',
|
|
189
|
+
'#pragma',
|
|
190
|
+
'#extension',
|
|
191
|
+
'#version',
|
|
192
|
+
'#line',
|
|
193
|
+
|
|
194
|
+
// 3.5 Preprocessor - Operators
|
|
195
|
+
'defined',
|
|
196
|
+
|
|
197
|
+
// 3.5 Preprocessor - Macros
|
|
198
|
+
'__LINE__',
|
|
199
|
+
'__FILE__',
|
|
200
|
+
'__VERSION__',
|
|
201
|
+
'GL_ES',
|
|
202
|
+
|
|
203
|
+
// 7.1 Vertex Shader Special Variables
|
|
204
|
+
'gl_VertexID',
|
|
205
|
+
'gl_InstanceID',
|
|
206
|
+
'gl_Position',
|
|
207
|
+
'gl_PointSize',
|
|
208
|
+
|
|
209
|
+
// 7.2 Fragment Shader Special Variables
|
|
210
|
+
'gl_FragCoord',
|
|
211
|
+
'gl_FrontFacing',
|
|
212
|
+
'gl_FragDepth',
|
|
213
|
+
'gl_PointCoord',
|
|
214
|
+
|
|
215
|
+
// 7.3 Built-in Constants
|
|
216
|
+
'gl_MaxVertexAttribs',
|
|
217
|
+
'gl_MaxVertexUniformVectors',
|
|
218
|
+
'gl_MaxVertexOutputVectors',
|
|
219
|
+
'gl_MaxFragmentInputVectors',
|
|
220
|
+
'gl_MaxVertexTextureImageUnits',
|
|
221
|
+
'gl_MaxCombinedTextureImageUnits',
|
|
222
|
+
'gl_MaxTextureImageUnits',
|
|
223
|
+
'gl_MaxFragmentUniformVectors',
|
|
224
|
+
'gl_MaxDrawBuffers',
|
|
225
|
+
'gl_MinProgramTexelOffset',
|
|
226
|
+
'gl_MaxProgramTexelOffset',
|
|
227
|
+
|
|
228
|
+
// 7.4 Built-in Uniform State
|
|
229
|
+
'gl_DepthRangeParameters',
|
|
230
|
+
'gl_DepthRange',
|
|
231
|
+
|
|
232
|
+
// 8.1 Angle and Trigonometry Functions
|
|
233
|
+
'radians',
|
|
234
|
+
'degrees',
|
|
235
|
+
'sin',
|
|
236
|
+
'cos',
|
|
237
|
+
'tan',
|
|
238
|
+
'asin',
|
|
239
|
+
'acos',
|
|
240
|
+
'atan',
|
|
241
|
+
'sinh',
|
|
242
|
+
'cosh',
|
|
243
|
+
'tanh',
|
|
244
|
+
'asinh',
|
|
245
|
+
'acosh',
|
|
246
|
+
'atanh',
|
|
247
|
+
|
|
248
|
+
// 8.2 Exponential Functions
|
|
249
|
+
'pow',
|
|
250
|
+
'exp',
|
|
251
|
+
'log',
|
|
252
|
+
'exp2',
|
|
253
|
+
'log2',
|
|
254
|
+
'sqrt',
|
|
255
|
+
'inversesqrt',
|
|
256
|
+
|
|
257
|
+
// 8.3 Common Functions
|
|
258
|
+
'abs',
|
|
259
|
+
'sign',
|
|
260
|
+
'floor',
|
|
261
|
+
'trunc',
|
|
262
|
+
'round',
|
|
263
|
+
'roundEven',
|
|
264
|
+
'ceil',
|
|
265
|
+
'fract',
|
|
266
|
+
'mod',
|
|
267
|
+
'modf',
|
|
268
|
+
'min',
|
|
269
|
+
'max',
|
|
270
|
+
'clamp',
|
|
271
|
+
'mix',
|
|
272
|
+
'step',
|
|
273
|
+
'smoothstep',
|
|
274
|
+
'isnan',
|
|
275
|
+
'isinf',
|
|
276
|
+
'floatBitsToInt',
|
|
277
|
+
'floatBitsToUint',
|
|
278
|
+
'intBitsToFloat',
|
|
279
|
+
'uintBitsToFloat',
|
|
280
|
+
|
|
281
|
+
// 8.4 Floating-Point Pack and Unpack Functions
|
|
282
|
+
'packSnorm2x16',
|
|
283
|
+
'unpackSnorm2x16',
|
|
284
|
+
'packUnorm2x16',
|
|
285
|
+
'unpackUnorm2x16',
|
|
286
|
+
'packHalf2x16',
|
|
287
|
+
'unpackHalf2x16',
|
|
288
|
+
|
|
289
|
+
// 8.5 Geometric Functions
|
|
290
|
+
'length',
|
|
291
|
+
'distance',
|
|
292
|
+
'dot',
|
|
293
|
+
'cross',
|
|
294
|
+
'normalize',
|
|
295
|
+
'faceforward',
|
|
296
|
+
'reflect',
|
|
297
|
+
'refract',
|
|
298
|
+
'matrixCompMult',
|
|
299
|
+
'outerProduct',
|
|
300
|
+
'transpose',
|
|
301
|
+
'determinant',
|
|
302
|
+
'inverse',
|
|
303
|
+
|
|
304
|
+
// 8.7 Vector Relational Functions
|
|
305
|
+
'lessThan',
|
|
306
|
+
'lessThanEqual',
|
|
307
|
+
'greaterThan',
|
|
308
|
+
'greaterThanEqual',
|
|
309
|
+
'equal',
|
|
310
|
+
'notEqual',
|
|
311
|
+
'any',
|
|
312
|
+
'all',
|
|
313
|
+
'not',
|
|
314
|
+
|
|
315
|
+
// 8.8 Texture Lookup Functions
|
|
316
|
+
'textureSize',
|
|
317
|
+
'texture',
|
|
318
|
+
'textureProj',
|
|
319
|
+
'textureLod',
|
|
320
|
+
'textureOffset',
|
|
321
|
+
'texelFetch',
|
|
322
|
+
'texelFetchOffset',
|
|
323
|
+
'textureProjOffset',
|
|
324
|
+
'textureLodOffset',
|
|
325
|
+
'textureProjLod',
|
|
326
|
+
'textureProjLodOffset',
|
|
327
|
+
'textureGrad',
|
|
328
|
+
'textureGradOffset',
|
|
329
|
+
'textureProjGrad',
|
|
330
|
+
'textureProjGradOffset',
|
|
331
|
+
|
|
332
|
+
// 8.9 Fragment Processing Functions
|
|
333
|
+
'dFdx',
|
|
334
|
+
'dFdy',
|
|
335
|
+
'fwidth',
|
|
336
|
+
|
|
337
|
+
// Additional directives
|
|
338
|
+
'#include',
|
|
339
|
+
|
|
340
|
+
// WEBGL_multi_draw https://registry.khronos.org/webgl/extensions/WEBGL_multi_draw
|
|
341
|
+
'gl_DrawID', // vertex only
|
|
342
|
+
|
|
343
|
+
// OVR_multiview2 https://registry.khronos.org/webgl/extensions/OVR_multiview2
|
|
344
|
+
// OCULUS_multiview https://github.com/KhronosGroup/WebGL/issues/2912
|
|
345
|
+
'gl_ViewID_OVR',
|
|
346
|
+
'GL_OVR_multiview2',
|
|
347
|
+
]
|
|
348
|
+
|
|
349
|
+
// 3.2 Character Set, 5.1 Operators
|
|
350
|
+
export const GLSL_SYMBOLS = [
|
|
351
|
+
// Preprocessor
|
|
352
|
+
'#',
|
|
353
|
+
|
|
354
|
+
// Line continuation
|
|
355
|
+
'\\',
|
|
356
|
+
|
|
357
|
+
// Comments
|
|
358
|
+
'//',
|
|
359
|
+
'/*',
|
|
360
|
+
'*/',
|
|
361
|
+
|
|
362
|
+
// Punctuation
|
|
363
|
+
'.',
|
|
364
|
+
',',
|
|
365
|
+
';',
|
|
366
|
+
'{',
|
|
367
|
+
'}',
|
|
368
|
+
'(',
|
|
369
|
+
')',
|
|
370
|
+
'[',
|
|
371
|
+
']',
|
|
372
|
+
|
|
373
|
+
// Comparison
|
|
374
|
+
'?',
|
|
375
|
+
':',
|
|
376
|
+
'<',
|
|
377
|
+
'>',
|
|
378
|
+
'<=',
|
|
379
|
+
'>=',
|
|
380
|
+
'&&',
|
|
381
|
+
'||',
|
|
382
|
+
|
|
383
|
+
// Modifier
|
|
384
|
+
'~',
|
|
385
|
+
'=',
|
|
386
|
+
'!',
|
|
387
|
+
'+',
|
|
388
|
+
'-',
|
|
389
|
+
'/',
|
|
390
|
+
'&',
|
|
391
|
+
'|',
|
|
392
|
+
'^',
|
|
393
|
+
'%',
|
|
394
|
+
'<<',
|
|
395
|
+
'>>',
|
|
396
|
+
|
|
397
|
+
// Operators
|
|
398
|
+
'++',
|
|
399
|
+
'--',
|
|
400
|
+
'==',
|
|
401
|
+
'!=',
|
|
402
|
+
'+=',
|
|
403
|
+
'-=',
|
|
404
|
+
'/=',
|
|
405
|
+
'&=',
|
|
406
|
+
'|=',
|
|
407
|
+
'^=',
|
|
408
|
+
'%=',
|
|
409
|
+
'<<=',
|
|
410
|
+
'>>=',
|
|
411
|
+
]
|
package/src/index.ts
ADDED
package/src/minifier.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { type Token, tokenize } from './tokenizer'
|
|
2
|
+
|
|
3
|
+
export type MangleMatcher = (token: Token, index: number, tokens: Token[]) => boolean
|
|
4
|
+
|
|
5
|
+
export interface MinifyOptions {
|
|
6
|
+
/** Whether to rename variables. Will call a {@link MangleMatcher} if specified. Default is `false`. */
|
|
7
|
+
mangle: boolean | MangleMatcher
|
|
8
|
+
/** A map to read and write renamed variables to when mangling. */
|
|
9
|
+
mangleMap: Map<string, string>
|
|
10
|
+
/** Whether to rename external variables such as uniforms or varyings. Default is `false`. */
|
|
11
|
+
mangleExternals: boolean
|
|
12
|
+
/** Whether to rename properties in structs or uniform buffers. Default is `false`. */
|
|
13
|
+
mangleProperties: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Minifies a string of GLSL code.
|
|
18
|
+
*/
|
|
19
|
+
export function minify(
|
|
20
|
+
code: string,
|
|
21
|
+
{
|
|
22
|
+
mangle = false,
|
|
23
|
+
mangleMap = new Map(),
|
|
24
|
+
mangleExternals = false,
|
|
25
|
+
mangleProperties = false,
|
|
26
|
+
}: Partial<MinifyOptions> = {},
|
|
27
|
+
): string {
|
|
28
|
+
const exclude = new Set<string>(mangleMap.values())
|
|
29
|
+
const tokens: Token[] = tokenize(code).filter((token) => token.type !== 'comment')
|
|
30
|
+
|
|
31
|
+
let mangleIndex: number = 0
|
|
32
|
+
let blockIndex: number | null = null
|
|
33
|
+
let prefix: string | null = null
|
|
34
|
+
let minified: string = ''
|
|
35
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
36
|
+
const token = tokens[i]
|
|
37
|
+
if (/keyword|identifier/.test(token.type)) {
|
|
38
|
+
if (/keyword|identifier/.test(tokens[i - 1]?.type)) minified += ' '
|
|
39
|
+
|
|
40
|
+
// Resolve nested keys from members or accessors
|
|
41
|
+
let key = token.value
|
|
42
|
+
if (prefix) {
|
|
43
|
+
key = `${prefix}.${token.value}`
|
|
44
|
+
} else if (tokens[i - 1]?.value === '.' && tokens[i - 2]?.type !== 'symbol') {
|
|
45
|
+
key = `${tokens[i - 2]?.value}.${token.value}`
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Mangle declarations and their references
|
|
49
|
+
let renamed = mangleMap.get(key)
|
|
50
|
+
if (
|
|
51
|
+
// no-op
|
|
52
|
+
!renamed &&
|
|
53
|
+
// Filter variable names
|
|
54
|
+
key !== 'main' &&
|
|
55
|
+
(typeof mangle === 'boolean' ? mangle : mangle(token, i, tokens)) &&
|
|
56
|
+
// Is declaration, reference, namespace, or comma-separated list
|
|
57
|
+
token.type === 'identifier' &&
|
|
58
|
+
(/keyword|identifier/.test(tokens[i - 1]?.type) || /}|,/.test(tokens[i - 1]?.value))
|
|
59
|
+
) {
|
|
60
|
+
if (
|
|
61
|
+
// Skip struct properties when specified
|
|
62
|
+
(!prefix || mangleProperties) &&
|
|
63
|
+
// Skip shader externals when disabled
|
|
64
|
+
(blockIndex != null
|
|
65
|
+
? // Struct member
|
|
66
|
+
(mangleExternals && mangleProperties) ||
|
|
67
|
+
!/(struct|uniform|in|out|attribute|varying)/.test(tokens[blockIndex - 1]?.value)
|
|
68
|
+
: // Struct header, fully specified uniform, or comma-separated list
|
|
69
|
+
mangleExternals ||
|
|
70
|
+
(!/(uniform|in|out|attribute|varying|,)/.test(tokens[i - 1]?.value) &&
|
|
71
|
+
!/(uniform|in|out|attribute|varying)/.test(tokens[i - 2]?.value)))
|
|
72
|
+
) {
|
|
73
|
+
while (!renamed || exclude.has(renamed)) {
|
|
74
|
+
renamed = ''
|
|
75
|
+
mangleIndex++
|
|
76
|
+
|
|
77
|
+
let j = mangleIndex
|
|
78
|
+
while (j > 0) {
|
|
79
|
+
renamed = String.fromCharCode(97 + ((j % 26) - 1)) + renamed
|
|
80
|
+
j = Math.floor(j / 26)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
mangleMap.set(key, renamed)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Start or stop capturing namespaces, prefer UBO suffix if specified.
|
|
88
|
+
// If UBOs don't specify a suffix, their inner declarations are global
|
|
89
|
+
if (tokens[i + 1]?.value === '{') {
|
|
90
|
+
let j = i
|
|
91
|
+
while (tokens[j].value !== '}') j++
|
|
92
|
+
const suffix = tokens[j + 1].value
|
|
93
|
+
if (suffix !== ';') prefix = suffix
|
|
94
|
+
blockIndex = i
|
|
95
|
+
} else if (tokens[i + 2]?.value === '}') {
|
|
96
|
+
prefix = null
|
|
97
|
+
blockIndex = null
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
minified += renamed ?? token.value
|
|
102
|
+
} else if (token.value === '#') {
|
|
103
|
+
// Don't pad consecutive directives
|
|
104
|
+
if (tokens[i - 1]?.value !== '\\') minified += '\n'
|
|
105
|
+
|
|
106
|
+
// Join preprocessor directives
|
|
107
|
+
while (tokens[i].value !== '\\') {
|
|
108
|
+
if ((tokens[i].type !== 'symbol' || tokens[i - 2]?.value === '#') && tokens[i - 1]?.type !== 'symbol')
|
|
109
|
+
minified += ' '
|
|
110
|
+
minified += tokens[i].value
|
|
111
|
+
i++
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
minified += '\n'
|
|
115
|
+
} else {
|
|
116
|
+
minified += token.value
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return minified.trim()
|
|
121
|
+
}
|
package/src/tokenizer.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { GLSL_SYMBOLS, GLSL_KEYWORDS } from './constants'
|
|
2
|
+
|
|
3
|
+
export type TokenType = 'comment' | 'symbol' | 'bool' | 'float' | 'int' | 'uint' | 'identifier' | 'keyword'
|
|
4
|
+
|
|
5
|
+
export interface Token<T = TokenType, V = string> {
|
|
6
|
+
type: T
|
|
7
|
+
value: V
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Tokenizes a string of GLSL code.
|
|
12
|
+
*/
|
|
13
|
+
export function tokenize(code: string, index: number = 0): Token[] {
|
|
14
|
+
const tokens: Token[] = []
|
|
15
|
+
|
|
16
|
+
while (index < code.length) {
|
|
17
|
+
let char = code[index]
|
|
18
|
+
let value = ''
|
|
19
|
+
|
|
20
|
+
// Skip whitespace
|
|
21
|
+
if (/\s/.test(char)) {
|
|
22
|
+
index++
|
|
23
|
+
continue
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Escape newlines after directives, skip comments
|
|
27
|
+
if (char === '#') {
|
|
28
|
+
let j = index
|
|
29
|
+
while (code[j] !== '\n' && !/\/[\/\*]/.test(code[j] + code[j + 1])) j++
|
|
30
|
+
code = code.substring(0, j) + '\\' + code.substring(j)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Parse values and identifiers
|
|
34
|
+
while ((/\d/.test(char) ? /[\d\.\-\w]/ : /\w/).test(code[index])) value += code[index++]
|
|
35
|
+
|
|
36
|
+
// Parse symbols, combine if able
|
|
37
|
+
if (!value) {
|
|
38
|
+
value = char
|
|
39
|
+
for (const symbol of GLSL_SYMBOLS) {
|
|
40
|
+
if (symbol.length > value.length && code.startsWith(symbol, index)) value = symbol
|
|
41
|
+
}
|
|
42
|
+
index += value.length
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Label and add token
|
|
46
|
+
if (/\/[\/\*]/.test(value)) {
|
|
47
|
+
const isMultiline = value === '/*'
|
|
48
|
+
while (!value.endsWith(isMultiline ? '*/' : '\n')) value += code[index++]
|
|
49
|
+
tokens.push({ type: 'comment', value })
|
|
50
|
+
} else if (!/\w/.test(char)) {
|
|
51
|
+
tokens.push({ type: 'symbol', value })
|
|
52
|
+
} else if (/\d/.test(char)) {
|
|
53
|
+
if (/[uU]/.test(value)) tokens.push({ type: 'uint', value })
|
|
54
|
+
else if (/[\.eE]/.test(value)) tokens.push({ type: 'float', value })
|
|
55
|
+
else tokens.push({ type: 'int', value })
|
|
56
|
+
} else if (/true|false/.test(value)) {
|
|
57
|
+
tokens.push({ type: 'bool', value })
|
|
58
|
+
} else if (GLSL_KEYWORDS.includes(tokens[tokens.length - 1]?.value === '#' ? `#${value}` : value)) {
|
|
59
|
+
tokens.push({ type: 'keyword', value })
|
|
60
|
+
} else {
|
|
61
|
+
tokens.push({ type: 'identifier', value })
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return tokens
|
|
66
|
+
}
|