Exemple #1
0
        public static Hashtable MapFilter(
            [DekiScriptParam("map value")] Hashtable map,
            [DekiScriptParam("condition to execute for each item (use '$' to refer to the key-value pair)")] string condition,
            DekiScriptRuntime runtime
            )
        {
            DekiScriptExpression expr   = DekiScriptParser.Parse(new Location("map.select(condition)"), condition);
            Hashtable            result = new Hashtable(StringComparer.OrdinalIgnoreCase);

            foreach (DictionaryEntry entry in map)
            {
                DekiScriptMap keyvalue = new DekiScriptMap();
                object        value    = Eval(entry.Value, runtime);
                keyvalue.Add("key", DekiScriptLiteral.FromNativeValue(entry.Key));
                keyvalue.Add("value", DekiScriptLiteral.FromNativeValue(value));
                DekiScriptEnv env = runtime.CreateEnv();
                env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, keyvalue);
                DekiScriptLiteral test = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env);
                if (!test.IsNilFalseZero)
                {
                    result.Add(entry.Key, value);
                }
            }
            return(result);
        }
 public static object StringDeserialize(
     [DekiScriptParam("string value")] string value,
     DekiScriptRuntime runtime
     )
 {
     return(runtime.Evaluate(DekiScriptParser.Parse(new Location("string.deserialize(value)"), value), DekiScriptEvalMode.EvaluateSafeMode, new DekiScriptEnv()).NativeValue);
 }
        public void Dom_evaluation_with_dynamic_content()
        {
            XDoc doc = new XDoc("html").UsePrefix("eval", "http://mindtouch.com/2007/dekiscript")
                       .Start("body")
                       .Start("div").Attr("block", "var x = string.toupper(date.now)")
                       .Elem("eval:expr", "x .. 3; string.nbsp")
                       .End()
                       .End();

            // parse node
            DekiScriptExpression node = DekiScriptParser.Parse(doc);

            Assert.AreEqual("<html><body>(discard (var x = string.toupper(date.now)); <div>(x .. 3; string.nbsp)</div>) !! web.showerror(__error)</body></html> !! web.showerror(__error)", node.ToString());

            // TODO (steveb): disabled the partial evaluation test for now

            // partial evaluation
            //node = node.Optimize(DekiScriptEvalMode.Evaluate, DekiScriptEnv.Create());
            //Assert.AreEqual("<html><body>discard (var x = \"native:///string.toupper\"( \"native:///$date.now\"())); <div>x .. 3; \"�\"; </div>; </body></html>", node.ToString());

            // full evaluation
            //string now = DekiScriptLibrary.DateNow();
            //XDoc value = node.Evaluate(DekiScriptEvalMode.Evaluate, DekiScriptEnv.Create()).AsEmbeddableXml(false);
            //Assert.AreEqual("<html><body><div>" + now.ToUpperInvariant() + "3&nbsp;</div></body></html>", value.ToXHtml());
        }
Exemple #4
0
        public static Hashtable ListGroupBy(
            [DekiScriptParam("list value")] ArrayList list,
            [DekiScriptParam("expression to apply for grouping (use '$' to refer to the current item)")] string expression,
            DekiScriptRuntime runtime
            )
        {
            Hashtable            result = new Hashtable(StringComparer.OrdinalIgnoreCase);
            DekiScriptExpression expr   = DekiScriptParser.Parse(new Location("list.groupby(expression)"), expression);

            // loop over all items in list
            foreach (object entry in list)
            {
                DekiScriptEnv env = runtime.CreateEnv();
                env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, DekiScriptLiteral.FromNativeValue(entry));

                // evalute grouping expression
                string key = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env).AsString();
                if (key != null)
                {
                    // check if an accumulation list already exists; otherwise, create one
                    ArrayList accumulator = (ArrayList)result[key];
                    if (accumulator == null)
                    {
                        accumulator = new ArrayList();
                        result[key] = accumulator;
                    }
                    accumulator.Add(entry);
                }
            }
            return(result);
        }
Exemple #5
0
        public void Can_parse_string()
        {
            var code = "(var x = 5; if(x > 5) { x })";
            var expr = DekiScriptParser.Parse(Location.Start, code);

            Assert.AreEqual(code, expr.ToString());
        }
