@powerlang/egg-js
Version:
A Smalltalk VM that runs on top of JavaScript
798 lines (740 loc) • 20.8 kB
JavaScript
import SInlinerOperation from './SInlinerOperation.js';
import SLiteral from './SLiteral.js';
import SMessage from './SMessage.js';
import SOpAssign from './SOpAssign.js';
import SOpDispatchMessage from './SOpDispatchMessage.js';
import SOpDropToS from './SOpDropToS.js';
import SOpJump from './SOpJump.js';
import SOpJumpFalse from './SOpJumpFalse.js';
import SOpJumpTrue from './SOpJumpTrue.js';
import SOpLoadRfromFrame from './SOpLoadRfromFrame.js';
import SOpLoadRfromStack from './SOpLoadRfromStack.js';
import SOpLoadRwithNil from './SOpLoadRwithNil.js';
import SOpLoadRwithSelf from './SOpLoadRwithSelf.js';
import SOpNonLocalReturn from './SOpNonLocalReturn.js';
import SOpPopR from './SOpPopR.js';
import SOpPrimitive from './SOpPrimitive.js';
import SOpPushR from './SOpPushR.js';
import SOpReturn from './SOpReturn.js';
import SOpStoreRintoFrame from './SOpStoreRintoFrame.js';
let SExpressionLinearizer = class {
constructor() {
this._operations = nil;
this._primitives = nil;
this._inBlock = nil;
this._runtime = nil;
this._greaterThan = nil;
this._one = nil;
this._plus = nil;
this._stackTop = nil;
this._dropsArguments = nil;
this._not = nil;
this._equalsEquals = nil;
this._ifTrue = nil;
this._ifFalse = nil;
this._ifTrueIfFalse = nil;
this._ifFalseIfTrue = nil;
this._ifNil = nil;
this._ifNotNil = nil;
this._ifNilIfNotNil = nil;
this._ifNotNilIfNil = nil;
this._whileTrue = nil;
this._whileFalse = nil;
this._whileTrue_ = nil;
this._whileFalse_ = nil;
this._toDo = nil;
this._toByDo = nil;
this._repeat = nil;
this._timesRepeat = nil;
this._andNot = nil;
this._orNot = nil;
}
assign_(aCollection) {
let op;
op = SOpAssign.new().assignees_(aCollection);
this._operations.add_(op);
return this;
}
branchIf_(aBoolean) {
let _class, op;
if (aBoolean)
{
_class = SOpJumpTrue
} else {
_class = SOpJumpFalse
}
;
op = _class.new();
this._operations.add_(op);
return op;
}
branchTargetOf_(branch) {
branch.target_(this.currentPC());
return this;
}
currentPC() {
return this._operations.size();
}
dispatch_(message) {
let op, count;
op = SOpDispatchMessage.new().message_(message);
this._operations.add_(op);
if (this._dropsArguments)
{
return this
}
;
count = message.arguments().size();
if (count._greaterThan(0))
{
count = (count+1)
}
;
this._stackTop = (this._stackTop-count);
return this;
}
dropCascadeMessageArgs_(argsize) {
if (argsize._equal(0).orNot_(() => {
return this._dropsArguments
}))
{
return this
}
;
this.dropToS_(argsize);
return this;
}
dropMessageArgs_(argsize) {
if (argsize._equal(0).orNot_(() => {
return this._dropsArguments
}))
{
return this
}
;
this.dropToS_((argsize+1));
return this;
}
dropToS() {
return this.dropToS_(1);
}
dropToS_(anInteger) {
let op;
op = SOpDropToS.new().count_(anInteger);
this._operations.add_(op);
this._stackTop = (this._stackTop-anInteger);
return this;
}
dropsArguments() {
this._dropsArguments = true;
return this;
}
initialize() {
this._dropsArguments = false;
return this;
}
inline_binaryWhile_(anSMessage, aBoolean) {
let start, end;
start = this.currentPC();
this.visitStatements_(anSMessage.receiver().statements());
end = this.branchIf_(aBoolean.not());
_cascade(this, (_recv) => {
_recv.visitStatements_(anSMessage.arguments().first().statements());
_recv.jumpTo_(start);
return _recv.branchTargetOf_(end);});
return this;
}
inline_if_(anSMessage, aBoolean) {
let branch, end;
anSMessage.receiver().acceptVisitor_(this);
branch = this.branchIf_(aBoolean.not());
end = _cascade(this, (_recv) => {
_recv.visitStatements_(anSMessage.arguments().first().statements());
return _recv.jump();});
_cascade(this, (_recv) => {
_recv.branchTargetOf_(branch);
_recv.loadRwithNil();
return _recv.branchTargetOf_(end);});
return this;
}
inline_ifNil_(anSMessage, aBoolean) {
let nilObj, message, branch, end, arg, index;
anSMessage.receiver().acceptVisitor_(this);
arg = anSMessage.arguments().first();
if (arg.isBlock().and_(() => {
return arg.inlinedArgs().size()._equal(1)
}))
{
index = arg.inlinedArgs().first();
this.storeRintoFrameAt_(index)
}
;
this.pushR();
nilObj = SLiteral.new().value_(this._runtime.nil());
message = _cascade(SMessage.new(), (_recv) => {
_recv.receiver_(SOpLoadRfromStack.new().index_(0));
_recv.arguments_([nilObj]);
return _recv.selector_(this._equalsEquals);});
this.visitMessage_(message);
branch = this.branchIf_(aBoolean.not());
if (arg.isBlock())
{
this.visitStatements_(arg.statements())
} else {
arg.acceptVisitor_(this)
}
;
end = _cascade(this, (_recv) => {
_recv.dropToS();
return _recv.jump();});
_cascade(this, (_recv) => {
_recv.branchTargetOf_(branch);
_recv.popR();
return _recv.branchTargetOf_(end);});
return this;
}
inline_ifNilIfNotNil_(anSMessage, aBoolean) {
let _arguments, nilObj, message, branch, end, arg, index;
anSMessage.receiver().acceptVisitor_(this);
_arguments = anSMessage.arguments();
if (aBoolean)
{
arg = _arguments.second()
} else {
arg = _arguments.first()
}
;
if (arg.isBlock().and_(() => {
return arg.inlinedArgs().size()._equal(1)
}))
{
index = arg.inlinedArgs().first();
this.storeRintoFrameAt_(index)
}
;
this.pushR();
nilObj = SLiteral.new().value_(this._runtime.nil());
message = _cascade(SMessage.new(), (_recv) => {
_recv.receiver_(SOpLoadRfromStack.new().index_(0));
_recv.arguments_([nilObj]);
return _recv.selector_(this._equalsEquals);});
this.visitMessage_(message);
branch = this.branchIf_(aBoolean.not());
end = _cascade(this, (_recv) => {
_recv.visitStatements_(_arguments.first().statements());
return _recv.jump();});
_cascade(this, (_recv) => {
_recv.branchTargetOf_(branch);
_recv.visitStatements_(_arguments.second().statements());
_recv.branchTargetOf_(end);
return _recv.dropToS();});
return this;
}
inline_ifTrueIfFalse_(anSMessage, aBoolean) {
let branch, end;
anSMessage.receiver().acceptVisitor_(this);
branch = this.branchIf_(aBoolean.not());
end = _cascade(this, (_recv) => {
_recv.visitStatements_(anSMessage.arguments().first().statements());
return _recv.jump();});
_cascade(this, (_recv) => {
_recv.branchTargetOf_(branch);
_recv.visitStatements_(anSMessage.arguments().second().statements());
return _recv.branchTargetOf_(end);});
return this;
}
inline_unitaryWhile_(anSMessage, aBoolean) {
let start, branch;
start = this.currentPC();
this.visitStatements_(anSMessage.receiver().statements());
branch = this.branchIf_(aBoolean);
branch.target_(start);
return this;
}
inlineAnd_(anSMessage) {
let branches;
anSMessage.receiver().acceptVisitor_(this);
branches = Array.new();
anSMessage.arguments().do_((block) => {
branches.add_(this.branchIf_(false));
return this.visitStatements_(block.statements())
});
branches.do_((branch) => {
return this.branchTargetOf_(branch)
});
return this;
}
inlineAndNot_(anSMessage) {
let branch, message;
anSMessage.receiver().acceptVisitor_(this);
branch = this.branchIf_(false);
message = _cascade(SMessage.new(), (_recv) => {
_recv.receiver_(SInlinerOperation.new());
_recv.selector_(this._not);
return _recv.arguments_([]);});
_cascade(this, (_recv) => {
_recv.visitStatements_(anSMessage.arguments().first().statements());
_recv.dispatch_(message);
return _recv.branchTargetOf_(branch);});
return this;
}
inlineOr_(anSMessage) {
let branches;
anSMessage.receiver().acceptVisitor_(this);
branches = Array.new();
anSMessage.arguments().do_((block) => {
branches.add_(this.branchIf_(true));
return this.visitStatements_(block.statements())
});
branches.do_((branch) => {
return this.branchTargetOf_(branch)
});
return this;
}
inlineOrNot_(anSMessage) {
let branch, message;
anSMessage.receiver().acceptVisitor_(this);
branch = this.branchIf_(true);
message = _cascade(SMessage.new(), (_recv) => {
_recv.receiver_(SInlinerOperation.new());
_recv.selector_(this._not);
return _recv.arguments_([]);});
_cascade(this, (_recv) => {
_recv.visitStatements_(anSMessage.arguments().first().statements());
_recv.dispatch_(message);
return _recv.branchTargetOf_(branch);});
return this;
}
inlineRepeat_(anSMessage) {
let start;
start = this.currentPC();
_cascade(this, (_recv) => {
_recv.visitStatements_(anSMessage.receiver().statements());
return _recv.jumpTo_(start);});
return this;
}
inlineTimesRepeat_(anSMessage) {
let current, limit, start, end, compare, increment;
this._operations.add_(this._one);
this.pushR();
current = SOpLoadRfromFrame.new().index_(this._stackTop);
anSMessage.receiver().acceptVisitor_(this);
this.pushR();
limit = SOpLoadRfromFrame.new().index_(this._stackTop);
start = this.currentPC();
compare = _cascade(SMessage.new(), (_recv) => {
_recv.receiver_(current);
_recv.selector_(this._greaterThan);
return _recv.arguments_([limit]);});
this.visitMessage_(compare);
end = this.branchIf_(true);
this.visitStatements_(anSMessage.arguments().first().statements());
increment = _cascade(SMessage.new(), (_recv) => {
_recv.receiver_(current);
_recv.selector_(this._plus);
return _recv.arguments_([this._one]);});
_cascade(this, (_recv) => {
_recv.visitMessage_(increment);
_recv.storeRintoFrameAt_(current.index());
_recv.jumpTo_(start);
_recv.branchTargetOf_(end);
return _recv.dropToS_(2);});
return this;
}
inlineToDo_(anSMessage) {
let index, current, limit, start, end, compare, increment;
anSMessage.receiver().acceptVisitor_(this);
index = anSMessage.arguments().second().inlinedArgs().first();
current = SOpLoadRfromFrame.new().index_(index);
this.storeRintoFrameAt_(index);
anSMessage.arguments().first().acceptVisitor_(this);
this.pushR();
limit = SOpLoadRfromFrame.new().index_(this._stackTop);
start = this.currentPC();
compare = _cascade(SMessage.new(), (_recv) => {
_recv.receiver_(current);
_recv.selector_(this._greaterThan);
return _recv.arguments_([limit]);});
this.visitMessage_(compare);
end = this.branchIf_(true);
this.visitStatements_(anSMessage.arguments().second().statements());
increment = _cascade(SMessage.new(), (_recv) => {
_recv.receiver_(current);
_recv.selector_(this._plus);
return _recv.arguments_([this._one]);});
_cascade(this, (_recv) => {
_recv.visitMessage_(increment);
_recv.storeRintoFrameAt_(index);
_recv.jumpTo_(start);
_recv.branchTargetOf_(end);
return _recv.dropToS_(1);});
return this;
}
jump() {
let op;
op = SOpJump.new();
this._operations.add_(op);
return op;
}
jumpTo_(anInteger) {
let op;
op = SOpJump.new().target_(anInteger);
this._operations.add_(op);
return this;
}
loadRfromStack_(anInteger) {
let op;
op = SOpLoadRfromStack.new().index_(anInteger);
this._operations.add_(op);
return this;
}
loadRwithNil() {
this._operations.add_(SOpLoadRwithNil.new());
return this;
}
loadRwithSelf() {
this._operations.add_(SOpLoadRwithSelf.new());
return this;
}
operations() {
return this._operations;
}
popR() {
this._operations.add_(SOpPopR.new());
this._stackTop = (this._stackTop-1);
return this;
}
primitive_(aClosure) {
this._operations.add_(SOpPrimitive.new().block_(aClosure));
return this;
}
primitives_(aCollection) {
this._primitives = aCollection;
return this;
}
pushR() {
this._operations.add_(SOpPushR.new());
this._stackTop = (this._stackTop+1);
return this;
}
reset() {
this._operations = Array.new();
this._inBlock = false;
return this;
}
return() {
this._operations.add_(SOpReturn.new());
return this;
}
return_(isLocal) {
let _class;
if (isLocal.andNot_(() => {
return this._inBlock
}))
{
_class = SOpReturn
} else {
_class = SOpNonLocalReturn
}
;
this._operations.add_(_class.new());
return this;
}
runtime_(anEggRuntime) {
let literal;
this._runtime = anEggRuntime;
literal = this._runtime.newInteger_(1);
this._one = SLiteral.new().value_(literal);
this._plus = this._runtime.symbolFromLocal_("+");
this._greaterThan = this._runtime.symbolFromLocal_(">");
this._equalsEquals = this._runtime.symbolFromLocal_("==");
this._not = this._runtime.symbolFromLocal_("not");
this._ifTrue = this._runtime.symbolFromLocal_("ifTrue:");
this._ifFalse = this._runtime.symbolFromLocal_("ifFalse:");
this._ifTrueIfFalse = this._runtime.symbolFromLocal_("ifTrue:ifFalse:");
this._ifFalseIfTrue = this._runtime.symbolFromLocal_("ifFalse:ifTrue:");
this._ifNil = this._runtime.symbolFromLocal_("ifNil:");
this._ifNotNil = this._runtime.symbolFromLocal_("ifNotNil:");
this._ifNilIfNotNil = this._runtime.symbolFromLocal_("ifNil:ifNotNil:");
this._ifNotNilIfNil = this._runtime.symbolFromLocal_("ifNotNil:ifNil:");
this._repeat = this._runtime.symbolFromLocal_("repeat");
this._whileTrue = this._runtime.symbolFromLocal_("whileTrue");
this._whileFalse = this._runtime.symbolFromLocal_("whileFalse");
this._whileTrue_ = this._runtime.symbolFromLocal_("whileTrue:");
this._whileFalse_ = this._runtime.symbolFromLocal_("whileFalse:");
this._timesRepeat = this._runtime.symbolFromLocal_("timesRepeat:");
this._toDo = this._runtime.symbolFromLocal_("to:do:");
this._toByDo = this._runtime.symbolFromLocal_("to:by:do:");
this._andNot = this._runtime.symbolFromLocal_("andNot:");
this._orNot = this._runtime.symbolFromLocal_("orNot:");
return this;
}
storeRintoFrameAt_(anInteger) {
let op;
op = SOpStoreRintoFrame.new().index_(anInteger);
this._operations.add_(op);
return this;
}
visitAssignment_(anSAssignment) {
anSAssignment.expression().acceptVisitor_(this);
this.assign_(anSAssignment.assignees());
return this;
}
visitBlock_(anSBlock) {
let prevInBlock, prevOperations, prevStackTop, statements, code, _retval;
this._operations.add_(anSBlock);
prevInBlock = this._inBlock;
prevOperations = this._operations;
prevStackTop = this._stackTop;
this._stackTop = this._runtime.blockTempCount_(anSBlock.compiledCode());
this._inBlock = true;
this._operations = Array.new();
statements = anSBlock.statements();
statements.do_((node) => {
return node.acceptVisitor_(this)
});
if (statements.isEmpty())
{
this.loadRwithNil()
} else {
_retval = nil;
if (!statements.last().isReturn())
{
_retval = this.return()
}
;
_retval
}
;
if (!anSBlock.isInlined())
{
code = this._runtime.newExecutableCodeFor_(this._operations.asArray());
this._runtime.blockExecutableCode_put_(anSBlock.compiledCode(), code)
}
;
this._stackTop = prevStackTop;
this._operations = prevOperations;
this._inBlock = prevInBlock;
return this;
}
visitCascade_(anSCascade) {
let args, argsize;
anSCascade.receiver().acceptVisitor_(this);
this.pushR();
anSCascade.messages().do_((msg) => {
args = msg.arguments();
argsize = args.size();
args.do_((arg) => {
arg.acceptVisitor_(this);
return this.pushR()
});
return _cascade(this, (_recv) => {
_recv.loadRfromStack_(argsize);
_recv.dispatch_(msg);
return _recv.dropCascadeMessageArgs_(argsize);})
});
this.dropToS();
return this;
}
visitFFIMethod_(anSMethod) {
let handler;
this.reset();
handler = this._primitives.at_("FFICall");
return _cascade(this, (_recv) => {
_recv.primitive_(handler);
return _recv.return();});
}
visitIdentifier_(anSIdentifier) {
this._operations.add_(anSIdentifier);
return this;
}
visitInlinedMessage_(anSMessage) {
let selector;
selector = anSMessage.selector();
if (selector._equalEqual(this._ifTrue))
{
return this.inline_if_(anSMessage, true)
}
;
if (selector._equalEqual(this._ifFalse))
{
return this.inline_if_(anSMessage, false)
}
;
if (selector._equalEqual(this._ifNil))
{
return this.inline_ifNil_(anSMessage, true)
}
;
if (selector._equalEqual(this._ifNotNil))
{
return this.inline_ifNil_(anSMessage, false)
}
;
if (selector._equalEqual(this._ifNilIfNotNil))
{
return this.inline_ifNilIfNotNil_(anSMessage, true)
}
;
if (selector._equalEqual(this._ifNotNilIfNil))
{
return this.inline_ifNilIfNotNil_(anSMessage, false)
}
;
if (selector._equalEqual(this._ifTrueIfFalse))
{
return this.inline_ifTrueIfFalse_(anSMessage, true)
}
;
if (selector._equalEqual(this._ifFalseIfTrue))
{
return this.inline_ifTrueIfFalse_(anSMessage, false)
}
;
if (selector._equalEqual(this._whileTrue))
{
return this.inline_unitaryWhile_(anSMessage, true)
}
;
if (selector._equalEqual(this._whileFalse))
{
return this.inline_unitaryWhile_(anSMessage, false)
}
;
if (selector._equalEqual(this._whileTrue_))
{
return this.inline_binaryWhile_(anSMessage, true)
}
;
if (selector._equalEqual(this._whileFalse_))
{
return this.inline_binaryWhile_(anSMessage, false)
}
;
if (selector._equalEqual(this._repeat))
{
return this.inlineRepeat_(anSMessage)
}
;
if (selector._equalEqual(this._toDo))
{
return this.inlineToDo_(anSMessage)
}
;
if (selector._equalEqual(this._toByDo))
{
return this.inlineToByDo_(anSMessage)
}
;
if (selector._equalEqual(this._timesRepeat))
{
return this.inlineTimesRepeat_(anSMessage)
}
;
if (selector._equalEqual(this._andNot))
{
return this.inlineAndNot_(anSMessage)
}
;
if (selector._equalEqual(this._orNot))
{
return this.inlineOrNot_(anSMessage)
}
;
selector = this._runtime.localSymbolFrom_(selector);
if (selector.beginsWith_("or:"))
{
return this.inlineOr_(anSMessage)
}
;
if (selector.beginsWith_("and:"))
{
return this.inlineAnd_(anSMessage)
}
;
this.ASSERT_(false);
return this;
}
visitLiteral_(anSLiteral) {
this._operations.add_(anSLiteral);
return this;
}
visitMessage_(anSMessage) {
let args, argsize;
if (anSMessage.isInlined())
{
return this.visitInlinedMessage_(anSMessage)
}
;
anSMessage.receiver().acceptVisitor_(this);
args = anSMessage.arguments();
argsize = args.size();
if (argsize._greaterThan(0))
{
this.pushR()
}
;
args.do_((arg) => {
arg.acceptVisitor_(this);
return this.pushR()
});
if (argsize._greaterThan(0))
{
this.loadRfromStack_(argsize)
}
;
_cascade(this, (_recv) => {
_recv.dispatch_(anSMessage);
return _recv.dropMessageArgs_(argsize);});
return this;
}
visitMethod_(anSMethod) {
let primitive, statements, symbol, descriptor, handler;
this.reset();
primitive = anSMethod.pragma();
if (nil !== primitive)
{
symbol = this._runtime.localSymbolFrom_(anSMethod.primitive());
handler = this._primitives.at_(symbol);
return _cascade(this, (_recv) => {
_recv.primitive_(handler);
return _recv.return();})
}
;
this._stackTop = this._runtime.methodTempCount_(anSMethod.compiledCode());
statements = anSMethod.statements();
statements.do_((node) => {
return node.acceptVisitor_(this)
});
if (statements.isEmpty().orNot_(() => {
return statements.last().isReturn()
}))
{
_cascade(this, (_recv) => {
_recv.loadRwithSelf();
return _recv.return();})
}
;
return this;
}
visitOpLoadRfromFrame_(anSOpLoadRfromFrame) {
this._operations.add_(anSOpLoadRfromFrame);
return this;
}
visitOpLoadRfromStack_(anSOpLoadRfromStack) {
this.loadRfromStack_(anSOpLoadRfromStack.index());
return this;
}
visitReturn_(anSReturn) {
anSReturn.expression().acceptVisitor_(this);
this.return_(anSReturn.local());
return this;
}
visitStatements_(aCollection) {
aCollection.do_((sexpression) => {
return sexpression.acceptVisitor_(this)
});
return this;
}
}
export default SExpressionLinearizer