# HG changeset patch # User Paul Boddie # Date 1532440424 -7200 # Node ID 645911c29199dd97e224ed3a4ce96a1cadba969b # Parent 848f3e4413f14a325bf23a6c3f2432b1d7fbcb2b# Parent 15c19a3d61e91702213e84071c618d3647717fea Merged changes from the default branch. diff -r 848f3e4413f1 -r 645911c29199 convert.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/convert.py Tue Jul 24 15:53:44 2018 +0200 @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +from moinformat import all_parsers, get_serialiser, parse, serialise +from os.path import split +import sys + +def test_option(args, name): + if name in args: + args.remove(name) + return True + else: + return False + +def main(): + dirname, progname = split(sys.argv[0]) + args = sys.argv[1:] + + l = filenames = [] + formats = [] + tree = False + + for arg in args: + + # Detect tree output. + + if arg == "--tree": + tree = True + + # Switch to collecting formats + + elif arg == "--format": + l = formats + continue + + # Collect options and arguments. + + else: + l.append(arg) + + # Collect filenames normally. + + l = filenames + + filename = filenames[0] + + f = open(filename) + try: + d = parse(f.read(), all_parsers) + if tree: + print d.prettyprint() + else: + format = formats and formats[0] or "html" + print serialise(d, get_serialiser(format)) + finally: + f.close() + +if __name__ == "__main__": + main() + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/__init__.py --- a/moinformat/__init__.py Tue Jul 24 10:47:53 2018 +0200 +++ b/moinformat/__init__.py Tue Jul 24 15:53:44 2018 +0200 @@ -22,4 +22,10 @@ from moinformat.parsers import parse, parsers as all_parsers from moinformat.serialisers import serialise, serialisers as all_serialisers +def get_serialiser(name): + + "Return the main serialiser for the format having the given 'name'." + + return all_serialisers["%s.moin" % name] + # vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/imports.py --- a/moinformat/imports.py Tue Jul 24 10:47:53 2018 +0200 +++ b/moinformat/imports.py Tue Jul 24 15:53:44 2018 +0200 @@ -3,7 +3,7 @@ """ Import utilities. -Copyright (C) 2017 Paul Boddie +Copyright (C) 2017, 2018 Paul Boddie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -23,18 +23,19 @@ from os import listdir from importlib import import_module -def get_extensions(dirname, modname, stores, reserved): +def get_extensions(dirname, modname, stores, reserved, prefix=None): "Import extensions inside 'dirname'." for filename in listdir(dirname): pathname = join(dirname, filename) - # Descend into directories. + # Descend into directories, prefixing the identified modules. if isdir(pathname): + store_name = prefix and "%s.%s" % (prefix, modname) or filename get_extensions(pathname, "%s.%s" % (modname, filename), - stores, reserved) + stores, reserved, store_name) continue # Identify modules and import them. @@ -42,6 +43,7 @@ leafname, ext = splitext(filename) if ext == ".py" and leafname not in reserved: - stores[leafname] = import_module("%s.%s" % (modname, leafname)) + store_name = prefix and "%s.%s" % (prefix, leafname) or leafname + stores[store_name] = import_module("%s.%s" % (modname, leafname)) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/parsers/common.py --- a/moinformat/parsers/common.py Tue Jul 24 10:47:53 2018 +0200 +++ b/moinformat/parsers/common.py Tue Jul 24 15:53:44 2018 +0200 @@ -20,7 +20,7 @@ """ from collections import defaultdict -from moinformat.tree import Block, Region, Text +from moinformat.tree.moin import Block, Region, Text import re # Pattern management. diff -r 848f3e4413f1 -r 645911c29199 moinformat/parsers/moin.py --- a/moinformat/parsers/moin.py Tue Jul 24 10:47:53 2018 +0200 +++ b/moinformat/parsers/moin.py Tue Jul 24 15:53:44 2018 +0200 @@ -20,13 +20,15 @@ """ from moinformat.parsers.common import ParserBase, get_patterns, \ - excl, expect, group, optional, recur, repeat + excl, expect, group, optional, recur, \ + repeat from moinformat.serialisers import serialise -from moinformat.tree import Break, DefItem, DefTerm, FontStyle, Heading, \ - Larger, Link, List, ListItem, Macro, Monospace, \ - Region, Rule, Smaller, Strikethrough, Subscript, \ - Superscript, Table, TableAttr, TableAttrs, \ - TableCell, TableRow, Text, Underline +from moinformat.tree.moin import Break, DefItem, DefTerm, FontStyle, Heading, \ + Larger, Link, List, ListItem, Macro, \ + Monospace, Region, Rule, Smaller, \ + Strikethrough, Subscript, Superscript, Table, \ + TableAttr, TableAttrs, TableCell, TableRow, \ + Text, Underline join = "".join diff -r 848f3e4413f1 -r 645911c29199 moinformat/parsers/table.py --- a/moinformat/parsers/table.py Tue Jul 24 10:47:53 2018 +0200 +++ b/moinformat/parsers/table.py Tue Jul 24 15:53:44 2018 +0200 @@ -22,8 +22,8 @@ from moinformat.parsers.common import get_patterns, \ excl, expect, group from moinformat.parsers.moin import MoinParser -from moinformat.tree import Continuation, Table, TableAttrs, TableCell, \ - TableRow, Text +from moinformat.tree.moin import Table, TableAttrs, TableCell, TableRow, Text +from moinformat.tree.table import Continuation join = "".join diff -r 848f3e4413f1 -r 645911c29199 moinformat/serialisers/__init__.py --- a/moinformat/serialisers/__init__.py Tue Jul 24 10:47:53 2018 +0200 +++ b/moinformat/serialisers/__init__.py Tue Jul 24 15:53:44 2018 +0200 @@ -20,7 +20,7 @@ """ from moinformat.serialisers.manifest import serialisers -from moinformat.serialisers.moin import MoinSerialiser +from moinformat.serialisers.moin.moin import MoinSerialiser # Top-level functions. @@ -28,12 +28,8 @@ "Serialise 'doc' using 'serialiser' or the Moin serialiser if omitted." - # Permit serialisation back to source form if Moin format is used. - - formats = serialiser is MoinSerialiser and serialisers or None - l = [] - doc.to_string(serialiser(l.append, formats)) + doc.to_string(serialiser(l.append, serialisers)) return "".join(l) # vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/serialisers/common.py --- a/moinformat/serialisers/common.py Tue Jul 24 10:47:53 2018 +0200 +++ b/moinformat/serialisers/common.py Tue Jul 24 15:53:44 2018 +0200 @@ -23,6 +23,8 @@ "General serialisation support." + format = None # defined by subclasses + def __init__(self, out, formats=None): """ diff -r 848f3e4413f1 -r 645911c29199 moinformat/serialisers/html.py --- a/moinformat/serialisers/html.py Tue Jul 24 10:47:53 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,255 +0,0 @@ -#!/usr/bin/env python - -""" -HTML serialiser. - -Copyright (C) 2017, 2018 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . -""" - -from moinformat.serialisers.common import escape_attr, escape_text, Serialiser - -class HTMLSerialiser(Serialiser): - - "Serialisation of the page." - - def _region_tag(self, type): - - # NOTE: Need to support types in general. - - type = type and type.split()[0] - - if type == "inline": - return "tt" - elif type in (None, "python"): - return "pre" - else: - return "span" - - def start_region(self, level, indent, type, extra): - l = [] - out = l.append - if level: - out("level-%d" % level) - - if indent: - out("indent-%d" % indent) - - # NOTE: Encode type details for CSS. - - out("type-%s" % escape_attr(type or "opaque")) - - tag = self._region_tag(type) - self.out("<%s class='%s'>" % (tag, " ".join(l))) - - def end_region(self, level, indent, type, extra): - tag = self._region_tag(type) - self.out("" % tag) - - def start_block(self): - self.out("

