In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

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
Get Adobe Flash player
by ser1zw 28 Jul 2010
/**
 * Copyright ser1zw ( http://wonderfl.net/user/ser1zw )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/pTWw
 */

/*
日経ソフトウエア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.*;

  /** MILのIDE */
  [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";
      editorLabel = new com.bit101.components.Label(this, 0, 5, "Editor");
      editor = new TextArea(this, 0, editorLabel.y + editorLabel.height);
      editor.width = 350;
      editor.height = 300;
      editor.text = SAMPLE_CODE;
      stdoutLabel = new com.bit101.components.Label(this, 0, editor.y + editor.height + 10, "Output");
      stdout = new TextArea(this, 0, stdoutLabel.y + stdoutLabel.height);
      stdout.width = editor.width;
      stdout.height = 100;
      button = new PushButton(this, editor.x + editor.width + 20, 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";
    }

    /**
    MILのコードを実行
    @param sourceCode ソースコード
    @param dumpAssemblyCodeMode trueならバイトコードを実行するかわりにアセンブリコードを出力
    */
    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; }

  /**
  コンストラクタ
  @param sourceCode ソースコード
  */
  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));
    }
  }
  
  /**
  エラーを発生
  @param エラーメッセージ
  */
  private function lexError(message:String):void {
    throw new Error("lex error: " + message);
  }

  /**
  現在読み込み中のtoken(文字列)にletter(1文字)を追加した文字列(token + letter)が
  演算子の文字列と前方一致でマッチするかどうかを判定
  @param token 読み込み中のトークン
  @param letter 追加する文字
  @return token+letterが演算子の文字列と前方一致でマッチすればtrue, しなければfalse
  */
  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;
  }

  /**
  演算子と区切り子を判定
  @param token トークン
  @return トークンの種類(TokenKind)
  */
  private function selectOperator(token:String):int {
    if (operatorTable[token] == null) {
      lexError("Invalid operator " + token);
      return 0;
    }
    return operatorTable[token];
  }

  /**
  与えられた文字が数字(0~9)であるかを判定
  @param ch 判定対象の文字
  @return chが数字であればtrue, 数字でなければfalse
  */
  private function isDigit(ch:String):Boolean {
    var charCode:Number = ch.charCodeAt(0);
    // "0" = 48, "9" = 57
    return charCode >= 48 && charCode <= 57;
  }

  /**
  与えられた文字が英字(A~Z, a~z)であるかを判定
  @param ch 判定対象の文字
  @return chが英字であればtrue, 英字でなければfalse
  */
  private function isAlpha(ch:String):Boolean {
    var charCode:Number = ch.charCodeAt(0);
    // "A" = 65, "Z" = 90, "a" = 97, "z" = 122
    return (charCode >= 65 && charCode <= 90) || (charCode >= 97 && charCode <= 122);
  }

  /**
  与えられた文字が空白文字であるかを判定
  @param ch 判定対象の文字
  @return chが空白文字であればtrue, 英字でなければfalse
  */
  private function isSpace(ch:String):Boolean {
    var charCode:Number = ch.charCodeAt(0);
    // " " = 32, "\f" = 12, "\n" = 10, "\r" = 13, "\t" = 9, "\v" = 11
    return (charCode >= 9 && charCode <= 13) || (charCode == 32);
  }

  /**
  次のトークンを取得
  @return トークン
  */
  public function lexGetToken():Token {
    var ret:Token = new Token();
    var state:int = LexerState.INITIAL_STATE; // 読み取り中のトークンの種別を保持
    var token:String = "";
    var ch:String;
    
    // ソースコードから1文字ずつ読み取る
    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;

  /**
  int型の値を表すトークンオブジェクトを作成
  @param intValue 値
  @return tokenKindとintValueがセットされたトークンオブジェクト
  */
  public static function getIntToken(intValue:int):Token {
    var token:Token = new Token();
    token._kind = TokenKind.INT_VALUE_TOKEN;
    token._intValue = intValue;
    return token;
  }

  /**
  String型の値を表すトークンオブジェクトを作成
  @param stringValue 値
  @return tokenKindとstringValueがセットされたトークンオブジェクト
  */
  public static function getStringToken(stringValue:String):Token {
    var token:Token = new Token();
    token._kind = TokenKind.STRING_LITERAL_TOKEN;
    token._stringValue = stringValue;
    return token;
  }

  /**
  識別子を表すトークンオブジェクトを作成
  @param tokenKind トークンの種類(TokenKind)
  @param identifier 値
  @return tokenKindとidentifierがセットされたトークンオブジェクト
  */
  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;
  }

  /**
  コンストラクタ
  @param identifier 識別子
  @param address アドレス(default = 0)
  */
  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; }

  /**
  コンストラクタ
  @param sourceCode ソースコード
  */
  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();
  }

  /**
  次のトークンを取得
  @return トークン
  */
  private function getToken():Token {
    var ret:Token;
    if (lookAheadToken != null) {
      ret = lookAheadToken;
      lookAheadToken = null;
    }
    else {
      ret = lex.lexGetToken();
    }
    return ret;
  }

  /**
  トークンを押し戻す
  @param token トークン
  */
  private function ungetToken(token:Token):void {
    lookAheadToken = token;
  }

  /**
  バイトコードに追加
  @param bytecode 追加するバイトコード
  */
  private function addBytecode(bytecode:int):void {
    _bytecode.push(bytecode);
  }
  
  /**
  エラーを発生
  @param message エラーメッセージ
  */
  private function parseError(message:String):void {
    throw new Error("Parse error: " + message + " at " + lex.lineNumber);
  }

  /**
  トークンが期待する種類でなければparse errorを発生させる
  @param expected 期待するトークンの種類(TokenKind)
  */
  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());
    }
  }
  
  /**
  変数を検索し、ない場合は新たに登録
  @param identifier 識別子
  @return 変数のインデックス
  */
  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();
  }

  /**
  新しいラベルを作成
  @return ラベルのインデックス
  */
  private function getLabel():int {
    _labelTable.push(new Label(null));
    return _labelTable.length - 1;
  }

  /**
  ラベルのアドレスをバイトコードの最後尾+1にセット
  @param labelIndex アドレスをセットするラベルのインデックス
  */
  private function setLabel(labelIndex:int):void {
    _labelTable[labelIndex].address = _bytecode.length;
  }

  /**
  ラベルを検索し、ない場合は新たに登録
  @param label ラベル
  @return ラベルのインデックス
  */
  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;
  }

  /** if文をパース */
  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);
    }
  }

  /** while文をパース */
  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);
  }

  /** print文をパース */
  private function parsePrintStatement():void {
    checkExpectedToken(TokenKind.LEFT_PAREN_TOKEN);
    parseExpression();
    checkExpectedToken(TokenKind.RIGHT_PAREN_TOKEN);
    addBytecode(OpCode.OP_PRINT);
    checkExpectedToken(TokenKind.SEMICOLON_TOKEN);
  }

  /**
  代入文をパース
  @param identifier 識別子
  */
  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);
  }

  /** goto文をパース */
  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);
  }

  /** gosub文をパース */
  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);
  }

  /** return文をパース */
  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();
    }
  }

  /**
  ジャンプ先の実アドレスを書き込む
  (パース時にバイトコードに入れたアドレスは_labelTableのインデックス)
  */
  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();
      }
    }
  }
}

/** MILの仮想マシン */
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;
  
  /**
  コンストラクタ
  @param bytecode バイトコード(Vector.<int>)
  @param strPool 文字列プール(Vector.<String>)
  */
  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;
      }
    }
  }

  /**
  バイトコードをアセンブリコードに変換
  @return アセンブリコード
  */
  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;
  }
}

/** MVMで扱うデータ */
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.");
    }
  }

  /**
  int型の値を表す値を作成
  @param intValue 値
  @return 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;
  }

  /**
  String型の値を表す値を作成
  @param stringValue 値
  @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;
  }
}

/** MVMで扱うデータ型の種類 */
class ValueType {
  public static const INT_VALUE_TYPE:int = 1;
  public static const STRING_VALUE_TYPE:int = 2;
}