public DekiScriptOutputBuffer.Range Visit(DekiScriptForeach expr, DekiScriptExpressionEvaluationState state)
        {
            state.ThrowIfTimedout();
            int marker = state.Buffer.Marker;

            try {
                DekiScriptGeneratorEvaluation.Generate(expr.Generator, delegate(DekiScriptEnv subEnv) {
                    // iterate over block statements
                    try {
                        expr.Body.VisitWith(this, state.With(subEnv));
                    } catch (DekiScriptContinueException) {
                        // ignore continue exceptions
                    }
                }, state);
            } catch (DekiScriptBreakException) {
                // ignore break exceptions
            }
            return(state.Buffer.Since(marker));
        }
        public DekiScriptLiteral Visit(DekiScriptForeach expr, DekiScriptEnv env)
        {
            bool safe = env.IsSafeMode;

            try {
                DekiScriptEvaluationAccumulator accumulator = new DekiScriptEvaluationAccumulator();
                DekiScriptGeneratorEvaluation.Instance.Generate(expr.Generator, delegate(DekiScriptEnv subEnv) {
                    // iterate over block statements
                    try {
                        accumulator.Add(expr.Block.VisitWith(this, subEnv), safe);
                    } catch (DekiScriptAbort.Exception e) {
                        accumulator.Add(e.AccumulatedState, safe);
                        if (e.FlowControl == DekiScriptAbort.Kind.Break)
                        {
                            throw new DekiScriptAbort.Exception(e.FlowControl, accumulator.Value);
                        }
                    }
                }, env.NewLocalScope());
                return(accumulator.Value);
            } catch (DekiScriptAbort.Exception e) {
                return(e.AccumulatedState);
            }
        }
        public DekiScriptExpression Visit(DekiScriptForeach expr, DekiScriptExpressionEvaluationState state)
        {
            // TODO (steveb): partial evaluation broke with the introduction of generators

            return(expr);

            //DekiScriptExpression collection = Collection.Optimize(mode, env);

            //// check if we can unroll the loop
            //if(collection is DekiScriptLiteral) {

            //    // 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 {
            //        return new DekiScriptForeach(Line, Column, Var, collection, Body);
            //    }

            //    // loop over collection
            //    List<DekiScriptExpression> expressions = new List<DekiScriptExpression>();
            //    int index = 0;
            //    foreach(DekiScriptLiteral value in list) {
            //        DekiScriptEnv subEnv = env.NewLocalScope();

            //        // set the environment variable
            //        subEnv.Vars.Add(Var, value);
            //        subEnv.Vars.Add(DekiScriptRuntime.COUNT_ID, DekiScriptExpression.Constant(index));
            //        subEnv.Vars.Add(DekiScriptRuntime.INDEX_ID, DekiScriptExpression.Constant(index));

            //        // NOTE (steveb): we wrap the outcome into a sequence to ensure proper handling of break/continue statements

            //        expressions.Add(DekiScriptSequence.New(DekiScriptSequence.ScopeKind.ScopeCatchContinue, value.Optimize(mode, subEnv)));
            //        ++index;
            //    }
            //    return DekiScriptSequence.New(DekiScriptSequence.ScopeKind.ScopeCatchBreakAndContinue, expressions.ToArray());
            //} else {
            //    DekiScriptEnv subEnv = env.NewLocalScope();

            //    // set the environment variable to unknown
            //    subEnv.Vars.Add(Var, DekiScriptUnknown.Value);
            //    subEnv.Vars.Add(DekiScriptRuntime.COUNT_ID, DekiScriptUnknown.Value);
            //    subEnv.Vars.Add(DekiScriptRuntime.INDEX_ID, DekiScriptUnknown.Value);

            //    // partially evaluate the inner loop
            //    DekiScriptExpression block = Body.Optimize(mode, subEnv);
            //    return new DekiScriptForeach(Line, Column, Var, collection, block);
            //}
        }
