Linter Demo Errors: 7Warnings: 13File: /home/fstrocco/Dart/dart/benchmark/markdown/lib/src/block_parser.dart library markdown.block_parser; import 'ast.dart'; import 'document.dart'; import 'util.dart'; final _RE_EMPTY = new RegExp(r'^([ \t]*)$'); final _RE_SETEXT = new RegExp(r'^((=+)|(-+))$'); final _RE_HEADER = new RegExp(r'^(#{1,6})(.*?)#*$'); final _RE_BLOCKQUOTE = new RegExp(r'^[ ]{0,3}>[ ]?(.*)$'); final _RE_INDENT = new RegExp(r'^(?: |\t)(.*)$'); final _RE_CODE = new RegExp(r'^(`{3,}|~{3,})(.*)$'); final _RE_HR = new RegExp(r'^[ ]{0,3}((-+[ ]{0,2}){3,}|' r'(_+[ ]{0,2}){3,}|' r'(\*+[ ]{0,2}){3,})$'); final _RE_HTML = new RegExp(r'^<[ ]*\w+[ >]'); final _RE_UL = new RegExp(r'^[ ]{0,3}[*+-][ \t]+(.*)$'); final _RE_OL = new RegExp(r'^[ ]{0,3}\d+\.[ \t]+(.*)$'); class BlockParser { final List lines; final Document document; int _pos; BlockParser(this.lines, this.document) : _pos = 0; String get current => lines[_pos]; String get next { if (_pos >= lines.length - 1) return null; return lines[_pos + 1]; } void advance() { _pos++; } bool get isDone => _pos >= lines.length; bool matches(RegExp regex) { if (isDone) return false; return regex.firstMatch(current) != null; } bool matchesNext(RegExp regex) { if (next == null) return false; return regex.firstMatch(next) != null; } } abstract class BlockSyntax { static const List syntaxes = const [const EmptyBlockSyntax(), const BlockHtmlSyntax(), const SetextHeaderSyntax(), const HeaderSyntax(), const CodeBlockSyntax(), const FencedCodeBlockSyntax(), const BlockquoteSyntax(), const HorizontalRuleSyntax(), const UnorderedListSyntax(), const OrderedListSyntax(), const ParagraphSyntax()]; const BlockSyntax(); RegExp get pattern => null; bool get canEndBlock => true; bool canParse(BlockParser parser) { return pattern.firstMatch(parser.current) != null; } Node parse(BlockParser parser); List parseChildLines(BlockParser parser) { final childLines = []; while (!parser.isDone) { final match = pattern.firstMatch(parser.current); if (match == null) break; childLines.add(match[1]); parser.advance(); } return childLines; } static bool isAtBlockEnd(BlockParser parser) { if (parser.isDone) return true; return syntaxes.any((s) => s.canParse(parser) && s.canEndBlock); } } class EmptyBlockSyntax extends BlockSyntax { RegExp get pattern => _RE_EMPTY; const EmptyBlockSyntax(); Node parse(BlockParser parser) { parser.advance(); return null; } } class SetextHeaderSyntax extends BlockSyntax { const SetextHeaderSyntax(); bool canParse(BlockParser parser) { return parser.matchesNext(_RE_SETEXT); } Node parse(BlockParser parser) { final match = _RE_SETEXT.firstMatch(parser.next); final tag = (match[1][0] == '=') ? 'h1' : 'h2'; final contents = parser.document.parseInline(parser.current); parser.advance(); parser.advance(); return new Element(tag, contents); } } class HeaderSyntax extends BlockSyntax { RegExp get pattern => _RE_HEADER; const HeaderSyntax(); Node parse(BlockParser parser) { final match = pattern.firstMatch(parser.current); parser.advance(); final level = match[1].length; final contents = parser.document.parseInline(match[2].trim()); return new Element('h$level', contents); } } class BlockquoteSyntax extends BlockSyntax { RegExp get pattern => _RE_BLOCKQUOTE; const BlockquoteSyntax(); Node parse(BlockParser parser) { final childLines = parseChildLines(parser); final children = parser.document.parseLines(childLines); return new Element('blockquote', children); } } class CodeBlockSyntax extends BlockSyntax { RegExp get pattern => _RE_INDENT; const CodeBlockSyntax(); List parseChildLines(BlockParser parser) { final childLines = []; while (!parser.isDone) { var match = pattern.firstMatch(parser.current); if (match != null) { childLines.add(match[1]); parser.advance(); } else { var nextMatch = parser.next != null ? pattern.firstMatch(parser.next) : null; if (parser.current.trim() == '' && nextMatch != null) { childLines.add(''); childLines.add(nextMatch[1]); parser.advance(); parser.advance(); } else { break; } } } return childLines; } Node parse(BlockParser parser) { final childLines = parseChildLines(parser); childLines.add(''); final escaped = escapeHtml(childLines.join('\n')); return new Element('pre', [new Element.text('code', escaped)]); } } class FencedCodeBlockSyntax extends BlockSyntax { RegExp get pattern => _RE_CODE; const FencedCodeBlockSyntax(); List parseChildLines(BlockParser parser, [String endBlock]) { if (endBlock == null) endBlock = ''; final childLines = []; parser.advance(); while (!parser.isDone) { var match = pattern.firstMatch(parser.current); if (match == null || !match[1].startsWith(endBlock)) { childLines.add(parser.current); parser.advance(); } else { parser.advance(); break; } } return childLines; } Node parse(BlockParser parser) { var match = pattern.firstMatch(parser.current); var endBlock = match.group(1); var syntax = match.group(2); final childLines = parseChildLines(parser, endBlock); childLines.add(''); final escaped = escapeHtml(childLines.join('\n')); var element = new Element('pre', [new Element.text('code', escaped)]); if (syntax != '') { element.attributes['class'] = syntax; } return element; } } class HorizontalRuleSyntax extends BlockSyntax { RegExp get pattern => _RE_HR; const HorizontalRuleSyntax(); Node parse(BlockParser parser) { parser.advance(); return new Element.empty('hr'); } } class BlockHtmlSyntax extends BlockSyntax { RegExp get pattern => _RE_HTML; bool get canEndBlock => false; const BlockHtmlSyntax(); Node parse(BlockParser parser) { final childLines = []; while (!parser.isDone && !parser.matches(_RE_EMPTY)) { childLines.add(parser.current); parser.advance(); } return new Text(childLines.join('\n')); } } class ListItem { bool forceBlock = false; final List lines; ListItem(this.lines); } abstract class ListSyntax extends BlockSyntax { bool get canEndBlock => false; String get listTag; const ListSyntax(); Node parse(BlockParser parser) { final items = []; var childLines = []; endItem() { if (childLines.length > 0) { items.add(new ListItem(childLines)); childLines = []; } } var match; tryMatch(RegExp pattern) { match = pattern.firstMatch(parser.current); return match != null; } while (!parser.isDone) { if (tryMatch(_RE_EMPTY)) { childLines.add(''); } else if (tryMatch(_RE_UL) || tryMatch(_RE_OL)) { endItem(); childLines.add(match[1]); } else if (tryMatch(_RE_INDENT)) { childLines.add(match[1]); } else if (BlockSyntax.isAtBlockEnd(parser)) { break; } else { if ((childLines.length > 0) && (childLines.last == '')) break; childLines.add(parser.current); } parser.advance(); } endItem(); for (int i = 0; i < items.length; i++) { for (int j = items[i].lines.length - 1; j > 0; j--) { if (_RE_EMPTY.firstMatch(items[i].lines[j]) != null) { if (i < items.length - 1) { items[i].forceBlock = true; items[i + 1].forceBlock = true; } items[i].lines.removeLast(); } else { break; } } } final itemNodes = []; for (final item in items) { bool blockItem = item.forceBlock || (item.lines.length > 1); final blocksInList = [_RE_BLOCKQUOTE, _RE_HEADER, _RE_HR, _RE_INDENT, _RE_UL, _RE_OL]; if (!blockItem) { for (final pattern in blocksInList) { if (pattern.firstMatch(item.lines[0]) != null) { blockItem = true; break; } } } if (blockItem) { final children = parser.document.parseLines(item.lines); itemNodes.add(new Element('li', children)); } else { final contents = parser.document.parseInline(item.lines[0]); itemNodes.add(new Element('li', contents)); } } return new Element(listTag, itemNodes); } } class UnorderedListSyntax extends ListSyntax { RegExp get pattern => _RE_UL; String get listTag => 'ul'; const UnorderedListSyntax(); } class OrderedListSyntax extends ListSyntax { RegExp get pattern => _RE_OL; String get listTag => 'ol'; const OrderedListSyntax(); } class ParagraphSyntax extends BlockSyntax { bool get canEndBlock => false; const ParagraphSyntax(); bool canParse(BlockParser parser) => true; Node parse(BlockParser parser) { final childLines = []; while (!BlockSyntax.isAtBlockEnd(parser)) { childLines.add(parser.current); parser.advance(); } final contents = parser.document.parseInline(childLines.join('\n')); return new Element('p', contents); } }