class CodeGenerator { private var code:Array; private var stackList:Array; private var localVariableList:Array; public function CodeGenerator () { initialize(); } public function initialize () : Void { code = new Array(); stackList = new Array(); localVariableList = new Array(); // reserved put(null); beginNewScope(); } private function error (message:String) : Void { throw new Error('CodeGenerator [error] '+message); } public function getCode () : Array { return code; } public function put (element:Object) : Number { return code.push(element)-1; } public function putStoreStack () : Void { stackList.push(put(null)); } public function putLoadStack () : Void { var _code:Array = code; var current:Number = Number(stackList.pop()); var next:Number; var address:Number = put(null); for (;;) { next = _code[current]; _code[current] = address; if (next == null) break; current = next; } } public function popStack () : Number { return Number(stackList.pop()); } public function pushStack (address:Number) : Void { stackList.push(address); } public function setStackPatch (address:Number) : Void { code[stackList[stackList.length-1]] = address; } public function putCrossLoadStack () : Void { swapStack(); putLoadStack(); putLoadStack(); } public function swapStack (a:Number, b:Number) : Void { a = stackList.length - (a !== undefined ? a : 0) - 1; b = stackList.length - (b !== undefined ? b : 1) - 1; var n:Number = stackList[a]; stackList[a] = stackList[b]; stackList[b] = n; } public function getStackLength () : Number { return stackList.length; } public function cleanUpStack (begin:Number) : Void { if (begin === undefined ) begin = 0; var _stackList:Array = stackList; for (; _stackList.length > begin; ) { popAndDestroyStack(); } } public function popAndDestroyStack () : Void { var _code:Array = code; var current:Number = Number(stackList.pop()); var next:Number; for (;;) { next = _code[current]; _code[current] = 0; if (next == null) break; current = next; } } public function putLabel (label:Label) : Void { if (label.isExists) { put(label.address); } else { label.address = put(label.address); } } public function setLabel (label:Label) : Void { var address:Number = code.length; setLabelAddress(label, address); label.commitAddress(address); } public function setLabelAddress (label:Label, address:Number) : Void { var _code:Array = code; var current:Number = label.address; var next:Number; for (;;) { if (current == null) break; next = _code[current]; _code[current] = address; current = next; } } public function beginNewScope () : Void { localVariableList.unshift(new Object()); } public function closeScope () : Void { localVariableList.shift(); } public function isLocalVariable (identifier:Object) : Boolean { return localVariableList[0].hasOwnProperty(String(identifier)); } public function addLocalVariable (identifier:Object) : Void { localVariableList[0][identifier] = true; } public function putExpressionResult (expressionResult:ExpressionResult) : Void { switch (expressionResult.type) { case 'variable': { putGetVariable(String(expressionResult.value)); expressionResult.setType('stack'); } break; case 'member': { putGetMember(expressionResult.getObjectExpression(), expressionResult.getMemberExpression()); expressionResult.setType('stack'); } break; } } private function putValue (expressionResult:ExpressionResult) : Void { switch (expressionResult.type) { case 'literal': { put(expressionResult.value); } break; case 'stack': { putLoadStack(); } break; default : { error('putValueError'); } } } private function putBinaryValue (left:ExpressionResult, right:ExpressionResult) : Void { if (left.isType('literal') && right.isType('literal')) { put(left.value); put(right.value); } else if (left.isType('stack') && right.isType('stack')) { putCrossLoadStack(); } else if (left.isType('stack')) { putLoadStack(); put(right.value); } else if (right.isType('stack')) { put(left.value); putLoadStack(); } else { error('putBinaryValueError'); } } public function putSuspend () : Void { put('SPD'); } public function putLiteral (expressionResult:ExpressionResult) : Void { put('LIT'); put(expressionResult.value); putStoreStack(); } public function putCall (identifier:ExpressionResult, numOfArguments:Number) : Void { if (isLocalVariable(identifier.value)) { put('CALLL'); } else { put('CALL'); } put(identifier.value); put(numOfArguments); putStoreStack(); } public function putCallMember (objectExpression:ExpressionResult, memberExpression:ExpressionResult, numOfArguments:Number) : Void { put('CALLM'); putBinaryValue(objectExpression, memberExpression); /* if (ExpressionResult(memberExpression.value).isType('literal')) { putLoadStack(); put(ExpressionResult(memberExpression.value).value); } else { putCrossLoadStack(); } */ put(numOfArguments); putStoreStack(); } public function putCallFunctor (numOfArguments:Number) : Void { put('CALLF'); putLoadStack(); put(numOfArguments); putStoreStack(); } public function putReturnFunction (returnValue:ExpressionResult) : Void { put('RET'); putValue(returnValue); } public function putReturnCoroutine (returnValue:ExpressionResult) : Void { put('CRET'); putValue(returnValue); } public function putFunction () : Label { var label:Label = new Label(); put('FUNC'); putLabel(label); putStoreStack(); return label; } public function putCoroutine () : Label { var label:Label = new Label(); put('COR'); putLabel(label); putStoreStack(); return label; } public function putArgument (argumentIndex:Number, identifier:String) : Void { put('ARG'); put(argumentIndex); put(identifier); addLocalVariable(identifier); } public function putJump (label:Label) : Void { put('JMP'); putLabel(label); } public function putIf (expressionResult:ExpressionResult, label:Label) : Void { put('IF'); putValue(expressionResult); putLabel(label); } public function putNif (expressionResult:ExpressionResult, label:Label) : Void { put('NIF'); putValue(expressionResult); putLabel(label); } // 񍀉Z public function putBinaryOperation (operation:String, left:ExpressionResult, right:ExpressionResult) : Void { put(operation); putBinaryValue(left, right); putStoreStack(); } // PZ public function putUnaryOperation (operation:String, expression:ExpressionResult) : Void { put(operation); putValue(expression); putStoreStack(); } public function putIncrement (expressionResult:ExpressionResult) : Void { putIncDec('INC', false, expressionResult); } public function putDecrement (expressionResult:ExpressionResult) : Void { putIncDec('DEC', false, expressionResult); } public function putPostfixIncrement (expressionResult:ExpressionResult) : Void { putIncDec('INC', true, expressionResult); } public function putPostfixDecrement (expressionResult:ExpressionResult) : Void { putIncDec('DEC', true, expressionResult); } private function putIncDec (operation:String, isPostfix:Boolean, expressionResult:ExpressionResult) : Void { switch (expressionResult.type) { case 'member': { var objectExpression:ExpressionResult = expressionResult.getObjectExpression(); var memberExpression:ExpressionResult = expressionResult.getMemberExpression(); if (!memberExpression.isLiteral()) { putExpressionResult(memberExpression); var s:Number = popStack(); putDuplicate(memberExpression); pushStack(s); putDuplicate(objectExpression); swapStack(1, 2); } else { putDuplicate(objectExpression); } putGetMember(objectExpression, memberExpression); expressionResult.setTypeStack(); if (isPostfix) { putDuplicate(expressionResult); var s:Number = popStack(); putUnaryOperation(operation, expressionResult); putSetMember(objectExpression, memberExpression, expressionResult); popAndDestroyStack(); pushStack(s); } else { putUnaryOperation(operation, expressionResult); putSetMember(objectExpression, memberExpression, expressionResult); } } break; case 'variable': { var identifier:String = String(expressionResult.value); putGetVariable(identifier); expressionResult.setTypeStack(); if (isPostfix) { putDuplicate(expressionResult); } putUnaryOperation(operation, expressionResult); putSetVariable(identifier, expressionResult); if (isPostfix) { popAndDestroyStack(); } } break; default: { error('putIncDecError'); } } } public function putWith (objectExpression:ExpressionResult) : Void { put('WITH'); putValue(objectExpression); } public function putEndWith () : Void { put('EWITH'); } public function putPush (expressionResult:ExpressionResult) : Void { put('PUSH'); putValue(expressionResult); } public function putPop () : Void { put('POP'); putStoreStack(); } public function putDuplicate (expressionResult:ExpressionResult) : Void { put('DUP'); putValue(expressionResult); putStoreStack(); putStoreStack(); } public function putThis () : Void { put('THIS'); putStoreStack(); } public function putArrayLiteral (numOfElements:Number) : Void { put('ARRAY'); put(numOfElements); putStoreStack(); } public function putObjectLiteral (numOfProperties:Number) : Void { put('OBJ'); put(numOfProperties); putStoreStack(); } public function putGetVariable (identifier:String) : Void { if (isLocalVariable(identifier)) { put('GETL'); } else { put('GET'); } put(identifier); putStoreStack(); } public function putSetVariable (identifier:String, right:ExpressionResult) : Void { if (isLocalVariable(identifier)) { put('SETL'); } else { put('SET'); } put(identifier); putValue(right); putStoreStack(); } public function putSetLocalVariable (identifier:String, right:ExpressionResult) : Void { put('SETL'); put(identifier); putValue(right); putStoreStack(); addLocalVariable(identifier); } public function putGetMember (objectExpression:ExpressionResult, memberExpression:ExpressionResult) : Void { put('GETM'); putBinaryValue(objectExpression, memberExpression); putStoreStack(); } public function putSetMember (objectExpression:ExpressionResult, memberExpression:ExpressionResult, right:ExpressionResult) : Void { put('SETM'); if (objectExpression.isLiteral()) { if (memberExpression.isLiteral()) { if (right.isLiteral()) { put(objectExpression.value); put(memberExpression.value); put(right.value); } else { put(objectExpression.value); put(memberExpression.value); putLoadStack(); } } else { if (right.isLiteral()) { put(objectExpression.value); putLoadStack(); put(right.value); } else { put(objectExpression.value); putCrossLoadStack(); } } } else { if (memberExpression.isLiteral()) { if (right.isLiteral()) { putLoadStack(); put(memberExpression.value); put(right.value); } else { swapStack(); putLoadStack(); put(memberExpression.value); putLoadStack(); } } else { if (right.isLiteral()) { putCrossLoadStack(); put(right.value); } else { swapStack(0, 2); putLoadStack(); putLoadStack(); putLoadStack(); } } } putStoreStack(); } public function putNew (numOfArguments:Number) : Void { put('NEW'); putLoadStack(); put(numOfArguments); putStoreStack(); } public function putDelete (expressionResult:ExpressionResult) : Void { if (expressionResult.isType('variable') || expressionResult.isType('literal')) { if (isLocalVariable(expressionResult.value)) { put('DELL'); } else { put('DEL'); } put(expressionResult.value); putStoreStack(); } else { put('DEL'); putLoadStack(); putStoreStack(); } } public function putDeleteMember (objectExpression:ExpressionResult, expressionResult:ExpressionResult) : Void { put('DELM'); putBinaryValue(objectExpression, expressionResult); /* if (ExpressionResult(expressionResult.value).isType('literal')) { putLoadStack(); put(ExpressionResult(expressionResult.value).value); } else { putCrossLoadStack(); } */ putStoreStack(); } }