Exemple #6
0
        public void Can_parse_stream()
        {
            var code   = "(var x = 5; if(x > 5) { x })";
            var stream = new MemoryStream(Encoding.UTF8.GetBytes(code));
            var expr   = DekiScriptParser.Parse(Location.Start, stream);

            Assert.AreEqual(code, expr.ToString());
        }
Exemple #7
0
        public static ArrayList ListOrderBy(
            [DekiScriptParam("list value")] ArrayList list,
            [DekiScriptParam("key name or list of key names; sort direction is controlled by appendending \" ascending\" or \" descending\" to the key(s); when omitted, the direction is asending by default")] object keys,
            DekiScriptRuntime runtime
            )
        {
            // check for trivial sort case
            if (keys is string)
            {
                string key = ((string)keys).Trim();

                // the key cannot contain access operators
                if (key.IndexOfAny(new[] { '.', '[', ']' }) < 0)
                {
                    return(ListOrderBy_IsReversed(ref key) ? ListSort(list, key, true, null, runtime) : ListSort(list, key, false, null, runtime));
                }

                // let's change 'keys' into an array list for processing convenience
                ArrayList temp = new ArrayList {
                    keys
                };
                keys = temp;
            }

            // check that 'keys' has a valid type
            if (!(keys is ArrayList))
            {
                throw new DekiScriptBadTypeException(Location.None, DekiScriptLiteral.AsScriptType(keys.GetType()), new[] { DekiScriptType.STR, DekiScriptType.LIST });
            }

            // build comparison expression
            StringBuilder compare = new StringBuilder();

            foreach (string key in (ArrayList)keys)
            {
                if (compare.Length > 0)
                {
                    compare.Append(" || ");
                }

                // process sort field
                string field = key;
                if (ListOrderBy_IsReversed(ref field))
                {
                    compare.AppendFormat("(if($left.{0} < $right.{0}) 1; else if($left.{0} > $right.{0}) -1; else 0;)", field);
                }
                else
                {
                    compare.AppendFormat("(if($left.{0} < $right.{0}) -1; else if($left.{0} > $right.{0}) 1; else 0;)", field);
                }
            }

            // sort list
            list.Sort(new DekiScriptComparer(runtime, DekiScriptParser.Parse(new Location("list.orderby(compare)"), compare.ToString())));
            return(list);
        }
        public void Dom_partial_evaluation_for_foreach2()
        {
            const string source = "<html xmlns:eval='http://mindtouch.com/2007/dekiscript'><body init='var x = 2'><div foreach='var y in num.series(1, 5)' where='y != x'><eval:expr>y</eval:expr></div></body></html>";
            XDoc         doc    = XDocFactory.From(source, MimeType.XML);

            DekiScriptExpression node = DekiScriptParser.Parse(doc);

            node = _dekiScriptTester.Runtime.Optimize(node, DekiScriptEvalMode.Evaluate, _dekiScriptTester.Runtime.CreateEnv());
            Assert.AreEqual("<html><body><div>1; </div><div>3; </div><div>4; </div><div>5; </div></body></html>", node.ToString());
        }
        public Yield Execute(DreamContext context, DreamMessage request, Result <DreamMessage> response)
        {
            string expression           = context.GetParam("expression");
            DekiScriptExpression expr   = DekiScriptParser.Parse(new Location("POST:execute"), expression);
            DekiScriptLiteral    result = _runtime.Evaluate(expr, DekiScriptEvalMode.Evaluate, _env);

            if (result.ScriptType == DekiScriptType.XML)
            {
                response.Return(DreamMessage.Ok(MimeType.XML, (XDoc)result.NativeValue));
            }
            else
            {
                response.Return(DreamMessage.Ok(MimeType.TEXT, result.ToString()));
            }
            yield break;
        }
