From c407fc805ece0aadbe9c45d466910574aa7d87b3 Mon Sep 17 00:00:00 2001 From: xBZZZZ <91378413+xBZZZZ@users.noreply.github.com> Date: Tue, 15 Mar 2022 16:01:04 +0000 Subject: [PATCH] JSCompiler stuff #2 --- src/threads.js | 133 +++++++++++++++++++++++++++++-------------------- 1 file changed, 80 insertions(+), 53 deletions(-) diff --git a/src/threads.js b/src/threads.js index fb245ada..43921743 100644 --- a/src/threads.js +++ b/src/threads.js @@ -7504,9 +7504,10 @@ VariableFrame.prototype.allNames = function (upTo, includeHidden) { function JSCompiler(aProcess) { this.process = aProcess; this.source = null; // a context - this.gensyms = null; // temp dictionary for parameter substitutions + this.gensyms = new Map(); // temp dictionary for parameter substitutions this.implicitParams = null; this.paramCount = null; + this.scriptVarCounter = null; } JSCompiler.prototype.toString = function () { @@ -7516,12 +7517,19 @@ JSCompiler.prototype.toString = function () { JSCompiler.prototype.compileFunction = function (aContext, implicitParamCount) { var block = aContext.expression, parameters = aContext.inputs, - parms = [], hasEmptySlots = false, - i; + plength = 0; this.source = aContext; - this.implicitParams = implicitParamCount || 1; + if (implicitParamCount == null || implicitParamCount === '') { + this.implicitParams = 1; + } else { + this.implicitParams = implicitParamCount >> 0; + if (this.implicitParams < 0) { + // use 1 if implicitParamCount doesn't make sense + this.implicitParams = 1; + } + } // scan for empty input slots hasEmptySlots = !isNil(detect( @@ -7530,7 +7538,7 @@ JSCompiler.prototype.compileFunction = function (aContext, implicitParamCount) { )); // translate formal parameters into gensyms - this.gensyms = new Map(); + this.gensyms.clear(); this.paramCount = 0; if (parameters.length) { // test for conflicts @@ -7543,33 +7551,34 @@ JSCompiler.prototype.compileFunction = function (aContext, implicitParamCount) { } // map explicit formal parameters parameters.forEach((pName, idx) => { - var pn = 'p' + idx; - parms.push(pn); - this.gensyms.set(pName, pn); + this.gensyms.set(pName, 'p[' + idx + ']'); }); + plength = parameters.length; } else if (hasEmptySlots) { - if (this.implicitParams > 1) { - for (i = 0; i < this.implicitParams; i += 1) { - parms.push('p' + i); - } - } else { - // allow for a single implicit formal parameter - parms = ['p0']; - } - } + plength = this.implicitParams; + } // compile using gensyms - if (block instanceof CommandBlockMorph) { - return Function.apply( - null, - parms.concat([this.compileSequence(block)]) - ); + this.scriptVarCounter = 0; + var code = 'proc=p.pop();\n'; + if (plength) { + // fill missing parameters with empty string + code += 'while(' + plength + '>p.length)p.push("");\n'; } - return Function.apply( - null, - parms.concat(['return ' + this.compileExpression(block)]) - ); + if (block instanceof CommandBlockMorph) { + code += this.compileSequence(block) + 'return ""'; + } else { + code += 'return ' + this.compileExpression(block); + } + block = 'var '; + this.gensyms.forEach(function (value) { + if (value.charAt(0) === 's') { + // declare script variable + block += value + '=0,'; + } + }); + return Function('...p', block + code); }; JSCompiler.prototype.compileExpression = function (block) { @@ -7604,21 +7613,49 @@ JSCompiler.prototype.compileExpression = function (block) { case 'evaluate': return 'invoke(' + this.compileInput(inputs[0]) + - ',' + + ', ' + this.compileInput(inputs[1]) + ')'; // special command forms - case 'doSetVar': // redirect var to process - return 'arguments[arguments.length - 1].setVarNamed(' + + case 'doDeclareVariables': + block = ''; + inputs[0].inputs().forEach(({children: {0: {blockSpec: name}}}) => { + if (gensym = this.gensyms.get(name)) { + // we already have that script variable, just set it to 0 + block += gensym + '='; + return; + } + var gensym = 's' + this.scriptVarCounter++; + block += gensym + '='; + this.gensyms.set(name, gensym); + }); + return block + '0'; + case 'reportGetVar': + return this.gensyms.get(block.blockSpec) || ('proc.getVarNamed("' + + this.escape(block.blockSpec) + + '")'); + case 'doSetVar': + if (inputs[0] instanceof ArgMorph && (target = this.gensyms.get(inputs[0].evaluate()))) { + // setting gensym (script or argument) variable + return target + ' = ' + this.compileInput(inputs[1]); + } + // redirect var to process + return 'proc.setVarNamed(' + this.compileInput(inputs[0]) + - ',' + + ', ' + this.compileInput(inputs[1]) + ')'; - case 'doChangeVar': // redirect var to process - return 'arguments[arguments.length - 1].incrementVarNamed(' + + case 'doChangeVar': + if (inputs[0] instanceof ArgMorph && (target = this.gensyms.get(inputs[0].evaluate()))) { + return '{const d=' + this.compileInput(inputs[1]) + + ',v=parseFloat(' + target + ');' + + target + '=isNaN(v)?d:v+parseFloat(d)}'; + } + // redirect var to process + return 'proc.incrementVarNamed(' + this.compileInput(inputs[0]) + - ',' + + ', ' + this.compileInput(inputs[1]) + ')'; case 'doReport': @@ -7637,7 +7674,9 @@ JSCompiler.prototype.compileExpression = function (block) { '} else {\n' + this.compileSequence(inputs[2].evaluate()) + '}'; - + case 'reportBoolean': + case 'reportNewList': + return this.compileInput(inputs[0]); default: target = this.process[selector] ? this.process : (this.source.receiver || this.process.receiver); @@ -7646,13 +7685,11 @@ JSCompiler.prototype.compileExpression = function (block) { if (isSnapObject(target)) { if (rcvr === 'SpriteMorph.prototype') { // fix for blocks like (x position) - rcvr = 'arguments[arguments.length - 1].blockReceiver()'; + rcvr = 'proc.blockReceiver()'; } - return rcvr + '.' + selector + '(' + args +')'; + return rcvr + '.' + selector + '(' + args + ')'; } else { - return 'arguments[arguments.length - 1].' + - selector + - '(' + args + ')'; + return 'proc.' + selector + '(' + args + ')'; } } }; @@ -7667,13 +7704,13 @@ JSCompiler.prototype.compileSequence = function (commandBlock) { JSCompiler.prototype.compileInfix = function (operator, inputs) { return '(' + this.compileInput(inputs[0]) + ' ' + operator + ' ' + - this.compileInput(inputs[1]) +')'; + this.compileInput(inputs[1]) + ')'; }; JSCompiler.prototype.compileInputs = function (array) { var args = ''; array.forEach(inp => { - if (args.length) { + if (args) { args += ', '; } args += this.compileInput(inp); @@ -7689,7 +7726,7 @@ JSCompiler.prototype.compileInput = function (inp) { if (this.implicitParams > 1) { if (this.paramCount < this.implicitParams) { this.paramCount += 1; - return 'p' + (this.paramCount - 1); + return 'p[' + (this.paramCount - 1) + ']'; } throw new Error( localize('expecting') + ' ' + this.implicitParams + ' ' @@ -7697,7 +7734,7 @@ JSCompiler.prototype.compileInput = function (inp) { + this.paramCount ); } - return 'p0'; + return 'p[0]'; } else if (inp instanceof MultiArgMorph) { return 'new List([' + this.compileInputs(inp.inputs()) + '])'; } else if (inp instanceof ArgLabelMorph) { @@ -7726,16 +7763,6 @@ JSCompiler.prototype.compileInput = function (inp) { ); } } else if (inp instanceof BlockMorph) { - if (inp.selector === 'reportGetVar') { - if (this.gensyms.has(inp.blockSpec)) { - // un-quoted gensym: - return this.gensyms.get(inp.blockSpec); - } - // redirect var query to process - return 'arguments[arguments.length - 1].getVarNamed("' + - this.escape(inp.blockSpec) + - '")'; - } return this.compileExpression(inp); } else { throw new Error(