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

forked from: 構文解析

ソースコードの構造を解析する
ソースコードをStringで受け取り、トークン列として返す
トークン列をステップ単位で管理する。ステップは「;」と「}」で区切りリストにする
ステップのツリー構造を抽出する。「{}」内に含まれるステップリストを取得する
予定
含まれる要素(クラス、メソッド、変数、定数)を抽出する
要素への参照箇所、代入箇所を抽出する
ツリー側で変更した内容をソースコード側に反映する
Get Adobe Flash player
by tepe 26 Jun 2014
    Embed
/**
 * Copyright tepe ( http://wonderfl.net/user/tepe )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/rHYH
 */

// forked from tepe's 構文解析
//ソースコードの構造を解析する

//ソースコードをStringで受け取り、トークン列として返す
//トークン列をステップ単位で管理する。ステップは「;」と「}」で区切りリストにする
//ステップのツリー構造を抽出する。「{}」内に含まれるステップリストを取得する

//予定
//含まれる要素(クラス、メソッド、変数、定数)を抽出する
//要素への参照箇所、代入箇所を抽出する
//ツリー側で変更した内容をソースコード側に反映する

package {
    import flash.display.*;
    import flash.text.*;
    import flash.events.*;
    import flash.net.*;
    import flash.utils.*;
    import net.hires.debug.Stats;
    


    public class FlashTest extends Sprite {
        private var t1:TextField = new TextField();//原文
        private var t2:TextField = new TextField();
        private var t3:TextField = new TextField();
        
        private var ui:TreeUI = new TreeUI();
        
        private var so : SharedObject;
        public function FlashTest() {
            
            // write as3 code here..
            init();
            //FPS計測    
            //addChild( new Stats() );
            //Wonderfl.capture(stage);
            onChange(null);

        }
        
        private var dic:Dictionary = new Dictionary();
        //ステップのツリー構造を視覚化する
        private function f5(b:Block):TreeNode{
            var node:TreeNode = new TreeNode();
            dic[node] = b;
            if(b.head==null)return null;// node.name=b.body;
            else node.name=b.head + " ["+b.length.toString()+"]";

            for(var i:int=0;i<b.length;i++){ 
                if(b.getStep(i).head==null){ 
                    //node.createNode(b.getStep(i).body);
                }
                else{
                    node.addNode( f5(b.getStep(i)) );
                }
            }
            return node;
        }
        
        //キーワードを探す 同じキーワード同士を結ぶ
        private function f6(b:Block,obj:Object=null):Object{
            var result:Object;
            if(obj==null)result = new Object();
            else result=obj;
            var cToken:Token = b.start;
            //t3.appendText("f6----\n");
            while(cToken!=null){
                
                if(cToken.type!=Token.WORD){
                    cToken=cToken.next;
                    if(cToken==b.end)break;
                    continue;
                }
                var str:String = cToken.text;
                //t3.appendText(str+"--\n");
                
                if(!result.hasOwnProperty(str)){//新しく現れたキーワード 
                    var list:Array = new Array();
                    list.push(cToken);
                    result[str] = list;
                }
                else{//1回以上現れたキーワード
                    result[str].push(cToken);//(n+1) as int;
                }


                cToken=cToken.next;
                if(cToken==b.end)break;
            }


            
            return result;
        }
        
        
 
        private function init():void{
            //原文テキスト入力ボックス
            t1.border = true;
            t1.width = 230;
            t1.height = 360;
            t1.y=100;
            t1.type = "input";
            t1.multiline = true;
            so = SharedObject.getLocal("data");
            if(so){ 
                if(so.data.text != null){
                    t1.text = so.data.text;
                }
                else{    
                    t1.text "";
                    so.data.text = t1.text;
                }
            }
            addChild(t1);
            t1.addEventListener(Event.CHANGE,onChange);
            t2.type ="input";
            t2.x = 230;
            t2.width=330;
            t2.height=460;
            t2.wordWrap =true;
            t2.multiline=true;
            t2.border = true;
            //addChild(t2);
            var panel:WindowPanel = new WindowPanel(t2);
            addChild(panel);
            
            t3.border = true;
            t3.multiline= true;
            t3.text = "test\n";
            t3.x = 100;
            addChild(t3);
            addChild(ui);
            /*
            var ozmap1:ozmap = new ozmap(455,455);
            ozmap1.addObject(ui);
            addChild(ozmap1);
            */
            //ozmap1.x = 250;
            //ui.x = 250;
            
            var node:TreeNode = new TreeNode();
            node.name="test";
            node.createNode("test2");
            ui.node = node;
            ui.render();
            
        }
        
        
        private var tk:Tokenizer;
        private function onChange(e:Event=null):void{//ソースコードに変更が加えられたら
            tk = new Tokenizer();
            tk.addEventListener(Event.COMPLETE,onComplete);
            //進捗率の表示
            tk.addEventListener(ProgressEvent.PROGRESS,function(e:ProgressEvent):void{
                t2.text = Math.round(100*e.bytesLoaded/e.bytesTotal).toString()+"%";
            });

            tk.createToken(t1.text);
            //var cnt:int=0;
            t2.text = "";
        }
        
        //ステップリストとスコープリストの生成
        private function onComplete(e:Event):void{
            t2.text = "onComplete";
            
            var bList:Vector.<Block> = getTree(tk.getToken());
            var b:Block;
            var i:int;
            var bufStr:String = new String();
            //bufStr += str;
            //t2.text = str;
            //return;

            for(i=0;i<bList.length;i++){ 

                if(bList[i].head == null){ 
                    bufStr+= bList[i].body+"\n-----------------\n";
                }
                else{
                    bufStr+=f3(bList[i])+"";
                    bufStr+="@step"+bList[i].stepNum.toString()+"\n";
                    bufStr+=bList[i].head+"{"
                    bufStr+=bList[i].length.toString()+"step}";

                    bufStr+="\n------------------"+i.toString()+"\n";
                }

            }
            
            
            var tree:TreeNode = new TreeNode();
            
            
            tree.name = "source";
            t3.appendText("blist.length: "+bList.length.toString()+"\n");
            for(i=0;i<bList.length;i++){
                //f4(bList[i]);
                var tree2:TreeNode = f5(bList[i]);
                if(tree2==null)continue;
                tree.addNode(tree2);
            }
            
            ui.addEventListener(MouseEvent.CLICK,function(e:MouseEvent):void{
                if(e.target.parent is TreeNodeUI){
                    
                    var key:* = e.target.parent.node;
                    var tb:* = dic[key];
                    if(tb != null){                                                 
                        t2.text = tb.text;
                    }
                    //else t2.text = e.target.parent.node.name;
                }
                //t2.appendText(e.target.toString());

            });
            ui.node = tree;
            ui.render();
            


            t2.text = bufStr;
            so.data.text = t1.text;
            t3.text = "onComplete---\n";
            
            var obj:Object = new Object();
            for(i=0;i<bList.length;i++){    
                obj = f6(bList[i],obj);
            }
            for(var str:String in obj){
                t3.appendText(str+" @ ");
                t3.appendText(obj[str].length.toString()+"\n");
                /*var ar:Array = obj[str];
                if(ar==null)t3.appendText("\n");
                else t3.appendText(" @ "+ar.length.toString()+"\n");
                */
            }

            
        }//function
        
        //パンくずリスト
        private function f3(block:Block):String{
            var b:Block = block.parent;
            var buf:Vector.<String> = new Vector.<String>;
            while(b != null){
                buf.push(b.head);
                b=b.parent;
            }   
            var treeStr:String = new String();
            for(var i:int=1;i<=buf.length;i++){//階層の表示
                treeStr+=">> "+buf[buf.length-i]+"";
            }
            
            return treeStr;
        }
        

        
        private function f2(b:Block):String{
            var str:String = new String();
            var tab:String = new String();
            for(var j:int=0;j<b.rootsNum;j++)tab+="  ";

            str = tab+"-- "+ b.rootsNum.toString()+"-"+b.stepNum.toString()+"  --\n";
            
            if(b.head==null)str += tab+b.body+"\n";
            else{ 
                str += tab + b.head+"{@\n";
                str += tab + b.length.toString()+"step\n";
                for(var i:int=0;i<b.length;i++){
                    str += tab + f2(b.getStep(i))+"\n";
                }
                str += tab+"@}//"+b.head+"\n\n";
            }
            return str;
        }

        //記号の中から演算子を探す
        //ブロックにスコープ範囲とステップ範囲を割り当てる
        //ブロックに親子関係を割り当てる
        public function getTree(t:Token):Vector.<Block>{
            
            var bStack:Vector.<Block> = new Vector.<Block>;//階層を保持する
            var parent:Block;
            var bList:Vector.<Block> = new Vector.<Block>;
            var start:Token = t;//開始位置のトークン
            var startStack:Vector.<Token> = new Vector.<Token>; 
            
            var errNum:int;
            
            var cToken:Token = t;//tk.getToken();
            while(cToken!=null){//無限ループ注意
                //記号の処理
                if(cToken.type != Token.MARK){//トークンが記号以外なら次のトークンへ
                    cToken = cToken.next;
                    continue;
                }                
                var s:String = cToken.text;//トークンの文字列
                if(s == ";"){ //1ステップ
                    var b1:Block = new Block();
                    b1.setArea(start,cToken);//開始位置と終了位置を登録
                    if(parent!=null){ 
                        errNum = parent.addStep(b1);//ステップ追加
                        //t3.appendText("#errNum:"+errNum.toString()+"\n");
                    }
                    else{
                        b1.stepNum=bList.length;
                        bList.push(b1);
                    }
                    
                    start = cToken.next;//開始位置のトークン更新
                }
                else if(s=="{"){ //スコープ開始 
                    cToken.type = Token.SCOPE;
                    var b2:Block = new Block();
                    b2.setArea(start);//開始位置だけ登録
                    b2.parent = parent;//親を登録
                    if(parent!=null)errNum = parent.addStep(b2);//カレントスコープにステップ追加
                    //t3.appendText("#errNum:"+errNum.toString()+"\n");
                    bStack.push(b2); //階層
                    parent = b2;//カレントスコープ更新
                    
                    start = cToken.next;
                    //t3.appendText(cToken.next.text+"\n");
                }
                else if(s=="}"){//スコープ終了
                    cToken.type = Token.SCOPE;
                    var b3:Block = bStack.pop();
                    b3.setArea(b3.start,cToken);//開始位置と終了位置を登録
                    parent = b3.parent;
                    if(parent==null){ 
                        b3.stepNum = bList.length;
                        bList.push(b3);
                    }
                    else{
                        //bList.push(b3);
                    }

                    start = cToken.next;//スタート位置更新
                }
                else if(s == "("){ 
                    startStack.push(start);
                    start = cToken.next;//開始位置更新
                    cToken.type = Token.SCOPE;
                }
                else if(s == ")"){ 
                    start = startStack.pop();//開始位置更新
                    cToken.type = Token.SCOPE;
                    //if(start.text!="(")return null;//エラー
                }
                else if(s == "="){ //代入
                    //算術演算子
                    if(cToken.prev.text=="+"){
                        cToken = cToken.prev.addToken(cToken);
                    }
                    else if(cToken.prev.text=="-"){
                        cToken = cToken.prev.addToken(cToken);
                    }
                    else if(cToken.prev.text=="*"){
                        cToken = cToken.prev.addToken(cToken);
                    }
                    else if(cToken.prev.text=="/"){
                        cToken = cToken.prev.addToken(cToken);
                    }
                    else if(cToken.prev.text=="%"){
                        cToken = cToken.prev.addToken(cToken);
                    }
                    //比較演算子
                    else if(cToken.prev.text=="="){
                        cToken = cToken.prev.addToken(cToken);
                    }
                    else if(cToken.prev.text=="!"){
                        cToken = cToken.prev.addToken(cToken);
                    }
                    else if(cToken.prev.text=="<"){
                        cToken = cToken.prev.addToken(cToken);
                    }
                    else if(cToken.prev.text==">"){
                        cToken = cToken.prev.addToken(cToken);
                    }
                    //ビット演算子
                    else if(cToken.prev.text=="&"){
                        cToken = cToken.prev.addToken(cToken);
                    }
                    else if(cToken.prev.text=="|"){
                        cToken = cToken.prev.addToken(cToken);
                    }
                    else if(cToken.prev.text=="^"){
                        cToken = cToken.prev.addToken(cToken);
                    }

                    cToken.type = Token.OPERATOR;
                    
                }
                else if(s == "!")cToken.type = Token.OPERATOR;
                else if(s == "+"){ 
                    if(cToken.prev.text=="+"){//インクリメント
                        cToken = cToken.prev.addToken(cToken);
                    }
                    cToken.type = Token.OPERATOR;
                }
                else if(s == "-"){ 
                    if(cToken.prev.text=="-"){//デクリメント
                        cToken = cToken.prev.addToken(cToken);
                    }
                    cToken.type = Token.OPERATOR;
                }
                else if(s == "*")cToken.type = Token.OPERATOR;
                else if(s == "/")cToken.type = Token.OPERATOR;
                else if(s == "%")cToken.type = Token.OPERATOR;
                else if(s == "&"){ 
                    if(cToken.prev.text=="&"){
                        cToken = cToken.prev.addToken(cToken);
                        t3.appendText("addToken &&\n");
                    }
                    cToken.type = Token.OPERATOR;
                }
                else if(s == "|"){ 
                    if(cToken.prev.text=="|"){
                        cToken = cToken.prev.addToken(cToken);
                        t3.appendText("addToken ||\n");
                    }
                    cToken.type = Token.OPERATOR;
                }
                else if(s == "^")cToken.type = Token.OPERATOR;
                else if(s == "~")cToken.type = Token.OPERATOR;
                else if(s == "?")cToken.type = Token.OPERATOR;
                else if(s == "<"){ 
                    while(cToken.next.text=="<"){//シフト演算
                        cToken.addToken(cToken.next);
                    }
                    cToken.type = Token.OPERATOR;
                }
                else if(s == ">"){ 
                    while(cToken.next.text==">"){//シフト演算
                        cToken.addToken(cToken.next);
                    }
                    cToken.type = Token.OPERATOR;
                }
                
                if(cToken.type == Token.OPERATOR){
                    t3.appendText(cToken.text+" "+cToken.id.toString()+"\n");
                }

                cToken = cToken.next;
            }//while
            
            return bList;
        }//function
        
    }//class
}//package