Exemple #10
0
        public static ArrayList ListApply(
            [DekiScriptParam("list value")] ArrayList list,
            [DekiScriptParam("expression to apply (use '$' to refer to the current item)")] string expression,
            DekiScriptRuntime runtime
            )
        {
            DekiScriptExpression expr   = DekiScriptParser.Parse(new Location("list.apply(expression)"), expression);
            ArrayList            result = new ArrayList();

            foreach (object entry in list)
            {
                DekiScriptEnv env = runtime.CreateEnv();
                env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, DekiScriptLiteral.FromNativeValue(entry));
                result.Add(runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env).NativeValue);
            }
            return(result);
        }
        public Empty Visit(DekiScriptXml expr, StringBuilder state)
        {
            XDoc doc = expr.Value;

            if (!doc.IsEmpty)
            {
                XmlNode node = doc.AsXmlNode;
                if (node.NodeType == XmlNodeType.Attribute)
                {
                    state.Append(node.OuterXml);
                }
                else
                {
                    DekiScriptParser.Parse(doc, false).VisitWith(this, state);
                }
            }
            return(Empty.Value);
        }
        public Empty Visit(DekiScriptAccess expr, StringBuilder state)
        {
            expr.Prefix.VisitWith(this, state);
            DekiScriptString index = expr.Index as DekiScriptString;

            if ((index != null) && DekiScriptParser.IsIdentifier(index.Value))
            {
                state.Append(".");
                state.Append(index.Value);
            }
            else
            {
                state.Append("[");
                expr.Index.VisitWith(this, state);
                state.Append("]");
            }
            return(Empty.Value);
        }
Exemple #13
0
        public static ArrayList ListIntersect(
            [DekiScriptParam("first list")] ArrayList first,
            [DekiScriptParam("second list")] ArrayList second,
            [DekiScriptParam("expression to determine if item from the first list should be included in final list (return true to include, false to exclude; use '$left' and '$right' to refer to the current item from the first and seconds lists respectively; default: equality condition)", true)] string condition,
            DekiScriptRuntime runtime
            )
        {
            ArrayList result = new ArrayList();

            if (condition != null)
            {
                // loop over both lists and keep items from the first list based on the outcome of the condition
                DekiScriptExpression expr = DekiScriptParser.Parse(new Location("list.intersect(condition)"), condition);
                foreach (object left in first)
                {
                    foreach (object right in second)
                    {
                        DekiScriptEnv env      = runtime.CreateEnv();
                        DekiScriptMap keyvalue = new DekiScriptMap();
                        keyvalue.Add("left", DekiScriptLiteral.FromNativeValue(left));
                        keyvalue.Add("right", DekiScriptLiteral.FromNativeValue(right));
                        env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, keyvalue);
                        DekiScriptLiteral test = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env);
                        if (!test.IsNilFalseZero)
                        {
                            result.Add(left);
                            break;
                        }
                    }
                }
            }
            else
            {
                // using simple containment check to keep items
                foreach (object item in first)
                {
                    if (second.Contains(item))
                    {
                        result.Add(item);
                    }
                }
            }
            return(result);
        }
 public static object StringEval(
     [DekiScriptParam("dekiscript expression")] string code,
     [DekiScriptParam("ignore errors during evaluation", true)] bool?ignoreErrors,
     DekiScriptRuntime runtime
     )
 {
     try {
         return(runtime.Evaluate(DekiScriptParser.Parse(new Location("string.eval(code)"), code), DekiScriptEvalMode.EvaluateSafeMode, runtime.CreateEnv()).NativeValue);
     } catch (Exception e) {
         if (ignoreErrors ?? false)
         {
             return(e.ToString());
         }
         else
         {
             throw;
         }
     }
 }
        public Empty Visit(DekiScriptMapConstructor expr, StringBuilder state)
        {
            state.Append("{");
            bool first = true;

            foreach (var field in expr.Fields)
            {
                if (!first)
                {
                    state.Append(", ");
                }
                else
                {
                    state.Append(" ");
                }
                first = false;

                // check if key is a string and a valid identifier
                if (field.Key is DekiScriptString)
                {
                    string key = ((DekiScriptString)field.Key).Value;
                    if (DekiScriptParser.IsIdentifier(key))
                    {
                        state.Append(key + " : " + field.Value);
                    }
                    else
                    {
                        state.Append(key.QuoteString() + " : " + field.Value);
                    }
                }
                else
                {
                    state.Append("(" + field.Key + ") : " + field.Value);
                }
            }
            if (expr.Generator != null)
            {
                state.Append(" foreach ");
                state.Append(expr.Generator.ToString());
            }
            state.Append(" }");
            return(Empty.Value);
        }
