forked from: MILという言語の処理系をつくってみました
forked from MILという言語の処理系をつくってみました (diff: 1418)
日経ソフトウエア2010年8月号の記事「スクリプト言語をゼロから作ろう」 で解説されていた「MIL」という言語の処理系をつくってみました。 ■使い方 ・エディタ部にMILのソースコードを入力 ・Runボタンを押して実行 ・チェックボックスがRun Bytecodeなら実行、Dump Assembly Code にするとアセンブリコードを出力 ■MILの仕様 ・使用できるデータ型 : 整数型と文字列型のみ ・制御構造 : if文, if-else文, while文, goto文, gosub-return文 goto, gosubのラベルには「*」をつける if文, if-else文, while文は{ }を省略できない ・出力 : print文 ・1行コメント : #から行末までコメント ・その他 : 文の最後はセミコロン「;」が必要 ■オリジナルの処理系(C言語) http : //itpro.nikkeibp.co.jp/article/MAG/20091120/340842/?ST=nsw#201008
ActionScript3 source code
/**
* Copyright ohisama ( http://wonderfl.net/user/ohisama )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/sLFb
*/
// forked from ser1zw's MILという言語の処理系をつくってみました
/*
日経ソフトウエア2010年8月号の記事「スクリプト言語をゼロから作ろう」
で解説されていた「MIL」という言語の処理系をつくってみました。
■使い方
・エディタ部にMILのソースコードを入力
・Runボタンを押して実行
・チェックボックスがRun Bytecodeなら実行、Dump Assembly Code
にするとアセンブリコードを出力
■MILの仕様
・使用できるデータ型 : 整数型と文字列型のみ
・制御構造 : if文, if-else文, while文, goto文, gosub-return文
goto, gosubのラベルには「*」をつける
if文, if-else文, while文は{ }を省略できない
・出力 : print文
・1行コメント : #から行末までコメント
・その他 : 文の最後はセミコロン「;」が必要
■オリジナルの処理系(C言語)
http : //itpro.nikkeibp.co.jp/article/MAG/20091120/340842/?ST=nsw#201008
*/
package
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import com.bit101.components.*;
[SWF(backgroundColor="#cccccc")]
public class Main extends Sprite
{
private const SAMPLE_CODE : String = "# MILのサンプル\na = 10;\nb = (a + 100) * 10 / 2 - 20; # 算術演算は+, -, *, /のみ\nhello = \"Hello, world\"; # 文字列型のデータ\nprint(hello); # print文で文字列を出力\nprint(b);\n\ni = 0;\nwhile (i < 5) { # while文とif~else文は{ }を省略できない\n if (i <= 2) {\n print(\"foo\");\n }\n else {\n print(\"bar\");\n }\n i = i + 1;\n}\n\ngoto *label; # goto文でラベルへジャンプする\nprint(\"これは出力されない\");\n\n*label\n print(\"gotoでジャンプ!\");\n gosub *fib; # gosub文でサブルーチンのラベルへジャンプする\n print(\"returnで戻ってきました\");\n goto *end;\n\n*fib # サブルーチン(フィボナッチ数列を計算)\n f0 = 0;\n f1 = 1;\n f2 = 0;\n print(f1);\n while (1) {\n f2 = f1 + f0;\n if (f2 > 100) {\n goto *ret;\n }\n print(f2);\n f0 = f1;\n f1 = f2;\n }\n *ret\n return; # return文でサブルーチンから戻る\n\n*end\n";
private var editorLabel : com.bit101.components.Label;
private var stdoutLabel : com.bit101.components.Label;
private var editor : TextArea;
private var stdout : TextArea;
private var button : PushButton;
private var radio1 : RadioButton;
private var radio2 : RadioButton;
public function Main()
{
Style.embedFonts = false;
Style.fontName = "_typewriter";
stdoutLabel = new com.bit101.components.Label(this, 0, 5, "Output");
stdout = new TextArea(this, 0, stdoutLabel.y + stdoutLabel.height);
stdout.width =350;
stdout.height = 100;
editorLabel = new com.bit101.components.Label(this, 0, 130," MIL");
editor = new TextArea(this, 0, editorLabel.y + editorLabel.height);
editor.width = 350;
editor.height = 300;
editor.text = SAMPLE_CODE;
button = new PushButton(this, editor.x + editor.width + 25, editor.y, "Run", function(e : MouseEvent) : void
{
stdout.text = "";
run(editor.text, radio2.selected);
});
button.width = 50;
radio1 = new RadioButton(this, button.x, button.y + button.height + 20)
radio1.label = "Run Bytecode";
radio1.selected = true;
radio2 = new RadioButton(this, radio1.x, radio1.y + radio1.height + 5);
radio2.label = "Dump Assembly Code";
}
private function run(sourceCode : String, dumpAssemblyCodeMode : Boolean = false) : void
{
try
{
var parser : Parser = new Parser(sourceCode);
var mvm : Mvm = new Mvm(parser.bytecode, parser.strPool, function(msg : String) : void
{
stdout.text += msg + "\n";
});
if (dumpAssemblyCodeMode)
{
stdout.text = mvm.dumpAsmCode().join("\n");
}
else
{
mvm.execute();
}
}
catch (e : Error)
{
stdout.text = e.toString();
}
}
}
}
class LexicalAnalyzer
{
private var sourceCodeCharArray : Vector.<String>;
private var currentLineNumber : int;
private var operatorTable : Object;
private var keywordTable : Object;
public function get lineNumber() : int
{
return currentLineNumber;
}
public function LexicalAnalyzer(sourceCode : String)
{
currentLineNumber = 1;
operatorTable =
{
"==" : TokenKind.EQ_TOKEN,
"!=" : TokenKind.NE_TOKEN,
">=" : TokenKind.GE_TOKEN,
"<=" : TokenKind.LE_TOKEN,
"+" : TokenKind.ADD_TOKEN,
"-" : TokenKind.SUB_TOKEN,
"*" : TokenKind.MUL_TOKEN,
"/" : TokenKind.DIV_TOKEN,
"=" : TokenKind.ASSIGN_TOKEN,
">" : TokenKind.GT_TOKEN,
"<" : TokenKind.LT_TOKEN,
"(" : TokenKind.LEFT_PAREN_TOKEN,
")" : TokenKind.RIGHT_PAREN_TOKEN,
"{" : TokenKind.LEFT_BRACE_TOKEN,
"}" : TokenKind.RIGHT_BRACE_TOKEN,
"," : TokenKind.COMMA_TOKEN,
";" : TokenKind.SEMICOLON_TOKEN
};
keywordTable =
{
"if" : TokenKind.IF_TOKEN,
"else" : TokenKind.ELSE_TOKEN,
"while" : TokenKind.WHILE_TOKEN,
"goto" : TokenKind.GOTO_TOKEN,
"gosub" : TokenKind.GOSUB_TOKEN,
"return" : TokenKind.RETURN_TOKEN,
"print" : TokenKind.PRINT_TOKEN
};
sourceCode = sourceCode.replace(/\r\n?/g, "\n");
sourceCodeCharArray = new Vector.<String>();
for (var i : int = 0; i < sourceCode.length; i++)
{
sourceCodeCharArray.push(sourceCode.charAt(i));
}
}
private function lexError(message : String) : void
{
throw new Error("lex error : " + message);
}
private function inOperator(token : String, letter : String) : Boolean
{
var newToken : String = token + letter;
for (var op : String in operatorTable)
{
if (op.length >= newToken.length && newToken == op.substring(0, newToken.length))
{
return true;
}
}
return false;
}
private function selectOperator(token : String) : int
{
if (operatorTable[token] == null)
{
lexError("Invalid operator " + token);
return 0;
}
return operatorTable[token];
}
private function isDigit(ch : String) : Boolean
{
var charCode : Number = ch.charCodeAt(0);
return charCode >= 48 && charCode <= 57;
}
private function isAlpha(ch : String) : Boolean
{
var charCode : Number = ch.charCodeAt(0);
return (charCode >= 65 && charCode <= 90) || (charCode >= 97 && charCode <= 122);
}
private function isSpace(ch : String) : Boolean
{
var charCode : Number = ch.charCodeAt(0);
return (charCode >= 9 && charCode <= 13) || (charCode == 32);
}
public function lexGetToken() : Token
{
var ret : Token = new Token();
var state : int = LexerState.INITIAL_STATE;
var token : String = "";
var ch : String;
LOOP : while ((ch = sourceCodeCharArray.shift()) != null)
{
switch (state)
{
case LexerState.INITIAL_STATE :
if (isDigit(ch))
{
token = token.concat(ch);
state = LexerState.INT_VALUE_STATE;
}
else if (isAlpha(ch) || ch == "_")
{
token = token.concat(ch);
state = LexerState.IDENTIFIER_STATE;
}
else if (ch == '"')
{
state = LexerState.STRING_STATE;
}
else if (inOperator(token, ch))
{
token = token.concat(ch);
state = LexerState.OPERATOR_STATE;
}
else if (isSpace(ch))
{
if (ch == "\n")
{
currentLineNumber++;
}
}
else if (ch == "#")
{
state = LexerState.COMMENT_STATE;
}
else
{
lexError("Bad charactor : " + ch);
}
break;
case LexerState.INT_VALUE_STATE :
if (isDigit(ch))
{
token = token.concat(ch);
}
else
{
ret = Token.getIntToken(parseInt(token));
sourceCodeCharArray.unshift(ch);
break LOOP;
}
break;
case LexerState.IDENTIFIER_STATE :
if (isAlpha(ch) || ch == "_" || isDigit(ch))
{
token = token.concat(ch);
}
else
{
ret = Token.getIdentifierToken(TokenKind.IDENTIFIER_TOKEN, token);
sourceCodeCharArray.unshift(ch);
break LOOP;
}
break;
case LexerState.STRING_STATE :
if (ch == '"')
{
ret = Token.getStringToken(token);
break LOOP;
}
else
{
token = token.concat(ch);
}
break;
case LexerState.OPERATOR_STATE :
if (inOperator(token, ch))
{
token = token.concat(ch);
}
else
{
sourceCodeCharArray.unshift(ch);
break LOOP;
}
break;
case LexerState.COMMENT_STATE :
if (ch == "\n")
{
state = LexerState.INITIAL_STATE;
}
break;
default :
throw new Error("lex error");
break;
}
}
if (ch == null)
{
if (state == LexerState.INITIAL_STATE || state == LexerState.COMMENT_STATE)
{
ret.kind = TokenKind.END_OF_FILE_TOKEN;
return ret;
}
}
if (state == LexerState.IDENTIFIER_STATE)
{
if (keywordTable[token] == null)
{
ret = Token.getIdentifierToken(TokenKind.IDENTIFIER_TOKEN, token);
}
else
{
ret = Token.getIdentifierToken(keywordTable[token], token);
}
}
else if (state == LexerState.OPERATOR_STATE)
{
ret.kind = selectOperator(token);
}
return ret;
}
}
class LexerState
{
public static const INITIAL_STATE : int = 1;
public static const INT_VALUE_STATE : int = 2;
public static const IDENTIFIER_STATE : int = 3;
public static const STRING_STATE : int = 4;
public static const OPERATOR_STATE : int = 5;
public static const COMMENT_STATE : int = 6;
}
class Token
{
private var _kind : int;
private var _intValue : int;
private var _stringValue : String;
private var _identifier : String;
public static function getIntToken(intValue : int) : Token
{
var token : Token = new Token();
token._kind = TokenKind.INT_VALUE_TOKEN;
token._intValue = intValue;
return token;
}
public static function getStringToken(stringValue : String) : Token
{
var token : Token = new Token();
token._kind = TokenKind.STRING_LITERAL_TOKEN;
token._stringValue = stringValue;
return token;
}
public static function getIdentifierToken(tokenKind : int, identifier : String) : Token
{
var token : Token = new Token();
token._kind = tokenKind;
token._identifier = identifier
return token;
}
public function get kind() : int
{
return _kind;
}
public function set kind(kind : int) : void
{
_kind = kind;
}
public function get intValue() : int
{
return _intValue;
}
public function get stringValue() : String
{
return _stringValue;
}
public function get identifier() : String
{
return _identifier;
}
public function toString() : String
{
return _kind + " [" + [_intValue, _stringValue, _identifier].join(", ") + "]";
}
}
class TokenKind
{
public static const INT_VALUE_TOKEN : int = 1; // 整数
public static const IDENTIFIER_TOKEN : int = 2; // 識別子
public static const STRING_LITERAL_TOKEN : int = 3; // 文字列
public static const EQ_TOKEN : int = 4; // ==
public static const NE_TOKEN : int = 5; // !=
public static const GE_TOKEN : int = 6; // >=
public static const LE_TOKEN : int = 7; // <=
public static const ADD_TOKEN : int = 8; // +
public static const SUB_TOKEN : int = 9; // -
public static const MUL_TOKEN : int = 10; // *
public static const DIV_TOKEN : int = 11; // /
public static const ASSIGN_TOKEN : int = 12; // =
public static const GT_TOKEN : int = 13; // >
public static const LT_TOKEN : int = 14; // <
public static const LEFT_PAREN_TOKEN : int = 15; // (
public static const RIGHT_PAREN_TOKEN : int = 16; // )
public static const LEFT_BRACE_TOKEN : int = 17; // {
public static const RIGHT_BRACE_TOKEN : int = 18; // }
public static const COMMA_TOKEN : int = 19; // ,
public static const SEMICOLON_TOKEN : int = 20; // ;
public static const IF_TOKEN : int = 21; // if
public static const ELSE_TOKEN : int = 22; // else
public static const WHILE_TOKEN : int = 23; // while
public static const GOTO_TOKEN : int = 24; // goto
public static const GOSUB_TOKEN : int = 25; // gosub
public static const RETURN_TOKEN : int = 26; // return
public static const PRINT_TOKEN : int = 27; // print
public static const END_OF_FILE_TOKEN : int = 28; // EOF
}
class Label
{
private var _identifier : String;
public var address : int;
public function get identifier() : String
{
return _identifier;
}
public function Label(identifier : String, address : int = 0)
{
this._identifier = identifier;
this.address = address;
}
}
class OpCode
{
public static const OP_PUSH_INT : int = 1;
public static const OP_PUSH_STRING : int = 2;
public static const OP_ADD : int = 3;
public static const OP_SUB : int = 4;
public static const OP_MUL : int = 5;
public static const OP_DIV : int = 6;
public static const OP_MINUS : int = 7;
public static const OP_EQ : int = 8;
public static const OP_NE : int = 9;
public static const OP_GT : int = 10;
public static const OP_GE : int = 11;
public static const OP_LT : int = 12;
public static const OP_LE : int = 13;
public static const OP_PUSH_VAR : int = 14;
public static const OP_ASSIGN_TO_VAR : int = 15;
public static const OP_JUMP : int = 16;
public static const OP_JUMP_IF_ZERO : int = 17;
public static const OP_GOSUB : int = 18;
public static const OP_RETURN : int = 19;
public static const OP_PRINT : int = 20;
}
class Parser
{
private var _bytecode : Vector.<int>;
private var _labelTable : Vector.<Label>;
private var _varTable : Vector.<String>;
private var _strPool : Vector.<String>;
private var lookAheadToken : Token;
private var lex : LexicalAnalyzer;
public function get bytecode() : Vector.<int>
{
return _bytecode;
}
public function get labelTable() : Vector.<Label>
{
return _labelTable;
}
public function get varTable() : Vector.<String>
{
return _varTable;
}
public function get strPool() : Vector.<String>
{
return _strPool;
}
public function Parser(sourceCode : String)
{
_bytecode = new Vector.<int>();
_labelTable = new Vector.<Label>();
_varTable = new Vector.<String>();
_strPool = new Vector.<String>();
lex = new LexicalAnalyzer(sourceCode);
parse();
fixLabels();
}
private function getToken() : Token
{
var ret : Token;
if (lookAheadToken != null)
{
ret = lookAheadToken;
lookAheadToken = null;
}
else
{
ret = lex.lexGetToken();
}
return ret;
}
private function ungetToken(token : Token) : void
{
lookAheadToken = token;
}
private function addBytecode(bytecode : int) : void
{
_bytecode.push(bytecode);
}
private function parseError(message : String) : void
{
throw new Error("Parse error : " + message + " at " + lex.lineNumber);
}
private function checkExpectedToken(expected : int) : void
{
var token : Token = getToken();
if (token.kind != expected)
{
parseError("parse error - TokenKind " + expected + " is expected, but is " + token.toString());
}
}
private function searchOrNewVar(identifier : String) : int
{
var ret : int = _varTable.indexOf(identifier);
if (ret < 0)
{
_varTable.push(identifier);
return _varTable.length - 1;
}
return ret;
}
private function parsePrimaryExpression() : void
{
var token : Token = getToken();
switch (token.kind)
{
case TokenKind.INT_VALUE_TOKEN :
addBytecode(OpCode.OP_PUSH_INT);
addBytecode(token.intValue);
break;
case TokenKind.STRING_LITERAL_TOKEN :
addBytecode(OpCode.OP_PUSH_STRING);
_strPool.push(token.stringValue);
addBytecode(_strPool.length - 1);
break;
case TokenKind.LEFT_PAREN_TOKEN :
parseExpression();
checkExpectedToken(TokenKind.RIGHT_PAREN_TOKEN);
break;
case TokenKind.IDENTIFIER_TOKEN :
var varIndex : int = _varTable.indexOf(token.identifier);
if (varIndex < 0)
{
parseError(token.identifier + " - identifier not found.");
}
addBytecode(OpCode.OP_PUSH_VAR);
addBytecode(varIndex);
break;
}
}
private function parseUnaryExpression() : void
{
var token : Token = getToken();
if (token.kind == TokenKind.SUB_TOKEN)
{
parsePrimaryExpression();
addBytecode(OpCode.OP_MINUS);
}
else
{
ungetToken(token);
parsePrimaryExpression();
}
}
private function parseMultiplicativeExpression() : void
{
var token : Token;
parseUnaryExpression();
while (true)
{
token = getToken();
if (token.kind != TokenKind.MUL_TOKEN && token.kind != TokenKind.DIV_TOKEN)
{
ungetToken(token);
break;
}
parseUnaryExpression();
if (token.kind == TokenKind.MUL_TOKEN)
{
addBytecode(OpCode.OP_MUL);
}
else
{
addBytecode(OpCode.OP_DIV);
}
}
}
private function parseAdditiveExpression() : void
{
var token : Token;
parseMultiplicativeExpression();
while (true)
{
token = getToken();
if (token.kind != TokenKind.ADD_TOKEN && token.kind != TokenKind.SUB_TOKEN)
{
ungetToken(token);
break;
}
parseMultiplicativeExpression();
if (token.kind == TokenKind.ADD_TOKEN)
{
addBytecode(OpCode.OP_ADD);
}
else
{
addBytecode(OpCode.OP_SUB);
}
}
}
private function parseCompareExpression() : void
{
var token : Token;
parseAdditiveExpression();
while (true)
{
token = getToken();
if (token.kind != TokenKind.EQ_TOKEN && token.kind != TokenKind.NE_TOKEN && token.kind != TokenKind.GT_TOKEN && token.kind != TokenKind.GE_TOKEN && token.kind != TokenKind.LT_TOKEN && token.kind != TokenKind.LE_TOKEN)
{
ungetToken(token);
break;
}
parseAdditiveExpression();
switch (token.kind)
{
case TokenKind.EQ_TOKEN :
addBytecode(OpCode.OP_EQ);
break;
case TokenKind.NE_TOKEN :
addBytecode(OpCode.OP_NE);
break;
case TokenKind.GT_TOKEN :
addBytecode(OpCode.OP_GT);
break;
case TokenKind.GE_TOKEN :
addBytecode(OpCode.OP_GE);
break;
case TokenKind.LT_TOKEN :
addBytecode(OpCode.OP_LT);
break;
case TokenKind.LE_TOKEN :
addBytecode(OpCode.OP_LE);
break;
}
}
}
private function parseExpression() : void
{
parseCompareExpression();
}
private function getLabel() : int
{
_labelTable.push(new Label(null));
return _labelTable.length - 1;
}
private function setLabel(labelIndex : int) : void
{
_labelTable[labelIndex].address = _bytecode.length;
}
private function searchOrNewLabel(label : String) : int
{
var i : int;
for (i = 0; i < _labelTable.length; i++)
{
if (_labelTable[i] != null && _labelTable[i].identifier != null && _labelTable[i].identifier == label)
{
return i;
}
}
_labelTable.push(new Label(label));
return _labelTable.length - 1;
}
private function parseIfStatement() : void
{
var token : Token;
var elseLabel : int;
var endIfLabel : int;
checkExpectedToken(TokenKind.LEFT_PAREN_TOKEN);
parseExpression();
checkExpectedToken(TokenKind.RIGHT_PAREN_TOKEN);
elseLabel = getLabel();
addBytecode(OpCode.OP_JUMP_IF_ZERO);
addBytecode(elseLabel);
parseBlock();
token = getToken();
if (token.kind == TokenKind.ELSE_TOKEN)
{
endIfLabel = getLabel();
addBytecode(OpCode.OP_JUMP);
addBytecode(endIfLabel);
setLabel(elseLabel);
parseBlock();
setLabel(endIfLabel);
}
else
{
ungetToken(token);
setLabel(elseLabel);
}
}
private function parseWhileStatement() : void
{
var loopLabel : int;
var endWhileLabel : int;
loopLabel = getLabel();
setLabel(loopLabel);
checkExpectedToken(TokenKind.LEFT_PAREN_TOKEN);
parseExpression();
checkExpectedToken(TokenKind.RIGHT_PAREN_TOKEN);
endWhileLabel = getLabel();
addBytecode(OpCode.OP_JUMP_IF_ZERO);
addBytecode(endWhileLabel);
parseBlock();
addBytecode(OpCode.OP_JUMP);
addBytecode(loopLabel);
setLabel(endWhileLabel);
}
private function parsePrintStatement() : void
{
checkExpectedToken(TokenKind.LEFT_PAREN_TOKEN);
parseExpression();
checkExpectedToken(TokenKind.RIGHT_PAREN_TOKEN);
addBytecode(OpCode.OP_PRINT);
checkExpectedToken(TokenKind.SEMICOLON_TOKEN);
}
private function parseAssignStatement(identifier : String) : void
{
var varIndex : int = searchOrNewVar(identifier);
checkExpectedToken(TokenKind.ASSIGN_TOKEN);
parseExpression();
addBytecode(OpCode.OP_ASSIGN_TO_VAR);
addBytecode(varIndex);
checkExpectedToken(TokenKind.SEMICOLON_TOKEN);
}
private function parseGotoStatement() : void
{
var token : Token;
var label : int;
checkExpectedToken(TokenKind.MUL_TOKEN);
token = getToken();
if (token.kind != TokenKind.IDENTIFIER_TOKEN)
{
parseError("label identifier expected");
}
label = searchOrNewLabel(token.identifier);
addBytecode(OpCode.OP_JUMP);
addBytecode(label);
checkExpectedToken(TokenKind.SEMICOLON_TOKEN);
}
private function parseGosubStatement() : void
{
var token : Token;
var label : int;
checkExpectedToken(TokenKind.MUL_TOKEN);
token = getToken();
if (token.kind != TokenKind.IDENTIFIER_TOKEN)
{
parseError("label identifier expected");
}
label = searchOrNewLabel(token.identifier);
addBytecode(OpCode.OP_GOSUB);
addBytecode(label);
checkExpectedToken(TokenKind.SEMICOLON_TOKEN);
}
private function parseLabelStatement() : void
{
var token : Token;
var label : int;
token = getToken();
if (token.kind != TokenKind.IDENTIFIER_TOKEN)
{
parseError("label identifier expected");
}
label = searchOrNewLabel(token.identifier);
setLabel(label);
}
private function parseReturnStatement() : void
{
addBytecode(OpCode.OP_RETURN);
checkExpectedToken(TokenKind.SEMICOLON_TOKEN);
}
private function parseStatement() : void
{
var token : Token = getToken();
switch (token.kind)
{
case TokenKind.IF_TOKEN :
parseIfStatement();
break;
case TokenKind.WHILE_TOKEN :
parseWhileStatement();
break;
case TokenKind.PRINT_TOKEN :
parsePrintStatement();
break;
case TokenKind.GOTO_TOKEN :
parseGotoStatement();
break;
case TokenKind.GOSUB_TOKEN :
parseGosubStatement();
break;
case TokenKind.RETURN_TOKEN :
parseReturnStatement();
break;
case TokenKind.MUL_TOKEN :
parseLabelStatement();
break;
case TokenKind.IDENTIFIER_TOKEN :
parseAssignStatement(token.identifier);
break;
default :
parseError("bad statement.");
break;
}
}
private function parseBlock() : void
{
var token : Token;
checkExpectedToken(TokenKind.LEFT_BRACE_TOKEN);
while (true)
{
token = getToken();
if (token.kind == TokenKind.RIGHT_BRACE_TOKEN)
{
break;
}
ungetToken(token);
parseStatement();
}
}
private function fixLabels() : void
{
var i : int;
for (i = 0; i < _bytecode.length; i++)
{
if (_bytecode[i] == OpCode.OP_PUSH_INT || _bytecode[i] == OpCode.OP_PUSH_STRING || _bytecode[i] == OpCode.OP_PUSH_VAR || _bytecode[i] == OpCode.OP_ASSIGN_TO_VAR)
{
i++;
}
else if (_bytecode[i] == OpCode.OP_JUMP || _bytecode[i] == OpCode.OP_JUMP_IF_ZERO || _bytecode[i] == OpCode.OP_GOSUB)
{
_bytecode[i + 1] = _labelTable[_bytecode[i + 1]].address;
i++; // オリジナルにはないが、たぶん必要
}
}
}
private function parse() : void
{
var token : Token;
while (true)
{
token = getToken();
if (token.kind == TokenKind.END_OF_FILE_TOKEN)
{
break;
}
else
{
ungetToken(token);
parseStatement();
}
}
}
}
class Mvm
{
private var _stack : Vector.<Value>;
private var _variable : Vector.<Value>;
private var _bytecode : Vector.<int>;
private var _strPool : Vector.<String>;
private var stdout : Function;
public function Mvm(bytecode : Vector.<int>, strPool : Vector.<String>, stdout : Function = null)
{
_stack = new Vector.<Value>();
_variable = new Vector.<Value>();
this._bytecode = bytecode;
this._strPool = strPool;
this.stdout = stdout;
}
public function execute() : void
{
var value : Value;
var n : int, m : int
var str : String;
var bool : Boolean;
var pc : int = 0;
while (pc < _bytecode.length)
{
switch (_bytecode[pc])
{
case OpCode.OP_PUSH_INT :
value = Value.createIntValue(_bytecode[pc + 1]);
_stack.push(value);
pc += 2;
break;
case OpCode.OP_PUSH_STRING :
str = _strPool[_bytecode[pc + 1]];
value = Value.createStringValue(str);
_stack.push(value);
pc += 2;
break;
case OpCode.OP_ADD :
n = _stack.pop().intValue + _stack.pop().intValue;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_SUB :
m = _stack.pop().intValue;
n = _stack.pop().intValue;
_stack.push(Value.createIntValue(n - m));
pc++;
break;
case OpCode.OP_MUL :
n = _stack.pop().intValue * _stack.pop().intValue;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_DIV :
m = _stack.pop().intValue;
n = _stack.pop().intValue;
_stack.push(Value.createIntValue(n / m));
pc++;
break;
case OpCode.OP_MINUS :
n = _stack.pop().intValue * -1;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_EQ :
n = (_stack.pop().intValue == _stack.pop().intValue) ? 1 : 0;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_NE :
n = (_stack.pop().intValue != _stack.pop().intValue) ? 1 : 0;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_GT :
m = _stack.pop().intValue;
n = _stack.pop().intValue;
n = (n > m) ? 1 : 0;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_GE :
m = _stack.pop().intValue;
n = _stack.pop().intValue;
n = (n >= m) ? 1 : 0;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_LT :
m = _stack.pop().intValue;
n = _stack.pop().intValue;
n = (n < m) ? 1 : 0;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_LE :
m = _stack.pop().intValue;
n = _stack.pop().intValue;
n = (n <= m) ? 1 : 0;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_PUSH_VAR :
value = _variable[_bytecode[pc + 1]];
_stack.push(value);
pc += 2;
break;
case OpCode.OP_ASSIGN_TO_VAR :
_variable[_bytecode[pc + 1]] = _stack.pop();
pc += 2;
break;
case OpCode.OP_JUMP :
pc = _bytecode[pc + 1];
break;
case OpCode.OP_JUMP_IF_ZERO :
if (_stack.pop().intValue == 0)
{
pc = _bytecode[pc + 1];
}
else
{
pc += 2;
}
break;
case OpCode.OP_GOSUB :
_stack.push(Value.createIntValue(pc + 2));
pc = _bytecode[pc + 1];
break;
case OpCode.OP_RETURN :
pc = _stack.pop().intValue;
break;
case OpCode.OP_PRINT :
value = _stack.pop();
if (stdout != null)
{
if (value.type == ValueType.INT_VALUE_TYPE)
{
stdout(value.intValue);
}
else
{
stdout(value.stringValue);
}
}
pc++;
break;
default :
pc++;
throw new Error("MVM Error");
break;
}
}
}
public function dumpAsmCode() : Array
{
var value : Value;
var n : int, m : int
var str : String;
var bool : Boolean;
var pc : int = 0;
var asm : Array = [];
if (_strPool.length > 0)
{
asm.push(".STRING_POOL");
for each (str in _strPool)
{
asm.push(str);
}
}
asm.push(".BYTECODE");
while (pc < _bytecode.length)
{
switch (_bytecode[pc])
{
case OpCode.OP_PUSH_INT :
asm.push("OP_PUSH_INT");
asm.push(_bytecode[pc + 1]);
value = Value.createIntValue(_bytecode[pc + 1]);
_stack.push(value);
pc += 2;
break;
case OpCode.OP_PUSH_STRING :
asm.push("OP_PUSH_STRING");
asm.push(_bytecode[pc + 1]);
str = _strPool[_bytecode[pc + 1]];
value = Value.createStringValue(str);
_stack.push(value);
pc += 2;
break;
case OpCode.OP_ADD :
asm.push("OP_ADD " + _stack[_stack.length - 2].intValue + " " + _stack[_stack.length - 1].intValue);
n = _stack.pop().intValue + _stack.pop().intValue;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_SUB :
asm.push("OP_SUB " + _stack[_stack.length - 2].intValue + " " + _stack[_stack.length - 1].intValue);
m = _stack.pop().intValue;
n = _stack.pop().intValue;
_stack.push(Value.createIntValue(n - m));
pc++;
break;
case OpCode.OP_MUL :
asm.push("OP_MUL " + _stack[_stack.length - 2].intValue + " " + _stack[_stack.length - 1].intValue);
n = _stack.pop().intValue * _stack.pop().intValue;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_DIV :
asm.push("OP_DIV " + _stack[_stack.length - 2].intValue + " " + _stack[_stack.length - 1].intValue);
m = _stack.pop().intValue;
n = _stack.pop().intValue;
_stack.push(Value.createIntValue(n / m));
pc++;
break;
case OpCode.OP_MINUS :
asm.push("OP_MINUS " + _stack[_stack.length - 1].intValue);
n = _stack.pop().intValue * -1;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_EQ :
asm.push("OP_EQ " + _stack[_stack.length - 2].intValue + " " + _stack[_stack.length - 1].intValue);
n = (_stack.pop().intValue == _stack.pop().intValue) ? 1 : 0;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_NE :
asm.push("OP_NE " + _stack[_stack.length - 2].intValue + " " + _stack[_stack.length - 1].intValue);
n = (_stack.pop().intValue != _stack.pop().intValue) ? 1 : 0;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_GT :
asm.push("OP_GT " + _stack[_stack.length - 2].intValue + " " + _stack[_stack.length - 1].intValue);
m = _stack.pop().intValue;
n = _stack.pop().intValue;
n = (n > m) ? 1 : 0;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_GE :
asm.push("OP_GE " + _stack[_stack.length - 2].intValue + " " + _stack[_stack.length - 1].intValue);
m = _stack.pop().intValue;
n = _stack.pop().intValue;
n = (n >= m) ? 1 : 0;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_LT :
asm.push("OP_LT " + _stack[_stack.length - 2].intValue + " " + _stack[_stack.length - 1].intValue);
m = _stack.pop().intValue;
n = _stack.pop().intValue;
n = (n < m) ? 1 : 0;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_LE :
asm.push("OP_LE " + _stack[_stack.length - 2].intValue + " " + _stack[_stack.length - 1].intValue);
m = _stack.pop().intValue;
n = _stack.pop().intValue;
n = (n <= m) ? 1 : 0;
_stack.push(Value.createIntValue(n));
pc++;
break;
case OpCode.OP_PUSH_VAR :
asm.push("OP_PUSH_VAR");
asm.push(_bytecode[pc + 1]);
value = _variable[_bytecode[pc + 1]];
_stack.push(value);
pc += 2;
break;
case OpCode.OP_ASSIGN_TO_VAR :
if (_stack[_stack.length - 1].type == ValueType.INT_VALUE_TYPE)
{
str = _stack[_stack.length - 1].intValue.toString();
}
else
{
str = _stack[_stack.length - 1].stringValue;
}
asm.push("OP_ASSIGN_TO_VAR " + str);
asm.push(_bytecode[pc + 1]);
_variable[_bytecode[pc + 1]] = _stack.pop();
pc += 2;
break;
case OpCode.OP_JUMP :
asm.push("OP_JUMP");
asm.push(_bytecode[pc + 1]);
pc += 2;
break;
case OpCode.OP_JUMP_IF_ZERO :
asm.push("OP_JUMP_IF_ZERO " + _stack[_stack.length - 1].intValue);
asm.push(_bytecode[pc + 1]);
pc += 2;
break;
case OpCode.OP_GOSUB :
asm.push("OP_GOSUB");
asm.push(_bytecode[pc + 1]);
_stack.push(Value.createIntValue(pc + 2));
pc += 2;
break;
case OpCode.OP_RETURN :
asm.push("OP_RETURN " + _stack.pop().intValue);
pc++;
break;
case OpCode.OP_PRINT :
value = _stack.pop();
if (stdout != null)
{
if (value.type == ValueType.INT_VALUE_TYPE)
{
asm.push("OP_PRINT " + value.intValue);
}
else
{
asm.push("OP_PRINT " + value.stringValue);
}
}
pc++;
break;
default :
asm.push(_bytecode[pc] + " << ERROR");
pc++;
break;
}
}
return asm;
}
}
class Value
{
private var _type : int;
private var _intValue : int;
private var _stringValue : String;
public function get type() : int
{
return _type;
}
public function get intValue() : int
{
if (_type == ValueType.INT_VALUE_TYPE)
{
return _intValue;
}
else
{
throw new Error("This is INT_VALUE.");
}
}
public function get stringValue() : String
{
if (_type == ValueType.STRING_VALUE_TYPE)
{
return _stringValue;
}
else
{
throw new Error("This is STRING_VALUE.");
}
}
public static function createIntValue(intValue : int) : Value
{
var value : Value = new Value();
value._type = ValueType.INT_VALUE_TYPE;
value._intValue = intValue;
value._stringValue = null;
return value;
}
public static function createStringValue(stringValue : String) : Value
{
var value : Value = new Value();
value._type = ValueType.STRING_VALUE_TYPE;
value._intValue = 0;
value._stringValue = stringValue;
return value;
}
}
class ValueType
{
public static const INT_VALUE_TYPE : int = 1;
public static const STRING_VALUE_TYPE : int = 2;
}