@shaderfrog/core 1.5.3 → 2.0.0-beta.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/graph/context.d.ts +1 -1
- package/graph/graph.d.ts +2 -1
- package/graph/graph.js +10 -9
- package/graph/graph.test.js +148 -55
- package/graph/parsers.js +3 -6
- package/graph/shader-sections.js +3 -8
- package/package.json +8 -9
- package/plugins/babylon/importers.js +1 -1
- package/plugins/three/importers.js +1 -1
- package/plugins/three/threngine.test.d.ts +1 -0
- package/plugins/three/threngine.test.js +89 -0
- package/strategy/assignemntTo.d.ts +2 -1
- package/strategy/assignemntTo.js +8 -5
- package/strategy/inject.js +1 -31
- package/strategy/stratgies.test.js +58 -13
- package/strategy/uniform.js +2 -2
- package/util/ast.d.ts +29 -11
- package/util/ast.js +98 -68
- package/util/whitespace.d.ts +18 -0
- package/util/whitespace.js +91 -0
- package/util/whitespace.test.d.ts +1 -0
- package/util/whitespace.test.js +13 -0
package/graph/context.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type GlslSyntaxError } from '@shaderfrog/glsl-parser';
|
|
2
2
|
import { AstNode, Program } from '@shaderfrog/glsl-parser/ast';
|
|
3
3
|
import { Engine, EngineContext } from '../engine';
|
|
4
4
|
import { NodeInput } from './base-node';
|
package/graph/graph.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Program } from '@shaderfrog/glsl-parser/ast';
|
|
|
2
2
|
import { Engine, EngineContext } from '../engine';
|
|
3
3
|
import { NodeErrors } from './context';
|
|
4
4
|
import { ShaderSections } from './shader-sections';
|
|
5
|
+
import { FrogProgram } from '../util/ast';
|
|
5
6
|
import { DataNode } from './data-nodes';
|
|
6
7
|
import { Edge } from './edge';
|
|
7
8
|
import { CodeNode, SourceNode } from './code-nodes';
|
|
@@ -15,7 +16,7 @@ export declare const doesLinkThruShader: (graph: Graph, node: GraphNode) => bool
|
|
|
15
16
|
export declare const nodeName: (node: GraphNode) => string;
|
|
16
17
|
export declare const mangleName: (name: string, node: GraphNode, nextSibling?: GraphNode) => string;
|
|
17
18
|
export declare const mangleVar: (name: string, engine: Engine, node: GraphNode, sibling?: GraphNode) => string;
|
|
18
|
-
export declare const mangleEntireProgram: (engine: Engine, ast:
|
|
19
|
+
export declare const mangleEntireProgram: (engine: Engine, ast: FrogProgram, node: GraphNode, sibling?: GraphNode) => void;
|
|
19
20
|
export declare const mangleMainFn: (ast: Program, node: GraphNode, sibling?: GraphNode) => void;
|
|
20
21
|
export declare const ensureFromNode: (graph: Graph, inputEdge: Edge) => GraphNode;
|
|
21
22
|
export declare const resetGraphIds: (graph: Graph) => Graph;
|
package/graph/graph.js
CHANGED
|
@@ -130,13 +130,13 @@ export var mangleName = function (name, node, nextSibling) {
|
|
|
130
130
|
};
|
|
131
131
|
export var mangleVar = function (name, engine, node, sibling) { return (engine.preserve.has(name) ? name : mangleName(name, node, sibling)); };
|
|
132
132
|
export var mangleEntireProgram = function (engine, ast, node, sibling) {
|
|
133
|
-
renameBindings(ast.scopes[0], function (name
|
|
134
|
-
return
|
|
133
|
+
ast.scopes[0].bindings = renameBindings(ast.scopes[0].bindings, function (name) {
|
|
134
|
+
return name === ast.outVar ? name : mangleVar(name, engine, node, sibling);
|
|
135
135
|
});
|
|
136
136
|
mangleMainFn(ast, node, sibling);
|
|
137
137
|
};
|
|
138
138
|
export var mangleMainFn = function (ast, node, sibling) {
|
|
139
|
-
renameFunctions(ast.scopes[0], function (name) {
|
|
139
|
+
ast.scopes[0].functions = renameFunctions(ast.scopes[0].functions, function (name) {
|
|
140
140
|
return name === 'main' ? nodeName(node) : mangleName(name, node, sibling);
|
|
141
141
|
});
|
|
142
142
|
};
|
|
@@ -365,11 +365,11 @@ export var compileNode = function (engine, graph, edges, engineContext, node, ac
|
|
|
365
365
|
];
|
|
366
366
|
// @ts-ignore
|
|
367
367
|
var scope = fc.ast.scopes[0];
|
|
368
|
-
renameBindings(scope,
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
});
|
|
368
|
+
// renameBindings(scope, (name, node) => {
|
|
369
|
+
// return node.type !== 'declaration' && name === 'vUv'
|
|
370
|
+
// ? 'vv'
|
|
371
|
+
// : name;
|
|
372
|
+
// });
|
|
373
373
|
}
|
|
374
374
|
// })
|
|
375
375
|
}
|
|
@@ -482,7 +482,8 @@ export var compileSource = function (graph, engine, ctx) { return __awaiter(void
|
|
|
482
482
|
return [2 /*return*/, result];
|
|
483
483
|
}
|
|
484
484
|
compileResult = compileGraph(ctx, engine, graph);
|
|
485
|
-
fragmentResult = generate(shaderSectionsToProgram(compileResult.fragment, engine.mergeOptions)
|
|
485
|
+
fragmentResult = generate(shaderSectionsToProgram(compileResult.fragment, engine.mergeOptions)
|
|
486
|
+
.program);
|
|
486
487
|
vertexResult = generate(shaderSectionsToProgram(compileResult.vertex, engine.mergeOptions).program);
|
|
487
488
|
dataInputs = filterGraphNodes(graph, [compileResult.outputFrag, compileResult.outputVert], { input: isDataInput }).inputs;
|
|
488
489
|
dataNodes = Object.entries(dataInputs).reduce(function (acc, _a) {
|
package/graph/graph.test.js
CHANGED
|
@@ -1,11 +1,52 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
12
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
import { expect, describe, it } from 'vitest';
|
|
1
38
|
import util from 'util';
|
|
2
39
|
import { parser } from '@shaderfrog/glsl-parser';
|
|
3
40
|
import { generate } from '@shaderfrog/glsl-parser';
|
|
4
|
-
import { addNode } from './graph-node';
|
|
41
|
+
import { addNode, outputNode, sourceNode } from './graph-node';
|
|
5
42
|
import { shaderSectionsToProgram, mergeShaderSections, findShaderSections, } from './shader-sections';
|
|
6
43
|
import { numberNode } from './data-nodes';
|
|
7
44
|
import { makeEdge } from './edge';
|
|
8
45
|
import { evaluateNode } from './evaluate';
|
|
46
|
+
import { compileSource } from './graph';
|
|
47
|
+
import { texture2DStrategy } from '../strategy';
|
|
48
|
+
import { isError } from './context';
|
|
49
|
+
import { fail } from '../test-util';
|
|
9
50
|
var inspect = function (thing) {
|
|
10
51
|
return console.log(util.inspect(thing, false, null, true));
|
|
11
52
|
};
|
|
@@ -65,60 +106,112 @@ var engine = {
|
|
|
65
106
|
preserve: new Set(),
|
|
66
107
|
parsers: {},
|
|
67
108
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
109
|
+
var makeSourceNode = function (id, source, stage, strategies) {
|
|
110
|
+
if (strategies === void 0) { strategies = [texture2DStrategy()]; }
|
|
111
|
+
return sourceNode(id, "Shader ".concat(id), p, {
|
|
112
|
+
version: 2,
|
|
113
|
+
preprocess: false,
|
|
114
|
+
strategies: strategies,
|
|
115
|
+
uniforms: [],
|
|
116
|
+
}, source, stage);
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* What exactly am I doing here?
|
|
120
|
+
*
|
|
121
|
+
* I opened shaderfrog to start looking at the backfilling case and inlining
|
|
122
|
+
* function calls at the top of functions
|
|
123
|
+
*
|
|
124
|
+
* WHie doing that I found jest not to work well anyore and switched to vitest,
|
|
125
|
+
* which is fine, but with esm by default I can't stub the mangleName() function
|
|
126
|
+
* call, which means mangling happens as-is in the tests.
|
|
127
|
+
*
|
|
128
|
+
* Without changing the mangling strategy, the strategies.test.ts file fails
|
|
129
|
+
* because the uniform strategy looks for a mangled variable name, but the
|
|
130
|
+
* program itself isn't mangled.
|
|
131
|
+
*
|
|
132
|
+
* One way to fix this is to make fillers not have to care about mangling names,
|
|
133
|
+
* which would be simpler on the surface.
|
|
134
|
+
*
|
|
135
|
+
* Then everything in the tests broke and you found out the reason why was
|
|
136
|
+
* trying to use scopes to rename things, and most of the ast manipulation steps
|
|
137
|
+
* don't modify scopes, so you made some of them modify scopes, and now things
|
|
138
|
+
* are fucked
|
|
139
|
+
*/
|
|
140
|
+
it('compileSource() fragment produces inlined output', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
141
|
+
var outV, outF, imageReplacemMe, input1, input2, graph, engineContext, result, imgOut;
|
|
142
|
+
return __generator(this, function (_a) {
|
|
143
|
+
switch (_a.label) {
|
|
144
|
+
case 0:
|
|
145
|
+
outV = outputNode(id(), 'Output v', p, 'vertex');
|
|
146
|
+
outF = outputNode(id(), 'Output f', p, 'fragment');
|
|
147
|
+
imageReplacemMe = makeSourceNode(id(), "uniform sampler2D image1;\nuniform sampler2D image2;\nvoid main() {\n vec3 col1 = texture2D(image1, posTurn - 0.4 * time).rgb + 1.0;\n vec3 col2 = texture2D(image2, negTurn - 0.4 * time).rgb + 2.0;\n gl_FragColor = vec4(col1 + col2, 1.0);\n}\n", 'fragment');
|
|
148
|
+
input1 = makeSourceNode(id(), "float a = 1.0;\nvoid main() {\n gl_FragColor = vec4(0.0);\n}\n", 'fragment');
|
|
149
|
+
input2 = makeSourceNode(id(), "float a = 2.0;\nvoid main() {\n gl_FragColor = vec4(1.0);\n}\n", 'fragment');
|
|
150
|
+
graph = {
|
|
151
|
+
nodes: [outV, outF, imageReplacemMe, input1, input2],
|
|
152
|
+
edges: [
|
|
153
|
+
makeEdge(id(), imageReplacemMe.id, outF.id, 'out', 'filler_frogFragOut', 'fragment'),
|
|
154
|
+
makeEdge(id(), input1.id, imageReplacemMe.id, 'out', 'filler_image1', 'fragment'),
|
|
155
|
+
makeEdge(id(), input2.id, imageReplacemMe.id, 'out', 'filler_image2', 'fragment'),
|
|
156
|
+
makeEdge(id(), input2.id, imageReplacemMe.id, 'out', 'filler_image2', 'fragment'),
|
|
157
|
+
],
|
|
158
|
+
};
|
|
159
|
+
engineContext = {
|
|
160
|
+
engine: 'three',
|
|
161
|
+
nodes: {},
|
|
162
|
+
runtime: {},
|
|
163
|
+
debuggingNonsense: {},
|
|
164
|
+
};
|
|
165
|
+
return [4 /*yield*/, compileSource(graph, engine, engineContext)];
|
|
166
|
+
case 1:
|
|
167
|
+
result = _a.sent();
|
|
168
|
+
if (isError(result)) {
|
|
169
|
+
fail(result);
|
|
170
|
+
}
|
|
171
|
+
expect(result.fragmentResult).toContain("vec4 main_Shader_".concat(input1.id, "() {"));
|
|
172
|
+
expect(result.fragmentResult).toContain("vec4 main_Shader_".concat(input2.id, "() {"));
|
|
173
|
+
imgOut = "frogOut_".concat(imageReplacemMe.id);
|
|
174
|
+
expect(result.fragmentResult).toContain("vec4 ".concat(imgOut, ";"));
|
|
175
|
+
expect(result.fragmentResult)
|
|
176
|
+
.toContain("vec4 main_Shader_".concat(imageReplacemMe.id, "() {\n vec3 col1 = main_Shader_").concat(input1.id, "().rgb + 1.0;\n vec3 col2 = main_Shader_").concat(input2.id, "().rgb + 2.0;\n ").concat(imgOut, " = vec4(col1 + col2, 1.0);\n return ").concat(imgOut, ";\n}"));
|
|
177
|
+
return [2 /*return*/];
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}); });
|
|
181
|
+
it('compileSource() base case', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
182
|
+
var outV, outF, imageReplacemMe, graph, engineContext, result, imgOut;
|
|
183
|
+
return __generator(this, function (_a) {
|
|
184
|
+
switch (_a.label) {
|
|
185
|
+
case 0:
|
|
186
|
+
outV = outputNode(id(), 'Output v', p, 'vertex');
|
|
187
|
+
outF = outputNode(id(), 'Output f', p, 'fragment');
|
|
188
|
+
imageReplacemMe = makeSourceNode(id(), "float a = 1.0;\nvoid main() {\n gl_FragColor = vec4(1.0);\n}\n", 'fragment');
|
|
189
|
+
graph = {
|
|
190
|
+
nodes: [outV, outF, imageReplacemMe],
|
|
191
|
+
edges: [
|
|
192
|
+
makeEdge(id(), imageReplacemMe.id, outF.id, 'out', 'filler_frogFragOut', 'fragment'),
|
|
193
|
+
],
|
|
194
|
+
};
|
|
195
|
+
engineContext = {
|
|
196
|
+
engine: 'three',
|
|
197
|
+
nodes: {},
|
|
198
|
+
runtime: {},
|
|
199
|
+
debuggingNonsense: {},
|
|
200
|
+
};
|
|
201
|
+
return [4 /*yield*/, compileSource(graph, engine, engineContext)];
|
|
202
|
+
case 1:
|
|
203
|
+
result = _a.sent();
|
|
204
|
+
if (isError(result)) {
|
|
205
|
+
fail(result);
|
|
206
|
+
}
|
|
207
|
+
imgOut = "frogOut_".concat(imageReplacemMe.id);
|
|
208
|
+
expect(result.fragmentResult).toContain("vec4 ".concat(imgOut, ";"));
|
|
209
|
+
expect(result.fragmentResult)
|
|
210
|
+
.toContain("vec4 main_Shader_".concat(imageReplacemMe.id, "() {\n ").concat(imgOut, " = vec4(1.0);\n return ").concat(imgOut, ";\n}"));
|
|
211
|
+
return [2 /*return*/];
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}); });
|
|
122
215
|
describe('evaluateNode()', function () {
|
|
123
216
|
it('evaluates binary nodes', function () {
|
|
124
217
|
var finalAdd = addNode(id(), p);
|
package/graph/parsers.js
CHANGED
|
@@ -24,7 +24,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
24
24
|
return to.concat(ar || Array.prototype.slice.call(from));
|
|
25
25
|
};
|
|
26
26
|
var _a;
|
|
27
|
-
import {
|
|
27
|
+
import { parser } from '@shaderfrog/glsl-parser';
|
|
28
28
|
import { visit, } from '@shaderfrog/glsl-parser/ast';
|
|
29
29
|
import preprocess from '@shaderfrog/glsl-parser/preprocessor';
|
|
30
30
|
import { convert300MainToReturn, from2To3, makeExpression, makeExpressionWithScopes, makeFnBodyStatementWithScopes, makeFnStatement, } from '../util/ast';
|
|
@@ -86,14 +86,11 @@ export var coreParsers = (_a = {},
|
|
|
86
86
|
ast = parser.parse(preprocessed);
|
|
87
87
|
if (node.config.version === 2 && node.stage) {
|
|
88
88
|
from2To3(ast, node.stage);
|
|
89
|
-
log('converted ', node, 'to version 3', {
|
|
90
|
-
code: generate(ast),
|
|
91
|
-
});
|
|
92
89
|
}
|
|
93
90
|
// This assumes that expressionOnly nodes don't have a stage and that all
|
|
94
91
|
// fragment source code shades have main function, which is probably wrong
|
|
95
92
|
if (node.stage === 'fragment') {
|
|
96
|
-
convert300MainToReturn(
|
|
93
|
+
convert300MainToReturn(ast);
|
|
97
94
|
}
|
|
98
95
|
}
|
|
99
96
|
return ast;
|
|
@@ -133,7 +130,7 @@ export var coreParsers = (_a = {},
|
|
|
133
130
|
nodeInput(MAGIC_OUTPUT_STMTS, "filler_".concat(MAGIC_OUTPUT_STMTS), 'filler', 'rgba', ['code'], false),
|
|
134
131
|
function (fillerAst) {
|
|
135
132
|
var fn = ast.program.find(function (stmt) { return stmt.type === 'function'; });
|
|
136
|
-
fn === null || fn === void 0 ? void 0 : fn.body.statements.unshift(makeFnStatement(generateFiller(fillerAst)));
|
|
133
|
+
fn === null || fn === void 0 ? void 0 : fn.body.statements.unshift(makeFnStatement(generateFiller(fillerAst))[0]);
|
|
137
134
|
return ast;
|
|
138
135
|
},
|
|
139
136
|
],
|
package/graph/shader-sections.js
CHANGED
|
@@ -62,7 +62,7 @@ export var highestPrecisions = function (nodes) {
|
|
|
62
62
|
return (__assign(__assign({}, precisions), (_a = {}, _a[stmt.declaration.specifier.specifier.token] = higherPrecision(precisions[stmt.declaration.specifier.specifier.token], stmt.declaration.qualifier.token), _a)));
|
|
63
63
|
}, {})).map(function (_a) {
|
|
64
64
|
var _b = __read(_a, 2), typeName = _b[0], precision = _b[1];
|
|
65
|
-
return makeStatement("precision ".concat(precision, " ").concat(typeName));
|
|
65
|
+
return makeStatement("precision ".concat(precision, " ").concat(typeName))[0];
|
|
66
66
|
});
|
|
67
67
|
};
|
|
68
68
|
export var dedupeQualifiedStatements = function (statements, qualifier) {
|
|
@@ -74,7 +74,7 @@ export var dedupeQualifiedStatements = function (statements, qualifier) {
|
|
|
74
74
|
}, {})), _a)));
|
|
75
75
|
}, {})).map(function (_a) {
|
|
76
76
|
var _b = __read(_a, 2), type = _b[0], varNames = _b[1];
|
|
77
|
-
return makeStatement("".concat(qualifier, " ").concat(type, " ").concat(Object.keys(varNames).join(', ')));
|
|
77
|
+
return makeStatement("".concat(qualifier, " ").concat(type, " ").concat(Object.keys(varNames).join(', ')))[0];
|
|
78
78
|
});
|
|
79
79
|
};
|
|
80
80
|
/**
|
|
@@ -165,7 +165,7 @@ export var dedupeUniforms = function (statements) {
|
|
|
165
165
|
var _b = __read(_a, 2), type = _b[0], variables = _b[1];
|
|
166
166
|
return makeStatement("uniform ".concat(type, " ").concat(Object.values(variables)
|
|
167
167
|
.map(function (v) { return v.generated; })
|
|
168
|
-
.join(', ')));
|
|
168
|
+
.join(', ')))[0];
|
|
169
169
|
});
|
|
170
170
|
};
|
|
171
171
|
export var mergeShaderSections = function (s1, s2) {
|
|
@@ -237,11 +237,6 @@ export var findShaderSections = function (ast) {
|
|
|
237
237
|
else if (node.type === 'declaration_statement' &&
|
|
238
238
|
'specified_type' in node.declaration &&
|
|
239
239
|
((_m = (_l = (_k = node.declaration) === null || _k === void 0 ? void 0 : _k.specified_type) === null || _l === void 0 ? void 0 : _l.qualifiers) === null || _m === void 0 ? void 0 : _m.find(function (n) { return 'token' in n && n.token === 'in'; }))) {
|
|
240
|
-
if (generate(node).includes('main_Fireball')) {
|
|
241
|
-
console.log('findShaderSections\n', generate(ast));
|
|
242
|
-
console.log("Tracking inStatement \"".concat(generate(node), "\""), node);
|
|
243
|
-
debugger;
|
|
244
|
-
}
|
|
245
240
|
return __assign(__assign({}, sections), { inStatements: sections.inStatements.concat(node) });
|
|
246
241
|
}
|
|
247
242
|
else if (node.type === 'declaration_statement' &&
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shaderfrog/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.0",
|
|
4
4
|
"description": "Shaderfrog core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"prepare": "npm run build && ./prepublish.sh",
|
|
18
18
|
"postpublish": "./postbuild.sh",
|
|
19
19
|
"build": "./build.sh",
|
|
20
|
-
"watch-test": "
|
|
21
|
-
"test": "
|
|
20
|
+
"watch-test": "vitest --watch",
|
|
21
|
+
"test": "vitest"
|
|
22
22
|
},
|
|
23
23
|
"repository": {
|
|
24
24
|
"type": "git",
|
|
@@ -34,21 +34,20 @@
|
|
|
34
34
|
"@babel/core": "^7.21.8",
|
|
35
35
|
"@babel/preset-env": "^7.21.5",
|
|
36
36
|
"@babel/preset-typescript": "^7.21.5",
|
|
37
|
-
"@
|
|
37
|
+
"@swc/core": "^1.6.7",
|
|
38
38
|
"@types/lodash.groupby": "^4.6.7",
|
|
39
39
|
"@types/three": "^0.156.0",
|
|
40
|
-
"babel-jest": "^29.5.0",
|
|
41
40
|
"babylonjs": "^6.2.0",
|
|
42
|
-
"
|
|
43
|
-
"prettier": "^2.8.8",
|
|
41
|
+
"prettier": "^3.3.2",
|
|
44
42
|
"three": "^0.156.1",
|
|
45
|
-
"typescript": "^5.0.4"
|
|
43
|
+
"typescript": "^5.0.4",
|
|
44
|
+
"vitest": "^1.6.0"
|
|
46
45
|
},
|
|
47
46
|
"dependencies": {
|
|
48
47
|
"lodash.groupby": "^4.6.0"
|
|
49
48
|
},
|
|
50
49
|
"peerDependencies": {
|
|
51
|
-
"@shaderfrog/glsl-parser": "^
|
|
50
|
+
"@shaderfrog/glsl-parser": "^5.0.0-beta.3",
|
|
52
51
|
"babylonjs": ">=4",
|
|
53
52
|
"playcanvas": "^1.65.3",
|
|
54
53
|
"three": ">=0.50"
|
|
@@ -22,7 +22,7 @@ var importers = {
|
|
|
22
22
|
// Babylon has no normalmatrix. They do have a normal attribute. So undo any
|
|
23
23
|
// multiplication by normalMatrix?
|
|
24
24
|
var seen = {};
|
|
25
|
-
renameBindings(ast.scopes[0], function (name) {
|
|
25
|
+
ast.scopes[0].bindings = renameBindings(ast.scopes[0].bindings, function (name) {
|
|
26
26
|
// console.log({ name }, 'seen:', seen[name]);
|
|
27
27
|
var renamed = name === 'vUv'
|
|
28
28
|
? 'vMainUV1'
|
|
@@ -2,7 +2,7 @@ import { renameBindings } from '@shaderfrog/glsl-parser/parser/utils';
|
|
|
2
2
|
var importers = {
|
|
3
3
|
babylon: {
|
|
4
4
|
convertAst: function (ast, type) {
|
|
5
|
-
renameBindings(ast.scopes[0], function (name) {
|
|
5
|
+
ast.scopes[0].bindings = renameBindings(ast.scopes[0].bindings, function (name) {
|
|
6
6
|
return name === 'vMainUV1' ? 'vUv' : name === 'vNormalW' ? 'vNormal' : name;
|
|
7
7
|
});
|
|
8
8
|
},
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
12
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
import { expect, it } from 'vitest';
|
|
38
|
+
import { outputNode, sourceNode } from '../../graph/graph-node';
|
|
39
|
+
import { makeEdge } from '../../graph/edge';
|
|
40
|
+
import { compileSource } from '../../graph/graph';
|
|
41
|
+
import { texture2DStrategy } from '../../strategy';
|
|
42
|
+
import { isError } from '../../graph/context';
|
|
43
|
+
import { threngine } from './threngine';
|
|
44
|
+
import { makeId } from '../../util/id';
|
|
45
|
+
import { fail } from '../../test-util';
|
|
46
|
+
var p = { x: 0, y: 0 };
|
|
47
|
+
var makeSourceNode = function (id, source, stage, strategies) {
|
|
48
|
+
if (strategies === void 0) { strategies = [texture2DStrategy()]; }
|
|
49
|
+
return sourceNode(id, "Shader ".concat(id), p, {
|
|
50
|
+
version: 2,
|
|
51
|
+
preprocess: false,
|
|
52
|
+
strategies: strategies,
|
|
53
|
+
uniforms: [],
|
|
54
|
+
}, source, stage);
|
|
55
|
+
};
|
|
56
|
+
it('threngine compileSource() and manipulateAst()', function () { return __awaiter(void 0, void 0, void 0, function () {
|
|
57
|
+
var outV, outF, vertInput, graph, engineContext, result;
|
|
58
|
+
return __generator(this, function (_a) {
|
|
59
|
+
switch (_a.label) {
|
|
60
|
+
case 0:
|
|
61
|
+
outV = outputNode(makeId(), 'Output v', p, 'vertex');
|
|
62
|
+
outF = outputNode(makeId(), 'Output f', p, 'fragment');
|
|
63
|
+
vertInput = makeSourceNode(makeId(), "uniform vec4 modelViewMatrix;\nattribute vec3 position;\nfloat a = 2.0;\nvoid main() {\n gl_Position = modelViewMatrix * vec4(position, 1.0);\n}\n", 'vertex');
|
|
64
|
+
graph = {
|
|
65
|
+
nodes: [outV, outF, vertInput],
|
|
66
|
+
edges: [
|
|
67
|
+
makeEdge(makeId(), vertInput.id, outV.id, 'out', 'filler_gl_Position', 'vertex'),
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
engineContext = {
|
|
71
|
+
engine: 'three',
|
|
72
|
+
nodes: {},
|
|
73
|
+
runtime: {},
|
|
74
|
+
debuggingNonsense: {},
|
|
75
|
+
};
|
|
76
|
+
return [4 /*yield*/, compileSource(graph, threngine, engineContext)];
|
|
77
|
+
case 1:
|
|
78
|
+
result = _a.sent();
|
|
79
|
+
if (isError(result)) {
|
|
80
|
+
fail(result);
|
|
81
|
+
}
|
|
82
|
+
// Threngine has parsers for vertex shaders, make sure that is set properly
|
|
83
|
+
expect(result.vertexResult).toContain("vec4 main_Shader_".concat(vertInput.id, "() {\n vec4 frogOut = modelViewMatrix * vec4(position, 1.0);\n return frogOut;\n}"));
|
|
84
|
+
// Check that it inlned. For fun.
|
|
85
|
+
expect(result.vertexResult).toContain("gl_Position = main_Shader_".concat(vertInput.id, "();"));
|
|
86
|
+
return [2 /*return*/];
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}); });
|
|
@@ -3,7 +3,8 @@ export interface AssignemntToStrategy extends BaseStrategy {
|
|
|
3
3
|
type: StrategyType.ASSIGNMENT_TO;
|
|
4
4
|
config: {
|
|
5
5
|
assignTo: string;
|
|
6
|
+
nth?: number;
|
|
6
7
|
};
|
|
7
8
|
}
|
|
8
|
-
export declare const assignemntToStrategy: (assignTo: string) => AssignemntToStrategy;
|
|
9
|
+
export declare const assignemntToStrategy: (assignTo: string, nth?: number) => AssignemntToStrategy;
|
|
9
10
|
export declare const applyAssignmentToStrategy: ApplyStrategy<AssignemntToStrategy>;
|
package/strategy/assignemntTo.js
CHANGED
|
@@ -2,13 +2,16 @@ import { findAssignmentTo } from '../util/ast';
|
|
|
2
2
|
import { nodeInput } from '../graph/base-node';
|
|
3
3
|
import { StrategyType } from '.';
|
|
4
4
|
// Constructor
|
|
5
|
-
export var assignemntToStrategy = function (assignTo) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
export var assignemntToStrategy = function (assignTo, nth) {
|
|
6
|
+
if (nth === void 0) { nth = 1; }
|
|
7
|
+
return ({
|
|
8
|
+
type: StrategyType.ASSIGNMENT_TO,
|
|
9
|
+
config: { assignTo: assignTo, nth: nth },
|
|
10
|
+
});
|
|
11
|
+
};
|
|
9
12
|
// Apply the strategy
|
|
10
13
|
export var applyAssignmentToStrategy = function (strategy, ast, graphNode, siblingNode) {
|
|
11
|
-
var assignNode = findAssignmentTo(ast, strategy.config.assignTo);
|
|
14
|
+
var assignNode = findAssignmentTo(ast, strategy.config.assignTo, strategy.config.nth || 1);
|
|
12
15
|
var name = strategy.config.assignTo;
|
|
13
16
|
return assignNode
|
|
14
17
|
? [
|
package/strategy/inject.js
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
var __assign = (this && this.__assign) || function () {
|
|
2
|
-
__assign = Object.assign || function(t) {
|
|
3
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
-
s = arguments[i];
|
|
5
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
-
t[p] = s[p];
|
|
7
|
-
}
|
|
8
|
-
return t;
|
|
9
|
-
};
|
|
10
|
-
return __assign.apply(this, arguments);
|
|
11
|
-
};
|
|
12
1
|
var __read = (this && this.__read) || function (o, n) {
|
|
13
2
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
14
3
|
if (!m) return o;
|
|
@@ -37,6 +26,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
37
26
|
import { generate } from '@shaderfrog/glsl-parser';
|
|
38
27
|
import { nodeInput } from '../graph/base-node';
|
|
39
28
|
import { StrategyType } from '.';
|
|
29
|
+
import { transferWhitespace } from '../util/whitespace';
|
|
40
30
|
/**
|
|
41
31
|
* Inject source code into an AST, in a find-and-replace style. This only
|
|
42
32
|
* operates on statements right now
|
|
@@ -45,26 +35,6 @@ export var injectStrategy = function (config) { return ({
|
|
|
45
35
|
type: StrategyType.INJECT,
|
|
46
36
|
config: config,
|
|
47
37
|
}); };
|
|
48
|
-
// Typescript fucked up flat https://stackoverflow.com/a/61420611/743464
|
|
49
|
-
var combineWs = function (a, b) {
|
|
50
|
-
return [a, b].flat(Infinity);
|
|
51
|
-
};
|
|
52
|
-
// Move whitespace from one node to another. Since whitespace is trailing, if a
|
|
53
|
-
// node is injected after a previous node, move the whitespace from the earlier
|
|
54
|
-
// node to the later one. This keeps comments in the same place
|
|
55
|
-
var transferWhitespace = function (to, from) {
|
|
56
|
-
return 'semi' in to && 'semi' in from
|
|
57
|
-
? [
|
|
58
|
-
__assign(__assign({}, to), { semi: __assign(__assign({}, to.semi), { whitespace: from.semi.whitespace }) }),
|
|
59
|
-
__assign(__assign({}, from), { semi: __assign(__assign({}, from.semi), { whitespace: '\n' }) }),
|
|
60
|
-
]
|
|
61
|
-
: 'whitespace' in to && 'semi' in from
|
|
62
|
-
? [
|
|
63
|
-
__assign(__assign({}, to), { whitespace: combineWs(to.whitespace, from.semi.whitespace) }),
|
|
64
|
-
__assign(__assign({}, from), { semi: __assign(__assign({}, from.semi), { whitespace: '\n' }) }),
|
|
65
|
-
]
|
|
66
|
-
: [to, from];
|
|
67
|
-
};
|
|
68
38
|
export var applyInjectStrategy = function (strategy, ast, graphNode, siblingNode) {
|
|
69
39
|
var program = ast.program;
|
|
70
40
|
var _a = strategy.config, find = _a.find, count = _a.count, insert = _a.insert;
|
|
@@ -14,23 +14,12 @@ var __read = (this && this.__read) || function (o, n) {
|
|
|
14
14
|
}
|
|
15
15
|
return ar;
|
|
16
16
|
};
|
|
17
|
+
import { expect, it } from 'vitest';
|
|
17
18
|
import { parser } from '@shaderfrog/glsl-parser';
|
|
18
19
|
import { generate } from '@shaderfrog/glsl-parser';
|
|
19
20
|
import { applyStrategy, StrategyType } from '.';
|
|
20
|
-
import * as graphModule from '../graph/graph';
|
|
21
21
|
import { makeExpression } from '../util/ast';
|
|
22
22
|
import preprocess from '@shaderfrog/glsl-parser/preprocessor';
|
|
23
|
-
var orig;
|
|
24
|
-
beforeEach(function () {
|
|
25
|
-
orig = graphModule.mangleName;
|
|
26
|
-
// Terrible hack. in the real world, strategies are applied after mangling
|
|
27
|
-
// @ts-ignore
|
|
28
|
-
graphModule.mangleName = function (name) { return name; };
|
|
29
|
-
});
|
|
30
|
-
afterEach(function () {
|
|
31
|
-
// @ts-ignore
|
|
32
|
-
graphModule.mangleName = orig;
|
|
33
|
-
});
|
|
34
23
|
it('named attribute strategy`', function () {
|
|
35
24
|
var source = "\nin vec3 replaceThisAtrribute;\nvoid main() {\n vec2 y = replaceThisAtrribute;\n}\n";
|
|
36
25
|
var ast = parser.parse(source, { quiet: true });
|
|
@@ -92,9 +81,65 @@ it('inject strategy before', function () {
|
|
|
92
81
|
// Should fill references
|
|
93
82
|
expect(result).toBe("\nuniform float x;\n// Some comment\nvoid main() {\n/* some comment */\nsomeOtherCall(x, y, z);\nsomeOtherCall(x, y, z);\nre(x, y, z);\n// Middle comment\nsomeOtherCall(x, y, z);\nsomeOtherCall(x, y, z);\nre(x, y, z);\n// Final comment\n}");
|
|
94
83
|
});
|
|
84
|
+
var constructor = function () { return ({
|
|
85
|
+
config: {
|
|
86
|
+
version: 3,
|
|
87
|
+
preprocess: false,
|
|
88
|
+
strategies: [],
|
|
89
|
+
uniforms: [],
|
|
90
|
+
},
|
|
91
|
+
id: '1',
|
|
92
|
+
name: '1',
|
|
93
|
+
engine: true,
|
|
94
|
+
type: '',
|
|
95
|
+
inputs: [],
|
|
96
|
+
outputs: [],
|
|
97
|
+
position: { x: 0, y: 0 },
|
|
98
|
+
source: '',
|
|
99
|
+
stage: undefined,
|
|
100
|
+
}); };
|
|
101
|
+
var engine = {
|
|
102
|
+
name: 'three',
|
|
103
|
+
displayName: 'Three.js',
|
|
104
|
+
evaluateNode: function (node) {
|
|
105
|
+
if (node.type === 'number') {
|
|
106
|
+
return parseFloat(node.value);
|
|
107
|
+
}
|
|
108
|
+
return node.value;
|
|
109
|
+
},
|
|
110
|
+
constructors: {
|
|
111
|
+
physical: constructor,
|
|
112
|
+
toon: constructor,
|
|
113
|
+
},
|
|
114
|
+
mergeOptions: {
|
|
115
|
+
includePrecisions: true,
|
|
116
|
+
includeVersion: true,
|
|
117
|
+
},
|
|
118
|
+
importers: {},
|
|
119
|
+
preserve: new Set(),
|
|
120
|
+
parsers: {},
|
|
121
|
+
};
|
|
95
122
|
it('correctly fills with uniform strategy', function () {
|
|
96
123
|
var _a, _b, _c;
|
|
97
124
|
var ast = parser.parse("\nlayout(std140,column_major) uniform;\nuniform sampler2D image;\nuniform vec4 input, output, other;\nuniform vec4 zenput;\nuniform Light0 { vec4 y; } x;\nvec3 topLevel = vec3(0.0);\nvoid other(in vec3 param) {}\nvoid main() {\n vec4 computed = texture2D(image, uvPow * 1.0);\n vec4 x = input;\n vec4 y = output;\n vec4 z = zenput;\n}", { quiet: true });
|
|
125
|
+
// TODO: Experimenting with strategy tests where we mangle in the test to
|
|
126
|
+
// avoid having to mangle in the strategy, in service of maybe mangling the
|
|
127
|
+
// AST as part of producing context. But as the test shows -
|
|
128
|
+
// mangleEntireProgram does NOT modify binding names
|
|
129
|
+
//
|
|
130
|
+
// You started updating binding names in the parser but realized that
|
|
131
|
+
// technically a mangler can produce different results for different nodes
|
|
132
|
+
// during the rename, since the parser takes in the node to mangle.
|
|
133
|
+
//
|
|
134
|
+
// which raised the question about why pass in the node at all to the mangler?
|
|
135
|
+
// looks like it's for "doNotDescope" hack to avoid renaming a specific
|
|
136
|
+
// varaible.
|
|
137
|
+
//
|
|
138
|
+
// But maybe that could be done here instead? And mangleEntireProgram could be
|
|
139
|
+
// aware of the output varaibles to ignore? Which means we need to track the
|
|
140
|
+
// output varialbe names somewhere... do we alredy?
|
|
141
|
+
var node = { name: 'fake', id: '1' };
|
|
142
|
+
// mangleEntireProgram(engine, ast, node);
|
|
98
143
|
var fillers = applyStrategy({ type: StrategyType.UNIFORM, config: {} }, ast, {}, {});
|
|
99
144
|
// It should find uniforms with simple types, excluding sampler2D
|
|
100
145
|
expect(fillers.map(function (_a) {
|
|
@@ -160,5 +205,5 @@ it('Make sure texture2D finds preprocessed texture() call', function () {
|
|
|
160
205
|
expect(applyStrategy({ type: StrategyType.TEXTURE_2D, config: {} }, ast, {}, {}).map(function (_a) {
|
|
161
206
|
var _b = __read(_a, 1), name = _b[0].displayName;
|
|
162
207
|
return name;
|
|
163
|
-
})).toEqual(['
|
|
208
|
+
})).toEqual(['normalMap']);
|
|
164
209
|
});
|
package/strategy/uniform.js
CHANGED
|
@@ -14,7 +14,6 @@ var __read = (this && this.__read) || function (o, n) {
|
|
|
14
14
|
}
|
|
15
15
|
return ar;
|
|
16
16
|
};
|
|
17
|
-
import { mangleName } from '../graph/graph';
|
|
18
17
|
import { nodeInput } from '../graph/base-node';
|
|
19
18
|
import { StrategyType } from '.';
|
|
20
19
|
import { generateFiller } from '../util/ast';
|
|
@@ -155,7 +154,8 @@ export var applyUniformStrategy = function (strategy, ast, graphNode, siblingNod
|
|
|
155
154
|
return names.map(function (name) { return [
|
|
156
155
|
nodeInput(name, "uniform_".concat(name), 'uniform', graphDataType, ['code', 'data'], true),
|
|
157
156
|
function (filler) {
|
|
158
|
-
|
|
157
|
+
//const mangledName = mangleName(name, graphNode, siblingNode);
|
|
158
|
+
var mangledName = name;
|
|
159
159
|
// Remove the declaration line, or the declared uniform
|
|
160
160
|
if (declarations_1.length === 1) {
|
|
161
161
|
program.program.splice(program.program.indexOf(node), 1);
|
package/util/ast.d.ts
CHANGED
|
@@ -1,18 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
* Utility functions to work with ASTs
|
|
3
|
-
*/
|
|
4
|
-
import { Filler } from '../graph/parsers';
|
|
5
|
-
import { AstNode, ExpressionStatementNode, FunctionNode, DeclarationNode } from '@shaderfrog/glsl-parser/ast';
|
|
1
|
+
import { AstNode, ExpressionStatementNode, FunctionNode, DeclarationStatementNode, DeclarationNode } from '@shaderfrog/glsl-parser/ast';
|
|
6
2
|
import { Program } from '@shaderfrog/glsl-parser/ast';
|
|
7
3
|
import { ShaderStage } from '../graph/graph-types';
|
|
8
4
|
import { Scope } from '@shaderfrog/glsl-parser/parser/scope';
|
|
5
|
+
import { Filler } from '../graph/parsers';
|
|
6
|
+
export interface FrogProgram extends Program {
|
|
7
|
+
outVar?: string;
|
|
8
|
+
}
|
|
9
9
|
export declare const findVec4Constructor: (ast: AstNode) => AstNode | undefined;
|
|
10
|
-
export declare const findAssignmentTo: (ast: AstNode | Program, assignTo: string) => DeclarationNode | ExpressionStatementNode;
|
|
10
|
+
export declare const findAssignmentTo: (ast: AstNode | Program, assignTo: string, nth?: number) => DeclarationNode | ExpressionStatementNode;
|
|
11
11
|
export declare const findDeclarationOf: (ast: AstNode | Program, declarationOf: string) => DeclarationNode | undefined;
|
|
12
12
|
export declare const from2To3: (ast: Program, stage: ShaderStage) => void;
|
|
13
|
-
export declare const
|
|
14
|
-
export declare const
|
|
15
|
-
|
|
13
|
+
export declare const makeStatement: (stmt: string) => readonly [DeclarationStatementNode | FunctionNode | import("@shaderfrog/glsl-parser/ast").PreprocessorNode, Scope];
|
|
14
|
+
export declare const makeFnStatement: (fnStmt: string) => readonly [AstNode, Scope];
|
|
15
|
+
/**
|
|
16
|
+
* Add a new scope into an existing one. Meant to be used for adding net new
|
|
17
|
+
* lines of coe to an AST, and wanting to add the new scope generated from those
|
|
18
|
+
* lines.
|
|
19
|
+
*
|
|
20
|
+
* DO NOT USE THIS TO MERGE SCOPES! If both the left and right scope contain the
|
|
21
|
+
* same binding name, this will override the left scope outright, rather than
|
|
22
|
+
* merge the binding.references.
|
|
23
|
+
*
|
|
24
|
+
* One reason I chose not to make a full merge: What happens if both sides
|
|
25
|
+
* contain a binding.declaration?
|
|
26
|
+
*/
|
|
27
|
+
export declare const addNewScope: (left: Scope, right: Scope) => Scope;
|
|
16
28
|
export declare const makeExpression: (expr: string) => AstNode;
|
|
17
29
|
export declare const makeExpressionWithScopes: (expr: string) => {
|
|
18
30
|
scope: Scope;
|
|
@@ -22,9 +34,15 @@ export declare const makeFnBodyStatementWithScopes: (body: string) => {
|
|
|
22
34
|
scope: Scope;
|
|
23
35
|
statements: AstNode[];
|
|
24
36
|
};
|
|
25
|
-
export declare const findFn: (
|
|
37
|
+
export declare const findFn: (name: string) => (ast: Program) => FunctionNode | undefined;
|
|
38
|
+
export declare const findMain: (ast: Program) => FunctionNode | undefined;
|
|
26
39
|
export declare const returnGlPosition: (fnName: string, ast: Program) => void;
|
|
27
40
|
export declare const returnGlPositionHardCoded: (fnName: string, ast: Program, returnType: string, hardCodedReturn: string) => void;
|
|
28
41
|
export declare const returnGlPositionVec3Right: (fnName: string, ast: Program) => void;
|
|
29
|
-
|
|
42
|
+
/**
|
|
43
|
+
* For either a fragment or vertex AST, convert the main() function that sets
|
|
44
|
+
* gl_FragColor, gl_Position, or "out vec4 ____" into a main() function that
|
|
45
|
+
* returns a vec4.
|
|
46
|
+
*/
|
|
47
|
+
export declare const convert300MainToReturn: (ast: FrogProgram) => void;
|
|
30
48
|
export declare const generateFiller: (filler: Filler) => string;
|
package/util/ast.js
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
1
12
|
var __read = (this && this.__read) || function (o, n) {
|
|
2
13
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
3
14
|
if (!m) return o;
|
|
@@ -25,6 +36,8 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
25
36
|
};
|
|
26
37
|
import { parser, generate } from '@shaderfrog/glsl-parser';
|
|
27
38
|
import { visit, } from '@shaderfrog/glsl-parser/ast';
|
|
39
|
+
import { addFnStmtWithIndent } from './whitespace';
|
|
40
|
+
import { renameBinding, } from '@shaderfrog/glsl-parser/parser/utils';
|
|
28
41
|
var log = function () {
|
|
29
42
|
var _a;
|
|
30
43
|
var args = [];
|
|
@@ -49,14 +62,20 @@ export var findVec4Constructor = function (ast) {
|
|
|
49
62
|
visit(ast, visitors);
|
|
50
63
|
return parent;
|
|
51
64
|
};
|
|
52
|
-
export var findAssignmentTo = function (ast, assignTo) {
|
|
65
|
+
export var findAssignmentTo = function (ast, assignTo, nth) {
|
|
66
|
+
if (nth === void 0) { nth = 1; }
|
|
53
67
|
var assign;
|
|
68
|
+
var foundth = 0;
|
|
54
69
|
var visitors = {
|
|
55
70
|
expression_statement: {
|
|
56
71
|
enter: function (path) {
|
|
57
72
|
var _a, _b;
|
|
58
73
|
if (((_b = (_a = path.node.expression) === null || _a === void 0 ? void 0 : _a.left) === null || _b === void 0 ? void 0 : _b.identifier) === assignTo) {
|
|
59
|
-
|
|
74
|
+
foundth++;
|
|
75
|
+
if (foundth === nth) {
|
|
76
|
+
assign = path.node;
|
|
77
|
+
path.stop();
|
|
78
|
+
}
|
|
60
79
|
}
|
|
61
80
|
path.skip();
|
|
62
81
|
},
|
|
@@ -66,7 +85,11 @@ export var findAssignmentTo = function (ast, assignTo) {
|
|
|
66
85
|
var _a, _b;
|
|
67
86
|
var foundDecl = (_b = (_a = path.node.declaration) === null || _a === void 0 ? void 0 : _a.declarations) === null || _b === void 0 ? void 0 : _b.find(function (decl) { var _a; return ((_a = decl === null || decl === void 0 ? void 0 : decl.identifier) === null || _a === void 0 ? void 0 : _a.identifier) === assignTo; });
|
|
68
87
|
if (foundDecl === null || foundDecl === void 0 ? void 0 : foundDecl.initializer) {
|
|
69
|
-
|
|
88
|
+
foundth++;
|
|
89
|
+
if (foundth === nth) {
|
|
90
|
+
assign = foundDecl;
|
|
91
|
+
path.stop();
|
|
92
|
+
}
|
|
70
93
|
}
|
|
71
94
|
path.skip();
|
|
72
95
|
},
|
|
@@ -101,7 +124,11 @@ export var from2To3 = function (ast, stage) {
|
|
|
101
124
|
// _: '\n',
|
|
102
125
|
// });
|
|
103
126
|
if (stage === 'fragment') {
|
|
104
|
-
|
|
127
|
+
// Add in "out vec4 fragmentColor" to convert gl_FragColor to an out statement
|
|
128
|
+
var _a = __read(makeStatement("out vec4 ".concat(glOut)), 2), outStmt = _a[0], scope = _a[1];
|
|
129
|
+
ast.program.unshift(outStmt);
|
|
130
|
+
// Add the out statement variable to the scope
|
|
131
|
+
ast.scopes[0] = addNewScope(ast.scopes[0], scope);
|
|
105
132
|
}
|
|
106
133
|
visit(ast, {
|
|
107
134
|
function_call: {
|
|
@@ -117,6 +144,7 @@ export var from2To3 = function (ast, stage) {
|
|
|
117
144
|
enter: function (path) {
|
|
118
145
|
if (path.node.identifier === 'gl_FragColor') {
|
|
119
146
|
path.node.identifier = glOut;
|
|
147
|
+
ast.scopes[0].bindings[glOut].references.push(path.node);
|
|
120
148
|
}
|
|
121
149
|
},
|
|
122
150
|
},
|
|
@@ -131,36 +159,6 @@ export var from2To3 = function (ast, stage) {
|
|
|
131
159
|
},
|
|
132
160
|
});
|
|
133
161
|
};
|
|
134
|
-
export var outDeclaration = function (name) { return ({
|
|
135
|
-
type: 'declaration_statement',
|
|
136
|
-
declaration: {
|
|
137
|
-
type: 'declarator_list',
|
|
138
|
-
specified_type: {
|
|
139
|
-
type: 'fully_specified_type',
|
|
140
|
-
qualifiers: [{ type: 'keyword', token: 'out', whitespace: ' ' }],
|
|
141
|
-
specifier: {
|
|
142
|
-
type: 'type_specifier',
|
|
143
|
-
specifier: { type: 'keyword', token: 'vec4', whitespace: ' ' },
|
|
144
|
-
quantifier: null,
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
declarations: [
|
|
148
|
-
{
|
|
149
|
-
type: 'declaration',
|
|
150
|
-
identifier: {
|
|
151
|
-
type: 'identifier',
|
|
152
|
-
identifier: name,
|
|
153
|
-
whitespace: undefined,
|
|
154
|
-
},
|
|
155
|
-
quantifier: null,
|
|
156
|
-
operator: undefined,
|
|
157
|
-
initializer: undefined,
|
|
158
|
-
},
|
|
159
|
-
],
|
|
160
|
-
commas: [],
|
|
161
|
-
},
|
|
162
|
-
semi: { type: 'literal', literal: ';', whitespace: '\n ' },
|
|
163
|
-
}); };
|
|
164
162
|
export var makeStatement = function (stmt) {
|
|
165
163
|
// log(`Parsing "${stmt}"`);
|
|
166
164
|
var ast;
|
|
@@ -172,20 +170,38 @@ export var makeStatement = function (stmt) {
|
|
|
172
170
|
throw new Error("Error parsing stmt \"".concat(stmt, "\": ").concat(error === null || error === void 0 ? void 0 : error.message));
|
|
173
171
|
}
|
|
174
172
|
// log(util.inspect(ast, false, null, true));
|
|
175
|
-
return ast.program[0];
|
|
173
|
+
return [ast.program[0], ast.scopes[0]];
|
|
176
174
|
};
|
|
177
175
|
export var makeFnStatement = function (fnStmt) {
|
|
178
176
|
var ast;
|
|
179
177
|
try {
|
|
180
|
-
|
|
178
|
+
// Create a statement with no trailing nor leading whitespace
|
|
179
|
+
ast = parser.parse("void main() {".concat(fnStmt, ";}"), { quiet: true });
|
|
181
180
|
}
|
|
182
181
|
catch (error) {
|
|
183
182
|
console.error({ fnStmt: fnStmt, error: error });
|
|
184
183
|
throw new Error("Error parsing fnStmt \"".concat(fnStmt, "\": ").concat(error === null || error === void 0 ? void 0 : error.message));
|
|
185
184
|
}
|
|
186
185
|
// log(util.inspect(ast, false, null, true));
|
|
187
|
-
|
|
186
|
+
var n = ast.program[0].body.statements[0];
|
|
187
|
+
n.semi.whitespace = '';
|
|
188
|
+
return [n, ast.scopes[1]];
|
|
188
189
|
};
|
|
190
|
+
/**
|
|
191
|
+
* Add a new scope into an existing one. Meant to be used for adding net new
|
|
192
|
+
* lines of coe to an AST, and wanting to add the new scope generated from those
|
|
193
|
+
* lines.
|
|
194
|
+
*
|
|
195
|
+
* DO NOT USE THIS TO MERGE SCOPES! If both the left and right scope contain the
|
|
196
|
+
* same binding name, this will override the left scope outright, rather than
|
|
197
|
+
* merge the binding.references.
|
|
198
|
+
*
|
|
199
|
+
* One reason I chose not to make a full merge: What happens if both sides
|
|
200
|
+
* contain a binding.declaration?
|
|
201
|
+
*/
|
|
202
|
+
export var addNewScope = function (left, right) { return (__assign(__assign({}, left), {
|
|
203
|
+
// name, parent comes from left
|
|
204
|
+
bindings: __assign(__assign({}, left.bindings), right.bindings), types: __assign(__assign({}, left.types), right.types), functions: __assign(__assign({}, left.functions), right.functions) })); };
|
|
189
205
|
export var makeExpression = function (expr) {
|
|
190
206
|
var ast;
|
|
191
207
|
try {
|
|
@@ -229,11 +245,15 @@ export var makeFnBodyStatementWithScopes = function (body) {
|
|
|
229
245
|
statements: ast.program[0].body.statements,
|
|
230
246
|
};
|
|
231
247
|
};
|
|
232
|
-
export var findFn = function (
|
|
233
|
-
return
|
|
234
|
-
return
|
|
235
|
-
|
|
248
|
+
export var findFn = function (name) {
|
|
249
|
+
return function (ast) {
|
|
250
|
+
return ast.program.find(function (stmt) {
|
|
251
|
+
return stmt.type === 'function' &&
|
|
252
|
+
stmt.prototype.header.name.identifier === name;
|
|
253
|
+
});
|
|
254
|
+
};
|
|
236
255
|
};
|
|
256
|
+
export var findMain = findFn('main');
|
|
237
257
|
export var returnGlPosition = function (fnName, ast) {
|
|
238
258
|
return convertVertexMain(fnName, ast, 'vec4', function (assign) { return assign.expression.right; });
|
|
239
259
|
};
|
|
@@ -264,9 +284,10 @@ export var returnGlPositionVec3Right = function (fnName, ast) {
|
|
|
264
284
|
return found;
|
|
265
285
|
});
|
|
266
286
|
};
|
|
287
|
+
var replacedReturn = 'frogOut';
|
|
267
288
|
var convertVertexMain = function (fnName, ast, returnType, generateRight) {
|
|
268
289
|
var mainReturnVar = "frogOut";
|
|
269
|
-
var main = findFn(
|
|
290
|
+
var main = findFn(fnName)(ast);
|
|
270
291
|
if (!main) {
|
|
271
292
|
throw new Error("No ".concat(fnName, " fn found!"));
|
|
272
293
|
}
|
|
@@ -282,14 +303,26 @@ var convertVertexMain = function (fnName, ast, returnType, generateRight) {
|
|
|
282
303
|
if (!assign) {
|
|
283
304
|
throw new Error("No gl position assign found in main fn!");
|
|
284
305
|
}
|
|
285
|
-
var rtnStmt = makeFnStatement("".concat(returnType, " ").concat(mainReturnVar, " = 1.0"));
|
|
306
|
+
var rtnStmt = makeFnStatement("".concat(returnType, " ").concat(mainReturnVar, " = 1.0"))[0];
|
|
286
307
|
rtnStmt.declaration.declarations[0].initializer =
|
|
287
308
|
generateRight(assign);
|
|
309
|
+
rtnStmt.semi.whitespace = '\n';
|
|
288
310
|
main.body.statements.splice(main.body.statements.indexOf(assign), 1, rtnStmt);
|
|
289
|
-
main.body.statements
|
|
311
|
+
main.body.statements = addFnStmtWithIndent(main, "return ".concat(mainReturnVar));
|
|
290
312
|
};
|
|
291
|
-
|
|
292
|
-
|
|
313
|
+
/**
|
|
314
|
+
* For either a fragment or vertex AST, convert the main() function that sets
|
|
315
|
+
* gl_FragColor, gl_Position, or "out vec4 ____" into a main() function that
|
|
316
|
+
* returns a vec4.
|
|
317
|
+
*/
|
|
318
|
+
export var convert300MainToReturn = function (ast) {
|
|
319
|
+
// Convert the main function to return a vec4
|
|
320
|
+
var main = findMain(ast);
|
|
321
|
+
if (!main) {
|
|
322
|
+
throw new Error('No main function found!');
|
|
323
|
+
}
|
|
324
|
+
main.prototype.header.returnType.specifier.specifier.token =
|
|
325
|
+
'vec4';
|
|
293
326
|
// Find the output variable, as in "pc_fragColor" from "out highp vec4 pc_fragColor;"
|
|
294
327
|
var outName;
|
|
295
328
|
ast.program.find(function (line, index) {
|
|
@@ -300,7 +333,8 @@ export var convert300MainToReturn = function (suffix, ast) {
|
|
|
300
333
|
((_b = (_a = declaration === null || declaration === void 0 ? void 0 : declaration.specified_type) === null || _a === void 0 ? void 0 : _a.qualifiers) === null || _b === void 0 ? void 0 : _b.find(function (n) { return n.token === 'out'; })) &&
|
|
301
334
|
declaration.specified_type.specifier.specifier.token ===
|
|
302
335
|
'vec4') {
|
|
303
|
-
// Remove the out declaration
|
|
336
|
+
// Remove the out declaration. This does NOT yet remove the declaration
|
|
337
|
+
// from the scope, that's done below
|
|
304
338
|
ast.program.splice(index, 1);
|
|
305
339
|
outName = declaration.declarations[0].identifier.identifier;
|
|
306
340
|
return true;
|
|
@@ -310,27 +344,23 @@ export var convert300MainToReturn = function (suffix, ast) {
|
|
|
310
344
|
console.error(generate(ast));
|
|
311
345
|
throw new Error('No "out vec4" line found in the fragment shader');
|
|
312
346
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
},
|
|
332
|
-
},
|
|
333
|
-
});
|
|
347
|
+
// Store the variable to avoid descoping it later
|
|
348
|
+
ast.outVar = outName;
|
|
349
|
+
// Rename the scope entry of "out vec4 ___" to our return variable, and rename
|
|
350
|
+
// all references to our new variable
|
|
351
|
+
ast.scopes[0].bindings[replacedReturn] = renameBinding(ast.scopes[0].bindings[outName], replacedReturn);
|
|
352
|
+
delete ast.scopes[0].bindings[outName];
|
|
353
|
+
// Add the declaration of the return variable to the top of the program, and
|
|
354
|
+
// add it to the AST scope, including the declaration
|
|
355
|
+
var decl = makeStatement("vec4 ".concat(replacedReturn))[0];
|
|
356
|
+
ast.program.unshift(decl);
|
|
357
|
+
ast.scopes[0].bindings[replacedReturn].declaration = decl;
|
|
358
|
+
ast.scopes[0].bindings[replacedReturn].references.push(decl.declaration.declarations[0]);
|
|
359
|
+
// Add a return statement to the main() function and add the return variable
|
|
360
|
+
// to scope
|
|
361
|
+
var rtn = makeFnStatement("return ".concat(replacedReturn))[0];
|
|
362
|
+
main.body.statements = addFnStmtWithIndent(main, rtn);
|
|
363
|
+
ast.scopes[0].bindings[replacedReturn].references.push(rtn.expression);
|
|
334
364
|
};
|
|
335
365
|
export var generateFiller = function (filler) {
|
|
336
366
|
if (!filler) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions to work with whitespace on nodes.
|
|
3
|
+
*
|
|
4
|
+
* This is for manual manipulaiton of code where adding new lines should
|
|
5
|
+
* attempt to keep the indentation of the original program.
|
|
6
|
+
*
|
|
7
|
+
* Another overal option is simply to pretty print, which this library
|
|
8
|
+
* does not yet support.
|
|
9
|
+
*/
|
|
10
|
+
import { AstNode, FunctionNode, Whitespace } from '@shaderfrog/glsl-parser/ast';
|
|
11
|
+
export declare const combineWs: (a: string | string[], b: string | string[]) => string[];
|
|
12
|
+
export declare const transferWhitespace: (to: AstNode, from: AstNode) => [AstNode, AstNode];
|
|
13
|
+
export declare const getLiteralIndent: (node: {
|
|
14
|
+
whitespace: Whitespace;
|
|
15
|
+
}) => string;
|
|
16
|
+
export declare const tryAddTrailingWhitespace: <T extends AstNode>(node: T, ws: string) => T;
|
|
17
|
+
export declare const guessFnIndent: (fnBody: FunctionNode) => string;
|
|
18
|
+
export declare const addFnStmtWithIndent: (fnBody: FunctionNode, newNode: string | AstNode) => AstNode[];
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
13
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
14
|
+
if (!m) return o;
|
|
15
|
+
var i = m.call(o), r, ar = [], e;
|
|
16
|
+
try {
|
|
17
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
18
|
+
}
|
|
19
|
+
catch (error) { e = { error: error }; }
|
|
20
|
+
finally {
|
|
21
|
+
try {
|
|
22
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
23
|
+
}
|
|
24
|
+
finally { if (e) throw e.error; }
|
|
25
|
+
}
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
29
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
30
|
+
if (ar || !(i in from)) {
|
|
31
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
32
|
+
ar[i] = from[i];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
36
|
+
};
|
|
37
|
+
import { makeFnStatement } from './ast';
|
|
38
|
+
var log = function () {
|
|
39
|
+
var _a;
|
|
40
|
+
var args = [];
|
|
41
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
42
|
+
args[_i] = arguments[_i];
|
|
43
|
+
}
|
|
44
|
+
return (_a = console.log).call.apply(_a, __spreadArray([console, '\x1b[31m(core.whitespace)\x1b[0m'], __read(args), false));
|
|
45
|
+
};
|
|
46
|
+
// Typescript fucked up flat https://stackoverflow.com/a/61420611/743464
|
|
47
|
+
export var combineWs = function (a, b) { return [a, b].flat(Infinity); };
|
|
48
|
+
// Move whitespace from one node to another. Since whitespace is trailing, if a
|
|
49
|
+
// node is injected after a previous node, move the whitespace from the earlier
|
|
50
|
+
// node to the later one. This keeps comments in the same place.
|
|
51
|
+
export var transferWhitespace = function (to, from) {
|
|
52
|
+
return 'semi' in to && 'semi' in from
|
|
53
|
+
? [
|
|
54
|
+
__assign(__assign({}, to), { semi: __assign(__assign({}, to.semi), { whitespace: from.semi.whitespace }) }),
|
|
55
|
+
__assign(__assign({}, from), { semi: __assign(__assign({}, from.semi), { whitespace: '\n' }) }),
|
|
56
|
+
]
|
|
57
|
+
: 'whitespace' in to && 'semi' in from
|
|
58
|
+
? [
|
|
59
|
+
__assign(__assign({}, to), { whitespace: combineWs(to.whitespace, from.semi.whitespace) }),
|
|
60
|
+
__assign(__assign({}, from), { semi: __assign(__assign({}, from.semi), { whitespace: '\n' }) }),
|
|
61
|
+
]
|
|
62
|
+
: [to, from];
|
|
63
|
+
};
|
|
64
|
+
export var getLiteralIndent = function (node) {
|
|
65
|
+
return [(node === null || node === void 0 ? void 0 : node.whitespace) || '']
|
|
66
|
+
.flat(Infinity)
|
|
67
|
+
.join('')
|
|
68
|
+
.split(/\r|\n/)
|
|
69
|
+
.sort()
|
|
70
|
+
.at(-1);
|
|
71
|
+
};
|
|
72
|
+
export var tryAddTrailingWhitespace = function (node, ws) {
|
|
73
|
+
return 'semi' in node
|
|
74
|
+
? __assign(__assign({}, node), { semi: __assign(__assign({}, node.semi), { whitespace: combineWs(node.semi.whitespace, ws) }) }) : node;
|
|
75
|
+
};
|
|
76
|
+
export var guessFnIndent = function (fnBody) {
|
|
77
|
+
return getLiteralIndent(fnBody.body.lb) ||
|
|
78
|
+
fnBody.body.statements.reduce(function (ws, n) {
|
|
79
|
+
return ws || getLiteralIndent(n.semi);
|
|
80
|
+
}, '');
|
|
81
|
+
};
|
|
82
|
+
export var addFnStmtWithIndent = function (fnBody, newNode) {
|
|
83
|
+
var statements = fnBody.body.statements;
|
|
84
|
+
var indent = guessFnIndent(fnBody);
|
|
85
|
+
return __spreadArray(__spreadArray([], __read(statements), false), [
|
|
86
|
+
// This simple hack is way easier than trying to modify the function body
|
|
87
|
+
// opening brace and/or the previous statement
|
|
88
|
+
{ type: 'literal', literal: '', whitespace: indent },
|
|
89
|
+
tryAddTrailingWhitespace(typeof newNode === 'string' ? makeFnStatement(newNode)[0] : newNode, "\n"),
|
|
90
|
+
], false);
|
|
91
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { expect, it } from 'vitest';
|
|
2
|
+
import { parser } from '@shaderfrog/glsl-parser';
|
|
3
|
+
import { generate } from '@shaderfrog/glsl-parser';
|
|
4
|
+
import { findMain } from './ast';
|
|
5
|
+
import { addFnStmtWithIndent } from './whitespace';
|
|
6
|
+
it("addFnStmtWithIndent", function () {
|
|
7
|
+
var source = "void main() {\n vec2 y;\n}\n";
|
|
8
|
+
var ast = parser.parse(source, { quiet: true });
|
|
9
|
+
var m = findMain(ast);
|
|
10
|
+
m.body.statements = addFnStmtWithIndent(m, "return x");
|
|
11
|
+
// Should line up the whitespace properly!
|
|
12
|
+
expect(generate(m)).toBe("void main() {\n vec2 y;\n return x;\n}\n");
|
|
13
|
+
});
|