forked from: MILという言語の処理系をつくってみました

by ohisama 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
♥0 | Line 1218 | Modified 2013-01-28 19:50:50 | MIT License
play

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;

}