1 #!/usr/bin/env python 2 3 """ 4 Moin wiki table 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 from moinformat.parsers.common import get_patterns, \ 23 excl, expect, group 24 from moinformat.parsers.moin import MoinParser 25 from moinformat.tree import Continuation, Table, TableAttrs, TableCell, \ 26 TableRow, Text 27 28 join = "".join 29 30 # Parser functionality. 31 32 class TableParser(MoinParser): 33 34 "A parser for improved table syntax." 35 36 # Principal parser methods. 37 38 def parse_region_content(self, items, region): 39 40 "Parse the data provided by 'items' to populate the given 'region'." 41 42 self.set_region(items, region) 43 self.parse_table_region() 44 45 def parse_table_region(self): 46 47 # Start to populate table rows. 48 49 cell = TableCell([]) 50 row = TableRow([cell]) 51 table = Table([row]) 52 self.append_node(self.region, table) 53 54 while True: 55 self.parse_region_details(cell, self.table_region_pattern_names) 56 57 # Detect the end of the table. 58 59 pattern = self.matching_pattern() 60 61 if pattern == "regionend": 62 break 63 64 elif pattern == "columnsep": 65 cell = TableCell([]) 66 row.append(cell) 67 68 elif pattern == "rowsep": 69 row = TableRow([]) 70 table.append(row) 71 cell = TableCell([]) 72 row.append(cell) 73 74 # Parser handler methods. 75 76 def parse_continuation(self, cell): 77 78 "Handle continuation padding." 79 80 feature = self.match_group("feature") 81 cell.append(Continuation(feature)) 82 83 def parse_table_end(self, cell): 84 85 "Handle the end of a region within 'cell'." 86 87 level = self.match_group("level") 88 feature = self.match_group("feature") 89 self.region.extra = self.match_group("extra") 90 91 if self.region.have_end(level): 92 raise StopIteration 93 else: 94 cell.append_inline(Text(feature)) 95 96 # Regular expressions. 97 98 syntax = {} 99 syntax.update(MoinParser.syntax) 100 syntax.update({ 101 # At start of line: 102 103 "rowsep" : join(("^==", # == 104 excl(r".*==\s*?$"), # not-heading 105 expect(r"\N*?"))), # ws-excl-nl 106 107 "continuation" : group("feature", 108 join(("^", 109 group("indent", r"\N*"), # ws... (optional) 110 r"\.\.", # .. 111 excl(r"\.")))), # not-. 112 113 # Within text: 114 115 "columnsep" : join((r"\|\|", # || 116 excl(r"\|"))), # not-| 117 }) 118 119 patterns = get_patterns(syntax) 120 121 122 123 # Pattern details. 124 125 table_region_pattern_names = [ 126 "columnsep", "continuation", "rowsep", 127 ] + MoinParser.region_without_table_pattern_names 128 129 130 131 # Pattern handlers. 132 133 handlers = {} 134 handlers.update(MoinParser.handlers) 135 handlers.update({ 136 "columnsep" : MoinParser.end_region, 137 "continuation" : parse_continuation, 138 "rowsep" : MoinParser.end_region, 139 "regionend" : parse_table_end, 140 }) 141 142 parser = TableParser 143 144 # vim: tabstop=4 expandtab shiftwidth=4