1 #!/usr/bin/env python 2 3 """ 4 Moin wiki format parser. 5 6 Copyright (C) 2017, 2018 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 # Document transformations. 23 24 from moinformat.macros import get_macro 25 26 # Parser functionality and pattern definition. 27 28 from moinformat.parsers.common import ParserBase, get_patterns, choice, \ 29 excl, expect, group, optional, recur, \ 30 repeat 31 32 # Serialisation. 33 34 from moinformat.serialisers import serialise 35 36 # Document tree nodes. 37 38 from moinformat.tree.moin import Anchor, Break, Comment, DefItem, DefTerm, \ 39 Directive, FontStyle, Heading, Larger, \ 40 LineBreak, Link, LinkLabel, LinkParameter, \ 41 List, ListItem, Macro, Monospace, Region, \ 42 Rule, Smaller, Strikethrough, Subscript, \ 43 Superscript, Table, TableAttr, TableAttrs, \ 44 TableCell, TableRow, Text, Transclusion, \ 45 Underline, Verbatim 46 47 join = "".join 48 49 class MoinParser(ParserBase): 50 51 "A wiki region parser." 52 53 format = "moin" 54 55 def __init__(self, metadata, parsers=None, root=None): 56 57 """ 58 Initialise the parser with the given 'metadata' and optional 'parsers'. 59 An optional 'root' indicates the document-level parser. 60 """ 61 62 ParserBase.__init__(self, metadata, parsers, root) 63 64 # Record certain node occurrences for later evaluation. 65 66 self.macros = [] 67 68 # Record headings for identifier disambiguation. 69 70 self.headings = [] 71 72 # Principal parser methods. 73 74 def parse(self, s): 75 76 """ 77 Parse page text 's'. Pages consist of regions delimited by markers. 78 """ 79 80 self.items = self.get_items(s) 81 self.region = Region([], type="moin") 82 83 # Parse page header and directives. 84 85 self.parse_region_header(self.region) 86 self.parse_region_directives(self.region) 87 88 # Handle pages directly with this parser. Pages do not need to use an 89 # explicit format indicator. 90 91 if not self.region.type: 92 self.parse_region_content(self.items, self.region) 93 94 # Otherwise, test the type and find an appropriate parser. 95 96 else: 97 self.parse_region_type(self.region) 98 99 # Assign heading identifiers. 100 101 self.identify_headings() 102 103 return self.region 104 105 106 107 # Macro evaluation. 108 109 def evaluate_macros(self): 110 111 "Evaluate the macro nodes in the document." 112 113 for node in self.macros: 114 115 # Obtain a class for the named macro. 116 117 macro_cls = get_macro(node.name) 118 if not macro_cls: 119 continue 120 121 # Instantiate the class and evaluate the macro. 122 123 macro = macro_cls(node, self.region) 124 macro.evaluate() 125 126 # Heading disambiguation. 127 128 def identify_headings(self): 129 130 "Assign identifiers to headings based on their textual content." 131 132 d = {} 133 134 for heading in self.headings: 135 text = heading.text_content() 136 137 if not d.has_key(text): 138 d[text] = 0 139 heading.identifier = text 140 else: 141 d[text] += 1 142 heading.identifier = "%s-%d" % (text, d[text]) 143 144 145 146 # Conversion back to text. 147 148 def get_serialiser(self): 149 150 "Return metadata employing Moin as the output format." 151 152 metadata = self.metadata.copy() 153 metadata.set("link_format", None) 154 metadata.set("output_context", "standalone") 155 metadata.set("output_format", "moin") 156 return metadata.get_serialiser() 157 158 159 160 # Parser methods supporting different page features. 161 162 def parse_attrname(self, attrs): 163 164 "Handle an attribute name within 'attrs'." 165 166 name = self.match_group("name") 167 attr = TableAttr(name) 168 169 preceding = self.read_until(["attrvalue"], False) 170 if preceding == "": 171 attr.quote = self.match_group("quote") 172 attr.value = self.match_group("value") 173 174 attrs.append(attr) 175 176 def parse_break(self, region): 177 178 "Handle a paragraph break within 'region'." 179 180 self.add_node(region, Break()) 181 self.new_block(region) 182 183 def parse_comment(self, region): 184 185 "Handle a comment within 'region'." 186 187 comment = self.match_group("comment") 188 extra = self.match_group("extra") 189 self.add_node(region, Comment(comment, extra)) 190 self.new_block(region) 191 192 def parse_defitem(self, region, extra=""): 193 194 "Handle a definition item within 'region'." 195 196 pad = self.match_group("pad") 197 item = DefItem([], pad, extra) 198 self.parse_region_details(item, self.listitem_pattern_names) 199 self.add_node(region, item) 200 self.new_block(region) 201 202 def parse_defterm(self, region): 203 204 "Handle a definition term within 'region'." 205 206 pad = self.match_group("pad") 207 term = DefTerm([], pad) 208 self.parse_region_details(term, ["deftermend", "deftermsep"]) 209 self.add_node(region, term) 210 211 if self.matching_pattern() == "deftermsep": 212 self.parse_defitem(region) 213 214 # Add padding from the separator to the term, there being no item. 215 216 else: 217 term.extra = self.match_group("pad") 218 219 def parse_defterm_empty(self, region): 220 221 "Handle an empty definition term within 'region'." 222 223 extra = self.match_group("pad") 224 self.parse_region_details(region, ["deftermsep"]) 225 self.parse_defitem(region, extra) 226 227 def parse_directive(self, region): 228 229 "Handle a processing directive within 'region'." 230 231 directive = self.match_group("directive") 232 extra = self.match_group("extra") 233 self.add_node(region, Directive(directive, extra)) 234 self.new_block(region) 235 236 def parse_fontstyle(self, region): 237 238 "Handle emphasis and strong styles." 239 240 n = len(self.match_group("style")) 241 242 # Handle endings. 243 244 if isinstance(region, FontStyle): 245 emphasis = n in (2, 4, 5) 246 strong = n in (3, 5, 6) 247 active = True 248 249 if region.emphasis and emphasis: 250 active = region.close_emphasis() 251 n -= 2 252 if region.strong and strong: 253 active = region.close_strong() 254 n -= 3 255 256 if not active: 257 if n: 258 self.items.rewind(n) 259 raise StopIteration 260 261 elif not n: 262 return 263 264 # Handle new styles. 265 266 emphasis = n in (2, 4, 5) 267 strong = n in (3, 5, 6) 268 double = n in (4, 6) 269 270 span = FontStyle([], emphasis, strong) 271 if not double: 272 self.parse_region_details(span, self.inline_pattern_names) 273 region.append_inline(span) 274 275 def parse_halign(self, attrs): 276 277 "Handle horizontal alignment within 'attrs'." 278 279 value = self.match_group("value") 280 attr = TableAttr("halign", value == "(" and "left" or value == ")" and "right" or "center", True) 281 attrs.append(attr) 282 283 def parse_heading(self, region): 284 285 "Handle a heading." 286 287 start_extra = self.match_group("extra") 288 level = len(self.match_group("level")) 289 start_pad = self.match_group("pad") 290 heading = Heading([], level, start_extra, start_pad) 291 self.parse_region_details(heading, ["headingend"] + self.inline_pattern_names) 292 self.add_node(region, heading) 293 self.new_block(region) 294 295 # Record the heading for later processing. 296 297 self.root.headings.append(heading) 298 299 def parse_heading_end(self, heading): 300 301 "Handle the end of a heading." 302 303 level = len(self.match_group("level")) 304 if heading.level == level: 305 heading.end_pad = self.match_group("pad") 306 heading.end_extra = self.match_group("extra") 307 raise StopIteration 308 309 def parse_list(self, item): 310 311 "Create a list, starting with 'item'." 312 313 list = List([item], item.indent, item.marker, item.num) 314 self.parse_region_details(list, self.list_pattern_names, True) 315 return list 316 317 def parse_listitem(self, region): 318 319 "Handle a list item marker within 'region'." 320 321 indent = len(self.match_group("indent")) 322 marker = self.match_group("marker") 323 num = self.match_group("num") 324 space = self.match_group("pad") 325 326 last = region.node(-1) 327 328 new_list = not isinstance(last, (List, ListItem)) 329 same_indent = not new_list and indent == last.indent 330 new_marker = not new_list and last.marker != marker and same_indent 331 new_num = not new_list and num is not None and last.num != num and same_indent 332 333 # If the marker or number changes at the same indent, or if the indent 334 # is smaller, queue the item and end the list. 335 336 # Note that Moin format does not seek to support item renumbering, 337 # instead starting new lists on number changes. 338 339 if not new_list and (new_marker or new_num or indent < last.indent): 340 self.queue_match() 341 self.end_region(region) 342 343 # Obtain a list item and populate it. 344 345 item = ListItem([], indent, marker, space, num) 346 self.parse_region_details(item, self.listitem_pattern_names) 347 348 # Start a new list if not preceded by a list item, adding a trailing 349 # block for new elements. 350 351 if new_list: 352 item = self.parse_list(item) 353 self.add_node(region, item) 354 self.new_block(region) 355 356 # Add a nested list to the last item. 357 358 elif indent > last.indent: 359 item = self.parse_list(item) 360 self.add_node(last, item) 361 362 # Add the item to the current list. 363 364 else: 365 self.add_node(region, item) 366 367 def parse_rule(self, region): 368 369 "Handle a horizontal rule within 'region'." 370 371 length = len(self.match_group("rule")) 372 rule = Rule(length) 373 self.add_node(region, rule) 374 self.new_block(region) 375 376 def parse_section(self, region): 377 378 "Handle the start of a new section within 'region'." 379 380 # Parse the section and start a new block after the section. 381 382 indent = len(self.match_group("indent")) 383 level = len(self.match_group("level")) 384 385 section = self.parse_region(level, indent, "inline") 386 387 # If the section is inline, treat it like any other inline element. 388 389 if section.type == "inline": 390 region.append_inline(section) 391 392 # Otherwise, add it as a new block element. 393 394 else: 395 self.add_node(region, section) 396 if region.allow_blocks: 397 self.new_block(region) 398 399 def parse_table_attrs(self, cell): 400 401 "Handle the start of table attributes within 'cell'." 402 403 attrs = TableAttrs([]) 404 self.parse_region_details(attrs, self.table_attr_pattern_names) 405 406 # Test the validity of the attributes. 407 408 last = None 409 410 for node in attrs.nodes: 411 412 # Text separator nodes must be whitespace. 413 414 if isinstance(node, Text): 415 if node.s.strip(): 416 break 417 418 # Named attributes must be preceded by space if not the first. 419 420 elif last and not node.concise and not isinstance(last, Text): 421 break 422 423 last = node 424 425 # All nodes were valid: preserve the collection. 426 427 else: 428 # Add the attributes as a node, also recording their presence. 429 430 cell.append(attrs) 431 cell.attrs = attrs 432 return 433 434 # Invalid nodes were found: serialise the attributes as text. 435 436 cell.append_inline(Text(serialise(attrs, self.get_serialiser()))) 437 438 def parse_table_row(self, region): 439 440 "Handle the start of a table row within 'region'." 441 442 # Identify any active table. 443 444 table = region.node(-2) 445 block = region.node(-1) 446 447 if not (isinstance(table, Table) and block.empty()): 448 new_table = table = Table([]) 449 else: 450 new_table = None 451 452 row = TableRow([]) 453 454 while True: 455 cell = TableCell([]) 456 self.parse_region_details(cell, self.table_row_pattern_names) 457 458 # Handle the end of the row. 459 460 if self.matching_pattern() == "tableend": 461 trailing = self.match_group("extra") 462 463 # If the cell was started but not finished, convert the row into text. 464 465 if not row.nodes or not cell.empty(): 466 467 # Convert the nodes back to text. 468 469 serialiser = self.get_serialiser() 470 471 for node in row.nodes: 472 region.append_inline(Text(serialise(node, serialiser))) 473 474 region.append_inline(Text(serialise(cell, serialiser) + trailing)) 475 476 self.new_block(region) 477 return 478 479 # Append the final cell, if not empty. 480 481 else: 482 row.trailing = trailing 483 484 if not cell.empty(): 485 row.append(cell) 486 break 487 488 # A cell separator has been found. 489 490 row.append(cell) 491 492 # Add the row to the table and any new table to the region. 493 494 table.add(row) 495 if new_table: 496 self.add_node(region, new_table) 497 498 self.new_block(region) 499 500 def parse_valign(self, attrs): 501 502 "Handle vertical alignment within 'attrs'." 503 504 value = self.match_group("value") 505 attr = TableAttr("valign", value == "^" and "top" or "bottom", True) 506 attrs.append(attr) 507 508 509 510 def inline_patterns_for(self, name): 511 names = self.inline_pattern_names[:] 512 names[names.index(name)] = "%send" % name 513 return names 514 515 516 517 # Inline formatting handlers. 518 519 def parse_inline(self, region, cls, pattern_name): 520 521 "Handle an inline region." 522 523 span = cls([]) 524 self.parse_region_details(span, self.inline_patterns_for(pattern_name)) 525 region.append_inline(span) 526 527 def parse_larger(self, region): 528 self.parse_inline(region, Larger, "larger") 529 530 def parse_monospace(self, region): 531 span = Monospace([]) 532 self.parse_region_details(span, ["monospaceend"]) 533 region.append_inline(span) 534 535 def parse_smaller(self, region): 536 self.parse_inline(region, Smaller, "smaller") 537 538 def parse_strike(self, region): 539 self.parse_inline(region, Strikethrough, "strike") 540 541 def parse_sub(self, region): 542 self.parse_inline(region, Subscript, "sub") 543 544 def parse_super(self, region): 545 self.parse_inline(region, Superscript, "super") 546 547 def parse_underline(self, region): 548 self.parse_inline(region, Underline, "underline") 549 550 # Link formatting handlers. 551 552 def _parse_link(self, region, cls, pattern_names): 553 target = self.match_group("target") 554 end = self.match_group("end") 555 556 span = cls([], target) 557 558 # Obtain the extra details. 559 560 if not end: 561 cls = LinkLabel 562 563 # Introduce a label or parameter for each separated region. 564 565 while True: 566 param = cls([]) 567 self.parse_region_details(param, pattern_names) 568 span.append(param) 569 570 if self.matching_pattern() != "linksep": 571 break 572 573 cls = LinkParameter 574 575 region.append_inline(span) 576 577 def parse_link(self, region): 578 self._parse_link(region, Link, self.link_pattern_names) 579 580 def parse_transclusion(self, region): 581 self._parse_link(region, Transclusion, self.transclusion_pattern_names) 582 583 584 585 # Complete inline pattern handlers. 586 587 def parse_anchor(self, region): 588 target = self.match_group("target") 589 anchor = Anchor(target) 590 region.append_inline(anchor) 591 592 def parse_linebreak(self, region): 593 region.append_inline(LineBreak()) 594 595 def parse_macro(self, region): 596 name = self.match_group("name") 597 args = self.match_group("args") 598 599 # Obtain the raw arguments. Moin usually leaves it to the macro to 600 # interpret the individual arguments. 601 602 arglist = args and args.split(",") or [] 603 macro = Macro(name, arglist, region.append_point(), region) 604 region.append_inline(macro) 605 606 # Record the macro for later processing. 607 608 self.root.macros.append(macro) 609 610 def parse_verbatim(self, region): 611 text = self.match_group("verbatim") 612 region.append_inline(Verbatim(text)) 613 614 615 616 # Table attribute handlers. 617 618 def parse_table_attr(self, attrs, pattern_name): 619 620 "Handle a table attribute." 621 622 attrs.append(TableAttr(pattern_name, self.match_group("value"), True)) 623 624 def parse_colour(self, cell): 625 self.parse_table_attr(cell, "colour") 626 627 def parse_colspan(self, cell): 628 self.parse_table_attr(cell, "colspan") 629 630 def parse_rowspan(self, cell): 631 self.parse_table_attr(cell, "rowspan") 632 633 def parse_width(self, cell): 634 self.parse_table_attr(cell, "width") 635 636 637 638 # Regular expressions. 639 640 syntax = { 641 # Page regions: 642 643 "regionstart" : join((group("indent", r"\N*"), # ws... (optional) 644 group("level", repeat("[{]", 3)))), # {{{... 645 646 "regionend" : join((r"\N*", # ws... (optional) 647 group("feature", join(( 648 group("level", repeat("[}]", 3)), # }}}... 649 optional(group("extra", r"\n"))))))), # nl (optional) 650 651 # Region header and directives: 652 653 "header" : join(("#!", # #! 654 group("args", ".*?"), "\n")), # text-excl-nl 655 656 "directive" : join((r"^#", # # 657 group("directive", r".*?$"), # rest of line 658 optional(group("extra", r"\n")))), # nl (optional) 659 660 # Region contents: 661 662 # Line-oriented patterns support features which require their own 663 # separate lines. 664 665 "break" : r"^(\s*?)\n", # blank line 666 667 "comment" : join((r"^##", # ## 668 group("comment", r".*?$"), # rest of line 669 optional(group("extra", r"\n")))), # nl (optional) 670 671 "defterm" : join(("^", 672 group("pad", r"\N+"), # ws... 673 expect(".+?::"))), # text :: 674 675 "defterm_empty" : join(("^", 676 group("pad", r"\N+"), # ws... 677 expect("::\s+"))), # :: ws... 678 679 "heading" : join(("^", 680 group("extra", r"\N*"), # ws... (optional) 681 group("level", "=+"), # =... 682 group("pad", r"\s+"), # ws... 683 expect(join((r".*?\N+", # text 684 recur("level"), # =... 685 r"\N*$"))))), # ws... (optional) 686 687 "listitem" : join(("^", 688 group("indent", r"\N+"), # ws... 689 group("marker", r"\*"), # list-marker 690 group("pad", r"\s*"))), # ws... (optional) 691 692 "listitem_num" : join(("^", 693 group("indent", r"\N+"), # ws... 694 group("marker", r"\d+\."), # decimal-marker 695 optional(join(("#", group("num", r"\d+")))), # # num (optional) 696 group("pad", r"\s+"))), # ws... 697 698 "listitem_alpha": join(("^", 699 group("indent", r"\N+"), # ws... 700 group("marker", r"[aA]\."), # alpha-marker 701 optional(join(("#", group("num", r"\d+")))), # # num (optional) 702 group("pad", r"\s+"))), # ws... 703 704 "listitem_roman": join(("^", 705 group("indent", r"\N+"), # ws... 706 group("marker", r"[iI]\."), # roman-marker 707 optional(join(("#", group("num", r"\d+")))), # # num (optional) 708 group("pad", r"\s+"))), # ws... 709 710 "listitem_dot" : join(("^", 711 group("indent", r"\N+"), # ws... 712 group("marker", r"\."), # dot-marker 713 group("pad", r"\s*"))), # ws... (optional) 714 715 "tablerow" : r"^\|\|", # || 716 717 # Region contents: 718 719 # Inline patterns are for markup features that appear within blocks. 720 # The patterns below start inline spans that can contain other markup 721 # features. 722 723 "fontstyle" : group("style", repeat("'", 2, 6)), # ''... 724 "larger" : r"~\+", # ~+ 725 "monospace" : r"`", # ` 726 "rule" : group("rule", "-----*"), # ----... 727 "smaller" : r"~-", # ~- 728 "strike" : r"--\(", # --( 729 "sub" : r",,", # ,, 730 "super" : r"\^", # ^ 731 "underline" : r"__", # __ 732 733 # Links and transclusions may start inline spans. 734 735 "link" : join((r"\[\[", # [[ 736 group("target", ".*?"), # ... 737 choice((r"\|", # | 738 group("end", r"]]"))))), # ]] 739 740 "transclusion" : join((r"\{\{", # {{ 741 excl(r"\{"), # not-{ 742 group("target", ".*?"), # ... 743 choice((r"\|", # | 744 group("end", r"}}"))))), # }} 745 746 # Complete inline patterns are for markup features that do not support 747 # arbitrary content within them: 748 749 "anchor" : join((r"\(\(", # (( 750 group("target", ".*?"), # target 751 r"\)\)")), # )) 752 753 "linebreak" : r"\\\\", # \\ 754 755 "macro" : join(("<<", # << 756 group("name", "\w+?"), # digit-letter... 757 optional(join((r"\(", # ( (optional) 758 group("args", ".*?"), # not-)... 759 r"\)"))), # ) (optional) 760 ">>")), # >> 761 762 "verbatim" : join(("<<<", # <<< 763 group("verbatim", ".*?"), # ... 764 ">>>")), 765 766 # Ending patterns for inline features: 767 768 "largerend" : r"\+~", # +~ 769 "linkend" : r"]]", # ]] 770 "monospaceend" : r"`", # ` 771 "smallerend" : r"-~", # -~ 772 "strikeend" : r"\)--", # )-- 773 "subend" : r",,", # ,, 774 "superend" : r"\^", # ^ 775 "transclusionend": r"}}", # }} 776 "underlineend" : r"__", # __ 777 778 # Heading contents: 779 780 "headingend" : join((group("pad", r"\N+"), # ws... 781 group("level", "=+"), # =... 782 group("extra", r"\N*\n"))), # ws (optional) nl 783 784 # Link/transclusion contents: 785 786 "linksep" : r"\|", # | 787 788 # List contents: 789 790 "deftermend" : join(("::", group("pad", r"\s*?\n"))), # :: 791 # ws... (optional) 792 # nl 793 794 "deftermsep" : join(("::", group("pad", r"\s+"))), # :: 795 # ws... 796 797 "listitemend" : join((r"^", # next line 798 choice((expect(r"[^\s]"), # without indent 799 expect(r"\Z"), # end of string 800 expect(r"\N+\*"), # or with ws... list-marker 801 expect(r"\N+\d\."), # or with ws... decimal-marker 802 expect(r"\N+[aA]\."), # or with ws... alpha-marker 803 expect(r"\N+[iI]\."), # or with ws... roman-marker 804 expect(r"\N+\."), # or with ws... dot-marker 805 expect(r"\N+.+?::\s"), # or with ws... text :: ws (next defterm) 806 expect(r"\N+::\s"))))), # or with ws... :: ws (next defitem) 807 808 # Table contents: 809 810 "tableattrs" : join(("<", # lt 811 excl("<"))), # not-lt 812 813 "tablecell" : r"\|\|", # || 814 815 "tableend" : join((group("extra", r"\s*?"), # ws... (optional) 816 "^")), # next line 817 818 # Table attributes: 819 820 "tableattrsend" : r">", # > 821 "halign" : group("value", "[(:)]"), # halign-marker 822 "valign" : group("value", "[v^]"), # valign-marker 823 "colour" : group("value", join(("\#", # # 824 repeat("[0-9A-F]", 6, 6)))), # nnnnnn 825 826 "colspan" : join(("-", # - 827 group("value", "\d+"))), # n... 828 829 "rowspan" : join((r"\|", # | 830 group("value", "\d+"))), # n... 831 832 "width" : group("value", "\d+%"), # n... % 833 834 "attrname" : join((excl(r"[-\d]"), # not-dash-or-digit 835 group("name", r"[-\w]+"))), # dash-digit-letter... 836 837 "attrvalue" : join(("=", group("quote", r"\Q"), # quote 838 group("value", ".*?"), # non-quote... (optional) 839 recur("quote"))), # quote 840 } 841 842 patterns = get_patterns(syntax) 843 844 845 846 # Patterns available within certain markup features. 847 848 table_attr_pattern_names = [ 849 "attrname", "colour", "colspan", "halign", "rowspan", "tableattrsend", 850 "valign", "width" 851 ] 852 853 inline_without_links_pattern_names = [ 854 "anchor", "fontstyle", "larger", "linebreak", "macro", 855 "monospace", "regionstart", "smaller", "strike", "sub", "super", 856 "underline", "verbatim" 857 ] 858 859 inline_pattern_names = inline_without_links_pattern_names + [ 860 "link", "transclusion"] 861 862 link_pattern_names = inline_without_links_pattern_names + [ 863 "linkend", "linksep", "transclusion"] 864 865 list_pattern_names = [ 866 "listitem", "listitem_alpha", "listitem_dot", "listitem_num", 867 "listitem_roman", 868 ] 869 870 listitem_pattern_names = inline_pattern_names + ["listitemend"] 871 872 region_without_table_pattern_names = inline_pattern_names + list_pattern_names + [ 873 "break", "comment", "heading", "defterm", "defterm_empty", 874 "regionend", "rule", 875 ] 876 877 table_row_pattern_names = inline_pattern_names + [ 878 "tableattrs", "tablecell", "tableend" 879 ] 880 881 transclusion_pattern_names = inline_without_links_pattern_names + [ 882 "linksep", "transclusionend"] 883 884 # The region pattern names are specifically used by the common parser 885 # functionality. 886 887 region_pattern_names = region_without_table_pattern_names + ["tablerow"] 888 889 890 891 # Pattern handlers. 892 893 end_region = ParserBase.end_region 894 parse_section_end = ParserBase.parse_region_end 895 896 handlers = { 897 None : end_region, 898 "anchor" : parse_anchor, 899 "attrname" : parse_attrname, 900 "break" : parse_break, 901 "colour" : parse_colour, 902 "colspan" : parse_colspan, 903 "comment" : parse_comment, 904 "defterm" : parse_defterm, 905 "defterm_empty" : parse_defterm_empty, 906 "deftermend" : end_region, 907 "deftermsep" : end_region, 908 "directive" : parse_directive, 909 "fontstyle" : parse_fontstyle, 910 "halign" : parse_halign, 911 "heading" : parse_heading, 912 "headingend" : parse_heading_end, 913 "larger" : parse_larger, 914 "largerend" : end_region, 915 "linebreak" : parse_linebreak, 916 "link" : parse_link, 917 "linkend" : end_region, 918 "linksep" : end_region, 919 "macro" : parse_macro, 920 "listitemend" : end_region, 921 "listitem" : parse_listitem, 922 "listitem_alpha" : parse_listitem, 923 "listitem_dot" : parse_listitem, 924 "listitem_num" : parse_listitem, 925 "listitem_roman" : parse_listitem, 926 "monospace" : parse_monospace, 927 "monospaceend" : end_region, 928 "regionstart" : parse_section, 929 "regionend" : parse_section_end, 930 "rowspan" : parse_rowspan, 931 "rule" : parse_rule, 932 "smaller" : parse_smaller, 933 "smallerend" : end_region, 934 "strike" : parse_strike, 935 "strikeend" : end_region, 936 "sub" : parse_sub, 937 "subend" : end_region, 938 "super" : parse_super, 939 "superend" : end_region, 940 "tableattrs" : parse_table_attrs, 941 "tableattrsend" : end_region, 942 "tablerow" : parse_table_row, 943 "tablecell" : end_region, 944 "tableend" : end_region, 945 "transclusion" : parse_transclusion, 946 "transclusionend" : end_region, 947 "underline" : parse_underline, 948 "underlineend" : end_region, 949 "valign" : parse_valign, 950 "verbatim" : parse_verbatim, 951 "width" : parse_width, 952 } 953 954 parser = MoinParser 955 956 # vim: tabstop=4 expandtab shiftwidth=4