//======== Token ===================================================================

import flash.events.ProgressEvent;

//トークンオブジェクト
class Token{
    private static var idCnt:int;
    private var tokenId:int;
    private var _text:String;//文字列
    public var type:int;//タイプ
    //public var lineNum:int;//属するプロセスライン
    
    //トークンの並びを保持する
    public var next:Token;
    public var prev:Token;
    public static const ERROR:int = -1;//エラー
    public static const SPACE:int = 0;//スペース
    public static const COMMENT:int = 1;//コメント 
    public static const NUMBER:int = 2;//数値
    public static const WORD:int = 3;//識別子
    public static const TEXT:int = 4;//文字列
    public static const MARK:int = 5;//記号
    public static const OTHER:int = 6;//その他(マルチバイトの文字列)
    public static const SCOPE:int = 7;//スコープの置き換え
    public static const OPERATOR:int = 8;//演算子
    public static const EOF:int = 9;//EOF
    
    public function Token(){
        idCnt++;
        tokenId = idCnt;
    }
    public function get id():int{
        return tokenId;
    }
    public function set text(str:String):void{
        _text = new String();
        _text += str;
        
    }
    public function get text():String{
        return _text;
    }
    //トークンを結合する
    public function addToken(t:Token):Token{
        _text+=t.text;
        next = t.next;
        idCnt++;
        tokenId = idCnt;
        return this;
    }


}


