1.1 --- a/moinformat/parsers/common.py Fri Jul 13 12:58:09 2018 +0200
1.2 +++ b/moinformat/parsers/common.py Fri Jul 13 16:59:27 2018 +0200
1.3 @@ -3,7 +3,7 @@
1.4 """
1.5 Moin wiki parsing functionality.
1.6
1.7 -Copyright (C) 2017 Paul Boddie <paul@boddie.org.uk>
1.8 +Copyright (C) 2017, 2018 Paul Boddie <paul@boddie.org.uk>
1.9
1.10 This program is free software; you can redistribute it and/or modify it under
1.11 the terms of the GNU General Public License as published by the Free Software
1.12 @@ -61,7 +61,15 @@
1.13 def __init__(self, s, pos=0):
1.14 self.s = s
1.15 self.pos = pos
1.16 +
1.17 + # Match details.
1.18 +
1.19 self.match = None
1.20 + self.queued = None
1.21 + self.match_start = None
1.22 +
1.23 + # Pattern name details.
1.24 +
1.25 self.matching = None
1.26
1.27 def rewind(self, length):
1.28 @@ -70,6 +78,12 @@
1.29
1.30 self.pos -= min(length, self.pos)
1.31
1.32 + def queue_match(self):
1.33 +
1.34 + "Rewind in the string to the start of the last match."
1.35 +
1.36 + self.queued = self.match
1.37 +
1.38 def read_until(self, patterns, remaining=True):
1.39
1.40 """
1.41 @@ -78,19 +92,23 @@
1.42 was found and 'remaining' is given as a false value.
1.43 """
1.44
1.45 - first = None
1.46 - self.matching = None
1.47 + if self.queued:
1.48 + self.match = self.queued
1.49 + self.queued = None
1.50 + else:
1.51 + self.match_start = None
1.52 + self.matching = None
1.53
1.54 - # Find the first matching pattern.
1.55 + # Find the first matching pattern.
1.56
1.57 - for pattern_name, pattern in patterns.items():
1.58 - match = pattern.search(self.s, self.pos)
1.59 - if match:
1.60 - start, end = match.span()
1.61 - if self.matching is None or start < first:
1.62 - first = start
1.63 - self.matching = pattern_name
1.64 - self.match = match
1.65 + for pattern_name, pattern in patterns.items():
1.66 + match = pattern.search(self.s, self.pos)
1.67 + if match:
1.68 + start, end = match.span()
1.69 + if self.matching is None or start < self.start:
1.70 + self.start = start
1.71 + self.matching = pattern_name
1.72 + self.match = match
1.73
1.74 if self.matching is None:
1.75 if remaining:
1.76 @@ -98,7 +116,7 @@
1.77 else:
1.78 return None
1.79 else:
1.80 - return self.s[self.pos:first]
1.81 + return self.s[self.pos:self.start]
1.82
1.83 def read_match(self, group=1):
1.84
1.85 @@ -137,7 +155,6 @@
1.86 """
1.87
1.88 self.formats = formats
1.89 - self.queued = defaultdict(list)
1.90
1.91 def get_parser(self, format_type):
1.92
1.93 @@ -324,14 +341,12 @@
1.94 "Add to 'region' the given 'node'."
1.95
1.96 region.add(node)
1.97 - self.unqueue_region(region, node)
1.98
1.99 def append_node(self, region, node):
1.100
1.101 "Append to 'region' the given 'node'."
1.102
1.103 region.append(node)
1.104 - self.unqueue_region(region, node)
1.105
1.106 def end_region(self, region):
1.107
1.108 @@ -339,25 +354,11 @@
1.109
1.110 raise StopIteration
1.111
1.112 - def queue_region(self, region, current):
1.113 -
1.114 - "Queue 'region' for appending after the 'current' region is ended."
1.115 -
1.116 - self.queued[current].append(region)
1.117 -
1.118 - def unqueue_region(self, region, ended):
1.119 -
1.120 - "Unqueue any queued region, adding it to 'region' after 'ended'."
1.121 + def queue_match(self):
1.122
1.123 - nodes = self.queued.get(ended)
1.124 + "Queue the current match."
1.125
1.126 - while nodes:
1.127 - node = nodes.pop()
1.128 - region.add(node)
1.129 - self.unqueue_region(region, node)
1.130 -
1.131 - if self.queued.has_key(ended):
1.132 - del self.queued[ended]
1.133 + self.items.queue_match()
1.134
1.135 def new_block(self, region):
1.136
2.1 --- a/moinformat/parsers/moin.py Fri Jul 13 12:58:09 2018 +0200
2.2 +++ b/moinformat/parsers/moin.py Fri Jul 13 16:59:27 2018 +0200
2.3 @@ -3,7 +3,7 @@
2.4 """
2.5 Moin wiki format parser.
2.6
2.7 -Copyright (C) 2017 Paul Boddie <paul@boddie.org.uk>
2.8 +Copyright (C) 2017, 2018 Paul Boddie <paul@boddie.org.uk>
2.9
2.10 This program is free software; you can redistribute it and/or modify it under
2.11 the terms of the GNU General Public License as published by the Free Software
2.12 @@ -212,31 +212,38 @@
2.13 marker = self.read_match(2)
2.14 space = self.read_match(3)
2.15
2.16 + last = region.node(-1)
2.17 + new_list = not isinstance(last, (List, ListItem))
2.18 +
2.19 + # If the marker is different or the indent is smaller, queue the item
2.20 + # and end the list.
2.21 +
2.22 + if not new_list and (last.marker != marker or indent < last.indent):
2.23 + self.queue_match()
2.24 + self.end_region(region)
2.25 +
2.26 + # Obtain a list item and populate it.
2.27 +
2.28 item = ListItem([], indent, marker, space)
2.29 self.parse_region_details(item, self.listitem_pattern_names)
2.30
2.31 - last = region.node(-1)
2.32 + # Start a new list if not preceded by a list item or if the indent is
2.33 + # greater.
2.34
2.35 - # Start a new list if not preceded by a list item.
2.36 -
2.37 - if not isinstance(last, ListItem):
2.38 + if new_list or indent > last.indent:
2.39 item = self.parse_list(item)
2.40
2.41 - # End the current list if the indent or marker is different from the
2.42 - # last list item.
2.43 + # Add a new or completed nested list.
2.44
2.45 - elif last.indent != indent or last.marker != marker:
2.46 -
2.47 - # Queue the new list, end this list, causing the new list to be
2.48 - # added after this one.
2.49 + self.add_node(region, item)
2.50
2.51 - self.queue_region(self.parse_list(item), region)
2.52 - self.end_region(region)
2.53 + if new_list:
2.54 + self.new_block(region)
2.55
2.56 - # Add a new item in a list or a completed nested list.
2.57 + # Add the item to the current list.
2.58
2.59 - self.add_node(region, item)
2.60 - self.new_block(region)
2.61 + else:
2.62 + self.add_node(region, item)
2.63
2.64 def parse_rule(self, region):
2.65