JSCompiler stuff #2

snap8
xBZZZZ 2022-03-15 16:01:04 +00:00 zatwierdzone przez GitHub
rodzic c1281384d8
commit c407fc805e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
1 zmienionych plików z 80 dodań i 53 usunięć

Wyświetl plik

@ -7504,9 +7504,10 @@ VariableFrame.prototype.allNames = function (upTo, includeHidden) {
function JSCompiler(aProcess) { function JSCompiler(aProcess) {
this.process = aProcess; this.process = aProcess;
this.source = null; // a context 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.implicitParams = null;
this.paramCount = null; this.paramCount = null;
this.scriptVarCounter = null;
} }
JSCompiler.prototype.toString = function () { JSCompiler.prototype.toString = function () {
@ -7516,12 +7517,19 @@ JSCompiler.prototype.toString = function () {
JSCompiler.prototype.compileFunction = function (aContext, implicitParamCount) { JSCompiler.prototype.compileFunction = function (aContext, implicitParamCount) {
var block = aContext.expression, var block = aContext.expression,
parameters = aContext.inputs, parameters = aContext.inputs,
parms = [],
hasEmptySlots = false, hasEmptySlots = false,
i; plength = 0;
this.source = aContext; 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 // scan for empty input slots
hasEmptySlots = !isNil(detect( hasEmptySlots = !isNil(detect(
@ -7530,7 +7538,7 @@ JSCompiler.prototype.compileFunction = function (aContext, implicitParamCount) {
)); ));
// translate formal parameters into gensyms // translate formal parameters into gensyms
this.gensyms = new Map(); this.gensyms.clear();
this.paramCount = 0; this.paramCount = 0;
if (parameters.length) { if (parameters.length) {
// test for conflicts // test for conflicts
@ -7543,33 +7551,34 @@ JSCompiler.prototype.compileFunction = function (aContext, implicitParamCount) {
} }
// map explicit formal parameters // map explicit formal parameters
parameters.forEach((pName, idx) => { parameters.forEach((pName, idx) => {
var pn = 'p' + idx; this.gensyms.set(pName, 'p[' + idx + ']');
parms.push(pn);
this.gensyms.set(pName, pn);
}); });
plength = parameters.length;
} else if (hasEmptySlots) { } else if (hasEmptySlots) {
if (this.implicitParams > 1) { plength = this.implicitParams;
for (i = 0; i < this.implicitParams; i += 1) { }
parms.push('p' + i);
}
} else {
// allow for a single implicit formal parameter
parms = ['p0'];
}
}
// compile using gensyms // compile using gensyms
if (block instanceof CommandBlockMorph) { this.scriptVarCounter = 0;
return Function.apply( var code = 'proc=p.pop();\n';
null, if (plength) {
parms.concat([this.compileSequence(block)]) // fill missing parameters with empty string
); code += 'while(' + plength + '>p.length)p.push("");\n';
} }
return Function.apply( if (block instanceof CommandBlockMorph) {
null, code += this.compileSequence(block) + 'return ""';
parms.concat(['return ' + this.compileExpression(block)]) } 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) { JSCompiler.prototype.compileExpression = function (block) {
@ -7604,21 +7613,49 @@ JSCompiler.prototype.compileExpression = function (block) {
case 'evaluate': case 'evaluate':
return 'invoke(' + return 'invoke(' +
this.compileInput(inputs[0]) + this.compileInput(inputs[0]) +
',' + ', ' +
this.compileInput(inputs[1]) + this.compileInput(inputs[1]) +
')'; ')';
// special command forms // special command forms
case 'doSetVar': // redirect var to process case 'doDeclareVariables':
return 'arguments[arguments.length - 1].setVarNamed(' + 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[0]) +
',' + ', ' +
this.compileInput(inputs[1]) + this.compileInput(inputs[1]) +
')'; ')';
case 'doChangeVar': // redirect var to process case 'doChangeVar':
return 'arguments[arguments.length - 1].incrementVarNamed(' + 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[0]) +
',' + ', ' +
this.compileInput(inputs[1]) + this.compileInput(inputs[1]) +
')'; ')';
case 'doReport': case 'doReport':
@ -7637,7 +7674,9 @@ JSCompiler.prototype.compileExpression = function (block) {
'} else {\n' + '} else {\n' +
this.compileSequence(inputs[2].evaluate()) + this.compileSequence(inputs[2].evaluate()) +
'}'; '}';
case 'reportBoolean':
case 'reportNewList':
return this.compileInput(inputs[0]);
default: default:
target = this.process[selector] ? this.process target = this.process[selector] ? this.process
: (this.source.receiver || this.process.receiver); : (this.source.receiver || this.process.receiver);
@ -7646,13 +7685,11 @@ JSCompiler.prototype.compileExpression = function (block) {
if (isSnapObject(target)) { if (isSnapObject(target)) {
if (rcvr === 'SpriteMorph.prototype') { if (rcvr === 'SpriteMorph.prototype') {
// fix for blocks like (x position) // fix for blocks like (x position)
rcvr = 'arguments[arguments.length - 1].blockReceiver()'; rcvr = 'proc.blockReceiver()';
} }
return rcvr + '.' + selector + '(' + args +')'; return rcvr + '.' + selector + '(' + args + ')';
} else { } else {
return 'arguments[arguments.length - 1].' + return 'proc.' + selector + '(' + args + ')';
selector +
'(' + args + ')';
} }
} }
}; };
@ -7667,13 +7704,13 @@ JSCompiler.prototype.compileSequence = function (commandBlock) {
JSCompiler.prototype.compileInfix = function (operator, inputs) { JSCompiler.prototype.compileInfix = function (operator, inputs) {
return '(' + this.compileInput(inputs[0]) + ' ' + operator + ' ' + return '(' + this.compileInput(inputs[0]) + ' ' + operator + ' ' +
this.compileInput(inputs[1]) +')'; this.compileInput(inputs[1]) + ')';
}; };
JSCompiler.prototype.compileInputs = function (array) { JSCompiler.prototype.compileInputs = function (array) {
var args = ''; var args = '';
array.forEach(inp => { array.forEach(inp => {
if (args.length) { if (args) {
args += ', '; args += ', ';
} }
args += this.compileInput(inp); args += this.compileInput(inp);
@ -7689,7 +7726,7 @@ JSCompiler.prototype.compileInput = function (inp) {
if (this.implicitParams > 1) { if (this.implicitParams > 1) {
if (this.paramCount < this.implicitParams) { if (this.paramCount < this.implicitParams) {
this.paramCount += 1; this.paramCount += 1;
return 'p' + (this.paramCount - 1); return 'p[' + (this.paramCount - 1) + ']';
} }
throw new Error( throw new Error(
localize('expecting') + ' ' + this.implicitParams + ' ' localize('expecting') + ' ' + this.implicitParams + ' '
@ -7697,7 +7734,7 @@ JSCompiler.prototype.compileInput = function (inp) {
+ this.paramCount + this.paramCount
); );
} }
return 'p0'; return 'p[0]';
} else if (inp instanceof MultiArgMorph) { } else if (inp instanceof MultiArgMorph) {
return 'new List([' + this.compileInputs(inp.inputs()) + '])'; return 'new List([' + this.compileInputs(inp.inputs()) + '])';
} else if (inp instanceof ArgLabelMorph) { } else if (inp instanceof ArgLabelMorph) {
@ -7726,16 +7763,6 @@ JSCompiler.prototype.compileInput = function (inp) {
); );
} }
} else if (inp instanceof BlockMorph) { } 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); return this.compileExpression(inp);
} else { } else {
throw new Error( throw new Error(