Skip to content

Commit eee82b4

Browse files
committed
feat(jstree): fix type inference in js tree
1 parent 3d89c76 commit eee82b4

File tree

5 files changed

+136
-79
lines changed

5 files changed

+136
-79
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type inference is supported by type checker.
4141
- [x] pow as arithmetic operator
4242
- [x] variable index expression for vectors
4343
- [ ] gentype for function outline
44-
- [ ] functional structs
44+
- [x] functional structs
4545
- [ ] destruct
4646
- [ ] symbols and enums
4747

src/glsl/index.js

+51-50
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ function handleAlloc(init, typeAnnotation, name) {
322322
if (init.type === 'CallExpression') {
323323

324324
switch (typeAnnotation) {
325+
// BuiltIn info!
325326
case 'int':
326327
case 'float':
327328
case 'bool':
@@ -336,21 +337,29 @@ function handleAlloc(init, typeAnnotation, name) {
336337
allocation = '';
337338
}
338339
break;
339-
default:
340-
if (init.arguments.length) {
340+
default: {
341+
const [first] = init.arguments;
342+
343+
if (typeAnnotation === init.callee.name || (first && first.type === 'ArrayExpression')) {
341344
if (init.arguments.length === 1) {
342-
const [first] = init.arguments;
345+
343346
if (first.type === 'ArrayExpression') {
344-
const els = first.elements.map((n) => `${handleNode(n)}`).join(', ');
347+
const els = first.elements.map((n) => `${handleNode(n)}`)
348+
.join(', ');
345349
allocation = ` = ${typeAnnotation}(${els});`;
346350
} else {
347351
allocation = ` = ${handleNode(init.arguments[0])}`;
348352
}
353+
} else if (!init.arguments.length) {
354+
allocation = '';
349355
} else {
350356
throwError(`classes dont support init calls yet ${typeAnnotation}`, init);
351357
}
358+
} else {
359+
allocation = ` = ${handleNode(init)};`;
352360
}
353361
break;
362+
}
354363
}
355364
} else if (init.type === 'NewExpression') {
356365
if (init.arguments.length) {
@@ -376,30 +385,10 @@ function handleBody(body, tabCount = 0) {
376385
.join('\n');
377386
}
378387

379-
// function replaceGenType(node) {
380-
// console.log('replaceGenType', node);
381-
// return node;
382-
// }
383-
//
384-
// function handleGenTypes(body) {
385-
// return body.map((node) => {
386-
// if (node.right && node.right.init && node.right.init.returnType === 'genType') {
387-
// return [
388-
// replaceGenType(JSON.parse(JSON.stringify(node)), 'float'),
389-
// replaceGenType(JSON.parse(JSON.stringify(node)), 'vec2'),
390-
// replaceGenType(JSON.parse(JSON.stringify(node)), 'vec3'),
391-
// replaceGenType(JSON.parse(JSON.stringify(node)), 'vec4')
392-
// ];
393-
// }
394-
// return [node];
395-
// }).flat(1);
396-
// }
397-
398388
function handeAst(node) {
399389
const { body } = node.body[0].expression;
400390

401391
body.body = body.body.filter(({ type }) => (type !== 'ReturnStatement'));
402-
// body.body = handleGenTypes(body.body);
403392
let sh = handleBody(body);
404393

405394
sh = sh.split('\n').map((s) => {
@@ -424,24 +413,26 @@ export function buildGLSL(fun, { glsl = true, js = undefined, ast = undefined }
424413
let node;
425414
let code;
426415
let text;
416+
417+
if (js) {
418+
if (js === true) {
419+
js = {};
420+
}
421+
if (js) {
422+
code = readOnlyView(sim(fun, { BuiltIn, ...js }));
423+
}
424+
}
427425
try {
428426
if (glsl || ast) {
429427
str = fun.toString();
430-
node = parse(str, TREE_SETTINGS);
428+
node = parse(str, { ...TREE_SETTINGS });
431429
}
432430

433431
if (glsl) {
434432
text = handeAst(node);
435433
}
436434

437-
if (js) {
438-
if (js === true) {
439-
js = {};
440-
}
441-
code = sim(fun, { BuiltIn, ...js });
442-
}
443-
444-
return { glsl: text, ast: node, js: readOnlyView(code), [ORIGINALS]: [fun] };
435+
return { glsl: text, ast: node, js: code, [ORIGINALS]: [fun] };
445436
} catch (e) {
446437
if (e[LINE]) {
447438
const allLines = str.split('\n');
@@ -459,28 +450,39 @@ ${e.message}`);
459450
}
460451
}
461452

462-
export function joinGLSL(args, { glsl: glslOn = true, js: jsOn = false, ast: astOn = false } = {}) {
453+
export function joinGLSL(args, { glsl: glslOn = true, js = undefined, ast: astOn = false } = {}) {
454+
if (js === true) {
455+
js = {};
456+
}
457+
if (js) {
458+
js = { BuiltIn, ...js };
459+
}
460+
463461
const options = { ...TREE_SETTINGS, scope: {} };
464-
const { asts, js, originals, keys } = args.reduce((mem, { [ORIGINALS]: originals }) => {
465-
if (jsOn) {
462+
const { asts, js: newJs, originals, keys } = args.reduce((mem, { [ORIGINALS]: originals }) => {
463+
464+
if (js) {
466465
originals.forEach((original) => {
467-
mem.js = sim(original, { BuiltIn }, mem.keys);
468466

469-
Object.entries(mem.js).forEach(([key, value]) => {
470-
mem.keys[key] = value;
471-
});
467+
mem.js = sim(original, mem.js, mem.keys);
468+
469+
Object.entries(mem.js)
470+
.forEach(([key, value]) => {
471+
mem.keys[key] = value;
472+
});
472473
});
473474
}
474475
if (glslOn || astOn) {
476+
const opt = { ...options };
475477
originals.forEach((fun) => {
476478
const str = fun.toString();
477-
const ast = parse(str, options);
479+
const ast = parse(str, opt);
478480
mem.asts.push(ast);
479481
});
480482
}
481483
mem.originals.push(...originals);
482484
return mem;
483-
}, { asts: [], js: undefined, keys: {}, originals: [] });
485+
}, { asts: [], js, keys: {}, originals: [] });
484486

485487
let glsl;
486488
if (glslOn) {
@@ -511,14 +513,13 @@ export function joinGLSL(args, { glsl: glslOn = true, js: jsOn = false, ast: ast
511513
glsl = handeAst({ body: [{ expression: { body: { body } } }] });
512514
}
513515

514-
if (js) {
515-
Object.entries(keys).forEach(([key, value]) => {
516-
if (!js[key]) {
517-
js[key] = value;
518-
}
519-
});
520-
}
521-
return { glsl, js: readOnlyView(js), [ORIGINALS]: originals };
516+
Object.entries(keys).forEach(([key, value]) => {
517+
if (!newJs[key]) {
518+
newJs[key] = value;
519+
}
520+
});
521+
522+
return { glsl, js: readOnlyView(newJs), [ORIGINALS]: originals };
522523
}
523524

524525
export function addErrorHandling(glsl) {

src/jstree.js

+29-5
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ function extractFromScope(scope, name, args) {
5959
}
6060

6161
function extractType(node, target, options) {
62-
const { qualifiers, integer, float, string, boolean, scope, operators } = options;
62+
const { qualifiers, integer, float, string, boolean, scope } = options;
6363
const { type, name, callee, arguments: args, value, raw } = node;
6464

6565
if (type === 'CallExpression' || type === 'NewExpression') {
@@ -136,10 +136,34 @@ function extractType(node, target, options) {
136136
const newScope = { ...scope };
137137
target.newInit = handleParams(node, { ...options, scope: newScope });
138138
target.newInit.returnType = 'void';
139-
} else if (operators && type === 'BinaryExpression') {
139+
} else if (type === 'BinaryExpression') {
140140
const left = extractFromScope(scope, node.left.name, []);
141141
const right = extractFromScope(scope, node.right.name, []);
142-
target.typeAnnotation = operators(left, node.operator, right);
142+
switch (node.operator) {
143+
case '+': {
144+
target.typeAnnotation = scope.add(left, right);
145+
break;
146+
}
147+
case '-': {
148+
target.typeAnnotation = scope.sub(left, right);
149+
break;
150+
}
151+
case '*': {
152+
target.typeAnnotation = scope.mul(left, right);
153+
break;
154+
}
155+
case '/': {
156+
target.typeAnnotation = scope.div(left, right);
157+
break;
158+
}
159+
case '%': {
160+
target.typeAnnotation = scope.mod(left, right);
161+
break;
162+
}
163+
default:
164+
// do nothing
165+
break;
166+
}
143167
target.newInit = node;
144168
} else {
145169
target.newInit = node;
@@ -202,10 +226,10 @@ function handleNode(node, options) {
202226
return node;
203227
}
204228

205-
export function parse(input, { qualifiers = [], float = 'Number', integer = float, string = 'String', boolean = 'Boolean', locations = false, ranges = false, operators, scope = {}, ...options } = {}) {
229+
export function parse(input, { qualifiers = [], float = 'Number', integer = float, string = 'String', boolean = 'Boolean', locations = false, ranges = false, scope = {}, ...options } = {}) {
206230
// TODO: use onToken !!!!
207231
const ast = acorn.parse(input, { ...options, locations, ranges, ecmaVersion: 'latest' });
208-
const node = handleNode(ast, { qualifiers, integer, float, string, boolean, scope, operators });
232+
const node = handleNode(ast, { qualifiers, integer, float, string, boolean, scope });
209233

210234
return node;
211235
}

test/glsl/index.js

+47-17
Original file line numberDiff line numberDiff line change
@@ -295,23 +295,6 @@ void fnFVoid() {
295295
assert.equal(glsl.trim(), expected.trim());
296296
});
297297

298-
it('interpret gentype works.', () => {
299-
const { glsl, ast } = buildGLSL(() => {
300-
const foo = gentype((x = gentype()) => {
301-
let res = gentype(x * 5.0);
302-
return res;
303-
});
304-
});
305-
306-
const expected = `
307-
gentype foo(gentype x) {
308-
\tgentype res = x * 5.0;
309-
\treturn res;
310-
}
311-
`;
312-
assert.equal(glsl.trim(), expected.trim());
313-
});
314-
315298
it('works glsl 3.0 in and out.', () => {
316299
const { glsl } = buildGLSL(() => {
317300
let foo = input(vec2);
@@ -447,4 +430,51 @@ int len = int(mat.length());
447430

448431
assert.equal(glsl.trim(), expected.trim());
449432
});
433+
434+
it('works fine with joining glsl snippets type inference', () => {
435+
const shader1 = ({ cls, vec2, float }) => {
436+
let Foo = cls({
437+
bar: vec2,
438+
zahl: float
439+
});
440+
let prepareBar = Foo((g = float()) => {
441+
let fee = new Foo();
442+
fee.bar = vec2(g, 2.0);
443+
fee.zahl = 5.0;
444+
return fee;
445+
});
446+
return { Foo, prepareBar };
447+
};
448+
const shader2 = ({ vec2, Foo, prepareBar }) => {
449+
let bar = Foo(() => {
450+
let baz = prepareBar(4.0);
451+
baz.bar = vec2(1.0, 2.0);
452+
baz.zahl = 5.0;
453+
return baz;
454+
});
455+
return { bar };
456+
};
457+
const one = buildGLSL(shader1);
458+
const two = buildGLSL(shader2);
459+
const mixed = joinGLSL([one, two], { glsl: true, js: true /* only for debugging */ });
460+
const { glsl } = mixed;
461+
462+
const expected = `
463+
struct Foo { vec2 bar; float zahl; };
464+
Foo prepareBar(float g) {
465+
\tFoo fee;
466+
\tfee.bar = vec2(g, 2.0);
467+
\tfee.zahl = 5.0;
468+
\treturn fee;
469+
}
470+
Foo bar() {
471+
\tFoo baz = prepareBar(4.0);
472+
\tbaz.bar = vec2(1.0, 2.0);
473+
\tbaz.zahl = 5.0;
474+
\treturn baz;
475+
}
476+
`;
477+
478+
assert.equal(glsl.trim(), expected.trim());
479+
});
450480
});

test/jstree.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,6 @@ describe('jstree autodetect primitive tests', () => {
371371
assert.equal(init.raw, '+5.0');
372372
});
373373

374-
375374
it('extract type via autodetect integer', () => {
376375
const node = parse('let x = 5;', { integer: 'SPECIAL' });
377376

@@ -503,11 +502,14 @@ describe('jstree autodetect primitive tests', () => {
503502
let foo = z * z;
504503
let bar = y * foo;
505504
return foo;
506-
});`, { operators: (left, operator, right) => {
507-
if (left === 'Mat3' && right === 'Mat3') {
508-
return 'Mat3';
509-
} if (left === 'Vec2' && right === 'Mat3') {
510-
return 'Vec2';
505+
});`, { scope: {
506+
507+
mul: (left, right) => {
508+
if (left === 'Mat3' && right === 'Mat3') {
509+
return 'Mat3';
510+
} if (left === 'Vec2' && right === 'Mat3') {
511+
return 'Vec2';
512+
}
511513
}
512514
} });
513515

0 commit comments

Comments
 (0)