MoinLight

Annotated moinformat/parsers/table.py

72:27f1e816fb49
2018-07-17 Paul Boddie Introduced continuation padding nodes for correct Moin format serialisation.
paul@38 1
#!/usr/bin/env python
paul@38 2
paul@38 3
"""
paul@38 4
Moin wiki table parser.
paul@38 5
paul@54 6
Copyright (C) 2017, 2018 Paul Boddie <paul@boddie.org.uk>
paul@38 7
paul@38 8
This program is free software; you can redistribute it and/or modify it under
paul@38 9
the terms of the GNU General Public License as published by the Free Software
paul@38 10
Foundation; either version 3 of the License, or (at your option) any later
paul@38 11
version.
paul@38 12
paul@38 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@38 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@38 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@38 16
details.
paul@38 17
paul@38 18
You should have received a copy of the GNU General Public License along with
paul@38 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@38 20
"""
paul@38 21
paul@55 22
from moinformat.parsers.common import get_patterns, \
paul@55 23
                                      excl, expect, group
paul@42 24
from moinformat.parsers.moin import MoinParser
paul@72 25
from moinformat.tree import Continuation, Table, TableAttrs, TableCell, \
paul@72 26
                            TableRow, Text
paul@38 27
paul@55 28
join = "".join
paul@38 29
paul@38 30
# Parser functionality.
paul@38 31
paul@42 32
class TableParser(MoinParser):
paul@38 33
paul@38 34
    "A parser for improved table syntax."
paul@38 35
paul@38 36
    # Principal parser methods.
paul@38 37
paul@38 38
    def parse_region_content(self, items, region):
paul@38 39
paul@38 40
        "Parse the data provided by 'items' to populate the given 'region'."
paul@38 41
paul@38 42
        self.set_region(items, region)
paul@38 43
        self.parse_table_region()
paul@38 44
paul@38 45
    def parse_table_region(self):
paul@38 46
paul@38 47
        # Start to populate table rows.
paul@38 48
paul@38 49
        cell = TableCell([])
paul@38 50
        row = TableRow([cell])
paul@38 51
        table = Table([row])
paul@43 52
        self.append_node(self.region, table)
paul@38 53
paul@38 54
        while True:
paul@38 55
            self.parse_region_details(cell, self.table_region_pattern_names)
paul@38 56
paul@38 57
            # Detect the end of the table.
paul@38 58
paul@54 59
            pattern = self.matching_pattern()
paul@54 60
paul@54 61
            if pattern == "regionend":
paul@38 62
                break
paul@38 63
paul@54 64
            elif pattern == "columnsep":
paul@38 65
                cell = TableCell([])
paul@38 66
                row.append(cell)
paul@38 67
paul@54 68
            elif pattern == "rowsep":
paul@38 69
                row = TableRow([])
paul@38 70
                table.append(row)
paul@38 71
                cell = TableCell([])
paul@38 72
                row.append(cell)
paul@38 73
paul@38 74
    # Parser handler methods.
paul@38 75
paul@38 76
    def parse_continuation(self, cell):
paul@72 77
paul@72 78
        "Handle continuation padding."
paul@72 79
paul@72 80
        feature = self.match_group("feature")
paul@72 81
        cell.append(Continuation(feature))
paul@38 82
paul@38 83
    def parse_table_end(self, cell):
paul@38 84
paul@38 85
        "Handle the end of a region within 'cell'."
paul@38 86
paul@67 87
        level = self.match_group("level")
paul@67 88
        feature = self.match_group("feature")
paul@67 89
        self.region.extra = self.match_group("extra")
paul@67 90
paul@67 91
        if self.region.have_end(level):
paul@38 92
            raise StopIteration
paul@38 93
        else:
paul@38 94
            cell.append_inline(Text(feature))
paul@38 95
paul@38 96
    # Regular expressions.
paul@38 97
paul@38 98
    syntax = {}
paul@42 99
    syntax.update(MoinParser.syntax)
paul@38 100
    syntax.update({
paul@38 101
        # At start of line:
paul@55 102
paul@55 103
        "rowsep"        : join(("^==",                      # ==
paul@55 104
                                excl(r".*==\s*?$"),         # not-heading
paul@55 105
                                expect(r"\N*?"))),          # ws-excl-nl
paul@55 106
paul@72 107
        "continuation"  : group("feature",
paul@72 108
                          join(("^",
paul@55 109
                                group("indent", r"\N*"),    # ws... (optional)
paul@55 110
                                r"\.\.",                    # ..
paul@72 111
                                excl(r"\.")))),             # not-.
paul@38 112
paul@38 113
        # Within text:
paul@55 114
paul@55 115
        "columnsep"     : join((r"\|\|",                    # ||
paul@72 116
                                excl(r"\|"))),              # not-|
paul@38 117
        })
paul@38 118
paul@38 119
    patterns = get_patterns(syntax)
paul@38 120
paul@38 121
paul@38 122
paul@38 123
    # Pattern details.
paul@38 124
paul@57 125
    table_region_pattern_names = MoinParser.region_without_table_pattern_names + [
paul@55 126
        "columnsep", "continuation", "rowsep",
paul@38 127
        ]
paul@38 128
paul@38 129
paul@38 130
paul@38 131
    # Pattern handlers.
paul@38 132
paul@38 133
    handlers = {}
paul@42 134
    handlers.update(MoinParser.handlers)
paul@38 135
    handlers.update({
paul@42 136
        "columnsep" : MoinParser.end_region,
paul@38 137
        "continuation" : parse_continuation,
paul@42 138
        "rowsep" : MoinParser.end_region,
paul@38 139
        "regionend" : parse_table_end,
paul@38 140
        })
paul@38 141
paul@40 142
parser = TableParser
paul@40 143
paul@38 144
# vim: tabstop=4 expandtab shiftwidth=4