    package flare.query
    {
        import flare.util.IEvaluable;
        import flare.util.IPredicate;

        /**
         * Base class for query expression operators. Expressions are organized
         * into a tree of operators that perform data processing or predicate
         * testing on input <code>Object</code> instances.
         */
        public class Expression implements IEvaluable, IPredicate
        {
            /**
             * Evaluates this expression with the given input object.
             * @param o the input object to this expression
             * @return the result of evaluating the expression
             */
            public function eval(o:Object=null):*
            {
                return o;
            }

            /**
             * Boolean predicate that tests the output of evaluating this
             * expression. Returns true if the expression evaluates to true, or
             * a non-null or non-zero value. Returns false if the expression
             * evaluates to false, or a null or zero value.
             * @param o the input object to this expression
             * @return the Boolean result of evaluating the expression
             */
            public function predicate(o:Object):Boolean
            {
                return Boolean(eval(o));
            }

            /**
             * The number of sub-expressions that are children of this expression.
             * @return the number of child expressions.
             */
            public function get numChildren():int
            {
                return 0;
            }

            /**
             * Returns the sub-expression at the given index.
             * @param idx the index of the child sub-expression
             * @return the requested sub-expression.
             */
            public function getChildAt(idx:int):Expression
            {
                return null;
            }

            /**
             * Set the sub-expression at the given index.
             * @param idx the index of the child sub-expression
             * @param expr the sub-expression to set
             * @return true if the the sub-expression was successfully set,
             *  false otherwise
             */
            public function setChildAt(idx:int, expr:Expression):Boolean
            {
                return false;
            }

            /**
             * Returns a string representation of the expression.
             * @return this expression as a string value
             */
            public function toString():String
            {
                return null;
            }

            /**
             * Creates a cloned copy of the expression. Recursively clones any
             * sub-expressions.
             * @return the cloned expression.
             */
            public function clone():Expression
            {
                throw new Error("This is an abstract method");
            }

            /**
             * Sequentially invokes the input function on this expression and all
             * sub-expressions. Complete either when all expressions have been
             * visited or the input function returns true, thereby signalling an
             * early exit.
             * @param f the visiting function to invoke on this expression
             * @return true if the input function signalled an early exit
             */
            public function visit(f:Function):Boolean
            {
                var iter:ExpressionIterator = new ExpressionIterator(this);
                return visitHelper(iter, f);
            }

            private function visitHelper(iter:ExpressionIterator, f:Function):Boolean
            {
                if (f(iter.current) as Boolean) return true;
                if (iter.down() != null)
                {
                    do {
                        if (visitHelper(iter, f)) return true;
                    } while (iter.next() != null);
                    iter.up();
                }
                return false;
            }

            // --------------------------------------------------------------------

            private static const _LBRACE:Number = "{".charCodeAt(0);
            private static const _RBRACE:Number = "}".charCodeAt(0);
            private static const _SQUOTE:Number = "'".charCodeAt(0);
            private static const _DQUOTE:Number = "\"".charCodeAt(0);

            /**
             * Utility method that maps an input value into an Expression. If the
             * input value is already an Expression, it is simply returned. If the
             * input value is a String, it is interpreted as either a variable or
             * string literal. If the first and last characters of the string are
             * single quotes (') or double quotes ("), the characters between the
             * quotes will be used as a string Literal. If there are no quotes, or
             * if the string is enclosed by curly braces ({}), the string value
             * (sans braces) will be used as the property name of a new Variable.
             * In all other cases (Numbers, Dates, etc), a new Literal expression
             * for the input value is returned.
             * @param o the input value
             * @return an Expression corresponding to the input value
             */
            public static function expr(o:*):Expression
            {
                if (o is Expression) {
                    return o as Expression;
                } else if (o is Class) {
                    return new IsA(Class(o));
                } else if (o is String) {
                    var s:String = o as String;
                    var c1:Number = s.charCodeAt(0);
                    var c2:Number = s.charCodeAt(s.length-1);

                    if (c1 == _LBRACE && c2 == _RBRACE) { // braces -> variable
                        return new Variable(s.substr(1, s.length-2));
                    } else if (c1 == _SQUOTE && c2 == _SQUOTE) { // quote -> string
                        return new Literal(s.substr(1, s.length-2));
                    } else if (c1 == _DQUOTE && c2 == _DQUOTE) { // quote -> string
                        return new Literal(s.substr(1, s.length-2));
                    } else { // default -> variable
                        return new Variable(s);
                    }
                } else {
                    return new Literal(o);
                }
            }

        } // end of class Expression
    }