# HG changeset patch # User Paul Boddie # Date 1527859112 -7200 # Node ID 518c6bf3b8ca590f080ca24f37f773580ec30a83 # Parent 083967e3240691a39d5360b1edc5f041bd20960b Introduced initial support for list creation, also making new_block a method, and adding some control over whitespace accumulation. diff -r 083967e32406 -r 518c6bf3b8ca moinformat/parsers/common.py --- a/moinformat/parsers/common.py Wed Dec 13 00:50:09 2017 +0100 +++ b/moinformat/parsers/common.py Fri Jun 01 15:18:32 2018 +0200 @@ -19,6 +19,7 @@ this program. If not, see . """ +from collections import defaultdict from moinformat.tree import Block, Region, Text import re @@ -120,16 +121,6 @@ -# Utility functions. - -def new_block(region): - - "Start a new block in 'region'." - - region.add(Block([])) - - - # Parser abstractions. class ParserBase: @@ -146,6 +137,7 @@ """ self.formats = formats + self.queued = defaultdict(list) def get_parser(self, format_type): @@ -225,7 +217,7 @@ # Define a block to hold text and start parsing. - new_block(region) + self.new_block(region) if self.region_pattern_names: self.parse_region_details(region, self.region_pattern_names) @@ -284,9 +276,12 @@ # Parsing utilities. - def parse_region_details(self, region, pattern_names): + def parse_region_details(self, region, pattern_names, strict=False): - "Search 'region' using the 'pattern_names'." + """ + Search 'region' using the 'pattern_names'. If 'strict' is set to a true + value, forbid the accumulation of additional textual padding. + """ try: while True: @@ -295,7 +290,10 @@ preceding = self.read_until(pattern_names) if preceding: - region.append_inline(Text(preceding)) + if not strict: + region.append_inline(Text(preceding)) + else: + break # End of input. @@ -311,18 +309,60 @@ if handler: handler(self, region) + elif not strict: + region.append_inline(Text(feature)) else: - region.append_inline(Text(feature)) + break except StopIteration: pass region.normalise() + def add_node(self, region, node): + + "Add to 'region' the given 'node'." + + region.add(node) + self.unqueue_region(region, node) + + def append_node(self, region, node): + + "Append to 'region' the given 'node'." + + region.append(node) + self.unqueue_region(region, node) + def end_region(self, region): "End the parsing of 'region', breaking out of the parsing loop." raise StopIteration + def queue_region(self, region, current): + + "Queue 'region' for appending after the 'current' region is ended." + + self.queued[current].append(region) + + def unqueue_region(self, region, ended): + + "Unqueue any queued region, adding it to 'region' after 'ended'." + + nodes = self.queued.get(ended) + + while nodes: + node = nodes.pop() + region.add(node) + self.unqueue_region(region, node) + + if self.queued.has_key(ended): + del self.queued[ended] + + def new_block(self, region): + + "Start a new block in 'region'." + + self.add_node(region, Block([])) + # vim: tabstop=4 expandtab shiftwidth=4 diff -r 083967e32406 -r 518c6bf3b8ca moinformat/parsers/moin.py --- a/moinformat/parsers/moin.py Wed Dec 13 00:50:09 2017 +0100 +++ b/moinformat/parsers/moin.py Fri Jun 01 15:18:32 2018 +0200 @@ -19,11 +19,11 @@ this program. If not, see . """ -from moinformat.parsers.common import ParserBase, get_patterns, get_subset, new_block +from moinformat.parsers.common import ParserBase, get_patterns, get_subset from moinformat.serialisers import serialise from moinformat.tree import Break, DefItem, DefTerm, FontStyle, Heading, \ - Larger, ListItem, Monospace, Region, Rule, Smaller, \ - Subscript, Superscript, Table, TableAttr, \ + Larger, List, ListItem, Monospace, Region, Rule, \ + Smaller, Subscript, Superscript, Table, TableAttr, \ TableAttrs, TableCell, TableRow, Text, Underline class MoinParser(ParserBase): @@ -95,8 +95,8 @@ "Handle a paragraph break within 'region'." - region.add(Break()) - new_block(region) + self.add_node(region, Break()) + self.new_block(region) def parse_defitem(self, region, extra=""): @@ -105,8 +105,8 @@ pad = self.read_match(1) item = DefItem([], pad, extra) self.parse_region_details(item, ["listitemend"]) - region.add(item) - new_block(region) + self.add_node(region, item) + self.new_block(region) def parse_defterm(self, region): @@ -115,7 +115,7 @@ pad = self.read_match(1) term = DefTerm([], pad) self.parse_region_details(term, ["deftermend", "deftermsep"]) - region.add(term) + self.add_node(region, term) if self.read_matching() == "deftermsep": self.parse_defitem(region) @@ -183,8 +183,8 @@ start_pad = self.read_match(3) heading = Heading([], level, start_extra, start_pad) self.parse_region_details(heading, ["headingend"] + self.inline_pattern_names) - region.add(heading) - new_block(region) + self.add_node(region, heading) + self.new_block(region) def parse_heading_end(self, heading): @@ -196,6 +196,14 @@ heading.end_extra = self.read_match(3) raise StopIteration + def parse_list(self, item): + + "Create a list, starting with 'item'." + + list = List([item], item.indent, item.marker) + self.parse_region_details(list, self.list_pattern_names, True) + return list + def parse_listitem(self, region): "Handle a list item marker within 'region'." @@ -203,10 +211,32 @@ indent = len(self.read_match(1)) marker = self.read_match(2) space = self.read_match(3) + item = ListItem([], indent, marker, space) self.parse_region_details(item, self.listitem_pattern_names) - region.add(item) - new_block(region) + + last = region.node(-1) + + # Start a new list if not preceded by a list item. + + if not isinstance(last, ListItem): + item = self.parse_list(item) + + # End the current list if the indent or marker is different from the + # last list item. + + elif last.indent != indent or last.marker != marker: + + # Queue the new list, end this list, causing the new list to be + # added after this one. + + self.queue_region(self.parse_list(item), region) + self.end_region(region) + + # Add a new item in a list or a completed nested list. + + self.add_node(region, item) + self.new_block(region) def parse_rule(self, region): @@ -214,8 +244,8 @@ length = len(self.read_match(1)) rule = Rule(length) - region.add(rule) - new_block(region) + self.add_node(region, rule) + self.new_block(region) def parse_section(self, region): @@ -225,8 +255,8 @@ indent = len(self.read_match(2)) level = len(self.read_match(3)) - region.add(self.parse_region(level, indent)) - new_block(region) + self.add_node(region, self.parse_region(level, indent)) + self.new_block(region) def parse_section_end(self, region): @@ -307,7 +337,7 @@ region.append_inline(Text(serialise(cell))) region.append_inline(Text(trailing)) - new_block(region) + self.new_block(region) return # Append the final cell, if not empty. @@ -327,9 +357,9 @@ table.add(row) if new_table: - region.add(new_table) + self.add_node(region, new_table) - new_block(region) + self.new_block(region) def parse_valign(self, attrs): @@ -483,11 +513,15 @@ "fontstyle", "larger", "monospace", "smaller", "sub", "super", "underline", ] + list_pattern_names = [ + "listitem", "listitem_alpha", "listitem_dot", "listitem_num", + "listitem_roman", + ] + listitem_pattern_names = inline_pattern_names + ["listitemend"] - region_pattern_names = inline_pattern_names + [ - "break", "heading", "defterm", "defterm_empty", "listitem", - "listitem_alpha", "listitem_dot", "listitem_num", "listitem_roman", + region_pattern_names = inline_pattern_names + list_pattern_names + [ + "break", "heading", "defterm", "defterm_empty", "regionstart", "regionend", "rule", "tablerow", ] diff -r 083967e32406 -r 518c6bf3b8ca moinformat/parsers/table.py --- a/moinformat/parsers/table.py Wed Dec 13 00:50:09 2017 +0100 +++ b/moinformat/parsers/table.py Fri Jun 01 15:18:32 2018 +0200 @@ -47,7 +47,7 @@ cell = TableCell([]) row = TableRow([cell]) table = Table([row]) - self.region.append(table) + self.append_node(self.region, table) while True: self.parse_region_details(cell, self.table_region_pattern_names) diff -r 083967e32406 -r 518c6bf3b8ca moinformat/serialisers/html.py --- a/moinformat/serialisers/html.py Wed Dec 13 00:50:09 2017 +0100 +++ b/moinformat/serialisers/html.py Fri Jun 01 15:18:32 2018 +0200 @@ -80,6 +80,15 @@ def end_larger(self): self.out("") + # NOTE: Need to employ list type information, perhaps moving it from the + # NOTE: individual items. + + def start_list(self): + self.out("
    ") + + def end_list(self): + self.out("
