@shaderfrog/core 2.0.0-beta.3 → 3.0.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.
Files changed (49) hide show
  1. package/README.md +2 -2
  2. package/engine.d.ts +39 -3
  3. package/engine.js +5 -0
  4. package/graph/code-nodes.d.ts +8 -2
  5. package/graph/code-nodes.js +5 -1
  6. package/graph/context.d.ts +8 -6
  7. package/graph/context.js +53 -50
  8. package/graph/data-nodes.d.ts +15 -6
  9. package/graph/data-nodes.js +1 -1
  10. package/graph/evaluate.js +7 -0
  11. package/graph/graph-types.d.ts +26 -6
  12. package/graph/graph-types.js +69 -0
  13. package/graph/graph.d.ts +35 -4
  14. package/graph/graph.js +187 -143
  15. package/graph/graph.test.js +244 -54
  16. package/graph/parsers.d.ts +4 -12
  17. package/graph/parsers.js +32 -18
  18. package/graph/shader-sections.d.ts +31 -14
  19. package/graph/shader-sections.js +93 -19
  20. package/package.json +4 -4
  21. package/plugins/babylon/bablyengine.js +9 -10
  22. package/plugins/playcanvas/playengine.js +9 -10
  23. package/plugins/three/importers.d.ts +1 -0
  24. package/plugins/three/importers.js +49 -1
  25. package/plugins/three/threngine.d.ts +6 -4
  26. package/plugins/three/threngine.js +59 -39
  27. package/plugins/three/threngine.test.js +51 -4
  28. package/strategy/assignmentTo.d.ts +10 -0
  29. package/strategy/assignmentTo.js +35 -0
  30. package/strategy/declarationOf.js +2 -2
  31. package/strategy/hardCode.js +1 -4
  32. package/strategy/index.d.ts +1 -1
  33. package/strategy/index.js +1 -1
  34. package/strategy/inject.js +3 -4
  35. package/strategy/namedAttribute.js +2 -2
  36. package/strategy/strategy.d.ts +30 -5
  37. package/strategy/strategy.js +1 -1
  38. package/strategy/stratgies.test.js +59 -31
  39. package/strategy/texture2D.js +20 -22
  40. package/strategy/uniform.js +54 -56
  41. package/strategy/variable.js +5 -5
  42. package/util/ast.d.ts +9 -4
  43. package/util/ast.js +37 -8
  44. package/util/ast.test.d.ts +1 -0
  45. package/util/ast.test.js +14 -0
  46. package/util/indexByid.d.ts +4 -0
  47. package/util/indexByid.js +18 -0
  48. package/util/whitespace.d.ts +2 -0
  49. package/util/whitespace.js +22 -3
@@ -20,6 +20,8 @@ import { generate } from '@shaderfrog/glsl-parser';
20
20
  import { applyStrategy, StrategyType } from '.';
21
21
  import { makeExpression } from '../util/ast';
22
22
  import preprocess from '@shaderfrog/glsl-parser/preprocessor';
23
+ import { NodeType } from '../graph/graph-types';
24
+ import { mangleEntireProgram } from '../graph';
23
25
  it('named attribute strategy`', function () {
24
26
  var source = "\nin vec3 replaceThisAtrribute;\nvoid main() {\n vec2 y = replaceThisAtrribute;\n}\n";
25
27
  var ast = parser.parse(source, { quiet: true });
@@ -30,11 +32,11 @@ it('named attribute strategy`', function () {
30
32
  },
31
33
  }, ast, { source: source }, {});
32
34
  expect(fillers.length).toBe(1);
33
- fillers[0][1]({
35
+ fillers[0][1](function () { return ({
34
36
  type: 'literal',
35
37
  literal: "myFiller()",
36
38
  whitespace: '',
37
- });
39
+ }); });
38
40
  var result = generate(ast);
39
41
  // Should replace the use of the filler, but not the declaration
40
42
  expect(result).toBe("\nin vec3 replaceThisAtrribute;\nvoid main() {\n vec2 y = myFiller();\n}\n");
@@ -51,11 +53,11 @@ it('inject strategy after', function () {
51
53
  },
52
54
  }, ast, { source: source }, {});
53
55
  expect(fillers.length).toBe(1);