//========  TokenPoint  ===============================================================


//トークンの移動操作をする
class TokenPoint{
    private var _current:Token;
    private var _start:Token;
    private var _end:Token;
    public function TokenPoint(t:Token,s:Token=null,e:Token=null){
        _current = t;
        _start = s;
        _end = e;
        
    }
    public function get getPos():Token{
        return _current;
    }

    public function movNext():int{//移動量を返す
        if(_current.next==null)return 0;
        _current = _current.next;
        return 1;
    }
    public function movPrev():int{
        if(_current.prev==null)return 0;
        _current = _current.prev;
        return -1;
    }
    //指定されたタイプの位置まで移動
    public function movNextType(type:int,end:Token=null):int{ 
        var cnt:int=0;
        var t:Token = _current;
        while(t.type!=type){ 
            if(t==null)return 0;
            if(t==end)return 0;
            t=t.next;
            cnt++;
        }
        _current = t;
        return cnt;
    }
    
    public function movPrevType(type:int,end:Token=null):int{
        var cnt:int=0;
        var t:Token = _current;
        while(t.type!=type){
            if(t==null)return 0;
            if(t==end)return 0;
            t=t.prev;
            cnt--;
        }
        _current = t;
        return cnt;

    }
    //指定された文字列の位置まで移動
    public function movNextText(text:String,end:Token=null):int{
        var cnt:int=0;
        var t:Token = _current;
        while(t.text != text){
            if(t==null)return 0;
            if(t==end)return 0;
            t=t.next;
            cnt++;
        }
        _current = t;
        return cnt;

    }
    
