private void Generate(DekiScriptGenerator expr, DekiScriptGeneratorEvaluationState state)
        {
            DekiScriptEnv env = state.Env;

            // check if __count variable is defined
            DekiScriptLiteral count;

            if (!env.Locals.TryGetValue(DekiScriptRuntime.COUNT_ID, out count) || !(count is DekiScriptNumber))
            {
                count = DekiScriptNumber.New(0);
                env.Locals.Add(DekiScriptRuntime.COUNT_ID, count);
            }

            // check if there is a chained generator
            if (expr.Next != null)
            {
                expr.Next.VisitWith(this, state);
            }
            else
            {
                // call delegate
                state.Callback(env.NewLocalScope());

                // increase __count variable
                env.Locals.Add(DekiScriptRuntime.COUNT_ID, DekiScriptNumber.New(((DekiScriptNumber)count).Value + 1));
            }
        }
        public Empty Visit(DekiScriptGeneratorForeachValue expr, DekiScriptGeneratorEvaluationState state)
        {
            DekiScriptLiteral collection = expr.Collection.VisitWith(DekiScriptExpressionEvaluation.Instance, state.Env);

            // retrieve collection
            List <DekiScriptLiteral> list;

            if (collection is DekiScriptList)
            {
                // loop over list values
                list = ((DekiScriptList)collection).Value;
            }
            else if (collection is DekiScriptMap)
            {
                // loop over map key-value pairs
                list = new List <DekiScriptLiteral>(((DekiScriptMap)collection).Value.Values);
            }
            else if (collection is DekiScriptXml)
            {
                // loop over xml selection
                List <XDoc> selection = ((DekiScriptXml)collection).Value.ToList();
                list = new List <DekiScriptLiteral>(selection.Count);
                foreach (XDoc doc in selection)
                {
                    list.Add(new DekiScriptXml(doc));
                }
            }
            else if (collection is DekiScriptNil)
            {
                // nothing to do
                list = new List <DekiScriptLiteral>();
            }
            else
            {
                throw new DekiScriptBadTypeException(expr.Line, expr.Column, collection.ScriptType, new DekiScriptType[] { DekiScriptType.LIST, DekiScriptType.MAP, DekiScriptType.XML, DekiScriptType.NIL });
            }

            // loop over collection
            int index = 0;

            for (int i = 0; i <= (list.Count - expr.Vars.Length); i += expr.Vars.Length)
            {
                // set the environment variable
                for (int j = 0; j < expr.Vars.Length; ++j)
                {
                    state.Env.Locals.Add(expr.Vars[j], list[i + j]);
                }
                state.Env.Locals.Add(DekiScriptRuntime.INDEX_ID, DekiScriptNumber.New(index));

                // iterate over block statements
                Generate(expr, state);
                index += expr.Vars.Length;
            }
            return(Empty.Value);
        }
        public DekiScriptLiteral Visit(DekiScriptUnary expr, DekiScriptEnv env)
        {
            switch (expr.OpCode)
            {
            case DekiScriptUnary.Op.Negate:
                return(DekiScriptNumber.New(-expr.Value.VisitWith(this, env).AsNumber()));

            case DekiScriptUnary.Op.LogicalNot:
                return(DekiScriptBool.New(expr.Value.VisitWith(this, env).IsNilFalseZero));

            case DekiScriptUnary.Op.TypeOf:
                return(DekiScriptString.New(expr.Value.VisitWith(this, env).ScriptTypeName));

            case DekiScriptUnary.Op.Length: {
                DekiScriptLiteral value = expr.Value.VisitWith(this, env);
                switch (value.ScriptType)
                {
                case DekiScriptType.NIL:
                    return(DekiScriptNumber.New(0));

                case DekiScriptType.LIST:
                    return(DekiScriptNumber.New(((DekiScriptList)value).Value.Count));

                case DekiScriptType.STR:
                    return(DekiScriptNumber.New(((DekiScriptString)value).Value.Length));

                case DekiScriptType.MAP:
                    return(DekiScriptNumber.New(((DekiScriptMap)value).Value.Count));

                case DekiScriptType.XML:
                    return(DekiScriptNumber.New(((DekiScriptXml)value).Value.ListLength));

                default:
                    return(DekiScriptNil.Value);
                }
            }
            }
            throw new InvalidOperationException("invalid op code:" + expr.OpCode);
        }
        public Empty Visit(DekiScriptGeneratorForeachKeyValue expr, DekiScriptGeneratorEvaluationState state)
        {
            DekiScriptLiteral collection = expr.Collection.VisitWith(DekiScriptExpressionEvaluation.Instance, state.Env);

            // retrieve collection
            Dictionary <string, DekiScriptLiteral> map;

            if (collection is DekiScriptMap)
            {
                // loop over map key-value pairs
                map = ((DekiScriptMap)collection).Value;
            }
            else if (collection is DekiScriptNil)
            {
                // nothing to do
                map = new Dictionary <string, DekiScriptLiteral>();
            }
            else
            {
                throw new DekiScriptBadTypeException(expr.Line, expr.Column, collection.ScriptType, new DekiScriptType[] { DekiScriptType.MAP, DekiScriptType.NIL });
            }

            // loop over collection
            int index = 0;

            foreach (KeyValuePair <string, DekiScriptLiteral> entry in map)
            {
                // set the environment variable
                state.Env.Locals.Add(expr.Key, DekiScriptString.New(entry.Key));
                state.Env.Locals.Add(expr.Value, entry.Value);
                state.Env.Locals.Add(DekiScriptRuntime.INDEX_ID, DekiScriptNumber.New(index));

                // iterate over block statements
                Generate(expr, state);
                ++index;
            }
            return(Empty.Value);
        }
 public DekiScriptExpression Visit(DekiScriptNumber expr, DekiScriptExpressionEvaluationState state)
 {
     return(expr);
 }
 public DekiScriptOutputBuffer.Range Visit(DekiScriptNumber expr, DekiScriptExpressionEvaluationState state)
 {
     return(state.Push(expr));
 }
 public DekiScriptExpression Visit(DekiScriptNumber expr, DekiScriptOptimizerState state)
 {
     return(expr);
 }
        internal DekiScriptLiteral Evaluate(DekiScriptAccess expr, DekiScriptEnv env, bool evaluateProperties)
        {
            DekiScriptLiteral prefix = expr.Prefix.VisitWith(this, env);
            DekiScriptLiteral index  = expr.Index.VisitWith(this, env);

            switch (prefix.ScriptType)
            {
            case DekiScriptType.MAP: {
                DekiScriptLiteral result = ((DekiScriptMap)prefix)[index];
                if (evaluateProperties)
                {
                    result = DekiScriptRuntime.EvaluateProperty(result, env);
                }
                return(result);
            }

            case DekiScriptType.LIST: {
                DekiScriptLiteral value  = DekiScriptNumber.New(index.AsNumber());
                DekiScriptLiteral result = ((DekiScriptList)prefix)[value];
                if (evaluateProperties)
                {
                    result = DekiScriptRuntime.EvaluateProperty(result, env);
                }
                return(result);
            }

            case DekiScriptType.URI: {
                DekiScriptUri uri = (DekiScriptUri)prefix;

                // coerce the index type to STR
                index = DekiScriptString.New(index.AsString());
                if (index.ScriptType != DekiScriptType.STR)
                {
                    throw new DekiScriptBadTypeException(expr.Line, expr.Column, index.ScriptType, new[] { DekiScriptType.STR });
                }

                // curry the argument
                DekiScriptList args;
                if (!uri.Arguments.IsNil)
                {
                    // the uri already has curried parameters, make sure they are in LIST format; otherwise fail
                    if (uri.Arguments.ScriptType != DekiScriptType.LIST)
                    {
                        throw new DekiScriptBadTypeException(expr.Line, expr.Column, uri.Arguments.ScriptType, new[] { DekiScriptType.NIL, DekiScriptType.LIST });
                    }
                    args = new DekiScriptList((DekiScriptList)uri.Arguments);
                }
                else
                {
                    args = new DekiScriptList();
                }
                args.Add(index);
                return(new DekiScriptUri(uri.Value, args));
            }

            case DekiScriptType.STR: {
                DekiScriptString text = (DekiScriptString)prefix;

                // coerce the index type to NUM
                double?value = index.AsNumber();
                if (value == null)
                {
                    throw new DekiScriptBadTypeException(expr.Line, expr.Column, index.ScriptType, new[] { DekiScriptType.NUM });
                }

                // retrieve character at given index position
                int position = (int)value;
                if ((position < 0) || (position >= text.Value.Length))
                {
                    // index is out of bounds, return nil
                    return(DekiScriptNil.Value);
                }
                return(DekiScriptString.New(text.Value[position].ToString()));
            }

            case DekiScriptType.XML: {
                string path = index.AsString();
                if (path == null)
                {
                    throw new DekiScriptBadTypeException(expr.Line, expr.Column, index.ScriptType, new[] { DekiScriptType.STR });
                }
                XDoc doc = ((DekiScriptXml)prefix).Value[path];
                if (doc.HasName("html"))
                {
                    doc = DekiScriptLibrary.CleanseHtmlDocument(doc);
                }
                return(new DekiScriptXml(doc));
            }

            case DekiScriptType.NIL:
                return(DekiScriptNil.Value);
            }
            throw new DekiScriptBadTypeException(expr.Line, expr.Column, prefix.ScriptType, new[] { DekiScriptType.MAP, DekiScriptType.LIST, DekiScriptType.XML, DekiScriptType.STR, DekiScriptType.URI });
        }
        public DekiScriptLiteral Visit(DekiScriptBinary expr, DekiScriptEnv env)
        {
            DekiScriptExpression Left  = expr.Left;
            DekiScriptExpression Right = expr.Right;

            switch (expr.OpCode)
            {
            case DekiScriptBinary.Op.LeftValue:
                return(Left.VisitWith(this, env));

            case DekiScriptBinary.Op.IdentityEqual: {
                DekiScriptLiteral left  = Left.VisitWith(this, env);
                DekiScriptLiteral right = Right.VisitWith(this, env);
                return(DekiScriptBinary.IdentityEqual(left, right));
            }

            case DekiScriptBinary.Op.IdentityNotEqual: {
                DekiScriptLiteral left  = Left.VisitWith(this, env);
                DekiScriptLiteral right = Right.VisitWith(this, env);
                return(DekiScriptBinary.IdentityNotEqual(left, right));
            }

            case DekiScriptBinary.Op.IsType: {
                DekiScriptLiteral left = Left.VisitWith(this, env);
                string            type = ((DekiScriptString)Right).Value;
                return(DekiScriptBool.New(StringUtil.EqualsInvariantIgnoreCase(type, "any") || StringUtil.EqualsInvariantIgnoreCase(left.ScriptTypeName, type)));
            }

            case DekiScriptBinary.Op.Equal: {
                DekiScriptLiteral left  = Left.VisitWith(this, env);
                DekiScriptLiteral right = Right.VisitWith(this, env);
                return(DekiScriptBinary.Equal(left, right));
            }

            case DekiScriptBinary.Op.NotEqual: {
                DekiScriptLiteral left  = Left.VisitWith(this, env);
                DekiScriptLiteral right = Right.VisitWith(this, env);
                return(DekiScriptBinary.NotEqual(left, right));
            }

            case DekiScriptBinary.Op.GreaterOrEqual: {
                DekiScriptLiteral left  = Left.VisitWith(this, env);
                DekiScriptLiteral right = Right.VisitWith(this, env);
                if (DekiScriptLiteral.CoerceValuesToSameType(ref left, ref right))
                {
                    switch (left.ScriptType)
                    {
                    case DekiScriptType.BOOL:
                    case DekiScriptType.NUM:
                        return(DekiScriptBool.New(left.AsNumber() >= right.AsNumber()));

                    case DekiScriptType.STR:
                        return(DekiScriptBool.New(StringUtil.CompareInvariant(left.AsString(), right.AsString()) >= 0));

                    default:
                        return(DekiScriptNil.Value);
                    }
                }
                else
                {
                    return(DekiScriptNil.Value);
                }
            }

            case DekiScriptBinary.Op.GreaterThan: {
                DekiScriptLiteral left  = Left.VisitWith(this, env);
                DekiScriptLiteral right = Right.VisitWith(this, env);
                if (DekiScriptLiteral.CoerceValuesToSameType(ref left, ref right))
                {
                    switch (left.ScriptType)
                    {
                    case DekiScriptType.BOOL:
                    case DekiScriptType.NUM:
                        return(DekiScriptBool.New(left.AsNumber() > right.AsNumber()));

                    case DekiScriptType.STR:
                        return(DekiScriptBool.New(StringUtil.CompareInvariant(left.AsString(), right.AsString()) > 0));

                    default:
                        return(DekiScriptNil.Value);
                    }
                }
                else
                {
                    return(DekiScriptNil.Value);
                }
            }

            case DekiScriptBinary.Op.LessOrEqual: {
                DekiScriptLiteral left  = Left.VisitWith(this, env);
                DekiScriptLiteral right = Right.VisitWith(this, env);
                if (DekiScriptLiteral.CoerceValuesToSameType(ref left, ref right))
                {
                    switch (left.ScriptType)
                    {
                    case DekiScriptType.BOOL:
                    case DekiScriptType.NUM:
                        return(DekiScriptBool.New(left.AsNumber() <= right.AsNumber()));

                    case DekiScriptType.STR:
                        return(DekiScriptBool.New(StringUtil.CompareInvariant(left.AsString(), right.AsString()) <= 0));

                    default:
                        return(DekiScriptNil.Value);
                    }
                }
                else
                {
                    return(DekiScriptNil.Value);
                }
            }

            case DekiScriptBinary.Op.LessThan: {
                DekiScriptLiteral left  = Left.VisitWith(this, env);
                DekiScriptLiteral right = Right.VisitWith(this, env);
                if (DekiScriptLiteral.CoerceValuesToSameType(ref left, ref right))
                {
                    switch (left.ScriptType)
                    {
                    case DekiScriptType.BOOL:
                    case DekiScriptType.NUM:
                        return(DekiScriptBool.New(left.AsNumber() < right.AsNumber()));

                    case DekiScriptType.STR:
                        return(DekiScriptBool.New(StringUtil.CompareInvariant(left.AsString(), right.AsString()) < 0));

                    default:
                        return(DekiScriptNil.Value);
                    }
                }
                else
                {
                    return(DekiScriptNil.Value);
                }
            }

            case DekiScriptBinary.Op.LogicalAnd: {
                DekiScriptLiteral result = Left.VisitWith(this, env);
                if (!result.IsNilFalseZero)
                {
                    result = Right.VisitWith(this, env);
                }
                return(result);
            }

            case DekiScriptBinary.Op.LogicalOr: {
                DekiScriptLiteral result = Left.VisitWith(this, env);
                if (result.IsNilFalseZero)
                {
                    result = Right.VisitWith(this, env);
                }
                return(result);
            }

            case DekiScriptBinary.Op.Addition:
                return(DekiScriptNumber.New(Left.VisitWith(this, env).AsNumber() + Right.VisitWith(this, env).AsNumber()));

            case DekiScriptBinary.Op.Division:
                return(DekiScriptNumber.New(Left.VisitWith(this, env).AsNumber() / Right.VisitWith(this, env).AsNumber()));

            case DekiScriptBinary.Op.Modulo: {
                DekiScriptLiteral left  = Left.VisitWith(this, env);
                DekiScriptLiteral right = Right.VisitWith(this, env);
                if ((left is DekiScriptString) && ((right is DekiScriptMap) || (right is DekiScriptList)))
                {
                    // NOTE (steveb): string.format shorthand notation: "abc = $abc" % { abc: 123 } -OR- "0 = $0" % [ 123 ]
                    return(DekiScriptLiteral.FromNativeValue(DekiScriptLibrary.StringFormat(((DekiScriptString)left).Value, right.NativeValue)));
                }
                else
                {
                    return(DekiScriptNumber.New(left.AsNumber() % right.AsNumber()));
                }
            }

            case DekiScriptBinary.Op.Multiplication:
                return(DekiScriptNumber.New(Left.VisitWith(this, env).AsNumber() * Right.VisitWith(this, env).AsNumber()));

            case DekiScriptBinary.Op.Subtraction:
                return(DekiScriptNumber.New(Left.VisitWith(this, env).AsNumber() - Right.VisitWith(this, env).AsNumber()));

            case DekiScriptBinary.Op.NullCoalesce: {
                DekiScriptLiteral result = Left.VisitWith(this, env);
                if (result.IsNil)
                {
                    result = Right.VisitWith(this, env);
                }
                return(result);
            }

            case DekiScriptBinary.Op.Concat: {
                DekiScriptLiteral left  = Left.VisitWith(this, env);
                DekiScriptLiteral right = Right.VisitWith(this, env);
                if (left is DekiScriptNil)
                {
                    return(right);
                }
                else if (right is DekiScriptNil)
                {
                    return(left);
                }
                else if ((left is DekiScriptMap) && (right is DekiScriptMap))
                {
                    // left and right expressions are maps, merge them
                    DekiScriptMap result = new DekiScriptMap();
                    result.AddRange((DekiScriptMap)left);
                    result.AddRange((DekiScriptMap)right);
                    return(result);
                }
                else if ((left is DekiScriptList) && (right is DekiScriptList))
                {
                    // left and right expressions are lists, concatenate them
                    DekiScriptList result = new DekiScriptList();
                    result.AddRange((DekiScriptList)left);
                    result.AddRange((DekiScriptList)right);
                    return(result);
                }
                else
                {
                    // treat left and right expressions as strings
                    string leftText  = left.AsString();
                    string rightText = right.AsString();
                    if ((leftText != null) && (rightText != null))
                    {
                        return(DekiScriptString.New(leftText + rightText));
                    }
                    else if (leftText != null)
                    {
                        return(DekiScriptString.New(leftText));
                    }
                    else if (rightText != null)
                    {
                        return(DekiScriptString.New(rightText));
                    }
                    else
                    {
                        return(DekiScriptNil.Value);
                    }
                }
            }

            case DekiScriptBinary.Op.UriAppend: {
                // TODO (steveb): we should throw an exception when the LHS is not a valid string or uri

                XUri   left   = XUri.TryParse(Left.VisitWith(this, env).AsString());
                string result = null;
                if (left != null)
                {
                    DekiScriptLiteral right = Right.VisitWith(this, env);
                    if (right is DekiScriptString)
                    {
                        result = DekiScriptLibrary.UriBuild(left, right.AsString(), null);
                    }
                    else if (right is DekiScriptMap)
                    {
                        result = DekiScriptLibrary.UriBuild(left, null, (Hashtable)right.NativeValue);
                    }
                    else
                    {
                        result = left.ToString();
                    }
                }
                return(DekiScriptLiteral.FromNativeValue(result));
            }

            case DekiScriptBinary.Op.InCollection: {
                DekiScriptLiteral left  = Left.VisitWith(this, env);
                DekiScriptLiteral right = Right.VisitWith(this, env);
                if (right is DekiScriptList)
                {
                    foreach (DekiScriptLiteral item in ((DekiScriptList)right).Value)
                    {
                        if (!DekiScriptBinary.Equal(left, item).IsNilFalseZero)
                        {
                            return(DekiScriptBool.True);
                        }
                    }
                    return(DekiScriptBool.False);
                }
                else if (right is DekiScriptMap)
                {
                    foreach (DekiScriptLiteral item in ((DekiScriptMap)right).Value.Values)
                    {
                        if (!DekiScriptBinary.Equal(left, item).IsNilFalseZero)
                        {
                            return(DekiScriptBool.True);
                        }
                    }
                    return(DekiScriptBool.False);
                }
                else
                {
                    return(DekiScriptBool.False);
                }
            }
            }
            throw new InvalidOperationException("invalid op code:" + expr.OpCode);
        }
 public DekiScriptLiteral Visit(DekiScriptNumber expr, DekiScriptEnv env)
 {
     return(expr);
 }