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 feature = self.match_group("level") 83 if self.region.have_end(feature): 84 raise StopIteration 85 else: 86 cell.append_inline(Text(feature)) 87 88 # Regular expressions. 89 90 syntax = {} 91 syntax.update(MoinParser.syntax) 92 syntax.update({ 93 # At start of line: 94 95 "rowsep" : join(("^==", # == 96 excl(r".*==\s*?$"), # not-heading 97 expect(r"\N*?"))), # ws-excl-nl 98 99 "continuation" : join(("^", 100 group("indent", r"\N*"), # ws... (optional) 101 r"\.\.", # .. 102 excl(r"\."), # not-. 103 expect(r"\N"))), # ws 104 105 # Within text: 106 107 "columnsep" : join((r"\|\|", # || 108 excl(r"\|"), # not-| 109 expect(r"\N"))), # ws 110 }) 111 112 patterns = get_patterns(syntax) 113 114 115 116 # Pattern details. 117 118 table_region_pattern_names = MoinParser.region_without_table_pattern_names + [ 119 "columnsep", "continuation", "rowsep", 120 ] 121 122 123 124 # Pattern handlers. 125 126 handlers = {} 127 handlers.update(MoinParser.handlers) 128 handlers.update({ 129 "columnsep" : MoinParser.end_region, 130 "continuation" : parse_continuation, 131 "rowsep" : MoinParser.end_region, 132 "regionend" : parse_table_end, 133 }) 134 135 parser = TableParser 136 137 # vim: tabstop=4 expandtab shiftwidth=4