    public function movPrevText(text:String,end:Token=null):int{
        var cnt:int=0;
        var t:Token = _current;
        while(t.text != text){
            if(t==null)return 0;
            if(t==end)return 0;
            t=t.prev;
            cnt--;
        }
        _current = t;
        return cnt;
    }
}


//=======  Block  ================================================================


//ステップ、スコープを扱う
class Block{ 
    private static var idCnt:int;
    private var _id:int;//識別値
    private var _stepNum:int = -1;//スコープ内でのステップ番号
    private var _rootsNum:int;//階層の深度
    private var _name:String;
    private var _text:String;//ブロック全体の文字列
    private var _head:String;//スコープ前の要素
    private var _body:String;//スコープの中身
    public var list:Vector.<Block>;
    
    private var _next:Block;
    private var _prev:Block;
    private var _parent:Block;
    private var _child:Block;//スコープ内の先頭ステップ
    
    private var _startToken:Token;
    private var _endToken:Token;
    private var _entryToken:Token;//スコープ入口。ない場合はnull
    public function Block(){
        idCnt++;
        _id=idCnt;
    }
    //ID取得
    public function get id():int{
        return _id;
    }
    //スコープ内のステップ数取得
    public function get length():int{
        if(list==null)return 0;
        return list.length;
    }
    
    //先頭からの順番
    public function get stepNum():int{
        if(_stepNum != -1)return _stepNum;
        var b:Block = this.prev;
        var cnt:int=0;
        while(b!=null){
            cnt++;
            b = b.prev;
        }
        _stepNum=cnt;

        return _stepNum;
    }
    
    public function set stepNum(n:int):void{ 
        if(_stepNum != -1)return;//未設定の場合のみ設定可能
        _stepNum=n;
    }
    
    
    //階層
    public function get rootsNum():int{ 
        return _rootsNum;
    }



    
    //スコープ内の任意のステップ取得
    public function getStep(n:int):Block{ 
        if(list==null)return null;
        return list[n];
    }

    //スコープ内にステップ追加
    public function addStep(block:Block):int{
        if(block==null)return -1;
        if(list == null)list = new Vector.<Block>;
        block.parent = this;
        list.push(block);
        
        var len:int = list.length;
        
        if(1<len){ 
            list[len-1].prev = list[len-2];
            list[len-2].next = list[len-1];
        }
        else _child = list[0];
        
        return len;
    }
    
    public function get start():Token{
        return _startToken;
    }
    public function get end():Token{
        return _endToken;
    }


    public function get next():Block{//次の要素
        return _next;
    }
    public function get prev():Block{//前の要素
        return _prev;
    }
    public function get parent():Block{//上の階層
        return _parent;
    }
    
    public function get stepIn():Block{//下の階層の先頭ステップ
        return _child;
    }
    
    public function get stepOver():Block{//次のステップ
        return _next;
    }
    public function get stepOut():Block{//現在の階層を出て次のステップ
        var p:Block = _parent;
        return p.stepOver;
    }


    
    public function set next(b:Block):void{ 
        if(_next == b)return;
        _next=b;
        if(_next==null)return;
        if(_next.prev.id!=this.id)_next.prev = this;
    }
    
    public function set prev(b:Block):void{ 
        _prev=b;
    }
    
    public function set parent(b:Block):void{
        _parent=b;
        var cnt:int=0;
        var b:Block =this;
        while(b.parent != null){
            b = b.parent;
            cnt++;
        }
        _rootsNum = cnt;
    }
    
