private Tag CollectForTag(Tag tag, ref int index)
        {
            if (tag.IsClosed) // if self-closing tag, do not collect inner elements
            {
                return tag;
            }

            if (string.Compare(tag.Name, "if", true) == 0)
            {
                tag = new TagIf(tag.Line, tag.Col, tag.AttributeValue("test"));
            }

            Tag collectTag = tag;

            for (index++; index < elements.Count; index++)
            {
                Element elem = elements[index];

                if (elem is Text)
                    collectTag.InnerElements.Add(elem);
                else if (elem is Expression)
                    collectTag.InnerElements.Add(elem);
                else if (elem is Tag)
                {
                    Tag innerTag = (Tag)elem;
                    if (string.Compare(innerTag.Name, "else", true) == 0)
                    {
                        if (collectTag is TagIf)
                        {
                            ((TagIf)collectTag).FalseBranch = innerTag;
                            collectTag = innerTag;
                        }
                        else
                            throw new ParseException("else tag has to be positioned inside of if or elseif tag", innerTag.Line, innerTag.Col);

                    }
                    else if (string.Compare(innerTag.Name, "elseif", true) == 0)
                    {
                        if (collectTag is TagIf)
                        {
                            Tag newTag = new TagIf(innerTag.Line, innerTag.Col, innerTag.AttributeValue("test"));
                            ((TagIf)collectTag).FalseBranch = newTag;
                            collectTag = newTag;
                        }
                        else
                            throw new ParseException("elseif tag is not positioned properly", innerTag.Line, innerTag.Col);
                    }
                    else
                        collectTag.InnerElements.Add(CollectForTag(innerTag, ref index));
                }
                else if (elem is TagClose)
                {
                    TagClose tagClose = (TagClose)elem;
                    if (string.Compare(tag.Name, tagClose.Name, true) == 0)
                        return tag;

                    throw new ParseException("Close tag for " + tagClose.Name + " doesn't have matching start tag.", elem.Line, elem.Col);
                }
                else
                    throw new ParseException("Invalid element: " + elem.GetType().ToString(), elem.Line, elem.Col);

            }

            throw new ParseException("Start tag: " + tag.Name + " does not have matching end tag.", tag.Line, tag.Col);
        }
        Tag ReadTag()
        {
            Consume(TokenKind.TagStart);
            Token name = Consume(TokenKind.ID);
            Tag tag = new Tag(name.Line, name.Col, name.Data);

            while (true)
            {
                if (Current.TokenKind == TokenKind.ID)
                    tag.Attributes.Add(ReadAttribute());
                else if (Current.TokenKind == TokenKind.TagEnd)
                {
                    Consume();
                    break;
                }
                else if (Current.TokenKind == TokenKind.TagEndClose)
                {
                    Consume();
                    tag.IsClosed = true;
                    break;
                }
                else
                    throw new ParseException("Invalid token in tag: " + Current.TokenKind, Current.Line, Current.Col);

            }

            return tag;
        }
 protected void ProcessTag(Tag tag)
 {
     string name = tag.Name.ToLowerInvariant();
     switch (name)
     {
         case "template":
             // skip those, because those are processed first
             break;
         case "else":
             ProcessElements(tag.InnerElements);
             break;
         case "apply":
             object val = EvalExpression(tag.AttributeValue("template"));
             ProcessTemplate(val.ToString(), tag);
             break;
         case "foreach":
             ProcessForEach(tag);
             break;
         default:
             ProcessTemplate(tag.Name, tag);
             break;
     }
 }
        protected void ProcessTemplate(string name, Tag tag)
        {
            Template useTemplate = _currentTemplate.FindTemplate(name);
            if (useTemplate == null)
            {
                string msg = string.Format("Template '{0}' not found", name);
                WriteError(msg, tag.Line, tag.Col);
                return;
            }

            // process inner elements and save content
            TextWriter saveWriter = _writer;
            _writer = new StringWriter();
            string content = string.Empty;

            try
            {
                ProcessElements(tag.InnerElements);

                content = _writer.ToString();
            }
            catch
            {
                return; // on error, do not do tag inclusion
            }
            finally
            {
                _writer = saveWriter;
            }

            Template saveTemplate = _currentTemplate;
            _variables = new VariableScope(_variables);
            _variables["innerText"] = content;

            try
            {
                foreach (TagAttribute attrib in tag.Attributes)
                {
                    object val = EvalExpression(attrib.Expression);
                    _variables[attrib.Name] = val;
                }

                _currentTemplate = useTemplate;
                ProcessElements(_currentTemplate.Elements);
            }
            finally
            {
                _variables = _variables.Parent;
                _currentTemplate = saveTemplate;
            }
        }
        protected void ProcessForEach(Tag tag)
        {
            Expression expCollection = tag.AttributeValue("collection");
            if (expCollection == null)
            {
                WriteError("Foreach is missing required attribute: collection", tag.Line, tag.Col);
                return;
            }

            object collection = EvalExpression(expCollection);
            if (!(collection is IEnumerable))
            {
                WriteError("Collection used in foreach has to be enumerable", tag.Line, tag.Col);
                return;
            }

            Expression expVar = tag.AttributeValue("var");
            if (expCollection == null)
            {
                WriteError("Foreach is missing required attribute: var", tag.Line, tag.Col);
                return;
            }
            object varObject = EvalExpression(expVar);
            if (varObject == null)
                varObject = "foreach";
            string varname = varObject.ToString();

            Expression expIndex = tag.AttributeValue("index");
            string indexname = null;
            if (expIndex != null)
            {
                object obj = EvalExpression(expIndex);
                if (obj != null)
                    indexname = obj.ToString();
            }

            IEnumerator ienum = ((IEnumerable)collection).GetEnumerator();
            int index = 0;
            while (ienum.MoveNext())
            {
                index++;
                object value = ienum.Current;
                _variables[varname] = value;
                if (indexname != null)
                    _variables[indexname] = index;

                ProcessElements(tag.InnerElements);
            }
        }