public Empty Visit(DekiScriptDomComment expr, DekiScriptDomEvaluationState state)
        {
            var context = state.Context;
            var env     = state.Env;
            var parent  = state.Parent;

            try {
                DekiScriptLiteral value = expr.Value.VisitWith(DekiScriptExpressionEvaluation.Instance, env);
                if (!value.IsNil)
                {
                    XmlNode result = context.CreateComment(value.AsString());
                    parent.AppendChild(result);
                }
            } catch (Exception e) {
                EmbedExceptionMessage(expr, env, context, e, parent);
            }
            return(Empty.Value);
        }
 private void EvaluateNamespaceDefinitionAttribute(DekiScriptDomElement.Attribute expr, DekiScriptEvalContext context, DekiScriptEnv env)
 {
     if (expr.IsNamespaceDefinition)
     {
         DekiScriptLiteral name  = expr.Name.VisitWith(DekiScriptExpressionEvaluation.Instance, env);
         DekiScriptLiteral value = expr.Value.VisitWith(DekiScriptExpressionEvaluation.Instance, env);
         if (!value.IsNil)
         {
             if (string.IsNullOrEmpty(expr.Prefix))
             {
                 context.Namespaces.AddNamespace(string.Empty, value.AsString());
             }
             else
             {
                 context.Namespaces.AddNamespace(XmlConvert.EncodeLocalName(name.AsString() ?? string.Empty), value.AsString());
             }
         }
     }
 }
        public DekiScriptExpression Visit(DekiScriptMapConstructor expr, DekiScriptExpressionEvaluationState state)
        {
            // TODO (steveb): need to figure out how to optimize maps with generators
            if (expr.Generator != null)
            {
                return(expr);
            }

            // optimize each item in the map
            var  fields    = new DekiScriptMapConstructor.FieldConstructor[expr.Fields.Length];
            bool isLiteral = true;

            for (int i = 0; i < expr.Fields.Length; i++)
            {
                var current = expr.Fields[i];
                DekiScriptExpression key   = current.Key.VisitWith(this, state);
                DekiScriptExpression value = current.Value.VisitWith(this, state);
                fields[i] = new DekiScriptMapConstructor.FieldConstructor(current.Location, key, value);
                isLiteral = isLiteral && (key is DekiScriptLiteral) && (value is DekiScriptLiteral);
            }
            if (!isLiteral)
            {
                return(DekiScriptExpression.Map(expr.Location, fields));
            }

            // convert expression to a map
            DekiScriptMap result = new DekiScriptMap();

            foreach (var field in fields)
            {
                DekiScriptLiteral key = (DekiScriptLiteral)field.Key;

                // check that key is a simple type
                string text = key.AsString();
                if (text != null)
                {
                    result.Add(text, (DekiScriptLiteral)field.Value);
                }
            }
            return(result);
        }
        public DekiScriptOutputBuffer.Range Visit(DekiScriptMapConstructor expr, DekiScriptExpressionEvaluationState state)
        {
            DekiScriptMap result = new DekiScriptMap();

            if (expr.Generator == null)
            {
                foreach (DekiScriptMapConstructor.FieldConstructor field in expr.Fields)
                {
                    DekiScriptLiteral key = state.Pop(field.Key.VisitWith(this, state));

                    // check that key is a simple type
                    string text = key.AsString();
                    if (text != null)
                    {
                        DekiScriptLiteral value = state.Pop(field.Value.VisitWith(this, state));
                        result.Add(text, value);
                    }
                }
            }
            else
            {
                DekiScriptGeneratorEvaluation.Generate(expr.Generator, delegate(DekiScriptEnv subEnv) {
                    foreach (DekiScriptMapConstructor.FieldConstructor field in expr.Fields)
                    {
                        DekiScriptLiteral key = state.Pop(field.Key.VisitWith(this, state.With(subEnv)));

                        // check that key is a simple type
                        string text = key.AsString();
                        if (text != null)
                        {
                            DekiScriptLiteral value = state.Pop(field.Value.VisitWith(this, state.With(subEnv)));
                            result.Add(text, value);
                        }
                    }
                }, state);
            }
            return(state.Push(result));
        }
        //--- Class Methods ---
        public static XmlNode Evaluate(
            XDoc script,
            XmlElement node,
            DekiScriptEvalContext context,
            DekiScriptEnv env,
            DekiScriptRuntime runtime,
            out bool scripted,
            ref bool error
            )
        {
            if ((context.Mode != DekiScriptEvalMode.Verify) && (context.Mode != DekiScriptEvalMode.EvaluateEditOnly) && (context.Mode != DekiScriptEvalMode.EvaluateSaveOnly))
            {
                throw new InvalidOperationException("DekiScript interpreter can only used for save, edit, or verify evaluations.");
            }
            scripted = false;
            XmlNode next = node.NextSibling;

            try {
                // check if element needs to be evaluated
                if (StringUtil.EqualsInvariant(node.NamespaceURI, XDekiScript.ScriptNS))
                {
                    scripted = true;

                    // NOTE (steveb): <eval:xyz> nodes are not processed by the interpreter anymore
                }
                else
                {
                    XDoc current        = script[node];
                    bool hasScriptClass = StringUtil.EqualsInvariant(node.GetAttribute("class"), "script");

                    #region <elem class="script" init="..." if="..." foreach="..." where="..." block="...">
                    // check if element has form <elem class="script" init="..." if="..." foreach="..." where="..." block="...">
                    scripted = node.HasAttribute("init") || node.HasAttribute("if") || node.HasAttribute("foreach") || node.HasAttribute("block");
                    if (context.Mode == DekiScriptEvalMode.Verify)
                    {
                        // check if "block" is present
                        string blockAttr = node.GetAttribute("block");
                        if (!string.IsNullOrEmpty(blockAttr))
                        {
                            // TODO (steveb): validate script expression
                        }

                        // check if "foreach" is present
                        string foreachAttr = node.GetAttribute("foreach");
                        if (!string.IsNullOrEmpty(foreachAttr))
                        {
                            // TODO (steveb): validate script expression
                        }

                        // check if "if" is present
                        string ifAttr = node.GetAttribute("if");
                        if (!string.IsNullOrEmpty(ifAttr))
                        {
                            // TODO (steveb): validate script expression
                        }

                        // check if "init" is present
                        string initAttr = node.GetAttribute("init");
                        if (!string.IsNullOrEmpty(initAttr))
                        {
                            // TODO (steveb): validate script expression
                        }
                    }
                    #endregion

                    // evaluate child nodes
                    EvaluateChildren(script, node, context, env, runtime, out scripted, ref error);

                    #region evaluate attributes
                    for (int i = 0; i < node.Attributes.Count; ++i)
                    {
                        XmlAttribute attribute = node.Attributes[i];

                        // check if attribute needs to be evaluated
                        if (attribute.NamespaceURI == XDekiScript.ScriptNS)
                        {
                            scripted = true;

                            // NOTE (steveb): eval:xyz="abc" attributes are not processed by the interpreter anymore
                        }
                        else if (StringUtil.StartsWithInvariant(attribute.Value, "{{") && StringUtil.EndsWithInvariant(attribute.Value, "}}"))
                        {
                            scripted = true;

                            // NOTE (steveb): key="{{value}}"
                            string code = attribute.Value.Substring(2, attribute.Value.Length - 4).Trim();

                            // check if script content is substituted
                            bool isPermanentReplacement = false;
                            if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SAVE_PATTERN))
                            {
                                isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly);
                                code = code.Substring(DekiScriptRuntime.ON_SAVE_PATTERN.Length);
                            }
                            else if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SUBST_PATTERN))
                            {
                                isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly);
                                code = code.Substring(DekiScriptRuntime.ON_SUBST_PATTERN.Length);
                            }
                            else if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_EDIT_PATTERN))
                            {
                                isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateEditOnly);
                                code = code.Substring(DekiScriptRuntime.ON_EDIT_PATTERN.Length);
                            }

                            // parse expression
                            if ((context.Mode == DekiScriptEvalMode.Verify) || isPermanentReplacement)
                            {
                                DekiScriptExpression expression = DekiScriptParser.Parse(ComputeNodeLocation(attribute), code);
                                if (isPermanentReplacement)
                                {
                                    DekiScriptLiteral eval = runtime.Evaluate(expression, DekiScriptEvalMode.EvaluateSafeMode, env);

                                    // determine what the outcome value is
                                    string value = eval.AsString();

                                    // check if we have a value to replace the current attribute with
                                    if ((value != null) && !DekiScriptLibrary.ContainsXSSVulnerability(attribute.LocalName, value))
                                    {
                                        attribute.Value = value;
                                    }
                                    else
                                    {
                                        node.Attributes.RemoveAt(i);
                                        --i;
                                    }
                                }
                            }
                        }
                    }
                    #endregion

                    #region evaluate <span class="script"> or <pre class="script">
                    if (hasScriptClass && (StringUtil.EqualsInvariant(node.LocalName, "pre") || StringUtil.EqualsInvariant(node.LocalName, "span")) && !node.HasAttribute("function"))
                    {
                        // replace the non-breaking space character with space
                        string code = node.InnerText.ReplaceAll("\u00A0", " ", "\u00AD", "").Trim();

                        // check if script content is substituted
                        bool isPermanentReplacement = false;
                        if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SAVE_PATTERN))
                        {
                            isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly);
                            code = code.Substring(DekiScriptRuntime.ON_SAVE_PATTERN.Length);
                        }
                        else if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_SUBST_PATTERN))
                        {
                            isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateSaveOnly);
                            code = code.Substring(DekiScriptRuntime.ON_SUBST_PATTERN.Length);
                        }
                        else if (StringUtil.StartsWithInvariantIgnoreCase(code, DekiScriptRuntime.ON_EDIT_PATTERN))
                        {
                            isPermanentReplacement = (context.Mode == DekiScriptEvalMode.EvaluateEditOnly);
                            code = code.Substring(DekiScriptRuntime.ON_EDIT_PATTERN.Length);
                        }

                        // parse expression
                        if ((context.Mode == DekiScriptEvalMode.Verify) || isPermanentReplacement)
                        {
                            DekiScriptExpression expression = DekiScriptParser.Parse(ComputeNodeLocation(node), code);
                            if (isPermanentReplacement)
                            {
                                DekiScriptLiteral value = runtime.Evaluate(expression, DekiScriptEvalMode.EvaluateSafeMode, env);
                                context.ReplaceNodeWithValue(node, value);
                            }
                            if (!isPermanentReplacement)
                            {
                                scripted = true;
                            }
                        }
                    }
                    #endregion
                }
            } catch (Exception e) {
                // only embed error in verify mode, not in save/edit modes
                if (context.Mode == DekiScriptEvalMode.Verify)
                {
                    context.InsertExceptionMessageBeforeNode(env, node.ParentNode, node, ComputeNodeLocation(node), e);
                    node.ParentNode.RemoveChild(node);
                }
                error |= true;
            }
            return(next);
        }
        internal DekiScriptLiteral Evaluate(DekiScriptAccess expr, DekiScriptExpressionEvaluationState state, bool evaluateProperties)
        {
            DekiScriptLiteral prefix = state.Pop(expr.Prefix.VisitWith(this, state));
            DekiScriptLiteral index  = state.Pop(expr.Index.VisitWith(this, state));

            switch (prefix.ScriptType)
            {
            case DekiScriptType.MAP: {
                DekiScriptLiteral result = ((DekiScriptMap)prefix)[index];
                if (evaluateProperties)
                {
                    result = state.Runtime.EvaluateProperty(expr.Location, result, state.Env);
                }
                return(result);
            }

            case DekiScriptType.LIST: {
                DekiScriptLiteral value  = DekiScriptExpression.Constant(index.AsNumber());
                DekiScriptLiteral result = ((DekiScriptList)prefix)[value];
                if (evaluateProperties)
                {
                    result = state.Runtime.EvaluateProperty(expr.Location, result, state.Env);
                }
                return(result);
            }

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

                // coerce the index type to STR
                index = DekiScriptExpression.Constant(index.AsString());
                if (index.ScriptType != DekiScriptType.STR)
                {
                    throw new DekiScriptBadTypeException(expr.Location, 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.Location, 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.Location, 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(DekiScriptExpression.Constant(text.Value[position].ToString()));
            }

            case DekiScriptType.XML: {
                string path = index.AsString();
                if (path == null)
                {
                    throw new DekiScriptBadTypeException(expr.Location, 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.Location, prefix.ScriptType, new[] { DekiScriptType.MAP, DekiScriptType.LIST, DekiScriptType.XML, DekiScriptType.STR, DekiScriptType.URI });
        }
        public DekiScriptOutputBuffer.Range Visit(DekiScriptBinary expr, DekiScriptExpressionEvaluationState state)
        {
            DekiScriptExpression Left  = expr.Left;
            DekiScriptExpression Right = expr.Right;

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

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

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

            case DekiScriptBinary.Op.IsType: {
                DekiScriptLiteral left = state.Pop(Left.VisitWith(this, state));
                string            type = ((DekiScriptString)Right).Value;
                return(state.Push(DekiScriptExpression.Constant(type.EqualsInvariantIgnoreCase("any") || left.ScriptTypeName.EqualsInvariantIgnoreCase(type))));
            }

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

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

            case DekiScriptBinary.Op.GreaterOrEqual: {
                DekiScriptLiteral left  = state.Pop(Left.VisitWith(this, state));
                DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state));
                if (DekiScriptLiteral.CoerceValuesToSameType(ref left, ref right))
                {
                    switch (left.ScriptType)
                    {
                    case DekiScriptType.BOOL:
                    case DekiScriptType.NUM:
                        return(state.Push(DekiScriptExpression.Constant(left.AsNumber() >= right.AsNumber())));

                    case DekiScriptType.STR:
                        return(state.Push(DekiScriptExpression.Constant(left.AsString().CompareInvariant(right.AsString()) >= 0)));

                    default:
                        return(DekiScriptOutputBuffer.Range.Empty);
                    }
                }
                else
                {
                    return(DekiScriptOutputBuffer.Range.Empty);
                }
            }

            case DekiScriptBinary.Op.GreaterThan: {
                DekiScriptLiteral left  = state.Pop(Left.VisitWith(this, state));
                DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state));
                if (DekiScriptLiteral.CoerceValuesToSameType(ref left, ref right))
                {
                    switch (left.ScriptType)
                    {
                    case DekiScriptType.BOOL:
                    case DekiScriptType.NUM:
                        return(state.Push(DekiScriptExpression.Constant(left.AsNumber() > right.AsNumber())));

                    case DekiScriptType.STR:
                        return(state.Push(DekiScriptExpression.Constant(left.AsString().CompareInvariant(right.AsString()) > 0)));

                    default:
                        return(DekiScriptOutputBuffer.Range.Empty);
                    }
                }
                else
                {
                    return(DekiScriptOutputBuffer.Range.Empty);
                }
            }

            case DekiScriptBinary.Op.LessOrEqual: {
                DekiScriptLiteral left  = state.Pop(Left.VisitWith(this, state));
                DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state));
                if (DekiScriptLiteral.CoerceValuesToSameType(ref left, ref right))
                {
                    switch (left.ScriptType)
                    {
                    case DekiScriptType.BOOL:
                    case DekiScriptType.NUM:
                        return(state.Push(DekiScriptExpression.Constant(left.AsNumber() <= right.AsNumber())));

                    case DekiScriptType.STR:
                        return(state.Push(DekiScriptExpression.Constant(left.AsString().CompareInvariant(right.AsString()) <= 0)));

                    default:
                        return(DekiScriptOutputBuffer.Range.Empty);
                    }
                }
                else
                {
                    return(DekiScriptOutputBuffer.Range.Empty);
                }
            }

            case DekiScriptBinary.Op.LessThan: {
                DekiScriptLiteral left  = state.Pop(Left.VisitWith(this, state));
                DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state));
                if (DekiScriptLiteral.CoerceValuesToSameType(ref left, ref right))
                {
                    switch (left.ScriptType)
                    {
                    case DekiScriptType.BOOL:
                    case DekiScriptType.NUM:
                        return(state.Push(DekiScriptExpression.Constant(left.AsNumber() < right.AsNumber())));

                    case DekiScriptType.STR:
                        return(state.Push(DekiScriptExpression.Constant(left.AsString().CompareInvariant(right.AsString()) < 0)));

                    default:
                        return(DekiScriptOutputBuffer.Range.Empty);
                    }
                }
                else
                {
                    return(DekiScriptOutputBuffer.Range.Empty);
                }
            }

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

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

            case DekiScriptBinary.Op.Addition:
                return(state.Push(DekiScriptExpression.Constant(state.Pop(Left.VisitWith(this, state)).AsNumber() + state.Pop(Right.VisitWith(this, state)).AsNumber())));

            case DekiScriptBinary.Op.Division:
                return(state.Push(DekiScriptExpression.Constant(state.Pop(Left.VisitWith(this, state)).AsNumber() / state.Pop(Right.VisitWith(this, state)).AsNumber())));

            case DekiScriptBinary.Op.Modulo: {
                DekiScriptLiteral left  = state.Pop(Left.VisitWith(this, state));
                DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state));
                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(state.Push(DekiScriptLiteral.FromNativeValue(DekiScriptLibrary.StringFormat(((DekiScriptString)left).Value, right.NativeValue))));
                }
                else
                {
                    return(state.Push(DekiScriptExpression.Constant(left.AsNumber() % right.AsNumber())));
                }
            }

            case DekiScriptBinary.Op.Multiplication:
                return(state.Push(DekiScriptExpression.Constant(state.Pop(Left.VisitWith(this, state)).AsNumber() * state.Pop(Right.VisitWith(this, state)).AsNumber())));

            case DekiScriptBinary.Op.Subtraction:
                return(state.Push(DekiScriptExpression.Constant(state.Pop(Left.VisitWith(this, state)).AsNumber() - state.Pop(Right.VisitWith(this, state)).AsNumber())));

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

            case DekiScriptBinary.Op.Concat: {
                DekiScriptLiteral left  = state.Pop(Left.VisitWith(this, state));
                DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state));
                if (left is DekiScriptNil)
                {
                    return(state.Push(right));
                }
                else if (right is DekiScriptNil)
                {
                    return(state.Push(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(state.Push(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(state.Push(result));
                }
                else
                {
                    // treat left and right expressions as strings
                    string leftText  = left.AsString();
                    string rightText = right.AsString();
                    if ((leftText != null) && (rightText != null))
                    {
                        return(state.Push(DekiScriptExpression.Constant(leftText + rightText)));
                    }
                    else if (leftText != null)
                    {
                        return(state.Push(DekiScriptExpression.Constant(leftText)));
                    }
                    else if (rightText != null)
                    {
                        return(state.Push(DekiScriptExpression.Constant(rightText)));
                    }
                    else
                    {
                        return(DekiScriptOutputBuffer.Range.Empty);
                    }
                }
            }

            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(state.Pop(Left.VisitWith(this, state)).AsString());
                string result = null;
                if (left != null)
                {
                    DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state));
                    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(state.Push(DekiScriptLiteral.FromNativeValue(result)));
            }

            case DekiScriptBinary.Op.InCollection: {
                DekiScriptLiteral left  = state.Pop(Left.VisitWith(this, state));
                DekiScriptLiteral right = state.Pop(Right.VisitWith(this, state));
                if (right is DekiScriptList)
                {
                    foreach (DekiScriptLiteral item in ((DekiScriptList)right).Value)
                    {
                        if (!DekiScriptBinary.Equal(left, item).IsNilFalseZero)
                        {
                            return(state.Push(DekiScriptBool.True));
                        }
                    }
                    return(state.Push(DekiScriptBool.False));
                }
                else if (right is DekiScriptMap)
                {
                    foreach (DekiScriptLiteral item in ((DekiScriptMap)right).Value.Values)
                    {
                        if (!DekiScriptBinary.Equal(left, item).IsNilFalseZero)
                        {
                            return(state.Push(DekiScriptBool.True));
                        }
                    }
                    return(state.Push(DekiScriptBool.False));
                }
                else
                {
                    return(state.Push(DekiScriptBool.False));
                }
            }
            }
            throw new InvalidOperationException("invalid op code:" + expr.OpCode);
        }
        internal void InsertValueBeforeNode(XmlNode parent, XmlNode reference, DekiScriptLiteral value)
        {
            if ((value is DekiScriptXml) || (value is DekiScriptUri))
            {
                XDoc xml = value.AsEmbeddableXml(Mode == DekiScriptEvalMode.EvaluateSafeMode);
                if (xml.HasName("html"))
                {
                    // TODO (steveb): merge XML namespaces

                    // merge <head> and <tail> sections
                    AddHeadElements(xml);
                    AddTailElements(xml);

                    // loop over body elements in response
                    foreach (XDoc body in xml["body"])
                    {
                        string target   = body["@target"].AsText;
                        string conflict = body["@conflict"].AsText ?? "ignore";

                        // check if the main body is targeted or something else
                        if (string.IsNullOrEmpty(target))
                        {
                            // append body nodes
                            foreach (XmlNode node in body.AsXmlNode.ChildNodes)
                            {
                                parent.InsertBefore(parent.OwnerDocument.ImportNode(node, true), reference);
                            }
                        }
                        else
                        {
                            // check if the targeted body already exists
                            if (Bodies.ContainsKey(target) && !StringUtil.EqualsInvariant(conflict, "replace"))
                            {
                                if (StringUtil.EqualsInvariant(conflict, "append"))
                                {
                                    // append nodes to existing body
                                    Bodies[target].Add(body.AsXmlNode);
                                }
                            }
                            else
                            {
                                // create a new body element
                                List <XmlNode> list = new List <XmlNode>();
                                list.Add(body.AsXmlNode);
                                Bodies[target] = list;
                            }
                        }
                    }
                }
                else if (!xml.IsEmpty)
                {
                    // replace the current node with the entire document
                    parent.InsertBefore(parent.OwnerDocument.ImportNode(xml.AsXmlNode, true), reference);
                }
            }
            else if (value is DekiScriptComplexLiteral)
            {
                // append text respresentation of value
                parent.InsertBefore(CreateTextNode(value.ToString()), reference);
            }
            else
            {
                // append value cast to text
                string text = value.AsString();
                if (!string.IsNullOrEmpty(text))
                {
                    parent.InsertBefore(CreateTextNode(text), reference);
                }
            }
        }