Exemple #16
0
        public static Hashtable MapApply(
            [DekiScriptParam("map value")] Hashtable map,
            [DekiScriptParam("expression to apply (use '$' to refer to the item)")] string expression,
            DekiScriptRuntime runtime
            )
        {
            DekiScriptExpression expr   = DekiScriptParser.Parse(new Location("map.apply(expression)"), expression);
            Hashtable            result = new Hashtable(StringComparer.OrdinalIgnoreCase);

            foreach (DictionaryEntry entry in map)
            {
                DekiScriptMap keyvalue = new DekiScriptMap();
                keyvalue.Add("key", DekiScriptLiteral.FromNativeValue(entry.Key));
                keyvalue.Add("value", DekiScriptLiteral.FromNativeValue(Eval(entry.Value, runtime)));
                DekiScriptEnv env = runtime.CreateEnv();
                env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, keyvalue);
                result.Add(entry.Key, runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env));
            }
            return(result);
        }
Exemple #17
0
        public static object ListReduce(
            [DekiScriptParam("list value")] ArrayList list,
            [DekiScriptParam("expression to compute combined value (use '$value' and '$item' to refer to the current value and item, respectively)")] string expression,
            [DekiScriptParam("starting value (default: nil)", true)] object value,
            DekiScriptRuntime runtime
            )
        {
            DekiScriptExpression expr = DekiScriptParser.Parse(new Location("list.reduce(expression)"), expression);

            foreach (object entry in list)
            {
                DekiScriptEnv env    = runtime.CreateEnv();
                DekiScriptMap values = new DekiScriptMap();
                values.Add("value", DekiScriptLiteral.FromNativeValue(value));
                values.Add("item", DekiScriptLiteral.FromNativeValue(entry));
                env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, values);
                value = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env).NativeValue;
            }
            return(value);
        }
        public Empty Visit(DekiScriptMap expr, StringBuilder state)
        {
            state.Append("{");

            // convert values to an array so they can be sorted
            var values = new List <KeyValuePair <string, DekiScriptLiteral> >();

            foreach (KeyValuePair <string, DekiScriptLiteral> entry in expr.Value)
            {
                values.Add(entry);
            }
            values.Sort((left, right) => left.Key.CompareInvariantIgnoreCase(right.Key));

            // emit values
            bool first = true;

            foreach (KeyValuePair <string, DekiScriptLiteral> entry in values)
            {
                if (!first)
                {
                    state.Append(", ");
                }
                else
                {
                    state.Append(" ");
                }
                first = false;
                if (DekiScriptParser.IsIdentifier(entry.Key) || DekiScriptParser.IsNumber(entry.Key))
                {
                    state.Append(entry.Key);
                }
                else
                {
                    state.Append(entry.Key.QuoteString());
                }
                state.Append(" : ");
                entry.Value.VisitWith(this, state);
            }
            state.Append(" }");
            return(Empty.Value);
        }
Exemple #19
0
        public static int ListCount(
            [DekiScriptParam("list value")] ArrayList list,
            [DekiScriptParam("condition to execute for each item (use '$' to refer to the item)")] string condition,
            DekiScriptRuntime runtime
            )
        {
            DekiScriptExpression expr = DekiScriptParser.Parse(new Location("list.count(condition)"), condition);
            int count = 0;

            foreach (object entry in list)
            {
                DekiScriptEnv env = runtime.CreateEnv();
                env.Vars.Add(DekiScriptRuntime.DEFAULT_ID, DekiScriptLiteral.FromNativeValue(entry));
                DekiScriptLiteral test = runtime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, env);
                if (!test.IsNilFalseZero)
                {
                    ++count;
                }
            }
            return(count);
        }
        public static DekiScriptLiteral Constant(XUri value, DekiScriptLiteral[] items)
        {
            DekiScriptLiteral args = DekiScriptNil.Value;

            if (!ArrayUtil.IsNullOrEmpty(items))
            {
                args = List(items);
            }
            else
            {
                // BUGBUGBUG (steveb): we should NOT do execution when building the model!
                if (value.Fragment != null)
                {
                    DekiScriptExpression expr = DekiScriptParser.TryParse(value.Fragment) ?? DekiScriptNil.Value;
                    args = EvalRuntime.Evaluate(expr, DekiScriptEvalMode.EvaluateSafeMode, new DekiScriptEnv());
                }
            }
            if ((args.ScriptType != DekiScriptType.NIL) && (args.ScriptType != DekiScriptType.LIST) && (args.ScriptType != DekiScriptType.MAP))
            {
                throw new DekiScriptBadTypeException(Location.None, args.ScriptType, new[] { DekiScriptType.NIL, DekiScriptType.LIST, DekiScriptType.MAP });
            }
            return(new DekiScriptUri(value.WithoutFragment(), args));
        }