54
- fillers[0][1]({
56
+ fillers[0][1](function () { return ({
55
57
  type: 'literal',
56
58
  literal: "someOtherCall(x, y, z);\nsomeOtherCall(x, y, z);",
57
59
  whitespace: '',
58
- });
60
+ }); });
59
61
  var result = generate(ast);
60
62
  // Should fill references
61
63
  expect(result).toBe("\nuniform float x;\n// Some comment\nvoid main() {\n/* some comment */\nre(x, y, z);\nsomeOtherCall(x, y, z);\nsomeOtherCall(x, y, z);\n// Middle comment\nre(x, y, z);\nsomeOtherCall(x, y, z);\nsomeOtherCall(x, y, z);\n// Final comment\n}");
@@ -72,11 +74,11 @@ it('inject strategy before', function () {
72
74
  },
73
75
  }, ast, { source: source }, {});
74
76
  expect(fillers.length).toBe(1);
75
- fillers[0][1]({
77
+ fillers[0][1](function () { return ({
76
78
  type: 'literal',
77
79
  literal: "someOtherCall(x, y, z);\nsomeOtherCall(x, y, z);",
78
80
  whitespace: '\n',
79
- });
81
+ }); });
80
82
  var result = generate(ast);
81
83
  // Should fill references
82
84
  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}");