") + def start_listitem(self, indent, marker, space): self.out("
  • ") diff -r 083967e32406 -r 518c6bf3b8ca moinformat/serialisers/moin.py --- a/moinformat/serialisers/moin.py Wed Dec 13 00:50:09 2017 +0100 +++ b/moinformat/serialisers/moin.py Fri Jun 01 15:18:32 2018 +0200 @@ -73,6 +73,12 @@ def end_larger(self): self.out("+~") + def start_list(self): + pass + + def end_list(self): + pass + def start_listitem(self, indent, marker, space): self.out("%s%s%s" % (indent * " ", marker, space)) diff -r 083967e32406 -r 518c6bf3b8ca moinformat/tree.py --- a/moinformat/tree.py Wed Dec 13 00:50:09 2017 +0100 +++ b/moinformat/tree.py Fri Jun 01 15:18:32 2018 +0200 @@ -266,6 +266,27 @@ self._to_string(out) out.end_heading(self.level, self.end_pad, self.end_extra) +class List(Container): + + "A list." + + def __init__(self, nodes, indent, marker): + Container.__init__(self, nodes) + self.indent = indent + self.marker = marker + + def __repr__(self): + return "List(%r, %r, %r)" % (self.nodes, self.indent, self.marker) + + def prettyprint(self, indent=""): + l = ["%sList: indent=%d marker=%r" % (indent, self.indent, self.marker)] + return self._prettyprint(l, indent) + + def to_string(self, out): + out.start_list() + self._to_string(out) + out.end_list() + class ListItem(Container): "A list item." diff -r 083967e32406 -r 518c6bf3b8ca tests/test3.txt --- a/tests/test3.txt Wed Dec 13 00:50:09 2017 +0100 +++ b/tests/test3.txt Fri Jun 01 15:18:32 2018 +0200 @@ -3,3 +3,6 @@ i. Romanus eunt domus! I. What did they do for us? + 1. {{{ +Doing for us}}} + a. The Romans.