Exemple #21
0
        public static ArrayList ListSort(
            [DekiScriptParam("list of values")] ArrayList list,
            [DekiScriptParam("key for value if list contains maps (default: nil)", true)] object key,
            [DekiScriptParam("sort in reverse order (default: false)", true)] bool?reverse,
            [DekiScriptParam("compare two items (return -1 if left item less than the right item, 0 if they are equal, and +1 if the left item is greater than the right item; use '$left' and '$right' to refer to the left and right items respectively)", true)] string compare,
            DekiScriptRuntime runtime
            )
        {
            // prepare custom comparer
            IComparer comparer = null;

            if (compare != null)
            {
                comparer = new DekiScriptComparer(runtime, DekiScriptParser.Parse(new Location("list.sort(compare)"), compare));
            }

            // sort list
            if (key == null || (key as string != null && string.IsNullOrEmpty(key as string)))
            {
                list.Sort(comparer);
            }
            else
            {
                Array keys   = (key is ArrayList) ? ((ArrayList)key).ToArray() : ListCollect(list, key.ToString(), runtime).ToArray();
                Array values = list.ToArray();
                Array.Sort(keys, values, comparer);
                list = new ArrayList(values);
            }

            // check if results need to be reveresed
            if (reverse ?? false)
            {
                list.Reverse();
            }
            return(list);
        }
        public void Dom_evaluation_with_static_content()
        {
            XDoc doc = new XDoc("html").UsePrefix("eval", "http://mindtouch.com/2007/dekiscript")
                       .Start("body")
                       .Start("div").Attr("block", "var x = string.toupper('foo')")
                       .Elem("eval:expr", "x .. 3; string.nbsp")
                       .End()
                       .End();

            // parse node
            DekiScriptExpression node = DekiScriptParser.Parse(doc);

            Assert.AreEqual("<html><body>(discard (var x = string.toupper(\"foo\")); <div>(x .. 3; string.nbsp)</div>) !! web.showerror(__error)</body></html> !! web.showerror(__error)", node.ToString());

            // TODO (steveb): disabled the partial evaluation test for now

            // partial evaluation
            //node = node.Optimize(DekiScriptEvalMode.Evaluate, DekiScriptEnv.Create());
            //Assert.AreEqual("<html><body><div>\"FOO3�\"; </div></body></html>", node.ToString());

            // full evaluation
            //XDoc value = node.Evaluate(DekiScriptEvalMode.Evaluate, DekiScriptEnv.Create()).AsEmbeddableXml(false);
            //Assert.AreEqual("<html><body><div>FOO3&nbsp;</div></body></html>", value.ToXHtml());
        }