@@ -91,7 +93,7 @@ var constructor = function () { return ({
91
93
  id: '1',
92
94
  name: '1',
93
95
  engine: true,
94
- type: '',
96
+ type: NodeType.SOURCE,
95
97
  inputs: [],
96
98
  outputs: [],
97
99
  position: { x: 0, y: 0 },
@@ -122,25 +124,7 @@ var engine = {
122
124
  it('correctly fills with uniform strategy', function () {
123
125
  var _a, _b, _c;
124
126
  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);
143
- var fillers = applyStrategy({ type: StrategyType.UNIFORM, config: {} }, ast, {}, {});
127
+ var fillers = applyStrategy({ type: StrategyType.UNIFORM, config: {} }, ast, {});
144
128
  // It should find uniforms with simple types, excluding sampler2D
145
129
  expect(fillers.map(function (_a) {
146
130
  var _b = __read(_a, 1), name = _b[0].displayName;
@@ -155,15 +139,21 @@ it('correctly fills with uniform strategy', function () {
155
139
  (_a = fillers.find(function (_a) {
156
140
  var _b = __read(_a, 1), name = _b[0].displayName;
157
141
  return name === 'input';
158
- })) === null || _a === void 0 ? void 0 : _a[1](makeExpression('a'));
142
+ })) === null || _a === void 0 ? void 0 : _a[1](function () {
143
+ return makeExpression('a');
144
+ });
159
145
  (_b = fillers.find(function (_a) {
160
146
  var _b = __read(_a, 1), name = _b[0].displayName;
161
147
  return name === 'output';
162
- })) === null || _b === void 0 ? void 0 : _b[1](makeExpression('b'));
148
+ })) === null || _b === void 0 ? void 0 : _b[1](function () {
149
+ return makeExpression('b');
150
+ });
163
151
  (_c = fillers.find(function (_a) {
164
152
  var _b = __read(_a, 1), name = _b[0].displayName;
165
153
  return name === 'zenput';
166
- })) === null || _c === void 0 ? void 0 : _c[1](makeExpression('c'));
154
+ })) === null || _c === void 0 ? void 0 : _c[1](function () {
155
+ return makeExpression('c');
156
+ });
167
157
  var result = generate(ast);
168
158
  // Should fill references
169
159
  expect(result).toContain('vec4 x = a;');
@@ -178,6 +168,44 @@ it('correctly fills with uniform strategy', function () {
178
168
  // Should remove uniform lines
179
169
  expect(result).not.toContain('uniform vec4 zenput');
180
170
  });
171
+ it('correctly fills with uniform strategy through mangling', function () {
172
+ var _a, _b, _c;
173
+ var ast = parser.parse("\nuniform sampler2D image;\nuniform vec4 input, output;\nvoid main() {\n vec4 computed = texture2D(image, uvPow * 1.0);\n vec4 x = input;\n vec4 y = output;\n}", { quiet: true });
174
+ var node = { id: '1', name: 'fake' };
175
+ var fillers = applyStrategy({ type: StrategyType.UNIFORM, config: {} }, ast, node);
176
+ mangleEntireProgram(engine, ast, node);
177
+ // It should find uniforms with simple types, excluding sampler2D
178
+ expect(fillers.map(function (_a) {
179
+ var _b = __read(_a, 1), name = _b[0].displayName;
180
+ return name;
181
+ })).toEqual([
182
+ 'image',
183
+ 'input',
184
+ 'output',
185
+ ]);
186
+ var a = (_a = fillers.find(function (_a) {
187
+ var _b = __read(_a, 1), name = _b[0].displayName;
188
+ return name === 'input';
189
+ })) === null || _a === void 0 ? void 0 : _a[1];
190
+ (_b = fillers.find(function (_a) {
191
+ var _b = __read(_a, 1), name = _b[0].displayName;
192
+ return name === 'input';
193
+ })) === null || _b === void 0 ? void 0 : _b[1](function () {
194
+ return makeExpression('a');
195
+ });
196
+ (_c = fillers.find(function (_a) {
197
+ var _b = __read(_a, 1), name = _b[0].displayName;
198
+ return name === 'output';
199
+ })) === null || _c === void 0 ? void 0 : _c[1](function () {
200
+ return makeExpression('b');
201
+ });
202
+ var result = generate(ast);
203
+ // Should fill references
204
+ expect(result).toContain('vec4 x = a;');
205
+ expect(result).toContain('vec4 y = b;');
206
+ // Should preserve things it shouldn't touch
207
+ expect(result).toContain("uniform sampler2D image_".concat(node.id, ";"));
208
+ });
181
209
  it('uses name without suffix for single call', function () {
182
210
  var ast = parser.parse("\nvoid main() {\n vec4 computed = texture2D(noiseImage, uvPow * 1.0);\n}", { quiet: true });
183
211
  expect(applyStrategy({ type: StrategyType.TEXTURE_2D, config: {} }, ast, {}, {}).map(function (_a) {
@@ -185,12 +213,12 @@ it('uses name without suffix for single call', function () {
185
213
  return name;
186
214
  })).toEqual(['noiseImage']);
187
215
  });
188
- it('finds multiple texture2D inputs for one uniform', function () {
216
+ it('finds one texture2D input for one texture2D() call', function () {
189
217
  var ast = parser.parse("\nvoid main() {\n vec4 computed = texture2D(noiseImage, uvPow * 1.0);\n computed += texture2D(noiseImage, uvPow * 2.0);\n}", { quiet: true });
190
218
  expect(applyStrategy({ type: StrategyType.TEXTURE_2D, config: {} }, ast, {}, {}).map(function (_a) {
191
219
  var _b = __read(_a, 1), name = _b[0].displayName;
192
220
  return name;
193
- })).toEqual(['noiseImage_0', 'noiseImage_1']);
221
+ })).toEqual(['noiseImage']);
194
222
  });
195
223
  it('Make sure texture2D finds preprocessed texture() call', function () {
196
224
  // I thought this was a regression, but it wasn't a real bug, but tests seems
@@ -33,7 +33,7 @@ export var texture2DStrategy = function () { return ({
33
33
  }); };
34
34
  export var applyTexture2DStrategy = function (strategy, ast, graphNode, siblingNode) {
35
35
  var texture2Dcalls = [];
36
- var seen = {};
36
+ var references = {};
37
37
  var visitors = {
38
38
  function_call: {
39
39
  enter: function (path) {
@@ -47,36 +47,34 @@ export var applyTexture2DStrategy = function (strategy, ast, graphNode, siblingN
47
47
  throw new Error('This error is impossible. A function call always has a parent.');
48
48
  }
49
49
  var name = generate(path.node.args[0]);
50
- seen[name] = (seen[name] || 0) + 1;
51
- texture2Dcalls.push([
52
- name,
53
- path.parent,
54
- path.key,
55
- // Remove the first argument and comma
56
- path.node.args.slice(2),
57
- ]);
50
+ if (!(name in references)) {
51
+ references[name] = [];
52
+ texture2Dcalls.push(name);
53
+ }
54
+ references[name].push({
55
+ parent: path.parent,
56
+ key: path.key,
57
+ args: path.node.args.slice(2),
58
+ });
58
59
  }
59
60
  },
60
61
  },
61
62
  };
62
63
  visit(ast, visitors);
63
- var names = new Set(Object.entries(seen).reduce(function (arr, _a) {
64
- var _b = __read(_a, 2), name = _b[0], count = _b[1];
65
- return __spreadArray(__spreadArray([], __read(arr), false), __read((count > 1 ? [name] : [])), false);
66
- }, []));
67
- var inputs = texture2Dcalls.map(function (_a, index) {
68
- var _b = __read(_a, 4), name = _b[0], parent = _b[1], key = _b[2], texture2dArgs = _b[3];
69
- // Suffix input name if it's used more than once
70
- var iName = names.has(name) ? "".concat(name, "_").concat(index) : name;
64
+ var inputs = texture2Dcalls.map(function (name) {
71
65
  return [
72
- nodeInput(iName, "filler_".concat(iName), 'filler', 'vector4', // Data type for what plugs into this filler
66
+ nodeInput(name, "filler_".concat(name), 'filler', 'vector4', // Data type for what plugs into this filler
73
67
  ['code', 'data'], false),
74
- function (fillerAst) {
75
- // @ts-ignore
76
- parent[key] = fillerAst;
68
+ function (filler) {
69
+ references[name].forEach(function (_a) {
70
+ var parent = _a.parent, key = _a.key, args = _a.args;
71
+ // Backfilling into the filler! Similar to parsers.ts filler
72
+ var f = filler.apply(void 0, __spreadArray([], __read(args.map(generate)), false));
73
+ // @ts-ignore
74
+ parent[key] = f;
75
+ });
77
76
  return ast;
78
77
  },
79
- texture2dArgs,
80
78
  ];
81
79
  });
82
80
  return inputs;
@@ -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;
@@ -17,6 +28,7 @@ var __read = (this && this.__read) || function (o, n) {
17
28
  import { nodeInput } from '../graph/base-node';
18
29
  import { StrategyType } from '.';
19
30
  import { generateFiller } from '../util/ast';
31
+ import { renameBinding } from '@shaderfrog/glsl-parser/parser/utils';
20
32
  export var uniformStrategy = function () { return ({
21
33
  type: StrategyType.UNIFORM,
22
34
  config: {},
@@ -127,64 +139,50 @@ var mapUniformType = function (type) {
127
139
  }
128
140
  // console.log(`Unknown uniform type, can't map to graph: ${type}`);
129
141
  };
130
- export var applyUniformStrategy = function (strategy, ast, graphNode, siblingNode) {
142
+ var isUniformDeclaration = function (node) {
143
+ var _a, _b, _c;
144
+ return node.type === 'declaration_statement' &&
145
+ node.declaration.type === 'declarator_list' &&
146
+ !!((_c = (_b = (_a = node.declaration) === null || _a === void 0 ? void 0 : _a.specified_type) === null || _b === void 0 ? void 0 : _b.qualifiers) === null || _c === void 0 ? void 0 : _c.find(function (n) { return n.token === 'uniform'; }));
147
+ };
148
+ // commented this out to allow for sampler2D uniforms to appear as inputs
149
+ // && uniformType !== 'sampler2D'
150
+ export var applyUniformStrategy = function (strategy, ast, graphNode) {
131
151
  var program = ast;
132
- return (program.program || []).flatMap(function (node) {
133
- var _a, _b, _c, _d, _e, _f, _g;
152
+ return (program.program || [])
153
+ .filter(isUniformDeclaration)
154
+ .flatMap(function (node) {
155
+ var _a, _b, _c;
156
+ var declaration = node.declaration;
134
157
  // The uniform declaration type, like vec4
135
- var uniformType = (_d = (_c = (_b = (_a = node.declaration) === null || _a === void 0 ? void 0 : _a.specified_type) === null || _b === void 0 ? void 0 : _b.specifier) === null || _c === void 0 ? void 0 : _c.specifier) === null || _d === void 0 ? void 0 : _d.token;
158
+ // TODO: File VSCode bug, this is highlighted like a function
159
+ var uniformType = (_c = (_b = (_a = declaration === null || declaration === void 0 ? void 0 : declaration.specified_type) === null || _a === void 0 ? void 0 : _a.specifier) === null || _b === void 0 ? void 0 : _b.specifier) === null || _c === void 0 ? void 0 : _c.token;
136
160
  var graphDataType = mapUniformType(uniformType);
137
- // If this is a uniform declaration line
138
- if (node.type === 'declaration_statement' &&
139
- node.declaration.type === 'declarator_list' &&
140
- ((_g = (_f = (_e = node.declaration) === null || _e === void 0 ? void 0 : _e.specified_type) === null || _f === void 0 ? void 0 : _f.qualifiers) === null || _g === void 0 ? void 0 : _g.find(function (n) { return n.token === 'uniform'; }))
141
- // commented this out to allow for sampler2D uniforms to appear as inputs
142
- // && uniformType !== 'sampler2D'
143
- ) {
144
- // Capture all the declared names, removing mangling suffix
145
- var declarations_1 = node.declaration.declarations;
146
- var names = declarations_1.map(function (d) { return d.identifier.identifier; });
147
- // Tricky code warning: The flow of preparing a node for the graph is:
148
- // 1. Produce/mangle the AST (with unmangled names)
149
- // 2. findInputs() (with unmangled names)
150
- // 3. The AST is *then* mangled in graph.ts
151
- // 4. Later, the inputs are filled in, and now, we have an input with
152
- // the name "x" but the ast now has the mangled name "x_1". So
153
- // here, we look for the *mangled* name in the strategy runner
154
- return names.map(function (name) { return [
155
- nodeInput(name, "uniform_".concat(name), 'uniform', graphDataType, ['code', 'data'], true),
156
- function (filler) {
157
- //const mangledName = mangleName(name, graphNode, siblingNode);
158
- var mangledName = name;
159
- // Remove the declaration line, or the declared uniform
160
- if (declarations_1.length === 1) {
161
- program.program.splice(program.program.indexOf(node), 1);
162
- }
163
- else {
164
- var decl = node.declaration;
165
- decl.declarations = decl.declarations.filter(function (d) { return d.identifier.identifier !== mangledName; });
166
- }
167
- // And rename all the references to said uniform
168
- program.scopes[0].bindings[name].references.forEach(function (ref) {
169
- if (ref.type === 'identifier' && ref.identifier === mangledName) {
170
- ref.identifier = generateFiller(filler);
171
- }
172
- else if (ref.type === 'parameter_declaration' &&
173
- 'identifier' in ref &&
174
- ref.identifier.identifier === mangledName) {
175
- ref.identifier.identifier = generateFiller(filler);
176
- }
177
- else if ('identifier' in ref) {
178
- ref.identifier = generateFiller(filler);
179
- }
180
- else {
181
- console.warn('Unknown uniform reference for', graphNode.name, 'ref');
182
- }
183
- });
184
- return ast;
185
- },
186
- ]; });
187
- }
188
- return [];
161
+ var declarations = declaration.declarations;
162
+ // Capture the uniform names, and then capture their references in the
163
+ // closure. This allows the scope binding to be renamed when the AST is
164
+ // mangled, but this strategy can still find the original named variables
165
+ // to work with
166
+ var names = declarations.map(function (d) { return d.identifier.identifier; });
167
+ var references = names.reduce(function (acc, name) {
168
+ var _a;
169
+ return (__assign(__assign({}, acc), (_a = {}, _a[name] = program.scopes[0].bindings[name], _a)));
170
+ }, {});
171
+ return names.map(function (name) { return [
172
+ nodeInput(name, "uniform_".concat(name), 'uniform', graphDataType, ['code', 'data'], true),
173
+ function (filler) {
174
+ // Remove the declaration line, or the declared uniform
175
+ if (declarations.length === 1) {
176
+ program.program.splice(program.program.indexOf(node), 1);
177
+ }
178
+ else {
179
+ var decl = node.declaration;
180
+ decl.declarations = decl.declarations.filter(function (d) { return d.identifier.identifier !== name; });
181
+ }
182
+ // Rename all the references to said uniform
183
+ renameBinding(references[name], generateFiller(filler()));
184
+ return ast;
185
+ },
186
+ ]; });
189
187
  });
190
188
  };
@@ -35,7 +35,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
35
35
  return to.concat(ar || Array.prototype.slice.call(from));
36
36
  };
37
37
  import { nodeInput } from '../graph/base-node';
38
- import { StrategyType } from '.';
38
+ import { StrategyType, } from '.';
39
39
  import { generateFiller } from '../util/ast';
40
40
  export var variableStrategy = function () { return ({
41
41
  type: StrategyType.VARIABLE,
@@ -48,15 +48,15 @@ export var applyVariableStrategy = function (strategy, ast, graphNode, siblingNo
48
48
  var identifier, replacer;
49
49
  if (ref.type === 'declaration') {
50
50
  identifier = ref.identifier.identifier;
51
- replacer = function (fillerAst) {
52
- ref.identifier.identifier = generateFiller(fillerAst);
51
+ replacer = function (filler) {
52
+ ref.identifier.identifier = generateFiller(filler());
53
53
  return ast;
54
54
  };
55
55
  }
56
56
  else if (ref.type === 'identifier') {
57
57
  identifier = ref.identifier;
58
- replacer = function (fillerAst) {
59
- ref.identifier = generateFiller(fillerAst);
58
+ replacer = function (filler) {
59
+ ref.identifier = generateFiller(filler());
60
60
  return ast;
61
61
  };
62
62
  // } else if (ref.type === 'parameter_declaration') {
package/util/ast.d.ts CHANGED
@@ -1,16 +1,19 @@
1
- import { AstNode, ExpressionStatementNode, FunctionNode, DeclarationStatementNode, DeclarationNode } from '@shaderfrog/glsl-parser/ast';
1
+ /**
2
+ * Utility functions to work with ASTs
3
+ */
4
+ import { AstNode, ExpressionStatementNode, FunctionNode, DeclarationStatementNode, DeclarationNode, LiteralNode } from '@shaderfrog/glsl-parser/ast';
2
5
  import { Program } from '@shaderfrog/glsl-parser/ast';
3
6
  import { ShaderStage } from '../graph/graph-types';
4
7
  import { Scope } from '@shaderfrog/glsl-parser/parser/scope';
5
- import { Filler } from '../graph/parsers';
6
8
  export interface FrogProgram extends Program {
7
9
  outVar?: string;
8
10
  }
11
+ export declare const makeLiteral: (literal: string, whitespace?: string) => LiteralNode;
9
12
  export declare const findVec4Constructor: (ast: AstNode) => AstNode | undefined;
10
13
  export declare const findAssignmentTo: (ast: AstNode | Program, assignTo: string, nth?: number) => DeclarationNode | ExpressionStatementNode | undefined;
11
14
  export declare const findDeclarationOf: (ast: AstNode | Program, declarationOf: string) => DeclarationNode | undefined;
12
15
  export declare const from2To3: (ast: Program, stage: ShaderStage) => void;
13
- export declare const makeStatement: (stmt: string) => readonly [DeclarationStatementNode | FunctionNode | import("@shaderfrog/glsl-parser/ast").PreprocessorNode, Scope];
16
+ export declare const makeStatement: (stmt: string, ws?: string) => readonly [import("@shaderfrog/glsl-parser/ast").ProgramStatement, Scope];
14
17
  export declare const makeFnStatement: (fnStmt: string) => readonly [AstNode, Scope];
15
18
  /**
16
19
  * Add a new scope into an existing one. Meant to be used for adding net new
@@ -46,4 +49,6 @@ export declare const returnGlPositionVec3Right: (fnName: string, ast: Program) =
46
49
  * returns a vec4.
47
50
  */
48
51
  export declare const convert300MainToReturn: (ast: FrogProgram) => void;
49
- export declare const generateFiller: (filler: Filler) => string;
52
+ export declare const generateFiller: (ast: AstNode | AstNode[] | void) => string;
53
+ export declare const isDeclarationStatement: (node: Program["program"][0]) => node is DeclarationStatementNode;
54
+ export declare const backfillAst: (ast: Program, fromType: string, targetVariable: string, mainFn?: FunctionNode) => Program;
package/util/ast.js CHANGED
@@ -1,3 +1,6 @@
1
+ /**
2
+ * Utility functions to work with ASTs
3
+ */
1
4
  var __assign = (this && this.__assign) || function () {
2
5
  __assign = Object.assign || function(t) {
3
6
  for (var s, i = 1, n = arguments.length; i < n; i++) {
@@ -37,7 +40,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
37
40
  import { parser, generate } from '@shaderfrog/glsl-parser';
38
41
  import { visit, } from '@shaderfrog/glsl-parser/ast';
39
42
  import { addFnStmtWithIndent } from './whitespace';
40
- import { renameBinding, } from '@shaderfrog/glsl-parser/parser/utils';
43
+ import { renameBinding } from '@shaderfrog/glsl-parser/parser/utils';
41
44
  var log = function () {
42
45
  var _a;
43
46
  var args = [];
@@ -46,6 +49,14 @@ var log = function () {
46
49
  }
47
50
  return (_a = console.log).call.apply(_a, __spreadArray([console, '\x1b[31m(core.manipulate)\x1b[0m'], __read(args), false));
48
51
  };
52
+ export var makeLiteral = function (literal, whitespace) {
53
+ if (whitespace === void 0) { whitespace = ''; }
54
+ return ({
55
+ type: 'literal',
56
+ literal: literal,
57
+ whitespace: whitespace,
58
+ });
59
+ };
49
60
  export var findVec4Constructor = function (ast) {
50
61
  var parent;
51
62
  var visitors = {
@@ -159,11 +170,12 @@ export var from2To3 = function (ast, stage) {
159
170
  },
160
171
  });
161
172
  };
162
- export var makeStatement = function (stmt) {
173
+ export var makeStatement = function (stmt, ws) {
174
+ if (ws === void 0) { ws = ''; }
163
175
  // log(`Parsing "${stmt}"`);
164
176
  var ast;
165
177
  try {
166
- ast = parser.parse("".concat(stmt, ";\n"), { quiet: true });
178
+ ast = parser.parse("".concat(stmt, ";").concat(ws, "\n"), { quiet: true });
167
179
  }
168
180
  catch (error) {
169
181
  console.error({ stmt: stmt, error: error });
@@ -366,11 +378,28 @@ export var convert300MainToReturn = function (ast) {
366
378
  main.body.statements = addFnStmtWithIndent(main, rtn);
367
379
  ast.scopes[0].bindings[replacedReturn].references.push(rtn.expression);
368
380
  };
369
- export var generateFiller = function (filler) {
370
- if (!filler) {
381
+ export var generateFiller = function (ast) {
382
+ if (!ast) {
371
383
  throw new Error('Cannot generate void filler!');
372
384
  }
373
- return Array.isArray(filler)
374
- ? filler.map(generate).join('')
375
- : generate(filler);
385
+ return Array.isArray(ast) ? ast.map(generate).join('') : generate(ast);
386
+ };
387
+ export var isDeclarationStatement = function (node) {
388
+ return node.type === 'declaration_statement' &&
389
+ node.declaration.type === 'declarator_list';
390
+ };
391
+ export var backfillAst = function (ast, fromType, targetVariable, mainFn) {
392
+ if (!ast.scopes[0].bindings[targetVariable]) {
393
+ console.warn("Variable \"".concat(targetVariable, "\" not found in global program scope to backfill! Variables: ").concat(Object.keys(ast.scopes[0].bindings)));
394
+ }
395
+ // Inject the backfill param as the arg
396
+ if (mainFn) {
397
+ mainFn.prototype.parameters = (mainFn.prototype.parameters || [])
398
+ .filter(
399
+ // Watch out for the main(void){} case!
400
+ function (arg) { return arg.specifier.specifier.token !== 'void'; })
401
+ .concat(parser.parse("void x(".concat(fromType, " ").concat(targetVariable, ") {}"))
402
+ .program[0].prototype.parameters);
403
+ }
404
+ return ast;
376
405
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import { expect, it } from 'vitest';
2
+ import { parser } from '@shaderfrog/glsl-parser';
3
+ import { generate } from '@shaderfrog/glsl-parser';
4
+ import { backfillAst, findMain } from '../util/ast';
5
+ it('backfillAst', function () {
6
+ var source = parser.parse("\nattribute vec2 vUv, xx;\nvoid main() {\n gl_FragColor = vec4(vUv, xx);\n}");
7
+ var result = backfillAst(source, 'vec2', 'vUv', findMain(source));
8
+ expect(generate(result)).toBe("\nattribute vec2 vUv, xx;\nvoid main(vec2 vUv) {\n gl_FragColor = vec4(vUv, xx);\n}");
9
+ });
10
+ it('backfillAst with void main fn', function () {
11
+ var source = parser.parse("\nattribute vec2 vUv;\nvoid main(void) {\n gl_FragColor = vec4(vUv, 1.0, 1.0);\n}");
12
+ var result = backfillAst(source, 'vec2', 'vUv', findMain(source));
13
+ expect(generate(result)).toBe("\nattribute vec2 vUv;\nvoid main(vec2 vUv) {\n gl_FragColor = vec4(vUv, 1.0, 1.0);\n}");
14
+ });
@@ -0,0 +1,4 @@
1
+ declare const indexById: <T extends {
2
+ id: string | number;
3
+ }>(records: T[]) => Record<string, T>;
4
+ export default indexById;
@@ -0,0 +1,18 @@
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 indexById = function (records) {
13
+ return records.reduce(function (acc, record) {
14
+ var _a;
15
+ return (__assign(__assign({}, acc), (_a = {}, _a[record.id] = record, _a)));
16
+ }, {});
17
+ };
18
+ export default indexById;
@@ -16,3 +16,5 @@ export declare const getLiteralIndent: (node: {
16
16
  export declare const tryAddTrailingWhitespace: <T extends AstNode>(node: T, ws: string) => T;
17
17
  export declare const guessFnIndent: (fnBody: FunctionNode) => string;
18
18
  export declare const addFnStmtWithIndent: (fnBody: FunctionNode, newNode: string | AstNode) => AstNode[];
19
+ export declare const unshiftFnStmtWithIndent: (fnBody: FunctionNode, newNode: string | AstNode) => AstNode[];
20
+ export declare const spliceFnStmtWithIndent: (fnBody: FunctionNode, index: number, ...newNodes: (string | AstNode)[]) => AstNode[];
@@ -34,7 +34,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
34
34
  }
35
35
  return to.concat(ar || Array.prototype.slice.call(from));
36
36
  };
37
- import { makeFnStatement } from './ast';
37
+ import { makeFnStatement, makeLiteral } from './ast';
38
38
  var log = function () {
39
39
  var _a;
40
40
  var args = [];
@@ -79,13 +79,32 @@ export var guessFnIndent = function (fnBody) {
79
79
  return ws || getLiteralIndent(n.semi) || '';
80
80
  }, '');
81
81
  };
82
+ var addWs = function (node, ws) {
83
+ return tryAddTrailingWhitespace(typeof node === 'string' ? makeFnStatement(node)[0] : node, ws);
84
+ };
82
85
  export var addFnStmtWithIndent = function (fnBody, newNode) {
83
86
  var statements = fnBody.body.statements;
84
87
  var indent = guessFnIndent(fnBody);
85
88
  return __spreadArray(__spreadArray([], __read(statements), false), [
86
89
  // This simple hack is way easier than trying to modify the function body
87
90
  // opening brace and/or the previous statement
88
- { type: 'literal', literal: '', whitespace: indent },
89
- tryAddTrailingWhitespace(typeof newNode === 'string' ? makeFnStatement(newNode)[0] : newNode, "\n"),
91
+ makeLiteral('', indent),
92
+ addWs(newNode, "\n"),
90
93
  ], false);
91
94
  };
95
+ export var unshiftFnStmtWithIndent = function (fnBody, newNode) {
96
+ var statements = fnBody.body.statements;
97
+ var indent = guessFnIndent(fnBody);
98
+ return __spreadArray([addWs(newNode, "\n"), makeLiteral('', indent)], __read(statements), false);
99
+ };
100
+ export var spliceFnStmtWithIndent = function (fnBody, index) {
101
+ var newNodes = [];
102
+ for (var _i = 2; _i < arguments.length; _i++) {
103
+ newNodes[_i - 2] = arguments[_i];
104
+ }
105
+ var statements = fnBody.body.statements;
106
+ var indent = guessFnIndent(fnBody);
107
+ return __spreadArray(__spreadArray(__spreadArray(__spreadArray([], __read(statements.slice(0, index)), false), __read(newNodes.map(function (n) { return addWs(n, "\n"); })), false), [
108
+ makeLiteral('', indent)
109
+ ], false), __read(statements.slice(index)), false);
110
+ };