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