Exemple #23
0
        private void Test(string expression)
        {
            DekiScriptExpression expr = DekiScriptParser.Parse(Location.Start, expression);

            Assert.AreEqual(expression, expr.ToString());
        }
        //--- Class Methods ---
        public static void StartExtensionService(DekiContext context, ServiceBE service, ServiceRepository.IServiceInfo serviceInfo, bool forceRefresh)
        {
            // retrieve document describing the extension functions
            XUri            uri       = new XUri(service.Uri);
            XDoc            manifest  = null;
            DekiWikiService deki      = context.Deki;
            var             extension = serviceInfo.Extension;

            if (!service.ServiceLocal)
            {
                lock (deki.RemoteExtensionLibraries) {
                    deki.RemoteExtensionLibraries.TryGetValue(uri, out manifest);
                }
            }
            if (manifest == null || forceRefresh)
            {
                manifest = Plug.New(uri).Get().ToDocument();

                // normalize the extension XML
                manifest = manifest.TransformAsXml(_extensionConverterXslt);

                // check if document describes a valid extension: either the extension has no functions, or the functions have end-points
                if (manifest.HasName("extension") && ((manifest["function"].ListLength == 0) || (manifest["function/uri"].ListLength > 0)))
                {
                    // add source uri for service
                    manifest.Attr("uri", uri);

                    // register service in extension list
                    lock (deki.RemoteExtensionLibraries) {
                        deki.RemoteExtensionLibraries[uri] = manifest;
                    }
                }
                else
                {
                    throw new ExtensionRemoveServiceInvalidOperationException(uri);
                }
            }
            extension.Manifest = manifest;

            // add function prefix if one is defined
            serviceInfo.Extension.SetPreference("namespace.custom", service.Preferences["namespace"]);
            string serviceNamespace = service.Preferences["namespace"] ?? manifest["namespace"].AsText;

            if (serviceNamespace != null)
            {
                serviceNamespace = serviceNamespace.Trim();
                if (string.IsNullOrEmpty(serviceInfo.Namespace))
                {
                    // Note (arnec): Namespace from preferences is assigned at service creation. If we do not have one at this
                    // point, it came from the extension manifest and needs to be registered as our default. Otherwise the
                    // preference override persists as the namespace.
                    context.Instance.RunningServices.RegisterNamespace(serviceInfo, serviceNamespace);
                }
                if (serviceNamespace.Length != 0)
                {
                    if (!DekiScriptParser.IsIdentifier(serviceNamespace))
                    {
                        throw new ExtensionNamespaceInvalidArgumentException(service.Preferences["namespace"] ?? manifest["namespace"].AsText);
                    }
                }
                else
                {
                    serviceNamespace = null;
                }
            }
            serviceNamespace = (serviceNamespace == null) ? string.Empty : (serviceNamespace + ".");

            // add custom library title

            extension.SetPreference("title.custom", service.Preferences["title"]);
            extension.SetPreference("label.custom", service.Preferences["label"]);
            extension.SetPreference("description.custom", service.Preferences["description"]);
            extension.SetPreference("uri.logo.custom", service.Preferences["uri.logo"]);
            extension.SetPreference("functions", service.Preferences["functions"]);
            extension.SetPreference("protected", service.Preferences["protected"]);

            // add each extension function
            bool.TryParse(service.Preferences["protected"], out extension.IsProtected);
            var functions = new List <ServiceRepository.ExtensionFunctionInfo>();

            foreach (XDoc function in manifest["function"])
            {
                XUri functionUri = function["uri"].AsUri;
                if (functionUri != null)
                {
                    functions.Add(new ServiceRepository.ExtensionFunctionInfo(serviceNamespace + function["name"].Contents, functionUri));
                }
            }
            extension.Functions = functions.ToArray();
        }