    //トークン列の中から開始要素と終了要素を指定
    public function setArea(start:Token,end:Token=null):int{ 
        _text=null;
        _head=null;
        _body=null;
        
        _startToken = start;//開始位置のトークン登録
        //先頭がスペースかコメントなら詰める
        while(_startToken.type == Token.SPACE || _startToken.type == Token.COMMENT)_startToken = _startToken.next;
        
        _endToken = end;//終了位置のトークン登録
        
        if(end==null)return 3;//未完了
        
        /*
        if(_startToken.prev==null){//ルート
            if(_endToken.next==null)return 0;
        }*/

        
        if(_endToken.text ==";")return 1;//ステップ
        
        if(_endToken.text =="}"){ //スコープの有無を確認
            var t:Token = _startToken;
            var tp:TokenPoint = new TokenPoint(_startToken);
            if(tp.movNextText("{")==0)return -1;
            _entryToken = tp.getPos;//スコープ入口
            return 2;
        }
        return -1;

    }
//-------------------------------------------------------------
    //テキスト取得
    public function get text():String{ 
        if(_text!=null)return _text;
        _text = new String();
        var t:Token = _startToken;
        while(t.next != null){
            if(t.type != Token.SPACE)_text += t.text;
            else{
                //改行を探す
                //if(t.text.indexOf("\r") != -1)_text += "\r";//インデント追加処理を入れたい
                //else _text += " ";
                _text += t.text;
            }

            if(t.id == _endToken.id)break;
            t = t.next;
        }
        return _text;
    }
    
    public function get name():String{
        if(_name!=null)return _name;
        if(_entryToken==null)return null;
        
        var t:Token = _entryToken;
        var st:Token = _startToken;
        var cnt:int=0;
        //func(  [)]  :***{ このカッコの位置まで移動
        var tp:TokenPoint = new TokenPoint(_startToken);
        if(tp.movNextText("(")!=0){
            tp.movPrevType(Token.WORD);
        }

        t = _startToken;
        while(t!=null){ 
            if(t.text=="(")break;
            if(t.id==_entryToken.id)break;
            t = t.next;
            cnt++;
        }
        for(var i:int=0;i<cnt;i++){
            t=t.prev;
            if(t.type==Token.WORD)break;
        }
        
        _name=t.text;
        return _name;

    }

    //スコープの前。条件式、クラス名、メソッド名
    public function get head():String{
        if(_entryToken==null)return null;
        if(_head!=null)return _head;
        _head = new String();
        var t:Token = _startToken;
        while(t.type==Token.SPACE || t.type==Token.COMMENT)t = t.next;//スペース、コメント除外
        while(t.next != null){
            _head += t.text;
            if(t.next.id == _entryToken.id)break;
            t = t.next;
        }
        return _head;

    }
    //スコープの中。スコープは含まない
    public function get body():String{ 
        if(_body!=null)return _body;
        _body = new String();
        var t:Token;
        
        if(_entryToken==null){ 
            t = _startToken;
            while(t.type==Token.SPACE || t.type==Token.COMMENT)t = t.next;//スペース、コメント除外
            while(t.next != null){
                _body += t.text;
                if(t.id == _endToken.id)break;
                t = t.next;
            }
            return _body;
        }
        t = _entryToken;
        while(t.next != null){
            _body += t.text;
            if(t.id == _endToken.id)break;
            t = t.next;
        }
        return _body;
    }

    

}




//======= Thread ==========================================================================


import flash.utils.*;
import flash.events.*;
    
class Thread extends Timer{ 
    static public var SPAN:Number = 1000/30; 
    static public var RATE:Number = 0.5;
    public var end:Boolean = false;
    public var currentLoop:Loop = null;
    
    static private var NUM:int = 0;
    private var _limit:Number = 5;
    private var _time:int = 0;
    private var _added:Boolean;
    private var currentLoops:Vector.<Loop>;
    private var loops:Vector.<Loop> = new Vector.<Loop>();
    
    public function Thread(){
        super(SPAN); start(); NUM++;
        addEventListener( "timer", onFrame  );
    }
        
    public function loop( func:Function, onComplete:Function = null):void{
        var loop:Loop = new Loop( func, currentLoop );     
        if( currentLoop == null ){ loops.push( loop );
        }else{ currentLoop.loops.push( loop ); _added = true }
        if( onComplete != null ){
            loop = new Loop( onComplete, currentLoop );   
            if( currentLoop == null ){ loops.push( loop );
            }else{ currentLoop.loops.push( loop ); }
        }
    }
        
    public function remove():void{
        NUM--; stop(); end = true;
        removeEventListener( "timer", onFrame  );
    }
        
    private function onFrame(e:Event):void{
        _time = getTimer(); _limit = (SPAN * RATE) / NUM;
        all: while(true){
            if( currentLoop == null ){
                currentLoops = loops;
                if( loops.length == 0 ){ remove(); break; }
                while( currentLoops[0].loops.length != 0 ){ currentLoops = currentLoops[0].loops;  }
                currentLoop = currentLoops[0];
            }
            do{
                if( _limit < (getTimer() - _time)){ break all; }
                if( _added ){ _added = false; currentLoop = null; continue all;  }
            }while( currentLoop.func() )
            currentLoop = null;
            currentLoops.reverse(); currentLoops.pop(); currentLoops.reverse();
        }
    }
}





//====== Loop ============================================================================


class Loop extends Object{
    
    public var func:Function;
    public var name:String;
    public var parent:Loop;
    public var loops:Vector.<Loop> = new Vector.<Loop>();
    
    function Loop( func:Function, parent:Loop = null, name:String = "" ){  
        this.func = func;
        this.parent = parent;
    }
}


