1 #!/usr/bin/env python 2 3 """ 4 Moin wiki table parser. 5 6 Copyright (C) 2017, 2018, 2021 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, excl, group 23 from moinformat.parsers.moin import MoinParser 24 from moinformat.tree.moin import Break, Table, TableAttrs, TableCell, \ 25 TableRow, Text 26 from moinformat.tree.table import Continuation 27 28 join = "".join 29 30 # Parser functionality. 31 32 class TableParser(MoinParser): 33 34 "A parser for improved table syntax." 35 36 formats = ["table"] 37 38 # Principal parser methods. 39 40 def parse_region_content(self, items, region): 41 42 """ 43 Parse the data provided by 'items' to populate the given 'region'. For 44 table regions, normal region handling is wrapped by management of the 45 table structure. 46 """ 47 48 self.set_region(items, region) 49 50 # Start to populate table rows. 51 52 cell = TableCell([]) 53 row = TableRow([cell]) 54 table = Table([row]) 55 self.append_node(self.region, table) 56 self.new_block(cell) 57 58 while True: 59 self.parse_region_details(cell, self.region_pattern_names) 60 61 # Suppress any initial paragraph break within a table cell. This is 62 # caused by a blank line which might be employed for clarity. 63 64 first = cell.node(0) 65 66 if isinstance(first, Break): 67 cell.remove(first) 68 cell.padding += "\n" 69 70 # Detect the end of the table. 71 72 pattern = self.matching_pattern() 73 74 if not pattern or pattern == "regionend": 75 break 76 77 elif pattern == "columnsep": 78 cell = TableCell([], leading=self.match_group("leading"), 79 padding=self.match_group("padding")) 80 row.append(cell) 81 self.new_block(cell) 82 83 elif pattern == "rowsep": 84 row = TableRow([], leading=self.match_group("leading"), 85 padding=self.match_group("padding")) 86 table.append(row) 87 cell = TableCell([]) 88 row.append(cell) 89 self.new_block(cell) 90 91 # Parser handler methods. 92 93 def parse_continuation(self, cell): 94 95 "Handle continuation padding." 96 97 feature = self.match_group("feature") 98 self.append_inline(cell, Continuation(feature)) 99 100 def parse_table_attrs(self, cell): 101 102 "Handle the start of table attributes within 'cell'." 103 104 MoinParser.parse_table_attrs(self, cell) 105 106 if isinstance(cell.node(-1), TableAttrs): 107 self.new_block(cell) 108 109 # Regular expressions. 110 111 syntax = {} 112 syntax.update(MoinParser.syntax) 113 syntax.update({ 114 # At start of line: 115 116 "rowsep" : join((group("leading", r"\s*"), # ws... (optional) 117 "^==", # == 118 excl(r".*==\s*?$"), # not-heading 119 group("padding", r"\s*"))), # ws... (optional) 120 121 "continuation" : group("feature", 122 join((group("leading", r"\s*"), # ws... (optional) 123 "^", 124 group("indent", r"\s*"), # ws... (optional) 125 r"\.\.", # .. 126 excl(r"\."), # not-. 127 group("padding", r"\s*")))),# ws... (optional) 128 129 # Within text: 130 131 "columnsep" : join((group("leading", r"\s*"), # ws... (optional) 132 r"\|\|", # || 133 excl(r"\|"), # not-| 134 group("padding", r"\s*"))), # ws... (optional) 135 }) 136 137 patterns = get_patterns(syntax) 138 139 140 141 # Pattern details. 142 143 region_pattern_names = [ 144 "columnsep", "continuation", "rowsep", "tableattrs", 145 ] + MoinParser.region_without_table_pattern_names 146 147 148 149 # Pattern handlers. 150 151 end_region = MoinParser.end_region 152 parse_table_end = MoinParser.parse_region_end 153 154 handlers = {} 155 handlers.update(MoinParser.handlers) 156 handlers.update({ 157 "columnsep" : end_region, 158 "continuation" : parse_continuation, 159 "rowsep" : end_region, 160 "regionend" : parse_table_end, 161 "tableattrs" : parse_table_attrs, 162 }) 163 164 parser = TableParser 165 166 # vim: tabstop=4 expandtab shiftwidth=4