Exemple #25
0
        private DekiScriptInvocationTargetDescriptor ConvertFunction(XDoc function)
        {
            string functionName = function["name"].AsText;

            if (string.IsNullOrEmpty(functionName))
            {
                _log.WarnFormat("function without name in script {0}; skipping function definition", _manifestUri);
                return(null);
            }

            // determine function access level
            DreamAccess access;

            switch (function["access"].AsText ?? "public")
            {
            case "private":
                access = DreamAccess.Private;
                break;

            case "internal":
                access = DreamAccess.Internal;
                break;

            case "public":
                access = DreamAccess.Public;
                break;

            default:
                _log.WarnFormat("unrecognized access level '{0}' for function {1} in script {2}; defaulting to public", function["access"].AsText, functionName, _manifestUri);
                access = DreamAccess.Public;
                break;
            }

            // convert parameters
            List <DekiScriptParameter> parameters = new List <DekiScriptParameter>();

            foreach (XDoc param in function["param"])
            {
                string paramName = param["@name"].AsText;

                // determine if parameter has a default value
                string            paramDefault           = param["@default"].AsText;
                DekiScriptLiteral paramDefaultExpression = DekiScriptNil.Value;
                bool paramOptional = false;
                if (paramDefault != null)
                {
                    paramOptional = true;
                    try {
                        paramDefaultExpression = ScriptRuntime.Evaluate(DekiScriptParser.Parse(Location.Start, paramDefault), DekiScriptEvalMode.Evaluate, ScriptRuntime.CreateEnv());
                    } catch (Exception e) {
                        _log.ErrorExceptionFormat(e, "invalid default value for parameter {0} in function {1} in script {2}; skipping function definition", paramName, functionName, _manifestUri);
                        return(null);
                    }
                }
                else
                {
                    paramOptional = (param["@optional"].AsText == "true");
                }

                // determine parameter type
                string         paramType = param["@type"].AsText ?? "any";
                DekiScriptType paramScriptType;
                if (!SysUtil.TryParseEnum(paramType, out paramScriptType))
                {
                    _log.WarnFormat("unrecognized param type '{0}' for parameter {1} in function {2} in script {3}; defaulting to any", paramType, paramName, functionName, _manifestUri);
                    paramScriptType = DekiScriptType.ANY;
                }

                // add parameter
                parameters.Add(new DekiScriptParameter(paramName, paramScriptType, paramOptional, param.Contents, typeof(object), paramDefaultExpression));
            }
            var parameterArray = parameters.ToArray();

            // determine function body
            XDoc   ret  = function["return"];
            string src  = ret["@src"].AsText;
            string type = ret["@type"].AsText;
            DekiScriptExpression expression;

            if (!string.IsNullOrEmpty(src))
            {
                // 'src' attribute is set, load the script from it
                XDoc script;
                if (_manifestUri != null)
                {
                    // check if uri is relative
                    XUri scriptUri = XUri.TryParse(src) ?? _manifestUri.AtPath(src);
                    script = Plug.New(scriptUri).Get().ToDocument();
                }
                else
                {
                    // check if filename is relative
                    if (!Path.IsPathRooted(src))
                    {
                        src = Path.Combine(_resourcesPath, src);
                    }
                    script = XDocFactory.LoadFrom(src, MimeType.XML);
                }
                expression = DekiScriptParser.Parse(script);
                type       = type ?? "xml";
            }
            else if (!ret["html"].IsEmpty)
            {
                // <return> element contains a <html> node; parse it as a script
                expression = DekiScriptParser.Parse(ret["html"]);
                type       = type ?? "xml";
            }
            else if (!ret.IsEmpty)
            {
                // <return> element contains something else; use the text contents as deki-script expression
                var location = new Location(string.Format("/function[name={0}]/return", functionName));
                expression = DekiScriptParser.Parse(location, function["return"].AsText ?? string.Empty);
                expression = DekiScriptExpression.ReturnScope(location, expression);
                type       = type ?? "any";
            }
            else
            {
                _log.WarnFormat("function {0} has no body in script {1}; skipping function definition", functionName, _manifestUri);
                return(null);
            }

            // determine return type
            DekiScriptType returnScriptType;

            if (!SysUtil.TryParseEnum(type, out returnScriptType))
            {
                _log.WarnFormat("unrecognized return type '{0}' for function {1} in script {2}; defaulting to any", type, functionName, _manifestUri);
                returnScriptType = DekiScriptType.ANY;
            }

            // create function descriptor
            var    target      = new DekiScriptScriptFunctionInvocationTarget(access, parameterArray, expression, _commonEnv, returnScriptType);
            string description = function["description"].AsText;
            string transform   = function["@transform"].AsText;

            return(new DekiScriptInvocationTargetDescriptor(access, false, false, functionName, parameterArray, returnScriptType, description, transform, target));
        }
 //--- Methods ---
 public DekiScriptExpression Parse(string code)
 {
     return(DekiScriptParser.Parse(Location.Start, code));
 }
        private void Add(DekiExtFunctionAttribute ext, MethodInfo method, XDoc script)
        {
            // convert DekiExtParamAttribute into DekiScriptNativeInvocationTarget.Parameter
            var parameters = from param in method.GetParameters()
                             let attr = (DekiExtParamAttribute[])param.GetCustomAttributes(typeof(DekiExtParamAttribute), false)
                                        select((attr != null) && (attr.Length > 0)) ? new DekiScriptNativeInvocationTarget.Parameter(attr[0].Hint, attr[0].Optional) : null;

            // create native target invocation
            var target = new DekiScriptNativeInvocationTarget(this, method, parameters.ToArray());
            DekiScriptInvocationTargetDescriptor function;

            // check if implementation is provided by a script instead
            if (script != null)
            {
                var scriptTarget = new DekiScriptExpressionInvocationTarget(target.Access, target.Parameters, DekiScriptParser.Parse(script));
                function = new DekiScriptInvocationTargetDescriptor(target.Access, ext.IsProperty, false, ext.Name ?? method.Name, target.Parameters, target.ReturnType, ext.Description, ext.Transform, scriptTarget);
            }
            else
            {
                function = new DekiScriptInvocationTargetDescriptor(target.Access, ext.IsProperty, false, ext.Name ?? method.Name, target.Parameters, target.ReturnType, ext.Description, ext.Transform, target);
            }
            _functions[Self.At(function.SystemName)] = function;
        }
        //--- 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);
        }