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