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