//=======================================================================================



//////////////////////////////////////////////////////////////////////////
//  トークン列取得
//////////////////////////////////////////////////////////////////////////



//テキストからトークンを抽出する
class Tokenizer extends Thread{
    private var blockList:Vector.<Block>;
    private var tokenList:Vector.<Token>;
    //private var scopeList:Array;
    private var _length:int;
    private var source:String;
    private var cansel:Boolean;
    public function Tokenizer(){
        
    }
    //スコープリストを生成する
    public function createScopeList():int{
        return 0;
    }

    public function getEof():Token{
        return getToken(length-1);
    }


    //指定インデクスからmax個までトークン抽出
    //抽出したトークンはlistに追加
    //抽出したところまでのインデクスを返す
    public function createToken(str:String):void{
        
        source = str;
        tokenList = new Vector.<Token>;
        index01 = 0;
         
        var i:int = 0;
        var cnt:int = 0;
        loop( function _while():Boolean{ 
            i= func01();
                
            if(cnt==0){//進捗率
                var e:ProgressEvent = new ProgressEvent(ProgressEvent.PROGRESS,false,true,i,str.length);
                dispatchEvent(e);
            }
            cnt++;
            cnt%=500;
            if(i<str.length)return true;
            return false;
        },onComplete);
    }

    private function onComplete():void{
        addToken("",Token.EOF);//EOF追加
        _length = tokenList.length;
        var event:Event = new Event(Event.COMPLETE);
        dispatchEvent(event);
    }
    
    public function get length():int{
        return _length;
    }
    //先頭からの順を指定してトークンを取得
    public function getToken(index:int=0):Token{
        if(index<0 && tokenList.length<=index)return null;
        return tokenList[index];
    }
    
    //トークン列に追加
    private function addToken(str:String,type:int):int{
        var obj:Token = new Token();
        obj.text = str;
        obj.type = type;
        obj.next = null;
        tokenList.push(obj);
        if(1<tokenList.length){ 
            tokenList[tokenList.length-1].prev = tokenList[tokenList.length-2];
            tokenList[tokenList.length-2].next = tokenList[tokenList.length-1];
        }
        else tokenList[tokenList.length-1].prev = null;
        return tokenList.length;
    }



//-------------------------------------------------------------
    //開始インデクスを受け取りトークンのタイプを返す
    private var index01:int=0;
    private function func01():int{//
        if(index01 >= source.length)return source.length;
        var index:int = index01;
        var type:int;//トークンタイプ
        var str:String;//トークン文字列
        var reg:RegExp = new RegExp();                
        var result:Object;
        //トークンタイプ:空白か?
        if(    source.charAt(index)== " " ||
               source.charAt(index)=="\t" ||
               source.charAt(index)=="\n" ||
               source.charAt(index)=="\r" ){
                   type = Token.SPACE;
                   reg = /[^\s]/mg;
                   reg.lastIndex = index;
                   result = reg.exec(source);
                   //トークン作成
                   if(result == null){//
                       str = source.substring(index,source.length);
                       index01 = source.length;//次の開始位置
                   }
                   else{//
                       str = source.substring(index,result.index);
                       index01 = result.index;                       
                   }
                   addToken(str,type);
                   return index01;
                                       
               }

        
        //トークンタイプ:文字列定数1
        if(source.charAt(index)== "'"){
            type = Token.TEXT;
            var indexA:int = index;
            reg = /['\n\r]/mg;//改行か次の[']が現れる位置
            do{//エスケープ処理
                reg.lastIndex = indexA+1;
                result = reg.exec(source);
                if(result==null)break;
                else indexA = result.index;
            }while(source.charAt(indexA-1)=="\\");//["]が現れてもその前がエスケープなら再検索
            
            //トークン作成
            if(result == null){//エラー
                str = "'";
                type = Token.ERROR;
                index01 = index+1;//次の開始位置
            }
            else{//
                str = source.substring(index,result.index+1);
                index01 = result.index+1;//次の開始位置
            }
            addToken(str,type);
            return index01;
                
        }
 
        //トークンタイプ:文字列定数2
        if(source.charAt(index)== '"'){
            type = Token.TEXT;
            var indexB:int = index;
            reg = /["\n\r]/mg;//改行か次の["]が現れる位置
            do{//エスケープ処理
                reg.lastIndex = indexB+1;
                result = reg.exec(source);
                if(result==null)break;
                else indexB = result.index;
            }while(source.charAt(indexB-1)=="\\");//["]が現れてもその前がエスケープなら再検索
            
            //トークン作成
            if(result == null){//エラー
                str = '"';
                type = Token.ERROR;
                index01 = index+1;//次の開始位置
                
            }
            else{//
                str = source.substring(index,result.index+1);
                index01 = result.index+1;//次の開始位置
                
            }
            addToken(str,type);
            return index01;
        }
        
        if(source.charAt(index)== "/"){
               //一行コメント
               if(source.charAt(index+1)== "/"){//
                   reg = /[\n\r]/mg;
                   reg.lastIndex = index+2;
                   result = reg.exec(source);
                   //トークン作成
                   type = Token.COMMENT;
                   if(result == null){
                       str = source.substring(index,source.length);
                       index01 = source.length;//次の開始位置                       
                   }
                   else{// 
                       str = source.substring(index,result.index);
                       index01 = result.index;//次の開始位置
                   }
                   addToken(str,type);
                   return index01;
               }
               //複数行コメント
               if(source.charAt(index+1)== "*"){
                   reg = /\*\//mg;
                   reg.lastIndex = index+2;
                   result = reg.exec(source);
                   
                   //トークン作成
                   type = Token.COMMENT;
                   if(result == null){//エラー
                       type = Token.ERROR;
                       str = '/*';
                       index01 = index+2;//次の開始位置
                   }
                   else{//
                       str = source.substring(index,result.index+2);
                       index01 = result.index+2;//次の開始位置                       
                   }
                   addToken(str,type);
                   return index01;
               }
        }

        //英数字
        if((source.charCodeAt(index) > 64 && source.charCodeAt(index) < 91) ||// A-Z
           (source.charCodeAt(index) > 96 && source.charCodeAt(index) < 123)||// a-z
            source.charCodeAt(index) == 95 ){// _
                reg = /[^a-zA-Z0-9_]/mg;//英数字以外が現れる位置
                reg.lastIndex = index+1;
                result = reg.exec(source);
                type = Token.WORD;
                if(result == null){//eof
                    str = source.substring(index,source.length);
                    index01 = source.length;
                }
                else{//
                    str = source.substring(index,result.index);
                    index01 = result.index;//次の開始位置
                }
                addToken(str,type);
                return index01;
                
        }
        
        
        //数値
        if(source.charCodeAt(index) > 47 && source.charCodeAt(index) < 58){
            reg = /[^a-zA-Z0-9_.]/mg;
            reg.lastIndex = index+1;
            result = reg.exec(source);
            type = Token.NUMBER;
            if(result == null){//eof
                str = source.substring(index,source.length);
                index01 = source.length;
            }
            else{// 
                str = source.substring(index,result.index);
                index01 = result.index;//次の開始位置
            }
            //num();//種類判別
            addToken(str,type);
            return index01;

        }
        
        //半角記号
        if(source.charCodeAt(index)<127 && source.charCodeAt(index)>32){
            type = Token.MARK;
            str = source.charAt(index);
            index01 = index+1;
            
            addToken(str,type);
            return index01;
        }
        
    
        //その他文字列 マルチバイト文字等はここに入る
        type = Token.OTHER;
        reg = /[\x01-\x7f]/mg;
        reg.lastIndex = index+1;
        result = reg.exec(source);
        if(result == null){//eof
            str = source.substring(index,source.length);
            index01 = source.length;
        }
        else{// 
            str = source.substring(index,result.index);
            index01 = result.index;//次の開始位置
        }
        
        addToken(str,type);
        return index01;
    }
     
}//class