示例#4
0
        //--- Methods ---
        private XmlNode Parse(XmlElement current, List <DekiScriptExpression> list)
        {
            XmlNode next = current.NextSibling;
            string  value;

            // check if element needs to be evaluated
            try {
                if (current.NamespaceURI.EqualsInvariant(XDekiScript.ScriptNS))
                {
                    // element has "eval:" prefix
                    switch (current.LocalName)
                    {
                        #region <if test="bool-expr">...</if>{<elseif test="bool-expr">...</elseif>}[<else>...</else>]
                    case "if": {
                        List <Tuplet <DekiScriptExpression, DekiScriptExpression> > conditionals = new List <Tuplet <DekiScriptExpression, DekiScriptExpression> >();

                        // initial "if" statement
                        DekiScriptExpression condition = Parse(SubLocation("/@test"), current.GetAttribute("test"));
                        conditionals.Add(new Tuplet <DekiScriptExpression, DekiScriptExpression>(condition, BuildChildren(current)));

                        // check for subsequent "elseif" and "else" statements
                        while (true)
                        {
                            // move to next node
                            XmlNode originalNext = next;

                            // skip empty text nodes
                            while ((next != null) && ((next.NodeType == XmlNodeType.Whitespace) || (next.NodeType == XmlNodeType.SignificantWhitespace) || ((next.NodeType == XmlNodeType.Text) && (next.Value.Trim().Length == 0))))
                            {
                                next = next.NextSibling;
                            }

                            // check if next node is an alternate branch
                            if ((next != null) && next.NamespaceURI.EqualsInvariant(XDekiScript.ScriptNS) && (next.LocalName.EqualsInvariant("elseif") || next.LocalName.EqualsInvariant("else")))
                            {
                                current = (XmlElement)next;
                                PopNode();
                                PushNode(current);
                                next = current.NextSibling;
                                if (current.LocalName.EqualsInvariant("elseif"))
                                {
                                    // process "elseif" branch
                                    condition = Parse(SubLocation("/@test"), current.GetAttribute("test"));
                                    conditionals.Add(new Tuplet <DekiScriptExpression, DekiScriptExpression>(condition, BuildChildren(current)));
                                }
                                else
                                {
                                    // process "else" branch
                                    conditionals.Add(new Tuplet <DekiScriptExpression, DekiScriptExpression>(null, BuildChildren(current)));
                                    break;
                                }
                            }
                            else
                            {
                                // couln't find an alternatte branch, restore the original next node
                                next = originalNext;
                                break;
                            }
                        }
                        list.Add(DekiScriptExpression.IfElseStatements(Location, conditionals));
                    }
                    break;
                        #endregion

                        #region <foreach [var="id"] in="list-or-map-or-xml-expr" [where|test="bool-expr"]>...</foreach>
                    case "foreach": {
                        string variable = current.HasAttribute("var") ? current.GetAttribute("var").Trim() : DekiScriptRuntime.DEFAULT_ID;
                        string where = current.GetAttribute("where");
                        if (string.IsNullOrEmpty(where))
                        {
                            where = current.GetAttribute("test");
                        }
                        DekiScriptGenerator generator = null;
                        if (!string.IsNullOrEmpty(where))
                        {
                            var location = SubLocation("/@where");
                            generator = new DekiScriptGeneratorIf(location, Parse(location, where), null);
                        }
                        generator = new DekiScriptGeneratorForeachValue(Location, new[] { variable }, Parse(Location, current.GetAttribute("in")), generator);
                        list.Add(DekiScriptExpression.ForeachStatement(Location, generator, BuildChildren(current)));
                    }
                    break;
                        #endregion

                        #region <expr value="expr" /> -OR- <expr>expr</expr>
                    case "expr": {
                        string code = current.HasAttribute("value") ? current.GetAttribute("value") : current.InnerText;
                        DekiScriptExpression expr = Parse(Location, code);
                        list.Add(expr);
                    }
                    break;
                        #endregion

                        #region <js value="expr" /> -OR- <js>expr</js>
                    case "js": {
                        string code = current.HasAttribute("value") ? current.GetAttribute("value") : current.InnerText;
                        DekiScriptExpression expr = Parse(Location, code);
                        list.Add(DekiScriptExpression.Call(Location, DekiScriptExpression.Access(Location, DekiScriptExpression.Id(Location, "json"), DekiScriptExpression.Constant("emit")), new DekiScriptListConstructor(null, expr)));
                    }
                    break;
                        #endregion

                        #region <block value="expr">...</block>
                    case "block": {
                        // TODO (steveb): it seems odd we use InnerText here instead of value; what is the motivation?
                        string code = current.HasAttribute("value") ? current.GetAttribute("value") : current.InnerText;
                        list.Add(DekiScriptExpression.BlockWithDeclaration(Location, Parse(Location, code), BuildChildren(current)));
                    }
                    break;
                        #endregion

                    default:
                        throw new DekiScriptParserException(string.Format("{0}, unknown elementn <eval:{1}>", Location, current.LocalName), Location.None);
                    }
                }
                else
                {
                    List <DekiScriptExpression> nodes = new List <DekiScriptExpression>();

                    // process "function" attribute
                    if (!string.IsNullOrEmpty(value = current.GetAttribute("function")))
                    {
                        // NOTE (steveb): process content transform

                        // check if function contains '$' sign, which is a place holder for the main argument
                        DekiScriptExpression evaluation = Parse(SubLocation("/@function"), (value.IndexOf('$') < 0) ? value + "($)" : value);

                        // determine if main argument is a string or an xml document
                        DekiScriptExpression arg;
                        if (current.LocalName.EqualsInvariant("pre"))
                        {
                            ConvertBrToNewline(current);

                            // pass argument in as a string
                            arg = DekiScriptExpression.Constant(StripCode(current.InnerText));
                        }
                        else
                        {
                            // pass argument in as a HTML document
                            List <DekiScriptExpression> inner = new List <DekiScriptExpression>();
                            BuildElement(current, inner);

                            DekiScriptExpression body = DekiScriptExpression.XmlElement(Location, null, DekiScriptExpression.Constant("body"), null, DekiScriptExpression.Block(Location, inner));
                            DekiScriptExpression html = DekiScriptExpression.XmlElement(Location, null, DekiScriptExpression.Constant("html"), null, body);
                            arg = html;
                        }

                        // create DOM expression
                        DekiScriptExpression assign     = DekiScriptExpression.VarStatement(Location, DekiScriptExpression.Id(Location, DekiScriptRuntime.DEFAULT_ID), arg);
                        DekiScriptExpression statements = DekiScriptExpression.BlockWithDeclaration(Location, assign, evaluation);
                        nodes.Add(TryCatch(statements, true));
                    }
                    else if (current.LocalName.EqualsInvariant("span") && current.GetAttribute("class").EqualsInvariant("script"))
                    {
                        ConvertBrToNewline(current);

                        // convert <span class="script">...</span> to <eval:expr>...</eval:expr>
                        nodes.Add(TryCatch(Parse(Location, new XmlNodePlainTextReadonlyByteStream(current)), true));
                    }
                    else if (current.LocalName.EqualsInvariant("pre"))
                    {
                        string cls = current.GetAttribute("class");
                        if (cls.EqualsInvariant("script"))
                        {
                            ConvertBrToNewline(current);

                            DekiScriptExpression expr = TryCatch(Parse(Location, new XmlNodePlainTextReadonlyByteStream(current)), true);
                            nodes.Add(expr);
                        }
                        else if (cls.EqualsInvariant("script-jem"))
                        {
                            ConvertBrToNewline(current);

                            // convert <pre class="script-jem">...</pre> to <html><body><script type="text/jem">...</script></body></html>
                            DekiScriptExpression html = Html("body", "script", "text/jem", DekiScriptExpression.Constant(current.InnerText.ReplaceAll("\u00A0", " ", "\00AD", "")));
                            nodes.Add(html);
                        }
                        else if (cls.EqualsInvariant("script-js"))
                        {
                            ConvertBrToNewline(current);

                            // convert <pre class="script-js">...</pre> to <html><body><script type="text/js">...</script></body></html>
                            DekiScriptExpression html = Html("body", "script", "text/javascript", DekiScriptExpression.Constant(current.InnerText.ReplaceAll("\u00A0", " ", "\00AD", "")));
                            nodes.Add(html);
                        }
                        else if (cls.EqualsInvariant("script-css"))
                        {
                            ConvertBrToNewline(current);

                            // convert <pre class="script-css">...</pre> to <html><head><style type="text/css">...</style></head></html>
                            DekiScriptExpression html = Html("head", "style", "text/css", DekiScriptExpression.Constant(current.InnerText.ReplaceAll("\u00A0", " ", "\00AD", "")));
                            nodes.Add(html);
                        }
                        else
                        {
                            BuildElement(current, nodes);
                        }
                    }
                    else
                    {
                        BuildElement(current, nodes);
                    }

                    // process "block" attribute
                    bool scripted = false;
                    if (!string.IsNullOrEmpty(value = current.GetAttribute("block")))
                    {
                        scripted = true;

                        // attribute "block" is present
                        var location = SubLocation("/@block");
                        DekiScriptExpression blockExpr = Parse(location, value);
                        blockExpr = DekiScriptExpression.BlockWithDeclaration(Location, blockExpr, nodes);
                        nodes.Clear();
                        nodes.Add(blockExpr);
                    }

                    // process "foreach" attribute
                    if (!string.IsNullOrEmpty(value = current.GetAttribute("foreach")))
                    {
                        scripted = true;

                        // attribute "foreach" is present
                        StringBuilder expression = new StringBuilder();
                        expression.Append(value);
                        string where = current.GetAttribute("where");
                        if (!string.IsNullOrEmpty(where))
                        {
                            expression.Append(", if ").Append(where);
                        }
                        var location = SubLocation("/@foreach");
                        DekiScriptForeach    foreachExpr  = (DekiScriptForeach)Parse(location, "foreach(" + expression + "){}");
                        DekiScriptExpression foreachExpr2 = DekiScriptExpression.ForeachStatement(location, foreachExpr.Generator, nodes);
                        nodes.Clear();
                        nodes.Add(foreachExpr2);
                    }

                    // process "if" attribute
                    if (!string.IsNullOrEmpty(value = current.GetAttribute("if")))
                    {
                        scripted = true;

                        // attribute "if" is present
                        var location = SubLocation("/@if");
                        DekiScriptExpression condition = Parse(location, value);
                        condition = DekiScriptExpression.IfElseStatements(location, new[] { new Tuplet <DekiScriptExpression, DekiScriptExpression>(condition, DekiScriptExpression.Block(Location, nodes)) });
                        nodes.Clear();
                        nodes.Add(condition);
                    }

                    // process "init" attribute
                    if (!string.IsNullOrEmpty(value = current.GetAttribute("init")))
                    {
                        scripted = true;

                        // attribute "init" is present
                        DekiScriptExpression init = Parse(Location, value);
                        DekiScriptExpression dom  = DekiScriptExpression.BlockWithDeclaration(SubLocation("/@init"), init, nodes);
                        nodes.Clear();
                        nodes.Add(dom);
                    }

                    // append inner nodes
                    switch (nodes.Count)
                    {
                    case 0:

                        // nothing to do
                        break;

                    case 1:
                        list.Add(TryCatch(nodes[0], scripted));
                        break;

                    default:
                        list.AddRange(nodes);
                        break;
                    }
                }
            } catch (Exception e) {
                XDoc warning = new XDoc("html").Start("body").Add(DekiScriptRuntime.CreateWarningFromException(null, Location, e)).End();
                list.Add(new DekiScriptXml(warning));
            }
            return(next);
        }