/// <summary>
        /// Converts lines into list items for a given prefix until the prefix no longer matches a line
        /// </summary>
        private IEnumerable <ListItemNode> CreateListItems(ListItemNode lastItemNode, string prefix, Parser parser, ParseData data, Lines lines, string scope)
        {
            do
            {
                var value = lines.Value().TrimEnd();

                if (!value.StartsWith(prefix))
                {
                    // No longer valid for this list
                    lines.Back();
                    yield break;
                }

                value = value.Substring(prefix.Length); // strip the prefix off the line

                // Possibilities:
                // empty string : not valid - stop parsing
                // first character is whitespace : trim and create list item
                // first character is list token, second character is whitespace: create sublist
                // anything else : not valid - stop parsing

                if (value.Length > 1 && value[0] == ' ' && prefix.Length > 0) // don't allow this if we're parsing at level 0
                {
                    // List item
                    value = value.Trim();
                    var pt = parser.ParseTags(data, value.Trim(), scope, "inline");
                    lastItemNode = new ListItemNode(pt);
                    yield return(lastItemNode);
                }
                else if (value.Length > 2 && IsListToken(value[0]) && value[1] == ' ' && lastItemNode != null)
                {
                    // Sublist
                    var tag     = IsSortedToken(value[0]) ? "ol" : "ul";
                    var sublist = new ListNode(tag, CreateListItems(lastItemNode, prefix + value[0], parser, data, lines, scope));
                    lastItemNode.Subtrees.Add(sublist);
                }
                else
                {
                    // Cannot parse this line, list is complete
                    lines.Back();
                    yield break;
                }
            } while (lines.Next());
        }
        public override INode Consume(Parser parser, ParseData data, Lines lines, string scope)
        {
            var arr = new List <TableRow>();

            do
            {
                var value = lines.Value().TrimEnd();
                if (value.Length < 2 || value[0] != '|' || (value[1] != '=' && value[1] != '-'))
                {
                    lines.Back();
                    break;
                }
                var cells = SplitTable(value.Substring(2)).Select(x => ResolveCell(x, parser, data, scope));
                arr.Add(new TableRow(value[1] == '=' ? "th" : "td", cells));
            } while (lines.Next());

            return(new HtmlNode("<table class=\"table table-bordered\">", new NodeCollection(arr), "</table>"));
        }
        public override INode Consume(Parser parser, ParseData data, Lines lines, string scope)
        {
            var value = lines.Value();
            var arr   = new List <string>
            {
                value.Substring(1).Trim()
            };

            while (lines.Next())
            {
                value = lines.Value().Trim();
                if (value.Length == 0 || value[0] != '>')
                {
                    lines.Back();
                    break;
                }
                arr.Add(value.Substring(1).Trim());
            }

            var text = String.Join("\n", arr);

            return(new HtmlNode("<blockquote>", parser.ParseElements(data, text, scope), "</blockquote>"));
        }