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: Series of Tubes

somebody please optimize this code!
source code: http://github.com/mash/ContextFreeArt-AS3
This software is a port of ContextFree.js to Actionscript3.
Context Free Art is http://www.contextfreeart.org/index.html
ContextFree.js is http://code.google.com/p/contextfree/
some hints about implementation for improvement
ContextFree is a combination of resized primitive shapes:
Circle, Square, Triangle
in this implementation, every primitive shapes are 
"children" of "ContextFreeArt", 
which makes it slow to "addChild" and render
after there's already plenty of children.
I thought it would be faster if we draw each primitive when needed,
but bitmaps quality drops when resized
誰か最適化してちょうだい!
ContextFreeはprimitiveな円と四角と三角形の組み合わせです
今の実装では、ContextFreeにどんどんaddChildしていくので
大量にaddChildした後にだんだんパフォーマンスが落ちていきます..
必要な時にbitmapdata.drawしていけばよいのでしょうが
リサイズ時に画質劣化が激しいので厳しいのでは,,(今ココ
Get Adobe Flash player
by Albert 21 Jul 2010
/**
 * Copyright Albert ( http://wonderfl.net/user/Albert )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/xEFo
 */

// forked from Albert's Series of Tubes
// forked from mash's Tree - ContextFreeArtAS3
// somebody please optimize this code!

// source code: http://github.com/mash/ContextFreeArt-AS3
// This software is a port of ContextFree.js to Actionscript3.
// Context Free Art is http://www.contextfreeart.org/index.html
// ContextFree.js is http://code.google.com/p/contextfree/

// some hints about implementation for improvement

// ContextFree is a combination of resized primitive shapes:
// Circle, Square, Triangle
// in this implementation, every primitive shapes are 
// "children" of "ContextFreeArt", 
// which makes it slow to "addChild" and render
// after there's already plenty of children.

// I thought it would be faster if we draw each primitive when needed,
// but bitmaps quality drops when resized

// 誰か最適化してちょうだい!

// ContextFreeはprimitiveな円と四角と三角形の組み合わせです
// 今の実装では、ContextFreeにどんどんaddChildしていくので
// 大量にaddChildした後にだんだんパフォーマンスが落ちていきます..

// 必要な時にbitmapdata.drawしていけばよいのでしょうが
// リサイズ時に画質劣化が激しいので厳しいのでは,,(今ココ

package {
    import flash.display.*;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;

    public class Tree extends Sprite{

        public function Tree(){
            var cfdg :String = <><![CDATA[
startshape s2
background{b -.1 sat 1 hue 100}
rule s2{s1{x 1.5 y .75}s1{x -1.5 y -.75}s1{x -.75}s1{x .75}s1{y -.75}s1{y .75}}
rule s1{s{s .05 a -.9}}
rule s{at{}at{r 180}}
rule a .5{al{}}
rule a .5{ar{}}
rule a .5{al2{}}
rule a .5{ar2{}}
rule a .5{al3{}}
rule a .5{ar3{}}
rule a .5{at{}}
rule a{as{}}
rule a{ab{}}
rule a .5{asp{}}
rule a 1.5{ax{}}
rule al{r{}al{x .1 r 1}}
rule ar{r{}ar{x .1 r -1}}
rule al2{r{}al2{x .1 r .5}}
rule ar2{r{}ar2{x .1 r -.5}}
rule al3{r{}al3{x .1 r 1.5}}
rule ar3{r{}ar3{x .1 r -1.5}}
rule at{sign{}as{r 90}}
rule at{sign{}as{r -90}}
rule at{sign{}as{}as{r 90}}
rule at{sign{}as{}as{r -90}}
rule at{sign{}as{r 90}as{r -90}}
rule at{sign{}as{}as{r 90}as{r -90}}
rule ab{r{}SQUARE{s 1.3}ab{x .1}}
rule as{r{}as{x .1 }}
rule ax{CIRCLE{s 3 a .8}CIRCLE{s 2.8 a .1 b 1}}
rule ax{SQUARE{s 2 a .8}SQUARE{s 1.8 a .1 b 1}}
rule ax 3{ak{}}
rule ak{r{}ak{x .1}}
rule ak .005{sign{}ak{}}
rule ak .001{ax{}}
rule asp{sign{}a{}a{}}
rule al .01{a{}}
rule ar .01{a{}}
rule al2 .01{a{}}
rule ar2 .01{a{}}
rule al3 .01{a{}}
rule ar3 .01{a{}}
rule as .01{a{}}
rule ab .05{a{}}
rule r{SQUARE{b 1 s 1.2 a -.5}SQUARE{a .1}SQUARE{b 1 s .1 a 1}}
rule sign{top{x 2}SQUARE{s 1 .2 a .8 y 1 x 1}}
rule top{stop{}}
rule top{warning{}}
rule top{dne{}}
rule top{go{}}
rule stop{CIRCLE{ a .7 y 1 sat 1 b .5}}
rule warning{TRIANGLE{ r -90 a .7 y 1 x -.2 sat 1 hue 50 b .5}}
rule dne{CIRCLE{ a .7 y 1 sat 1 b .5}SQUARE{s .2 .8 y 1 a 1 b 1}}
rule go{SQUARE{ a .7 y 1 sat .4 b .5 hue 100}}
                                    ]]></>;

            start( cfdg );
        }
        private function start( cfdg :String ) :void {
            var art :ContextFreeArt = new ContextFreeArt( cfdg, 465, 465 );
            addChild( art );

            stage.addEventListener( MouseEvent.CLICK, function(ev:MouseEvent) :void {
                art.tick();
            });
        }
    }
}

function log() :void {}

//package jp.maaash {
    import flash.display.Sprite;
    ////////import jp.maaash.contextfreeart.Tokenizer;
    //import jp.maaash.contextfreeart.Compiler;
    //import jp.maaash.contextfreeart.Renderer;

    //public class ContextFreeArt extends Sprite {
class ContextFreeArt extends Sprite {
        private var renderer :Renderer;

        public function ContextFreeArt( cfdg_text :String, width :Number = 640, height :Number = 480 ) {
            var t :Tokenizer = new Tokenizer;
            var tokens :Array = t.tokenize( cfdg_text );

            var c :Compiler = new Compiler;
            var compiled :Object = c.compile( tokens );

            logger("compiled: ",compiled);

            renderer = new Renderer( width, height );
            renderer.clearQueue();
            renderer.render( compiled, this );
        }

        public function tick() :void {
            renderer.tick();
        }

        private function logger(... args) :void {
            if ( 1 ) {
                return; 
            }
            log.apply(null, (new Array("[ContextFreeArt]", this)).concat(args));
        }
    }
//}


//package jp.maaash.contextfreeart {
    //import jp.maaash.contextfreeart.state.*;
    import flash.utils.getDefinitionByName;

    //public class Compiler{
class Compiler{
        private const keywords :Array = [ "startshape", "rule", "background" ];
        public var compiled :Object = {};
        public var state :IState;

        private var curKey :String;
        private var curValues :Array;
        private var obj :Object;

        public function Compiler(){
        }

        public function compile( tokens :Array ) :Object {
            state = new General;

            while ( tokens.length > 0 ) {
                var token :String = tokens.shift();
                var nextState :Array = state.eat( token, this );

                //logger("[compile]token: "+token+" nextState: "+nextState);

                if ( nextState ) {
                    next( nextState );
                }
            }
            return compiled;
        }

        private function next( state_and_args :Array ) :void {
            var className :String = state_and_args.shift();

            // uppercase the 1st char
            className = className.substr(0,1).toUpperCase() + className.substr(1);
            switch( className ) {
            case "Startshape":
                state = new Startshape;
                break;
            case "General":
                state = new General;
                break;
            case "Background":
                state = new Background;
                break;
            case "Rule":
                state = new Rule;
                break;
            case "RuleWeight":
                state = new RuleWeight( state_and_args );
                break;
            case "RuleDraw":
                state = new RuleDraw( state_and_args );
                break;
            case "ShapeAdjustment":
                state = new ShapeAdjustment( state_and_args );
                break;
            }
//            var classObject :Class = getDefinitionByName( className ) as Class;
//
//            if ( state_and_args.length > 0 ) {
//                state = new classObject( state_and_args );
//            }
//            else {
//                state = new classObject;
//            }
        }

        private var stateGeneral :General;
        private var stateStartshape :Startshape;
        private var stateBackground :Background;
        private var stateRule :Rule;
        private var stateRuleWeight :RuleWeight;
        private var stateRuleDraw :RuleDraw;
        private var stateShape :ShapeAdjustment;

        private function logger(... args) :void {
            if ( 1 ) { 
                return; 
            }
            log.apply(null, (new Array("[compiler]", this)).concat(args));
        }
    }
//}


//package jp.maaash.contextfreeart {

    //public class Tokenizer{
class Tokenizer{
        private var input :String;
        private const stopChars :Array = [" ", "{", "}", "\n", "\r", "\t"];

        public function Tokenizer() {
        }

        // TODO: String comments
        // TODO: Handle ordered arguments (i.e., square brakets)
        // TODO: Handle the | operator
        public function tokenize( _input :String ) :Array {
            input = _input;

            // To make it easier to parse, we pad the brackets with spaces.
            input = input.replace( /([{}])/g, " $1");
  
            var tokens :Array = new Array;
  
            var head :Object = { lastPos: 0 };
            while( 1 ) {
                head = tokenizeNext( head.lastPos );
  
                if ( head == null ) { break; }
  
                if ( head.token ) {
                    tokens.push( head.token );
                }
  
            }

            logger("[tokenize]tokens: ",tokens);
  
            return tokens;
        }

        private function tokenizeNext( pos :Number ) :Object {
            var stops :Array = new Array;

            var len :int = stopChars.length;
            for ( var i:int=0; i<len; i++ ) {
                var stopChar :String = stopChars[ i ];
                var foundPos :int    = input.indexOf( stopChar, pos );
                if ( foundPos != -1 ) {
                    stops.push( foundPos + 1 );
                }
            }

            if ( stops.length == 0 ) { return null; }
  
            var stopPos :Number = Math.min.apply( null, stops );
  
            var token :String   = input.substr(pos, stopPos-pos);
  
            // Remove whitespace characters as they can't be
            // tokens. Brackets can be tokens, so those don't
            // get removed.
            token = token.replace( /[ \n\r\t]/, "" );
  
            return { token: token, lastPos: stopPos }
        }

        private function logger(... args) :void {
            if ( 1 ) {
                return; 
            }
            log.apply(null, (new Array("[Tokenizer]", this)).concat(args));
        }
    }
//}


//package jp.maaash.contextfreeart {
    import flash.display.DisplayObjectContainer;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.display.Graphics;
    import flash.events.TimerEvent;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.utils.Timer;

    //public class Renderer{
class Renderer{
        private var width  :Number = 640;
        private var height :Number = 480;
        private var globalScale :Number = 300;

        private var centeringScale  :Number = 1;
        private var centeringMatrix :Matrix = new Matrix;

        private var queue :Array;
        private var compiled :Object;
        private var container :Sprite;
        private var background :Sprite;
        private var isRendering :Boolean = false;
        private var maxThreads :int = 1000;
        private var tickTimer :Timer;

        public function Renderer( _width :Number = 0, _height :Number = 0 ){
            if ( _width  ) { width  = _width;  }
            if ( _height ) { height = _height; }

            logger("w: "+width+" h: "+height);
        }

        public function render( _compiled :Object, _container :DisplayObjectContainer ) :void {
            compiled   = _compiled;
            background = new Sprite;
            _container.addChild( background );
            container  = new Sprite;
            _container.addChild( container );

            if ( ! queue ) { queue = new Array; }

            drawBackground();
            draw();

            tickTimer = new Timer( 30 );
            tickTimer.addEventListener( TimerEvent.TIMER, tick );
            tickTimer.start();
        }

        public function tick( e :TimerEvent = null ) :void {

            //while ( queue.length > 0 ) {
            if ( queue.length > 0 ) {
                isRendering = true;

                var concurrent :int = Math.min( queue.length - 1, maxThreads );

                for ( var i :int=0; i <= concurrent; i++ ) {
                    var args :Array = queue.shift();
                    drawRule.apply( null, args );
                }
                center();
            }

        }

        private function center() :void {

            var rect :Rectangle = container.getRect( container );

            // resize
            centeringScale    = Math.min( width / rect.width, height / rect.height ) * 0.9;
            centeringMatrix.a = centeringMatrix.d = centeringScale;

            // centering
            centeringMatrix.tx         = width /2 - (rect.left + rect.right ) / 2 * centeringScale;
            centeringMatrix.ty         = height/2 - (rect.top  + rect.bottom) / 2 * centeringScale;

            container.transform.matrix = centeringMatrix;

            //logger("[center]mtx,rect,container: ",centeringMatrix,rect,container);
        }

        private function draw() :void {
            var ruleName :String = compiled.startshape;
            var foregroundColor :Color = new Color;

            drawRule( ruleName, new Matrix, foregroundColor );
        }

        private function drawRule( ruleName :String, mtx :Matrix, color :Color, priority :Number = 0 ) :void {
            //logger("[drawRule]ruleName: "+ruleName+" mtx: ",mtx);

            // When things get too small, we can stop rendering.
            // Too small, in this case, means less than a pixel.
            if( Math.abs( mtx.a ) * globalScale * centeringScale < 1 && Math.abs( mtx.b ) * globalScale * centeringScale < 1 ){
                //logger("[drawRule]return");
                return;
            }

            var shape :Object = chooseShape( ruleName );
            drawShape( shape, mtx, color, priority );
        }

        private function chooseShape( ruleName :String ) :Object {
            // Choose which rule to go with...
            //logger("[chooseShape]ruleName: "+ruleName);

            var choices :Array = compiled[ ruleName ];
            if ( ! choices ) { throw("no rule found for "+ruleName); }

            var sum :Number = 0;
            for( var i :int=0; i<choices.length; i++) {
                sum += choices[i].weight;
            }

            var shape :Object;
            var r :Number = Math.random() * sum;
            sum = 0;
            for( i=0; i <= choices.length-1; i++) {
                sum += choices[i].weight;
                if( r <= sum ){
                    shape = choices[i];
                    break;
                }
            }
            if ( ! shape ) { throw("chooseShape failed, rule: "+ruleName+" invalid"); }

            return shape;
        }

        private function drawShape( shape :Object, mtx :Matrix, color :Color, priority :Number = 0 ) :void {

            //logger("[drawShape]shape: ",shape, mtx );

            var len :int = shape.draw.length;
            for ( var i :int = 0; i < len; i++ ) {
                var adj :Adjustment = shape.draw[ i ];

                var localTransform :Matrix = mtx.clone();
                localTransform             = adjustTransform( adj, localTransform );
                var localColor :Color     = adjustColor( adj, color );

                switch( adj.name ){
                    case "CIRCLE":
                        drawCIRCLE( localTransform, localColor );
                        break;
                        
                    case "SQUARE":
                        drawSQUARE( localTransform, localColor );
                        break;
                        
                    case "TRIANGLE":
                        drawTRIANGLE( localTransform, localColor );
                        break;
                        
                    default:
                        var args :Array = [ adj.name, localTransform, localColor ];
                          
                        if( priority == 1 ){ queue.unshift( args ); }
                        else{ queue.push( args ); }
                        
                        break;
                }
            }

        }

        private function drawCIRCLE( transform :Matrix, color :Color ) :void {
            var sh :Shape = new Shape;
            sh.graphics.beginFill.apply( null, colorToRgba(color) );
            sh.graphics.drawCircle( 0, 0, globalScale * 0.5 );
            sh.transform.matrix = transform;

            container.addChild( sh );
        }

        private function drawSQUARE( transform :Matrix, color :Color ) :void {
            var sh :Shape = new Shape;
            sh.graphics.beginFill.apply( null, colorToRgba( color ) );
            sh.graphics.drawRect( - globalScale * 0.5, - globalScale * 0.5, globalScale, globalScale );
            sh.transform.matrix = transform;

            container.addChild( sh );
        }

        private function drawTRIANGLE( transform :Matrix, color :Color ) :void {
            var sh :Shape = new Shape;
            sh.graphics.beginFill.apply( null, colorToRgba( color ) );

            // 1,2,sqrt(3)
            sh.graphics.drawTriangles(
                                      Vector.<Number>([
                                                       -globalScale * 0.5, Math.sqrt(3) * globalScale / 6,
                                                       +globalScale * 0.5, Math.sqrt(3) * globalScale / 6,
                                                       0,                  - Math.sqrt(3) * globalScale / 3]),
                                      Vector.<int>([0,1,2])
                                      );
            sh.transform.matrix = transform;

            container.addChild( sh );
        }

        private function drawBackground() :void {
            if ( compiled.background ) {
                var colorAdj :Adjustment = compiled.background;
                var backgroundColor :Color = new Color;
                backgroundColor.b = 1; // { h:0, s:0, b:1, a:1 };

                var color :Color = adjustColor( colorAdj, backgroundColor );
                var color_alpha :Array = colorToRgba( color );

                logger("[drawBackground]color: ",color, backgroundColor,color_alpha);

                var bg :Shape = new Shape;
                bg.graphics.beginFill( color_alpha[0], color_alpha[1] );
                bg.graphics.drawRect( 0, 0, width, height );
                background.addChild( bg );
            }
        }

        // order: move rotate scale
        private function adjustTransform( adjs :Adjustment, base :Matrix ) :Matrix {

            //logger("[adjustTransform][0]adjs: ",adjs," base: ",base);

            var mtx :Matrix = new Matrix;

            // Flip around a line through the origin;
            if ( adjs.flipDefined ){
                var flip :Number = adjs.flip;
                // Flip 0 means to flip along the X axis. Flip 90 means to flip along the Y axis.
                // That's why the flip vector (vX, vY) is Pi/2 radians further along than expected. 
                var vX :Number   = Math.cos( -2*Math.PI * flip / 360 );
                var vY :Number   = Math.sin( -2*Math.PI * flip / 360 );
                var norm :Number = 1/(vX*vX + vY*vY);
                //var flip :Matrix = new Matrix((vX*vX-vY*vY)/norm, 2*vX*vY/norm, 2*vX*vY/norm, (vY*vY-vX*vX)/norm, 0, 0);
                mtx.a = (vX*vX-vY*vY)/norm;
                mtx.b = 2*vX*vY/norm;
                mtx.c = 2*vX*vY/norm;
                mtx.d = (vY*vY-vX*vX)/norm;
            }

            // Scaling
            var sizeX :Number = adjs.sizeX;
            var sizeY :Number = adjs.sizeY;
            if ( sizeX || sizeY ) {
                mtx.scale( sizeX, sizeY );
            }

            // Rotation
            var r :Number = adjs.rotate;
            if ( r != 0 ) {
                mtx.rotate( - Math.PI * r / 180 );
            }

            // Tranalsation
            var x :Number =  adjs.x;
            var y :Number = -adjs.y;
            if ( x != 0 || y != 0 ) {
                var point :Point = new Point( x * globalScale, y * globalScale );
                mtx.translate( point.x, point.y );
            }

            mtx.concat( base );

            //logger("[adjustTransform][9]mtx: ",mtx);
            
            return mtx;
        }

        private function colorToRgba( color :Color ) :Array {
            return hsl2rgb( color.h, color.s, color.b, color.a );
        }

        // hue, saturation, brightness, alpha
        // hue: [0,360) default 0
        // saturation: [0,1] default 0
        // brightness: [0,1] default 1
        // alpha: [0,1] default 1
        private function hsl2rgb(h :Number, s :Number, l :Number, a :Number) :Array {

            if (h == 360){ h = 0;}

            //
            // based on C code from http://astronomy.swin.edu.au/~pbourke/colour/hsl/
            //

            while (h < 0){ h += 360; }
            while (h > 360){ h -= 360; }
            var r :Number, g :Number, b :Number;
            if (h < 120){
                r = (120 - h) / 60;
                g = h / 60;
                b = 0;
            }else if (h < 240){
                r = 0;
                g = (240 - h) / 60;
                b = (h - 120) / 60;
            }else{
                r = (h - 240) / 60;
                g = 0;
                b = (360 - h) / 60;
            }

            r = Math.min(r, 1);
            g = Math.min(g, 1);
            b = Math.min(b, 1);

            r = 2 * s * r + (1 - s);
            g = 2 * s * g + (1 - s);
            b = 2 * s * b + (1 - s);

            if (l < 0.5){
                r = l * r;
                g = l * g;
                b = l * b;
            }else{
                r = (1 - l) * r + 2 * l - 1;
                g = (1 - l) * g + 2 * l - 1;
                b = (1 - l) * b + 2 * l - 1;
            }

            r = Math.ceil(r * 255);
            g = Math.ceil(g * 255);
            b = Math.ceil(b * 255);

            // Putting a semicolon at the end of an rgba definition
            // causes it to not work.
            //return "rgba(" + r + ", " + g + ", " + b + ", " + a + ")";

            // <uint>,<Number> to do: graphics.beginFill.apply( null, color.split(',') )
            return [ (r * 256*256 + g * 256 + b), a ];
        }

        // hsba to hsba
        private function adjustColor( adjs :Adjustment, color :Color ) :Color {
            // See http://www.contextfreeart.org/mediawiki/index.php/Shape_adjustments
            var newColor :Color = new Color;
            newColor.h = color.h;
            newColor.s = color.s;
            newColor.b = color.b;
            newColor.a = color.a;

            // Add num to the drawing hue value, modulo 360 
            newColor.h += adjs.hue;
            newColor.h %= 360;

            // If adj<0 then change the drawing [blah] adj% toward 0.
            // If adj>0 then change the drawing [blah] adj% toward 1. 
            if ( adjs.saturation != 0 ) {
                if( adjs.saturation > 0 ){
                    newColor.s += adjs.saturation * (1-color.s);
                } else {
                    newColor.s += adjs.saturation * color.s;
                }
            }
            if ( adjs.brightness != 0 ) {
                if( adjs.brightness > 0 ){
                    newColor.b += adjs.brightness * (1-color.b);
                } else {
                    newColor.b += adjs.brightness * color.b;
                }
            }
            if ( adjs.alpha != 0 ) {
                if( adjs.alpha > 0 ){
                    newColor.a += adjs.alpha * (1-color.a);
                } else {
                    newColor.a += adjs.alpha * color.a;
                }
            }
            
            return newColor;
        }

        public function clearQueue() :void {
            queue = new Array;
        }

        private function logger(... args) :void {
            if ( 1 ) {
                return; 
            }
            log.apply(null, (new Array("[Renderer]", this)).concat(args));
        }
    }
//}


//package jp.maaash.contextfreeart {

    //public class Color {
class Color {
        public var h :Number = 0;
        public var s :Number = 0;
        public var b :Number = 0;
        public var a :Number = 1;

        public function Color(){
        }
    }
//}


//package jp.maaash.contextfreeart {

    //public class Adjustment{
class Adjustment{
        public var name :String;
        public var flipDefined :Boolean = false;
        public var flip :Number;
        public var sizeX :Number = 1;
        public var sizeY :Number = 1;
        public var rotate :Number = 0;
        public var x :Number = 0;
        public var y :Number = 0;
        public var hue :Number = 0;
        public var saturation :Number = 0;
        public var brightness :Number = 0;
        public var alpha :Number = 0;

        public function Adjustment() {
        }
        public function fill( obj :Object ) :void {
            for( var key :String in obj ){
                switch( key ) {
                    case "f":
                    case "flip":
                        flipDefined = true;
                        flip = obj[key];
                        break;
                    case "s":
                    case "size":
                        var size :* = obj[key];
                        if ( typeof(size) == "number" ) { size = [size,size]; }
                        sizeX = size[0];
                        sizeY = size[1];
                        break;
                    case "r":
                        rotate     = obj[key];
                        break;
                    case "h":
                        hue        = obj[key];
                        break;
                    case "sat":
                        saturation = obj[key];
                        break;
                    case "b":
                        brightness = obj[key];
                        break;
                    case "a":
                        alpha      = obj[key];
                        break;
                    case "rotate":
                    case "x":
                    case "y":
                    case "hue":
                    case "saturation":
                    case "brightness":
                    case "alpha":
                        this[key] = obj[key];
                        break;
                    default:
                        throw("unsupported adjustment: "+key);
                }
            }
            
        }

    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public class Rule implements IState {
class Rule implements IState {

        public function Rule(){

        }
        public function eat( token :String, compiler :Compiler ) :Array {
            var ruleName :String = token;

            // Create a blank rule if it doesn't aleady exist
            if ( ! compiler.compiled[ ruleName ] ) {
                compiler.compiled[ ruleName ] = [];
            }
            return [ "ruleWeight", ruleName ];
        }
    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public class RuleDraw implements IState {
class RuleDraw implements IState {
        private var weight :Number = 1;
        private var ruleName :String;

        public function RuleDraw( args :Array ) {
            ruleName = args[0];
        }
        public function eat( token :String, compiler :Compiler ) :Array {
            if( token == "}" ){
                return [ "general" ];
            }
        
            return [ "shapeAdjustment", token, ruleName ];
        }
    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public class Startshape implements IState {
class Startshape implements IState {

        public function Startshape() {

        }
        public function eat( token :String, compiler :Compiler ) :Array {
            // uppercase the 1st char
            compiler.compiled[ "startshape" ] = token;
            return [ "general" ];
        }
    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public class AbstractArgument implements IState {
class AbstractArgument implements IState {
        protected var curKey :String = null;
        protected var curValues :Array = [];
        protected var obj :Object = {};
        protected var compiler :Compiler;

        public function AbstractArgument(){

        }
        public function eat( token :String, _compiler :Compiler ) :Array {
            compiler = _compiler;

            switch ( token ) {
                case "}":
                    flushKey();
                    return onDone( obj );
                case "{":
                    return null;
            }

            // If it's a keyword name...
            if( token.match(/[a-z_]+/i) ) {
                flushKey();
                curKey = token;
                curValues = [];
            }
            // Otherwise it's a value (and hence a number)
            else {
                curValues.push( parseFloat(token) );
            }

            return null;
        }

        protected function onDone( obj :Object ) :Array { return null; } // abstract

        protected function flushKey() :void {
            if ( curKey ) {
                // If there is only one value for the key, we don't need to wrap
                // it in an array.
                if ( curValues.length == 1 ) {
                    obj[ curKey ] = curValues[0];
                }
                else {
                    obj[ curKey ] = curValues;
                }
            }
        }

    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;
    //import jp.maaash.contextfreeart.Adjustment;

    //public class Background extends AbstractArgument {
class Background extends AbstractArgument {

        public function Background() {
        }

        override protected function onDone( obj :Object ) :Array {
            var adj :Adjustment = new Adjustment;
            adj.fill( obj );
            compiler.compiled[ "background" ] = adj;
            compiler = null;
            return [ "general" ];
        }
    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;
    //import jp.maaash.contextfreeart.Adjustment;

    //public class ShapeAdjustment extends AbstractArgument {
class ShapeAdjustment extends AbstractArgument {
        private var name :String;
        private var ruleName :String;

        public function ShapeAdjustment( args :Array ) {
            name     = args[0];
            ruleName = args[1];
        }

        override protected function onDone( obj :Object ) :Array {
            trace(this + ".onDone(obj :Object ) : " + obj );
            var shape :Adjustment = new Adjustment();
            shape.name = name;
            shape.fill( obj );
            
            // We are always adding to the lastest rule we've created.
            var last :int = compiler.compiled[ ruleName ].length - 1;
            compiler.compiled[ ruleName ][ last ].draw.push( shape )

            compiler = null;
            return [ "ruleDraw", ruleName ];
        }
    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public interface IState {
interface IState {

        function eat( token :String, compiler :Compiler ) :Array;
    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public class RuleWeight implements IState {
class RuleWeight implements IState {
        private var weight :Number = 1;
        private var ruleName :String;

        public function RuleWeight( args :Array ) {
            ruleName = args[0];
        }
        public function eat( token :String, compiler :Compiler ) :Array {
            if ( token != "{" ) {
                weight = parseFloat( token );
                return null;
            }
            else {
                // "{"
                compiler.compiled[ ruleName ].push({ weight: weight, draw: [] });
                return [ "ruleDraw", ruleName ];
            }
        }
    }
//}


//package jp.maaash.contextfreeart.state {
    //import jp.maaash.contextfreeart.Compiler;

    //public class General implements IState {
class General implements IState {

        public function General(){

        }
        public function eat( token :String, compiler :Compiler ) :Array {
            return [ token ];
        }
    }
//}