") - - def end_block(self): - self.out("

") - - def start_defitem(self, pad, extra): - self.out("
") - - def end_defitem(self, pad, extra): - self.out("
") - - def start_defterm(self, pad): - self.out("
") - - def end_defterm(self, pad): - self.out("
") - - def start_emphasis(self): - self.out("") - - def end_emphasis(self): - self.out("") - - def start_heading(self, level, extra, pad): - self.out("" % level) - - def end_heading(self, level, pad, extra): - self.out("" % level) - - def start_larger(self): - self.out("") - - def end_larger(self): - self.out("") - - def start_link(self, target): - self.out('' % escape_attr(target)) - - def end_link(self): - self.out("") - - def start_linktext(self): - pass - - def end_linktext(self): - pass - - list_tags = { - "i" : "lower-roman", - "I" : "upper-roman", - "a" : "lower-latin", - "A" : "upper-latin", - } - - def _get_list_tag(self, marker): - if marker: - if marker[0].isdigit(): - return "ol", "decimal" - style_type = self.list_tags.get(marker[0]) - if style_type: - return "ol", style_type - - return "ul", None - - def start_list(self, indent, marker, num): - tag, style_type = self._get_list_tag(marker) - style = style_type and ' style="list-style-type: %s"' % escape_attr(style_type) or "" - start = style_type and num is not None and ' start="%s"' % escape_attr(num) or "" - self.out("<%s%s%s>" % (tag, style, start)) - - def end_list(self, indent, marker, num): - tag, style = self._get_list_tag(marker) - self.out("" % tag) - - def start_listitem(self, indent, marker, space, num): - self.out("
  • ") - - def end_listitem(self, indent, marker, space, num): - self.out("
  • ") - - def start_monospace(self): - self.out("") - - def end_monospace(self): - self.out("") - - def start_smaller(self): - self.out("") - - def end_smaller(self): - self.out("") - - def start_strikethrough(self): - self.out("") - - def end_strikethrough(self): - self.out("") - - def start_strong(self): - self.out("") - - def end_strong(self): - self.out("") - - def start_subscript(self): - self.out("") - - def end_subscript(self): - self.out("") - - def start_superscript(self): - self.out("") - - def end_superscript(self): - self.out("") - - def start_table(self): - self.out("") - - def end_table(self): - self.out("
    ") - - def start_table_attrs(self): - pass - - def end_table_attrs(self): - pass - - def start_table_cell(self, attrs): - self.out("") - - def end_table_cell(self): - self.out("") - - def start_table_row(self): - self.out("") - - def end_table_row(self, trailing): - self.out("") - - def start_underline(self): - self.out("") - - def end_underline(self): - self.out("") - - def break_(self): - pass - - def continuation(self, text): - pass - - def macro(self, name, args): - - # NOTE: Special case. - - if name == "BR": - self.out("
    ") - return - - # Fallback case. - - self.out("") - self.out(escape_text("<<")) - self.out("%s" % escape_text(name)) - if args: - self.out("(") - first = True - for arg in args: - if not first: - self.out(",") - self.out("%s" % escape_text(arg)) - first = False - if args: - self.out(")") - self.out(escape_text(">>")) - self.out("") - - def rule(self, length): - self.out("
    " % min(length, 10)) - - def table_attr(self, name, value, concise, quote): - self.out(" %s%s" % (escape_text(name), value is not None and - "='%s'" % escape_attr(value) or "")) - - def text(self, s): - self.out(escape_text(s)) - -serialiser = HTMLSerialiser - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/serialisers/html/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/serialisers/html/__init__.py Tue Jul 24 15:53:44 2018 +0200 @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +A package of modules containing HTML format serialisers. + +Copyright (C) 2018 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/serialisers/html/moin.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/serialisers/html/moin.py Tue Jul 24 15:53:44 2018 +0200 @@ -0,0 +1,254 @@ +#!/usr/bin/env python + +""" +HTML serialiser. + +Copyright (C) 2017, 2018 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from moinformat.serialisers.common import escape_attr, escape_text, Serialiser + +class HTMLSerialiser(Serialiser): + + "Serialisation of the page." + + format = "html" + + def _region_tag(self, type): + + # NOTE: Need to support types in general. + + type = type and type.split()[0] + + if type == "inline": + return "tt" + elif type in (None, "python"): + return "pre" + else: + return "span" + + def start_region(self, level, indent, type, extra): + l = [] + out = l.append + if level: + out("level-%d" % level) + + if indent: + out("indent-%d" % indent) + + # NOTE: Encode type details for CSS. + + out("type-%s" % escape_attr(type or "opaque")) + + tag = self._region_tag(type) + self.out("<%s class='%s'>" % (tag, " ".join(l))) + + def end_region(self, level, indent, type, extra): + tag = self._region_tag(type) + self.out("" % tag) + + def start_block(self): + self.out("

    ") + + def end_block(self): + self.out("

    ") + + def start_defitem(self, pad, extra): + self.out("
    ") + + def end_defitem(self, pad, extra): + self.out("
    ") + + def start_defterm(self, pad): + self.out("
    ") + + def end_defterm(self, pad): + self.out("
    ") + + def start_emphasis(self): + self.out("") + + def end_emphasis(self): + self.out("") + + def start_heading(self, level, extra, pad): + self.out("" % level) + + def end_heading(self, level, pad, extra): + self.out("" % level) + + def start_larger(self): + self.out("") + + def end_larger(self): + self.out("") + + def start_link(self, target): + self.out('' % escape_attr(target)) + + def end_link(self): + self.out("") + + def start_linktext(self): + pass + + def end_linktext(self): + pass + + list_tags = { + "i" : "lower-roman", + "I" : "upper-roman", + "a" : "lower-latin", + "A" : "upper-latin", + } + + def _get_list_tag(self, marker): + if marker: + if marker[0].isdigit(): + return "ol", "decimal" + style_type = self.list_tags.get(marker[0]) + if style_type: + return "ol", style_type + + return "ul", None + + def start_list(self, indent, marker, num): + tag, style_type = self._get_list_tag(marker) + style = style_type and ' style="list-style-type: %s"' % escape_attr(style_type) or "" + start = style_type and num is not None and ' start="%s"' % escape_attr(num) or "" + self.out("<%s%s%s>" % (tag, style, start)) + + def end_list(self, indent, marker, num): + tag, style = self._get_list_tag(marker) + self.out("" % tag) + + def start_listitem(self, indent, marker, space, num): + self.out("
  • ") + + def end_listitem(self, indent, marker, space, num): + self.out("
  • ") + + def start_monospace(self): + self.out("") + + def end_monospace(self): + self.out("") + + def start_smaller(self): + self.out("") + + def end_smaller(self): + self.out("") + + def start_strikethrough(self): + self.out("") + + def end_strikethrough(self): + self.out("") + + def start_strong(self): + self.out("") + + def end_strong(self): + self.out("") + + def start_subscript(self): + self.out("") + + def end_subscript(self): + self.out("") + + def start_superscript(self): + self.out("") + + def end_superscript(self): + self.out("") + + def start_table(self): + self.out("") + + def end_table(self): + self.out("
    ") + + def start_table_attrs(self): + pass + + def end_table_attrs(self): + pass + + def start_table_cell(self, attrs): + self.out("") + + def end_table_cell(self): + self.out("") + + def start_table_row(self): + self.out("") + + def end_table_row(self, trailing): + self.out("") + + def start_underline(self): + self.out("") + + def end_underline(self): + self.out("") + + def break_(self): + pass + + def macro(self, name, args): + + # NOTE: Special case. + + if name == "BR": + self.out("
    ") + return + + # Fallback case. + + self.out("") + self.out(escape_text("<<")) + self.out("%s" % escape_text(name)) + if args: + self.out("(") + first = True + for arg in args: + if not first: + self.out(",") + self.out("%s" % escape_text(arg)) + first = False + if args: + self.out(")") + self.out(escape_text(">>")) + self.out("") + + def rule(self, length): + self.out("
    " % min(length, 10)) + + def table_attr(self, name, value, concise, quote): + self.out(" %s%s" % (escape_text(name), value is not None and + "='%s'" % escape_attr(value) or "")) + + def text(self, s): + self.out(escape_text(s)) + +serialiser = HTMLSerialiser + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/serialisers/html/table.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/serialisers/html/table.py Tue Jul 24 15:53:44 2018 +0200 @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +""" +HTML serialiser. + +Copyright (C) 2017, 2018 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from moinformat.serialisers.common import escape_attr, escape_text +from moinformat.serialisers.html.moin import HTMLSerialiser + +class HTMLTableSerialiser(HTMLSerialiser): + + "Serialisation of the page." + + def continuation(self, text): + pass + +serialiser = HTMLTableSerialiser + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/serialisers/moin.py --- a/moinformat/serialisers/moin.py Tue Jul 24 10:47:53 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,201 +0,0 @@ -#!/usr/bin/env python - -""" -Moin wiki text serialiser. - -Copyright (C) 2017, 2018 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . -""" - -from moinformat.serialisers.common import escape_attr, escape_text, Serialiser - -class MoinSerialiser(Serialiser): - - "Serialisation of the page." - - def start_region(self, level, indent, type, extra): - out = self.out - if level: - out(" " * indent + "{" * level) - - # Produce a header for regions within a top-level region. - - if type and type != "inline" and level: - out("#!%s\n" % type) - - def end_region(self, level, indent, type, extra): - out = self.out - if level: - out("%s%s" % ("}" * level, extra or "")) - - def start_block(self): - pass - - def end_block(self): - pass - - def start_defitem(self, pad, extra): - self.out((extra and "\n" + extra + "::" or "") + pad) - - def end_defitem(self, pad, extra): - pass - - def start_defterm(self, pad): - self.out(pad) - - def end_defterm(self, pad): - self.out("::") - - def start_emphasis(self): - self.out("''") - - def end_emphasis(self): - self.out("''") - - def start_heading(self, level, extra, pad): - self.out(extra + "=" * level + pad) - - def end_heading(self, level, pad, extra): - self.out(pad + "=" * level + extra) - - def start_larger(self): - self.out("~+") - - def end_larger(self): - self.out("+~") - - def start_link(self, target): - self.out("[[%s" % target) - - def end_link(self): - self.out("]]") - - def start_linktext(self): - self.out("|") - - def end_linktext(self): - pass - - def start_list(self, indent, marker, num): - pass - - def end_list(self, indent, marker, num): - pass - - def start_listitem(self, indent, marker, space, num): - self.out("%s%s%s%s" % (indent * " ", marker, num and "#%s" % num or "", space)) - - def end_listitem(self, indent, marker, space, num): - pass - - def start_monospace(self): - self.out("`") - - def end_monospace(self): - self.out("`") - - def start_smaller(self): - self.out("~-") - - def end_smaller(self): - self.out("-~") - - def start_strong(self): - self.out("'''") - - def end_strong(self): - self.out("'''") - - def start_strikethrough(self): - self.out("--(") - - def end_strikethrough(self): - self.out(")--") - - def start_subscript(self): - self.out(",,") - - def end_subscript(self): - self.out(",,") - - def start_superscript(self): - self.out("^") - - def end_superscript(self): - self.out("^") - - def start_table(self): - pass - - def end_table(self): - pass - - def start_table_attrs(self): - self.out("<") - - def end_table_attrs(self): - self.out(">") - - def start_table_cell(self, attrs): - self.out("||") - if attrs and not attrs.empty(): - attrs.to_string(self) - - def end_table_cell(self): - pass - - def start_table_row(self): - pass - - def end_table_row(self, trailing): - self.out("||") - self.out(trailing) - - def start_underline(self): - self.out("__") - - def end_underline(self): - self.out("__") - - def break_(self): - self.out("\n") - - def continuation(self, text): - self.out(text) - - def macro(self, name, args): - self.out("<<%s%s>>" % (name, args and "(%s)" % ",".join(args) or "")) - - def rule(self, length): - self.out("-" * length) - - def table_attr(self, name, value, concise, quote): - if concise: - if name == "colour": self.out(value) - elif name == "colspan": self.out("-%s" % value) - elif name == "halign" : self.out(value == "left" and "(" or value == "right" and ")" or ":") - elif name == "rowspan": self.out("|%s" % value) - elif name == "valign" : self.out(value == "top" and "^" or "v") - elif name == "width" : self.out(value) - else: - self.out("%s%s" % (escape_text(name), value is not None and - "=%s%s%s" % (quote or '"', escape_attr(value), quote or '"') or "")) - - def text(self, s): - self.out(s) - -serialiser = MoinSerialiser - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/serialisers/moin/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/serialisers/moin/__init__.py Tue Jul 24 15:53:44 2018 +0200 @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +A package of modules containing Moin format serialisers. + +Copyright (C) 2018 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/serialisers/moin/moin.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/serialisers/moin/moin.py Tue Jul 24 15:53:44 2018 +0200 @@ -0,0 +1,203 @@ +#!/usr/bin/env python + +""" +Moin wiki text serialiser. + +Copyright (C) 2017, 2018 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from moinformat.serialisers.common import escape_attr, escape_text, Serialiser + +class MoinSerialiser(Serialiser): + + "Serialisation of the page." + + format = "moin" + + def start_region(self, level, indent, type, extra): + out = self.out + if level: + out(" " * indent + "{" * level) + + # Produce a header for regions within a top-level region. + + if type and type != "inline" and level: + out("#!%s\n" % type) + + def end_region(self, level, indent, type, extra): + out = self.out + if level: + out("%s%s" % ("}" * level, extra or "")) + + def start_block(self): + pass + + def end_block(self): + pass + + def start_defitem(self, pad, extra): + self.out((extra and "\n" + extra + "::" or "") + pad) + + def end_defitem(self, pad, extra): + pass + + def start_defterm(self, pad): + self.out(pad) + + def end_defterm(self, pad): + self.out("::") + + def start_emphasis(self): + self.out("''") + + def end_emphasis(self): + self.out("''") + + def start_heading(self, level, extra, pad): + self.out(extra + "=" * level + pad) + + def end_heading(self, level, pad, extra): + self.out(pad + "=" * level + extra) + + def start_larger(self): + self.out("~+") + + def end_larger(self): + self.out("+~") + + def start_link(self, target): + self.out("[[%s" % target) + + def end_link(self): + self.out("]]") + + def start_linktext(self): + self.out("|") + + def end_linktext(self): + pass + + def start_list(self, indent, marker, num): + pass + + def end_list(self, indent, marker, num): + pass + + def start_listitem(self, indent, marker, space, num): + self.out("%s%s%s%s" % (indent * " ", marker, num and "#%s" % num or "", space)) + + def end_listitem(self, indent, marker, space, num): + pass + + def start_monospace(self): + self.out("`") + + def end_monospace(self): + self.out("`") + + def start_smaller(self): + self.out("~-") + + def end_smaller(self): + self.out("-~") + + def start_strong(self): + self.out("'''") + + def end_strong(self): + self.out("'''") + + def start_strikethrough(self): + self.out("--(") + + def end_strikethrough(self): + self.out(")--") + + def start_subscript(self): + self.out(",,") + + def end_subscript(self): + self.out(",,") + + def start_superscript(self): + self.out("^") + + def end_superscript(self): + self.out("^") + + def start_table(self): + pass + + def end_table(self): + pass + + def start_table_attrs(self): + self.out("<") + + def end_table_attrs(self): + self.out(">") + + def start_table_cell(self, attrs): + self.out("||") + if attrs and not attrs.empty(): + attrs.to_string(self) + + def end_table_cell(self): + pass + + def start_table_row(self): + pass + + def end_table_row(self, trailing): + self.out("||") + self.out(trailing) + + def start_underline(self): + self.out("__") + + def end_underline(self): + self.out("__") + + def break_(self): + self.out("\n") + + def continuation(self, text): + self.out(text) + + def macro(self, name, args): + self.out("<<%s%s>>" % (name, args and "(%s)" % ",".join(args) or "")) + + def rule(self, length): + self.out("-" * length) + + def table_attr(self, name, value, concise, quote): + if concise: + if name == "colour": self.out(value) + elif name == "colspan": self.out("-%s" % value) + elif name == "halign" : self.out(value == "left" and "(" or value == "right" and ")" or ":") + elif name == "rowspan": self.out("|%s" % value) + elif name == "valign" : self.out(value == "top" and "^" or "v") + elif name == "width" : self.out(value) + else: + self.out("%s%s" % (escape_text(name), value is not None and + "=%s%s%s" % (quote or '"', escape_attr(value), quote or '"') or "")) + + def text(self, s): + self.out(s) + +serialiser = MoinSerialiser + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/serialisers/moin/table.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/serialisers/moin/table.py Tue Jul 24 15:53:44 2018 +0200 @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +""" +Moin wiki table serialiser. + +Copyright (C) 2017, 2018 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from moinformat.serialisers.moin.moin import MoinSerialiser + +class MoinTableSerialiser(MoinSerialiser): + + "Serialisation of the page." + + def init(self): + self.first_cell = False + self.first_row = False + + def start_table(self): + self.first_row = True + + def start_table_cell(self, attrs): + if not self.first_cell: + self.out("||") + else: + self.first_cell = False + + if attrs and not attrs.empty(): + attrs.to_string(self) + + def start_table_row(self): + self.first_cell = True + if not self.first_row: + self.out("==") + else: + self.first_row = False + + def end_table_row(self, trailing): + self.out(trailing) + +serialiser = MoinTableSerialiser + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/serialisers/table.py --- a/moinformat/serialisers/table.py Tue Jul 24 10:47:53 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -#!/usr/bin/env python - -""" -Moin wiki table serialiser. - -Copyright (C) 2017 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . -""" - -from moinformat.serialisers.moin import MoinSerialiser - -class TableSerialiser(MoinSerialiser): - - "Serialisation of the page." - - def init(self): - self.first_cell = False - self.first_row = False - - def start_table(self): - self.first_row = True - - def start_table_cell(self, attrs): - if not self.first_cell: - self.out("||") - else: - self.first_cell = False - - if attrs and not attrs.empty(): - attrs.to_string(self) - - def start_table_row(self): - self.first_cell = True - if not self.first_row: - self.out("==") - else: - self.first_row = False - - def end_table_row(self, trailing): - self.out(trailing) - -serialiser = TableSerialiser - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/tree.py --- a/moinformat/tree.py Tue Jul 24 10:47:53 2018 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,621 +0,0 @@ -#!/usr/bin/env python - -""" -Moin wiki format document tree nodes. - -Copyright (C) 2017, 2018 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 3 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -You should have received a copy of the GNU General Public License along with -this program. If not, see . -""" - -class Container: - - "A container of document nodes." - - def __init__(self, nodes): - self.nodes = nodes - - # In principle, allow blocks within containers. Some nodes may forbid - # them to simplify the document structure. - - self.allow_blocks = True - - def append(self, node): - self.nodes.append(node) - - def append_many(self, nodes): - for node in nodes: - self.append(node) - - add = append - - append_inline = append - - def append_inline_many(self, nodes): - for node in nodes: - self.append_inline(node) - - def empty(self): - return not self.nodes - - def node(self, index): - try: - return self.nodes[index] - except IndexError: - return None - - def normalise(self): - - "Combine adjacent text nodes." - - nodes = self.nodes - self.nodes = [] - text = None - - for node in nodes: - - # Open a text node or merge text into an open node. - - if isinstance(node, Text): - if not text: - text = node - else: - text.merge(node) - - # Close any open text node and append the current node. - - else: - if text: - self.append(text) - text = None - self.append(node) - - # Add any open text node. - - if text: - self.append(text) - - def __str__(self): - return self.prettyprint() - - def _prettyprint(self, l, indent=""): - for node in self.nodes: - l.append(node.prettyprint(indent + " ")) - return "\n".join(l) - - def _to_string(self, out): - for node in self.nodes: - node.to_string(out) - -class Region(Container): - - "A region of the page." - - def __init__(self, nodes, level=0, indent=0, type=None, transparent=True, extra=None): - Container.__init__(self, nodes) - self.level = level - self.indent = indent - self.type = type - self.transparent = transparent - self.extra = extra - - def add(self, node): - last = self.node(-1) - if last and last.empty(): - self.nodes[-1] = node - else: - self.append(node) - - def append_inline(self, node): - if self.transparent: - self.nodes[-1].append(node) - else: - self.append(node) - - def have_end(self, s): - return self.level and s.startswith("}") and self.level == len(s) - - def __repr__(self): - return "Region(%r, %r, %r, %r, %r, %r)" % (self.nodes, self.level, - self.indent, self.type, self.transparent, self.extra) - - def prettyprint(self, indent=""): - l = ["%sRegion: level=%d indent=%d type=%s extra=%r" % (indent, - self.level, self.indent, self.type, self.extra)] - return self._prettyprint(l, indent) - - def to_string(self, out): - out.start_region(self.level, self.indent, self.type, self.extra) - - # Obtain a serialiser for the region, if appropriate. - - serialiser = out.formats and out.formats.get(self.type) - region_out = serialiser and serialiser(out.out, out.formats) or out - - # Serialise the region. - - self._to_string(region_out) - - out.end_region(self.level, self.indent, self.type, self.extra) - - - -# Block nodes. - -class Block(Container): - - "A block in the page." - - def __repr__(self): - return "Block(%r)" % self.nodes - - def prettyprint(self, indent=""): - l = ["%sBlock" % indent] - return self._prettyprint(l, indent) - - def to_string(self, out): - out.start_block() - self._to_string(out) - out.end_block() - -class DefItem(Container): - - "A definition item." - - def __init__(self, nodes, pad, extra): - Container.__init__(self, nodes) - self.pad = pad - self.extra = extra - - def __repr__(self): - return "DefItem(%r, %r, %r)" % (self.nodes, self.pad, self.extra) - - def prettyprint(self, indent=""): - l = ["%sDefItem: pad=%r extra=%r" % (indent, self.pad, self.extra)] - return self._prettyprint(l, indent) - - def to_string(self, out): - out.start_defitem(self.pad, self.extra) - self._to_string(out) - out.end_defitem(self.pad, self.extra) - -class DefTerm(Container): - - "A definition term." - - def __init__(self, nodes, pad): - Container.__init__(self, nodes) - self.pad = pad - - def __repr__(self): - return "DefTerm(%r, %r)" % (self.nodes, self.pad) - - def prettyprint(self, indent=""): - l = ["%sDefTerm: pad=%r" % (indent, self.pad)] - return self._prettyprint(l, indent) - - def to_string(self, out): - out.start_defterm(self.pad) - self._to_string(out) - out.end_defterm(self.pad) - -class FontStyle(Container): - - "Emphasised and/or strong text." - - def __init__(self, nodes, emphasis=False, strong=False): - Container.__init__(self, nodes) - self.emphasis = emphasis - self.strong = strong - - def close_emphasis(self): - if self.strong: - span = FontStyle(self.nodes, emphasis=True) - self.nodes = [span] - self.emphasis = False - return self.strong - - def close_strong(self): - if self.emphasis: - span = FontStyle(self.nodes, strong=True) - self.nodes = [span] - self.strong = False - return self.emphasis - - def __repr__(self): - return "FontStyle(%r, %r, %r)" % (self.nodes, self.emphasis, self.strong) - - def prettyprint(self, indent=""): - l = ["%sFontStyle: emphasis=%r strong=%r" % (indent, self.emphasis, self.strong)] - return self._prettyprint(l, indent) - - def to_string(self, out): - if self.emphasis: - out.start_emphasis() - elif self.strong: - out.start_strong() - self._to_string(out) - if self.emphasis: - out.end_emphasis() - elif self.strong: - out.end_strong() - -class Heading(Container): - - "A heading." - - def __init__(self, nodes, level, start_extra="", start_pad="", end_pad="", end_extra=""): - Container.__init__(self, nodes) - self.level = level - self.start_extra = start_extra - self.start_pad = start_pad - self.end_pad = end_pad - self.end_extra = end_extra - - def __repr__(self): - return "Heading(%r, %d, %r, %r, %r, %r)" % ( - self.nodes, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra) - - def prettyprint(self, indent=""): - l = ["%sHeading: level=%d start_extra=%r start_pad=%r end_pad=%r end_extra=%r" % ( - indent, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra)] - return self._prettyprint(l, indent) - - def to_string(self, out): - out.start_heading(self.level, self.start_extra, self.start_pad) - 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, num): - Container.__init__(self, nodes) - self.indent = indent - self.marker = marker - self.num = num - - def __repr__(self): - return "List(%r, %r, %r, %r)" % (self.nodes, self.indent, self.marker, self.num) - - def prettyprint(self, indent=""): - l = ["%sList: indent=%d marker=%r num=%r" % (indent, self.indent, self.marker, self.num)] - return self._prettyprint(l, indent) - - def to_string(self, out): - out.start_list(self.indent, self.marker, self.num) - self._to_string(out) - out.end_list(self.indent, self.marker, self.num) - -class ListItem(Container): - - "A list item." - - def __init__(self, nodes, indent, marker, space, num): - Container.__init__(self, nodes) - self.indent = indent - self.marker = marker - self.space = space - self.num = num - - # Forbid blocks within list items for simpler structure. - - self.allow_blocks = False - - def __repr__(self): - return "ListItem(%r, %r, %r, %r, %r)" % (self.nodes, self.indent, self.marker, self.space, self.num) - - def prettyprint(self, indent=""): - l = ["%sListItem: indent=%d marker=%r space=%r num=%r" % (indent, self.indent, self.marker, self.space, self.num)] - return self._prettyprint(l, indent) - - def to_string(self, out): - out.start_listitem(self.indent, self.marker, self.space, self.num) - self._to_string(out) - out.end_listitem(self.indent, self.marker, self.space, self.num) - -class TableAttrs(Container): - - "A collection of table attributes." - - def __repr__(self): - return "TableAttrs(%r)" % self.nodes - - def prettyprint(self, indent=""): - l = ["%sTableAttrs:" % indent] - return self._prettyprint(l, indent) - - def to_string(self, out): - out.start_table_attrs() - self._to_string(out) - out.end_table_attrs() - -class Table(Container): - - "A table." - - def __repr__(self): - return "Table(%r)" % self.nodes - - def prettyprint(self, indent=""): - l = ["%sTable:" % indent] - return self._prettyprint(l, indent) - - def to_string(self, out): - out.start_table() - self._to_string(out) - out.end_table() - -class TableCell(Container): - - "A table cell." - - def __init__(self, nodes, attrs=None): - Container.__init__(self, nodes) - self.attrs = attrs - - def __repr__(self): - return "TableCell(%r, %r)" % (self.nodes, self.attrs) - - def prettyprint(self, indent=""): - l = ["%sTableCell:" % indent] - return self._prettyprint(l, indent) - - def to_string(self, out): - out.start_table_cell(self.attrs) - for node in self.nodes: - if node is not self.attrs: - node.to_string(out) - out.end_table_cell() - -class TableRow(Container): - - "A table row." - - def __init__(self, nodes, trailing=""): - Container.__init__(self, nodes) - self.trailing = trailing - - def __repr__(self): - return "TableRow(%r, %r)" % (self.nodes, self.trailing) - - def prettyprint(self, indent=""): - l = ["%sTableRow: trailing=%r" % (indent, self.trailing)] - return self._prettyprint(l, indent) - - def to_string(self, out): - out.start_table_row() - self._to_string(out) - out.end_table_row(self.trailing) - - - -# Inline nodes with children. - -class Inline(Container): - - "Generic inline formatting." - - def __repr__(self): - return "%s(%r)" % (self.__class__.__name__, self.nodes) - - def prettyprint(self, indent=""): - l = ["%s%s" % (indent, self.__class__.__name__)] - return self._prettyprint(l, indent) - -class Larger(Inline): - - "Larger text." - - def to_string(self, out): - out.start_larger() - self._to_string(out) - out.end_larger() - -class Link(Container): - - "Link details." - - def __init__(self, nodes, target): - Container.__init__(self, nodes) - self.target = target - - def __repr__(self): - return "Link(%r, %r)" % (self.nodes, self.target) - - def prettyprint(self, indent=""): - l = ["%sLink: target=%r" % (indent, self.target)] - return self._prettyprint(l, indent) - - def to_string(self, out): - out.start_link(self.target) - if self.nodes: - out.start_linktext() - self._to_string(out) - out.end_linktext() - out.end_link() - -class Monospace(Inline): - - "Monospaced text." - - def to_string(self, out): - out.start_monospace() - self._to_string(out) - out.end_monospace() - -class Smaller(Inline): - - "Smaller text." - - def to_string(self, out): - out.start_smaller() - self._to_string(out) - out.end_smaller() - -class Strikethrough(Inline): - - "Crossed-out text." - - def to_string(self, out): - out.start_strikethrough() - self._to_string(out) - out.end_strikethrough() - -class Subscript(Inline): - - "Subscripted text." - - def to_string(self, out): - out.start_subscript() - self._to_string(out) - out.end_subscript() - -class Superscript(Inline): - - "Superscripted text." - - def to_string(self, out): - out.start_superscript() - self._to_string(out) - out.end_superscript() - -class Underline(Inline): - - "Underlined text." - - def to_string(self, out): - out.start_underline() - self._to_string(out) - out.end_underline() - - - -# Nodes without children. - -class Node: - - "A document node without children." - - def empty(self): - return False - -class Break(Node): - - "A paragraph break." - - def __repr__(self): - return "Break()" - - def prettyprint(self, indent=""): - return "%sBreak" % indent - - def to_string(self, out): - out.break_() - -class Continuation(Node): - - "Continuation padding for table content." - - def __init__(self, text): - self.text = text - - def __repr__(self): - return "Continuation(%r)" % self.text - - def prettyprint(self, indent=""): - return "%sContinuation: %r" % (indent, self.text) - - def to_string(self, out): - out.continuation(self.text) - -class Macro(Node): - - "Macro details." - - def __init__(self, name, args): - self.name = name - self.args = args - - def __repr__(self): - return "Macro(%r, %r)" % (self.name, self.args) - - def prettyprint(self, indent=""): - return "%sMacro: name=%r args=%r" % (indent, self.name, self.args) - - def to_string(self, out): - out.macro(self.name, self.args) - -class Rule(Node): - - "A horizontal rule." - - def __init__(self, length): - self.length = length - - def __repr__(self): - return "Rule(%d)" % self.length - - def prettyprint(self, indent=""): - return "%sRule: length=%d" % (indent, self.length) - - def to_string(self, out): - out.rule(self.length) - -class TableAttr(Node): - - "A table attribute." - - def __init__(self, name, value=None, concise=False, quote=None): - self.name = name - self.value = value - self.concise = concise - self.quote = quote - - def __repr__(self): - return "TableAttr(%r, %r, %r, %r)" % (self.name, self.value, self.concise, self.quote) - - def prettyprint(self, indent=""): - return "%sTableAttr: name=%r value=%r concise=%r quote=%r" % (indent, self.name, self.value, self.concise, self.quote) - - def to_string(self, out): - out.table_attr(self.name, self.value, self.concise, self.quote) - -class Text(Node): - - "A text node." - - def __init__(self, s): - self.s = s - - def empty(self): - return not self.s - - def multiline(self): - return "\n" in self.s - - def merge(self, text): - self.s += text.s - - def __repr__(self): - return "Text(%r)" % self.s - - def prettyprint(self, indent=""): - return "%sText: %r" % (indent, self.s) - - def to_string(self, out): - out.text(self.s) - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/tree/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/tree/__init__.py Tue Jul 24 15:53:44 2018 +0200 @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +""" +A package of modules containing document tree nodes. + +Copyright (C) 2018 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/tree/moin.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/tree/moin.py Tue Jul 24 15:53:44 2018 +0200 @@ -0,0 +1,611 @@ +#!/usr/bin/env python + +""" +Moin wiki format document tree nodes. + +Copyright (C) 2017, 2018 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +class Container: + + "A container of document nodes." + + def __init__(self, nodes): + self.nodes = nodes + + # In principle, allow blocks within containers. Some nodes may forbid + # them to simplify the document structure. + + self.allow_blocks = True + + def append(self, node): + self.nodes.append(node) + + def append_many(self, nodes): + for node in nodes: + self.append(node) + + add = append + + append_inline = append + + def append_inline_many(self, nodes): + for node in nodes: + self.append_inline(node) + + def empty(self): + return not self.nodes + + def node(self, index): + try: + return self.nodes[index] + except IndexError: + return None + + def normalise(self): + + "Combine adjacent text nodes." + + nodes = self.nodes + self.nodes = [] + text = None + + for node in nodes: + + # Open a text node or merge text into an open node. + + if isinstance(node, Text): + if not text: + text = node + else: + text.merge(node) + + # Close any open text node and append the current node. + + else: + if text: + self.append(text) + text = None + self.append(node) + + # Add any open text node. + + if text: + self.append(text) + + def __str__(self): + return self.prettyprint() + + def _prettyprint(self, l, indent=""): + for node in self.nodes: + l.append(node.prettyprint(indent + " ")) + return "\n".join(l) + + def _to_string(self, out): + for node in self.nodes: + node.to_string(out) + +class Region(Container): + + "A region of the page." + + def __init__(self, nodes, level=0, indent=0, type=None, transparent=True, extra=None): + Container.__init__(self, nodes) + self.level = level + self.indent = indent + self.type = type + self.transparent = transparent + self.extra = extra + + def add(self, node): + last = self.node(-1) + if last and last.empty(): + self.nodes[-1] = node + else: + self.append(node) + + def append_inline(self, node): + if self.transparent: + self.nodes[-1].append(node) + else: + self.append(node) + + def have_end(self, s): + return self.level and s.startswith("}") and self.level == len(s) + + def __repr__(self): + return "Region(%r, %r, %r, %r, %r, %r)" % (self.nodes, self.level, + self.indent, self.type, self.transparent, self.extra) + + def prettyprint(self, indent=""): + l = ["%sRegion: level=%d indent=%d type=%s extra=%r" % (indent, + self.level, self.indent, self.type, self.extra)] + return self._prettyprint(l, indent) + + def to_string(self, out): + out.start_region(self.level, self.indent, self.type, self.extra) + + # Obtain a serialiser class for the region from the same format family. + + serialiser_name = "%s.%s" % (out.format, self.type) + serialiser_cls = out.formats and out.formats.get(serialiser_name) + + # Retain the same serialiser if no appropriate serialiser could be + # obtained. + + region_out = serialiser_cls and serialiser_cls(out.out, out.formats) \ + or out + + # Serialise the region. + + self._to_string(region_out) + + out.end_region(self.level, self.indent, self.type, self.extra) + + + +# Block nodes. + +class Block(Container): + + "A block in the page." + + def __repr__(self): + return "Block(%r)" % self.nodes + + def prettyprint(self, indent=""): + l = ["%sBlock" % indent] + return self._prettyprint(l, indent) + + def to_string(self, out): + out.start_block() + self._to_string(out) + out.end_block() + +class DefItem(Container): + + "A definition item." + + def __init__(self, nodes, pad, extra): + Container.__init__(self, nodes) + self.pad = pad + self.extra = extra + + def __repr__(self): + return "DefItem(%r, %r, %r)" % (self.nodes, self.pad, self.extra) + + def prettyprint(self, indent=""): + l = ["%sDefItem: pad=%r extra=%r" % (indent, self.pad, self.extra)] + return self._prettyprint(l, indent) + + def to_string(self, out): + out.start_defitem(self.pad, self.extra) + self._to_string(out) + out.end_defitem(self.pad, self.extra) + +class DefTerm(Container): + + "A definition term." + + def __init__(self, nodes, pad): + Container.__init__(self, nodes) + self.pad = pad + + def __repr__(self): + return "DefTerm(%r, %r)" % (self.nodes, self.pad) + + def prettyprint(self, indent=""): + l = ["%sDefTerm: pad=%r" % (indent, self.pad)] + return self._prettyprint(l, indent) + + def to_string(self, out): + out.start_defterm(self.pad) + self._to_string(out) + out.end_defterm(self.pad) + +class FontStyle(Container): + + "Emphasised and/or strong text." + + def __init__(self, nodes, emphasis=False, strong=False): + Container.__init__(self, nodes) + self.emphasis = emphasis + self.strong = strong + + def close_emphasis(self): + if self.strong: + span = FontStyle(self.nodes, emphasis=True) + self.nodes = [span] + self.emphasis = False + return self.strong + + def close_strong(self): + if self.emphasis: + span = FontStyle(self.nodes, strong=True) + self.nodes = [span] + self.strong = False + return self.emphasis + + def __repr__(self): + return "FontStyle(%r, %r, %r)" % (self.nodes, self.emphasis, self.strong) + + def prettyprint(self, indent=""): + l = ["%sFontStyle: emphasis=%r strong=%r" % (indent, self.emphasis, self.strong)] + return self._prettyprint(l, indent) + + def to_string(self, out): + if self.emphasis: + out.start_emphasis() + elif self.strong: + out.start_strong() + self._to_string(out) + if self.emphasis: + out.end_emphasis() + elif self.strong: + out.end_strong() + +class Heading(Container): + + "A heading." + + def __init__(self, nodes, level, start_extra="", start_pad="", end_pad="", end_extra=""): + Container.__init__(self, nodes) + self.level = level + self.start_extra = start_extra + self.start_pad = start_pad + self.end_pad = end_pad + self.end_extra = end_extra + + def __repr__(self): + return "Heading(%r, %d, %r, %r, %r, %r)" % ( + self.nodes, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra) + + def prettyprint(self, indent=""): + l = ["%sHeading: level=%d start_extra=%r start_pad=%r end_pad=%r end_extra=%r" % ( + indent, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra)] + return self._prettyprint(l, indent) + + def to_string(self, out): + out.start_heading(self.level, self.start_extra, self.start_pad) + 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, num): + Container.__init__(self, nodes) + self.indent = indent + self.marker = marker + self.num = num + + def __repr__(self): + return "List(%r, %r, %r, %r)" % (self.nodes, self.indent, self.marker, self.num) + + def prettyprint(self, indent=""): + l = ["%sList: indent=%d marker=%r num=%r" % (indent, self.indent, self.marker, self.num)] + return self._prettyprint(l, indent) + + def to_string(self, out): + out.start_list(self.indent, self.marker, self.num) + self._to_string(out) + out.end_list(self.indent, self.marker, self.num) + +class ListItem(Container): + + "A list item." + + def __init__(self, nodes, indent, marker, space, num): + Container.__init__(self, nodes) + self.indent = indent + self.marker = marker + self.space = space + self.num = num + + # Forbid blocks within list items for simpler structure. + + self.allow_blocks = False + + def __repr__(self): + return "ListItem(%r, %r, %r, %r, %r)" % (self.nodes, self.indent, self.marker, self.space, self.num) + + def prettyprint(self, indent=""): + l = ["%sListItem: indent=%d marker=%r space=%r num=%r" % (indent, self.indent, self.marker, self.space, self.num)] + return self._prettyprint(l, indent) + + def to_string(self, out): + out.start_listitem(self.indent, self.marker, self.space, self.num) + self._to_string(out) + out.end_listitem(self.indent, self.marker, self.space, self.num) + +class TableAttrs(Container): + + "A collection of table attributes." + + def __repr__(self): + return "TableAttrs(%r)" % self.nodes + + def prettyprint(self, indent=""): + l = ["%sTableAttrs:" % indent] + return self._prettyprint(l, indent) + + def to_string(self, out): + out.start_table_attrs() + self._to_string(out) + out.end_table_attrs() + +class Table(Container): + + "A table." + + def __repr__(self): + return "Table(%r)" % self.nodes + + def prettyprint(self, indent=""): + l = ["%sTable:" % indent] + return self._prettyprint(l, indent) + + def to_string(self, out): + out.start_table() + self._to_string(out) + out.end_table() + +class TableCell(Container): + + "A table cell." + + def __init__(self, nodes, attrs=None): + Container.__init__(self, nodes) + self.attrs = attrs + + def __repr__(self): + return "TableCell(%r, %r)" % (self.nodes, self.attrs) + + def prettyprint(self, indent=""): + l = ["%sTableCell:" % indent] + return self._prettyprint(l, indent) + + def to_string(self, out): + out.start_table_cell(self.attrs) + for node in self.nodes: + if node is not self.attrs: + node.to_string(out) + out.end_table_cell() + +class TableRow(Container): + + "A table row." + + def __init__(self, nodes, trailing=""): + Container.__init__(self, nodes) + self.trailing = trailing + + def __repr__(self): + return "TableRow(%r, %r)" % (self.nodes, self.trailing) + + def prettyprint(self, indent=""): + l = ["%sTableRow: trailing=%r" % (indent, self.trailing)] + return self._prettyprint(l, indent) + + def to_string(self, out): + out.start_table_row() + self._to_string(out) + out.end_table_row(self.trailing) + + + +# Inline nodes with children. + +class Inline(Container): + + "Generic inline formatting." + + def __repr__(self): + return "%s(%r)" % (self.__class__.__name__, self.nodes) + + def prettyprint(self, indent=""): + l = ["%s%s" % (indent, self.__class__.__name__)] + return self._prettyprint(l, indent) + +class Larger(Inline): + + "Larger text." + + def to_string(self, out): + out.start_larger() + self._to_string(out) + out.end_larger() + +class Link(Container): + + "Link details." + + def __init__(self, nodes, target): + Container.__init__(self, nodes) + self.target = target + + def __repr__(self): + return "Link(%r, %r)" % (self.nodes, self.target) + + def prettyprint(self, indent=""): + l = ["%sLink: target=%r" % (indent, self.target)] + return self._prettyprint(l, indent) + + def to_string(self, out): + out.start_link(self.target) + if self.nodes: + out.start_linktext() + self._to_string(out) + out.end_linktext() + out.end_link() + +class Monospace(Inline): + + "Monospaced text." + + def to_string(self, out): + out.start_monospace() + self._to_string(out) + out.end_monospace() + +class Smaller(Inline): + + "Smaller text." + + def to_string(self, out): + out.start_smaller() + self._to_string(out) + out.end_smaller() + +class Strikethrough(Inline): + + "Crossed-out text." + + def to_string(self, out): + out.start_strikethrough() + self._to_string(out) + out.end_strikethrough() + +class Subscript(Inline): + + "Subscripted text." + + def to_string(self, out): + out.start_subscript() + self._to_string(out) + out.end_subscript() + +class Superscript(Inline): + + "Superscripted text." + + def to_string(self, out): + out.start_superscript() + self._to_string(out) + out.end_superscript() + +class Underline(Inline): + + "Underlined text." + + def to_string(self, out): + out.start_underline() + self._to_string(out) + out.end_underline() + + + +# Nodes without children. + +class Node: + + "A document node without children." + + def empty(self): + return False + +class Break(Node): + + "A paragraph break." + + def __repr__(self): + return "Break()" + + def prettyprint(self, indent=""): + return "%sBreak" % indent + + def to_string(self, out): + out.break_() + +class Macro(Node): + + "Macro details." + + def __init__(self, name, args): + self.name = name + self.args = args + + def __repr__(self): + return "Macro(%r, %r)" % (self.name, self.args) + + def prettyprint(self, indent=""): + return "%sMacro: name=%r args=%r" % (indent, self.name, self.args) + + def to_string(self, out): + out.macro(self.name, self.args) + +class Rule(Node): + + "A horizontal rule." + + def __init__(self, length): + self.length = length + + def __repr__(self): + return "Rule(%d)" % self.length + + def prettyprint(self, indent=""): + return "%sRule: length=%d" % (indent, self.length) + + def to_string(self, out): + out.rule(self.length) + +class TableAttr(Node): + + "A table attribute." + + def __init__(self, name, value=None, concise=False, quote=None): + self.name = name + self.value = value + self.concise = concise + self.quote = quote + + def __repr__(self): + return "TableAttr(%r, %r, %r, %r)" % (self.name, self.value, self.concise, self.quote) + + def prettyprint(self, indent=""): + return "%sTableAttr: name=%r value=%r concise=%r quote=%r" % (indent, self.name, self.value, self.concise, self.quote) + + def to_string(self, out): + out.table_attr(self.name, self.value, self.concise, self.quote) + +class Text(Node): + + "A text node." + + def __init__(self, s): + self.s = s + + def empty(self): + return not self.s + + def multiline(self): + return "\n" in self.s + + def merge(self, text): + self.s += text.s + + def __repr__(self): + return "Text(%r)" % self.s + + def prettyprint(self, indent=""): + return "%sText: %r" % (indent, self.s) + + def to_string(self, out): + out.text(self.s) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 moinformat/tree/table.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/moinformat/tree/table.py Tue Jul 24 15:53:44 2018 +0200 @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +""" +Extended table syntax document tree nodes. + +Copyright (C) 2018 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see . +""" + +from moinformat.tree.moin import Node + +class Continuation(Node): + + "Continuation padding for table content." + + def __init__(self, text): + self.text = text + + def __repr__(self): + return "Continuation(%r)" % self.text + + def prettyprint(self, indent=""): + return "%sContinuation: %r" % (indent, self.text) + + def to_string(self, out): + out.continuation(self.text) + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 848f3e4413f1 -r 645911c29199 tests/test_parser.py --- a/tests/test_parser.py Tue Jul 24 10:47:53 2018 +0200 +++ b/tests/test_parser.py Tue Jul 24 15:53:44 2018 +0200 @@ -12,8 +12,8 @@ if split(parent)[1] == "MoinLight": sys.path.append(parent) -from moinformat import all_parsers, all_serialisers, parse, serialise -from moinformat.tree import Container +from moinformat import all_parsers, get_serialiser, parse, serialise +from moinformat.tree.moin import Container from glob import glob def test_input(d, s): @@ -36,7 +36,7 @@ print "-" * 60 print s print "-" * 60 - print serialise(d, all_serialisers["html"]) + print serialise(d, get_serialiser("html")) print "-" * 60 print