import flash.display.*;
import flash.events.*;
import flash.text.*;
class WindowPanel extends Sprite{
    private var _title:String = "";
    private var _mx:Number = 20;
    private var _my:Number = 10;
    private var _target:DisplayObject;
    private var _tf:TextField;
    public function WindowPanel(o:DisplayObject=null){
        if(o!=null)target=o;
        draw();
        this.addEventListener(MouseEvent.MOUSE_MOVE,onMove);
        this.addEventListener(MouseEvent.MOUSE_OUT,onOut);
        this.addEventListener(MouseEvent.MOUSE_DOWN,onDrag);
        this.addEventListener(MouseEvent.MOUSE_UP,onDrop);
    }
    public function set target(o:DisplayObject):void{
        _target = o;
        this.addChild(o);
        this.x = o.x;
        this.y = o.y;
        o.x=0;
        o.y=0;
        
        draw();
    }
    
    public function set title(t:String):void{
        _title = t;
        draw();
    }


    //タイトル表示
    private function initTF():void{
        _tf = new TextField();
        _tf.text = _title;
        _tf.y = -_my;
        _tf.selectable = false;
        _tf.width = _tf.textWidth+5;
        _tf.height = _tf.textHeight+5;
        this.addChild(_tf);
    }

    //ウィンドウ更新
    public function draw(c:uint=0xffffff,a:Number=0.8):void{
        initTF();
        var w:Number,h:Number;
        if(_target==null){w=this.width;h=this.height;}
        else{ 
            w=_target.width;h=_target.height;
            this.x += _target.x;
            this.y += _target.y;
            _target.x = 0;
            _target.y = 0;
        }
        
        this.graphics.clear();
        this.graphics.lineStyle(0,0x0,a);
        this.graphics.beginFill(c,a);
        this.graphics.drawRect(-_mx,-_my,w+_mx*2,h+_my*2);
        this.graphics.endFill();
    }
    
    private function onOut(e:MouseEvent):void{
        Mouse.cursor = MouseCursor.AUTO;
    }

    private function onMove(e:MouseEvent):void{
        if(e.target == this)Mouse.cursor = MouseCursor.HAND;
        else Mouse.cursor = MouseCursor.AUTO;
    }

        
    private function onDrag(e:MouseEvent):void{
        e.currentTarget.parent.setChildIndex(e.currentTarget, e.currentTarget.parent.numChildren-1); //並べ替え
        if(e.target == this){
            this.startDrag();
            //var s:DisplayObject = stage.getChildByName(e.currentTarget.name);//ディスプレイオブジェクト取得
            
        }
        draw();
        
        //this.startDrag();
    }
    private function onDrop(e:MouseEvent):void{
        this.stopDrag();
        draw();
    }
}


