Ejemplo n.º 1
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);
        }