import flash.events.*;
import flash.text.*;
import flash.display.*;
import flash.geom.*;
import flash.ui.*;

    class TreeUI extends WindowPanel{
        
        
        private var rootTree:TreeNode = new TreeNode();
        private var treeUI:Sprite = new Sprite();
        private var pen:Pen = new Pen(treeUI.graphics,0x444444);
        public function TreeUI(){
            this.target = treeUI;
            rootTree.name="/"
            rootTree.closed = false;
            render();
        }
        
        public function set node(tree:TreeNode):void{
            rootTree = tree;
            render();
            
        }

        
        //描画処理
        public function render():void {
            treeUI.graphics.clear();
            while (treeUI.numChildren > 0) treeUI.removeChildAt(0);
            renderCell(rootTree, 0, 0);//ノード描画
            draw();
        }
        
        private function renderCell(tree:TreeNode, x:Number, y:Number):Number {
            var sy:Number = y;
            var tui:TreeNodeUI = new TreeNodeUI(tree, x, y,this);
            var h:Number = tui.height;
            treeUI.addChild(tui);
            
            y += h+5;
            if (!tree.closed) {//子リストの描画
                var n:Number = 10;
                var vx:Number = x + 7;
                var vy:Number = y;
                
                for each(var child:TreeNode in tree.list) {
                    //pen.moveTo(vx, y+10);
                    pen.moveTo(vx, y+n);
                    pen.lineTo(vx + 10, y + n);//横線
                    //vy = y+10;
                    vy = y+n;
                    y = renderCell(child, x + 20, y);//子リスト
                }
                if(0<tree.list.length){
                    pen.moveTo(vx, sy+15);
                    pen.lineTo(vx, vy);//縦線
                }
            }
            return y;
        }
    }
    



import com.bit101.components.PushButton;

class TreeNodeUI extends Sprite{
    public var node:TreeNode;
    public var treeUI:TreeUI;
    private var _minBtn:PushButton;
    private var tf:TextField;
    
    public function TreeNodeUI(node:TreeNode,x:Number,y:Number,main:TreeUI) {
        this.node = node;
        this.treeUI = main;
        
        
        tf = new TextField();
        tf.autoSize = "left";
        tf.text = node.name;
        tf.backgroundColor = 0xffffff;
        tf.addEventListener(MouseEvent.MOUSE_OVER,function():void{
            tf.border=true;
            tf.background = true;
        });
        tf.addEventListener(MouseEvent.MOUSE_OUT,function():void{
            tf.border=false;
            tf.background = false;
        });

        //tf.background =true;
        //tf.border=true;
        tf.selectable=false;

        addChild(tf);
        if(0<node.length){
            _minBtn = createMinBtn(); 
            addChild(_minBtn);
            tf.x = _minBtn.width+2;
        }
        
        this.x = x;
        this.y = y;
    }
    

    
    //最小化ボタン
    private function createMinBtn():PushButton {
        var result:PushButton = new PushButton(null, 0, 3, "+", minimize);
        result.width = result.height = 14; result.draw();
        result.addEventListener(MouseEvent.CLICK,minimize);
        result.label = (node.closed) ? "+" : "-"; 
        return result;
    }
    
    private function minimize(event:MouseEvent = null):void {
        node.closed = !node.closed;
        treeUI.render();
        if (_minBtn) { 
            _minBtn.label = (node.closed) ? "-" : "+"; 
        }
    }
    
}

class TreeNode {
    
    public static var ID:int = 0;
    public var parent:TreeNode;//親
    public var closed:Boolean = true;//展開状態
    public var name:String = "node" + (ID++);//表示名
    private var _children:Vector.<TreeNode>;//子リスト
    
    public function TreeNode(str:String=null){
        if(str!=null)name=str;
        _children= new Vector.<TreeNode>;
    }

    public function get length():int{
        return _children.length;
    }

    
    public function get list():Vector.<TreeNode>{
        //createChild();
        return _children;
    }
    
    //名前を指定してノード取得
    public function getNode(n:String=null):TreeNode{
        for(var i:int=0;i<_children.length;i++){
            if(_children[i].name==n)return _children[i];
        }
        return null;
    }


    //ノードを追加
    public function addNode(t:TreeNode):void{
        _children.push(t);
    }
    //ノード生成
    public function createNode(str:String):void{
        var node:TreeNode = new TreeNode(str);
        _children.push(node);
    }
    //パスを指定して生成する
    public function createPath(path:String):void{
        var ar:Array = path.split("/");
        var node:TreeNode = this;
        for(var i:int=0;i<ar.length;i++){
            
            //指定されたツリーが存在しなければ生成。存在すればそのまま
            if(node.getNode(ar[i])==null)node.createNode(ar[i]);
            node = node.getNode(ar[i]);
        }

    }



}

class Pen{
    
        private var g:Graphics;
        private var sx:Number;
        private var sy:Number;
        private var color:uint;
        public function Pen(g:Graphics,c:uint=0x000000){
            this.g = g;
            this.color = c;
        }
        public function moveTo(x:Number, y:Number):void {
            g.moveTo(x, y);
            sx = x;
            sy = y;
        }
        
        public function lineTo(x:Number, y:Number):void {
            g.lineStyle(0,color);
            g.lineTo(x, y);
            sx = x;
            sy = y;
        }
    }