MoinLight

Changeset

90:519478d7f35f
2018-07-24 Paul Boddie raw files shortlog changelog graph Merged changes from the default branch. single-regexp-searching
moinformat/parsers/common.py (file)
     1.1 --- a/convert.py	Tue Jul 24 15:53:44 2018 +0200
     1.2 +++ b/convert.py	Tue Jul 24 23:37:44 2018 +0200
     1.3 @@ -1,16 +1,9 @@
     1.4  #!/usr/bin/env python
     1.5  
     1.6 -from moinformat import all_parsers, get_serialiser, parse, serialise
     1.7 +from moinformat import get_serialiser, make_parser, parse, serialise
     1.8  from os.path import split
     1.9  import sys
    1.10  
    1.11 -def test_option(args, name):
    1.12 -    if name in args:
    1.13 -        args.remove(name)
    1.14 -        return True
    1.15 -    else:
    1.16 -        return False
    1.17 -
    1.18  def main():
    1.19      dirname, progname = split(sys.argv[0])
    1.20      args = sys.argv[1:]
    1.21 @@ -18,6 +11,7 @@
    1.22      l = filenames = []
    1.23      formats = []
    1.24      tree = False
    1.25 +    macros = False
    1.26  
    1.27      for arg in args:
    1.28  
    1.29 @@ -26,6 +20,11 @@
    1.30          if arg == "--tree":
    1.31              tree = True
    1.32  
    1.33 +        # Detect macro evaluation.
    1.34 +
    1.35 +        elif arg == "--macros":
    1.36 +            macros = True
    1.37 +
    1.38          # Switch to collecting formats
    1.39  
    1.40          elif arg == "--format":
    1.41 @@ -45,7 +44,12 @@
    1.42  
    1.43      f = open(filename)
    1.44      try:
    1.45 -        d = parse(f.read(), all_parsers)
    1.46 +        p = make_parser()
    1.47 +        d = parse(f.read(), p)
    1.48 +
    1.49 +        if macros:
    1.50 +            p.evaluate_macros()
    1.51 +
    1.52          if tree:
    1.53              print d.prettyprint()
    1.54          else:
     2.1 --- a/moinformat/__init__.py	Tue Jul 24 15:53:44 2018 +0200
     2.2 +++ b/moinformat/__init__.py	Tue Jul 24 23:37:44 2018 +0200
     2.3 @@ -22,9 +22,21 @@
     2.4  from moinformat.parsers import parse, parsers as all_parsers
     2.5  from moinformat.serialisers import serialise, serialisers as all_serialisers
     2.6  
     2.7 +def get_parser(name="moin"):
     2.8 +
     2.9 +    "Return the parser class supporting the format with the given 'name'."
    2.10 +
    2.11 +    return all_parsers[name]
    2.12 +
    2.13 +def make_parser(name="moin"):
    2.14 +
    2.15 +    "Return a parser instance for the format with the given 'name'."
    2.16 +
    2.17 +    return get_parser(name)(all_parsers)
    2.18 +
    2.19  def get_serialiser(name):
    2.20  
    2.21 -    "Return the main serialiser for the format having the given 'name'."
    2.22 +    "Return the main serialiser class for the format having the given 'name'."
    2.23  
    2.24      return all_serialisers["%s.moin" % name]
    2.25  
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/moinformat/macros/__init__.py	Tue Jul 24 23:37:44 2018 +0200
     3.3 @@ -0,0 +1,32 @@
     3.4 +#!/usr/bin/env python
     3.5 +
     3.6 +"""
     3.7 +Moin macro implementations.
     3.8 +
     3.9 +Copyright (C) 2018 Paul Boddie <paul@boddie.org.uk>
    3.10 +
    3.11 +This program is free software; you can redistribute it and/or modify it under
    3.12 +the terms of the GNU General Public License as published by the Free Software
    3.13 +Foundation; either version 3 of the License, or (at your option) any later
    3.14 +version.
    3.15 +
    3.16 +This program is distributed in the hope that it will be useful, but WITHOUT
    3.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    3.18 +FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    3.19 +details.
    3.20 +
    3.21 +You should have received a copy of the GNU General Public License along with
    3.22 +this program.  If not, see <http://www.gnu.org/licenses/>.
    3.23 +"""
    3.24 +
    3.25 +from moinformat.macros.manifest import macros
    3.26 +
    3.27 +# Top-level functions.
    3.28 +
    3.29 +def get_macro(name):
    3.30 +
    3.31 +    "Return the macro with the given 'name' or None if no macro is found."
    3.32 +
    3.33 +    return macros.get(name)
    3.34 +
    3.35 +# vim: tabstop=4 expandtab shiftwidth=4
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/moinformat/macros/common.py	Tue Jul 24 23:37:44 2018 +0200
     4.3 @@ -0,0 +1,33 @@
     4.4 +#!/usr/bin/env python
     4.5 +
     4.6 +"""
     4.7 +Common macro functionality.
     4.8 +
     4.9 +Copyright (C) 2018 Paul Boddie <paul@boddie.org.uk>
    4.10 +
    4.11 +This program is free software; you can redistribute it and/or modify it under
    4.12 +the terms of the GNU General Public License as published by the Free Software
    4.13 +Foundation; either version 3 of the License, or (at your option) any later
    4.14 +version.
    4.15 +
    4.16 +This program is distributed in the hope that it will be useful, but WITHOUT
    4.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    4.18 +FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    4.19 +details.
    4.20 +
    4.21 +You should have received a copy of the GNU General Public License along with
    4.22 +this program.  If not, see <http://www.gnu.org/licenses/>.
    4.23 +"""
    4.24 +
    4.25 +class Macro:
    4.26 +
    4.27 +    "Common macro functionality."
    4.28 +
    4.29 +    def __init__(self, node, doc):
    4.30 +
    4.31 +        "Initialise the macro with its tree 'node' and document root, 'doc'."
    4.32 +
    4.33 +        self.node = node
    4.34 +        self.doc = doc
    4.35 +
    4.36 +# vim: tabstop=4 expandtab shiftwidth=4
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/moinformat/macros/manifest.py	Tue Jul 24 23:37:44 2018 +0200
     5.3 @@ -0,0 +1,47 @@
     5.4 +#!/usr/bin/env python
     5.5 +
     5.6 +"""
     5.7 +Moin macro implementation manifest.
     5.8 +
     5.9 +Copyright (C) 2017, 2018 Paul Boddie <paul@boddie.org.uk>
    5.10 +
    5.11 +This program is free software; you can redistribute it and/or modify it under
    5.12 +the terms of the GNU General Public License as published by the Free Software
    5.13 +Foundation; either version 3 of the License, or (at your option) any later
    5.14 +version.
    5.15 +
    5.16 +This program is distributed in the hope that it will be useful, but WITHOUT
    5.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    5.18 +FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    5.19 +details.
    5.20 +
    5.21 +You should have received a copy of the GNU General Public License along with
    5.22 +this program.  If not, see <http://www.gnu.org/licenses/>.
    5.23 +"""
    5.24 +
    5.25 +from moinformat.imports import get_extensions
    5.26 +from os.path import split
    5.27 +
    5.28 +reserved = ["__init__", "common", "manifest"]
    5.29 +
    5.30 +# Obtain details of this module's package.
    5.31 +
    5.32 +dirname = split(__file__)[0]
    5.33 +package = __name__.rsplit(".", 1)[0]
    5.34 +
    5.35 +# Define an attribute mapping names to modules.
    5.36 +
    5.37 +modules = {}
    5.38 +get_extensions(dirname, package, modules, reserved)
    5.39 +
    5.40 +# Obtain all macros.
    5.41 +
    5.42 +macros = {}
    5.43 +
    5.44 +# Use names declared in each handler to register the handlers:
    5.45 +# macro.name -> macro
    5.46 +
    5.47 +for module in modules.values():
    5.48 +    macros[module.macro.name] = module.macro
    5.49 +
    5.50 +# vim: tabstop=4 expandtab shiftwidth=4
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/moinformat/macros/toc.py	Tue Jul 24 23:37:44 2018 +0200
     6.3 @@ -0,0 +1,153 @@
     6.4 +#!/usr/bin/env python
     6.5 +
     6.6 +"""
     6.7 +Table of contents macro.
     6.8 +
     6.9 +Copyright (C) 2018 Paul Boddie <paul@boddie.org.uk>
    6.10 +
    6.11 +This program is free software; you can redistribute it and/or modify it under
    6.12 +the terms of the GNU General Public License as published by the Free Software
    6.13 +Foundation; either version 3 of the License, or (at your option) any later
    6.14 +version.
    6.15 +
    6.16 +This program is distributed in the hope that it will be useful, but WITHOUT
    6.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    6.18 +FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    6.19 +details.
    6.20 +
    6.21 +You should have received a copy of the GNU General Public License along with
    6.22 +this program.  If not, see <http://www.gnu.org/licenses/>.
    6.23 +"""
    6.24 +
    6.25 +from moinformat.macros.common import Macro
    6.26 +from moinformat.tree.moin import Container, Heading, List, ListItem, Text
    6.27 +
    6.28 +class TableOfContents(Macro):
    6.29 +
    6.30 +    "A table of contents macro."
    6.31 +
    6.32 +    name = "TableOfContents"
    6.33 +
    6.34 +    def evaluate(self):
    6.35 +
    6.36 +        "Evaluate the macro, producing a table of contents."
    6.37 +
    6.38 +        arglist = []
    6.39 +        _defaults = [None] * 2
    6.40 +
    6.41 +        for arg, default in map(None, self.node.args, _defaults):
    6.42 +            if arg is not None:
    6.43 +                try:
    6.44 +                    arg = max(1, int(arg.strip()))
    6.45 +                except ValueError:
    6.46 +                    arg = None
    6.47 +            arglist.append(arg)
    6.48 +
    6.49 +        self.make_table(arglist[0], arglist[1])
    6.50 +
    6.51 +    def make_table(self, min_level=None, max_level=None):
    6.52 +
    6.53 +        """
    6.54 +        Make a table of contents with the given 'min_level' and 'max_level' of
    6.55 +        headings.
    6.56 +        """
    6.57 +
    6.58 +        headings = []
    6.59 +        self.find_headings(self.doc, headings)
    6.60 +
    6.61 +        if not headings:
    6.62 +            return
    6.63 +
    6.64 +        # Common list features.
    6.65 +
    6.66 +        marker = "1."
    6.67 +        space = " "
    6.68 +        num = "1"
    6.69 +        nl = [Text("\n")]
    6.70 +
    6.71 +        # Start with no lists, no current item.
    6.72 +
    6.73 +        lists = []
    6.74 +        item = None
    6.75 +        level = 0
    6.76 +
    6.77 +        for heading in headings:
    6.78 +            new_level = heading.level
    6.79 +
    6.80 +            # Create new lists if the level increases.
    6.81 +
    6.82 +            if new_level > level:
    6.83 +                while level < new_level:
    6.84 +                    level += 1
    6.85 +
    6.86 +                    if not (min_level <= level <= max_level):
    6.87 +                        continue
    6.88 +
    6.89 +                    # Determine whether the heading should be generated at this
    6.90 +                    # level.
    6.91 +
    6.92 +                    nodes = level == new_level and heading.nodes[:] + nl or []
    6.93 +                    indent = level - 1
    6.94 +
    6.95 +                    # Make a list and add an item to it.
    6.96 +
    6.97 +                    new_items = []
    6.98 +                    new_list = List(new_items, indent, marker, num)
    6.99 +                    new_item = ListItem(nodes, indent, marker, space, None)
   6.100 +                    new_items.append(new_item)
   6.101 +
   6.102 +                    # Add the list to the current item, if any.
   6.103 +
   6.104 +                    if item:
   6.105 +                        item.nodes.append(new_list)
   6.106 +
   6.107 +                    # Record the new list.
   6.108 +
   6.109 +                    lists.append(new_list)
   6.110 +
   6.111 +                    # Reference the new list's items and current item.
   6.112 +
   6.113 +                    items = new_items
   6.114 +                    item = new_item
   6.115 +
   6.116 +            else:
   6.117 +                # Retrieve an existing list if the level decreases.
   6.118 +
   6.119 +                if new_level < level:
   6.120 +                    while level > new_level:
   6.121 +                        if min_level <= level <= max_level:
   6.122 +                            lists.pop()
   6.123 +                        level -= 1
   6.124 +
   6.125 +                    # Obtain the existing list and the current item.
   6.126 +
   6.127 +                    items = lists[-1].nodes
   6.128 +                    item = items[-1]
   6.129 +
   6.130 +                # Add the heading as an item.
   6.131 +
   6.132 +                if min_level <= level <= max_level:
   6.133 +                    indent = level - 1
   6.134 +                    nodes = heading.nodes[:] + nl
   6.135 +
   6.136 +                    item = ListItem(nodes, indent, marker, space, None)
   6.137 +                    items.append(item)
   6.138 +
   6.139 +        # Replace the macro node's children with the top-level list.
   6.140 +
   6.141 +        self.node.nodes = [lists[0]]
   6.142 +
   6.143 +    def find_headings(self, node, headings):
   6.144 +
   6.145 +        "Find headings under 'node', adding them to the 'headings' list."
   6.146 +
   6.147 +        if node.nodes:
   6.148 +            for n in node.nodes:
   6.149 +                if isinstance(n, Heading):
   6.150 +                    headings.append(n)
   6.151 +                elif isinstance(n, Container):
   6.152 +                    self.find_headings(n, headings)
   6.153 +
   6.154 +macro = TableOfContents
   6.155 +
   6.156 +# vim: tabstop=4 expandtab shiftwidth=4
     7.1 --- a/moinformat/parsers/__init__.py	Tue Jul 24 15:53:44 2018 +0200
     7.2 +++ b/moinformat/parsers/__init__.py	Tue Jul 24 23:37:44 2018 +0200
     7.3 @@ -3,7 +3,7 @@
     7.4  """
     7.5  Moin wiki parsers.
     7.6  
     7.7 -Copyright (C) 2017 Paul Boddie <paul@boddie.org.uk>
     7.8 +Copyright (C) 2017, 2018 Paul Boddie <paul@boddie.org.uk>
     7.9  
    7.10  This program is free software; you can redistribute it and/or modify it under
    7.11  the terms of the GNU General Public License as published by the Free Software
    7.12 @@ -20,10 +20,15 @@
    7.13  """
    7.14  
    7.15  from moinformat.parsers.manifest import parsers
    7.16 +from moinformat.parsers.moin import MoinParser
    7.17  
    7.18  # Top-level functions.
    7.19  
    7.20 -def parse(s, formats=None):
    7.21 -    return parsers["moin"](formats).parse(s)
    7.22 +def parse(s, parser=None):
    7.23 +
    7.24 +    "Parse 's' with 'parser' or the Moin format parser if omitted."
    7.25 +
    7.26 +    parser = parser or MoinParser(parsers)
    7.27 +    return parser.parse(s)
    7.28  
    7.29  # vim: tabstop=4 expandtab shiftwidth=4
     8.1 --- a/moinformat/parsers/common.py	Tue Jul 24 15:53:44 2018 +0200
     8.2 +++ b/moinformat/parsers/common.py	Tue Jul 24 23:37:44 2018 +0200
     8.3 @@ -253,14 +253,16 @@
     8.4  
     8.5      region_pattern_names = None
     8.6  
     8.7 -    def __init__(self, formats=None):
     8.8 +    def __init__(self, formats=None, root=None):
     8.9  
    8.10          """
    8.11          Initialise the parser with any given 'formats' mapping from region type
    8.12 -        names to parser objects.
    8.13 +        names to parser objects. An optional 'root' indicates the document-level
    8.14 +        parser.
    8.15          """
    8.16  
    8.17          self.formats = formats
    8.18 +        self.root = root
    8.19  
    8.20      def get_parser(self, format_type):
    8.21  
    8.22 @@ -273,7 +275,7 @@
    8.23  
    8.24          cls = self.formats.get(format_type)
    8.25          if cls:
    8.26 -            return cls(self.formats)
    8.27 +            return cls(self.formats, self.root or self)
    8.28          else:
    8.29              return None
    8.30  
     9.1 --- a/moinformat/parsers/moin.py	Tue Jul 24 15:53:44 2018 +0200
     9.2 +++ b/moinformat/parsers/moin.py	Tue Jul 24 23:37:44 2018 +0200
     9.3 @@ -19,6 +19,7 @@
     9.4  this program.  If not, see <http://www.gnu.org/licenses/>.
     9.5  """
     9.6  
     9.7 +from moinformat.macros import get_macro
     9.8  from moinformat.parsers.common import ParserBase, get_patterns, \
     9.9                                        excl, expect, group, optional, recur, \
    9.10                                        repeat
    9.11 @@ -36,11 +37,12 @@
    9.12  
    9.13      "A wiki region parser."
    9.14  
    9.15 -    def __init__(self, formats=None):
    9.16 +    def __init__(self, formats=None, root=None):
    9.17  
    9.18          """
    9.19          Initialise the parser with any given 'formats' mapping from region type
    9.20 -        names to parser objects.
    9.21 +        names to parser objects. An optional 'root' indicates the document-level
    9.22 +        parser.
    9.23          """
    9.24  
    9.25          # Introduce this class as the default parser for the wiki format.
    9.26 @@ -49,7 +51,11 @@
    9.27          if formats:
    9.28              default_formats.update(formats)
    9.29  
    9.30 -        ParserBase.__init__(self, default_formats)
    9.31 +        ParserBase.__init__(self, default_formats, root)
    9.32 +
    9.33 +        # Record macro occurrences for later evaluation.
    9.34 +
    9.35 +        self.macros = []
    9.36  
    9.37      # Principal parser methods.
    9.38  
    9.39 @@ -81,6 +87,27 @@
    9.40  
    9.41  
    9.42  
    9.43 +    # Macro evaluation.
    9.44 +
    9.45 +    def evaluate_macros(self):
    9.46 +
    9.47 +        "Evaluate the macro nodes in the document."
    9.48 +
    9.49 +        for node in self.macros:
    9.50 +
    9.51 +            # Obtain a class for the named macro.
    9.52 +
    9.53 +            macro_cls = get_macro(node.name)
    9.54 +            if not macro_cls:
    9.55 +                continue
    9.56 +
    9.57 +            # Instantiate the class and evaluate the macro.
    9.58 +
    9.59 +            macro = macro_cls(node, self.region)
    9.60 +            macro.evaluate()
    9.61 +
    9.62 +
    9.63 +
    9.64      # Parser methods supporting different page features.
    9.65  
    9.66      def parse_attrname(self, attrs):
    9.67 @@ -464,6 +491,10 @@
    9.68          macro = Macro(name, arglist)
    9.69          region.append_inline(macro)
    9.70  
    9.71 +        # Record the macro for later processing.
    9.72 +
    9.73 +        self.root.macros.append(macro)
    9.74 +
    9.75  
    9.76  
    9.77      # Table attribute handlers.
    10.1 --- a/moinformat/serialisers/html/moin.py	Tue Jul 24 15:53:44 2018 +0200
    10.2 +++ b/moinformat/serialisers/html/moin.py	Tue Jul 24 23:37:44 2018 +0200
    10.3 @@ -141,6 +141,29 @@
    10.4      def end_listitem(self, indent, marker, space, num):
    10.5          self.out("</li>")
    10.6  
    10.7 +    def start_macro(self, name, args, nodes):
    10.8 +        self.out("<span class='macro'>")
    10.9 +
   10.10 +        # Fallback case for when macros are not replaced.
   10.11 +
   10.12 +        if not nodes:
   10.13 +            self.out(escape_text("<<"))
   10.14 +            self.out("<span class='name'>%s</span>" % escape_text(name))
   10.15 +            if args:
   10.16 +                self.out("(")
   10.17 +            first = True
   10.18 +            for arg in args:
   10.19 +                if not first:
   10.20 +                    self.out(",")
   10.21 +                self.out("<span class='arg'>%s</span>" % escape_text(arg))
   10.22 +                first = False
   10.23 +            if args:
   10.24 +                self.out(")")
   10.25 +            self.out(escape_text(">>"))
   10.26 +
   10.27 +    def end_macro(self):
   10.28 +        self.out("</span>")
   10.29 +
   10.30      def start_monospace(self):
   10.31          self.out("<tt>")
   10.32  
   10.33 @@ -213,32 +236,6 @@
   10.34      def break_(self):
   10.35          pass
   10.36  
   10.37 -    def macro(self, name, args):
   10.38 -
   10.39 -        # NOTE: Special case.
   10.40 -
   10.41 -        if name == "BR":
   10.42 -            self.out("<br />")
   10.43 -            return
   10.44 -
   10.45 -        # Fallback case.
   10.46 -
   10.47 -        self.out("<span class='macro'>")
   10.48 -        self.out(escape_text("<<"))
   10.49 -        self.out("<span class='name'>%s</span>" % escape_text(name))
   10.50 -        if args:
   10.51 -            self.out("(")
   10.52 -        first = True
   10.53 -        for arg in args:
   10.54 -            if not first:
   10.55 -                self.out(",")
   10.56 -            self.out("<span class='arg'>%s</span>" % escape_text(arg))
   10.57 -            first = False
   10.58 -        if args:
   10.59 -            self.out(")")
   10.60 -        self.out(escape_text(">>"))
   10.61 -        self.out("</span>")
   10.62 -
   10.63      def rule(self, length):
   10.64          self.out("<hr style='height: %dpt' />" % min(length, 10))
   10.65  
    11.1 --- a/moinformat/serialisers/moin/moin.py	Tue Jul 24 15:53:44 2018 +0200
    11.2 +++ b/moinformat/serialisers/moin/moin.py	Tue Jul 24 23:37:44 2018 +0200
    11.3 @@ -102,6 +102,16 @@
    11.4      def end_listitem(self, indent, marker, space, num):
    11.5          pass
    11.6  
    11.7 +    def start_macro(self, name, args, nodes):
    11.8 +
    11.9 +        # Fallback case for when macros are not replaced.
   11.10 +
   11.11 +        if not nodes:
   11.12 +            self.out("<<%s%s>>" % (name, args and "(%s)" % ",".join(args) or ""))
   11.13 +
   11.14 +    def end_macro(self):
   11.15 +        pass
   11.16 +
   11.17      def start_monospace(self):
   11.18          self.out("`")
   11.19  
   11.20 @@ -177,9 +187,6 @@
   11.21      def continuation(self, text):
   11.22          self.out(text)
   11.23  
   11.24 -    def macro(self, name, args):
   11.25 -        self.out("<<%s%s>>" % (name, args and "(%s)" % ",".join(args) or ""))
   11.26 -
   11.27      def rule(self, length):
   11.28          self.out("-" * length)
   11.29  
    12.1 --- a/moinformat/tree/moin.py	Tue Jul 24 15:53:44 2018 +0200
    12.2 +++ b/moinformat/tree/moin.py	Tue Jul 24 23:37:44 2018 +0200
    12.3 @@ -453,6 +453,28 @@
    12.4              out.end_linktext()
    12.5          out.end_link()
    12.6  
    12.7 +class Macro(Container):
    12.8 +
    12.9 +    "Macro details."
   12.10 +
   12.11 +    def __init__(self, name, args, nodes=None):
   12.12 +        Container.__init__(self, nodes or [])
   12.13 +        self.name = name
   12.14 +        self.args = args
   12.15 +
   12.16 +    def __repr__(self):
   12.17 +        return "Macro(%r, %r, %r)" % (self.name, self.args, self.nodes)
   12.18 +
   12.19 +    def prettyprint(self, indent=""):
   12.20 +        l = ["%sMacro: name=%r args=%r" % (indent, self.name, self.args)]
   12.21 +        return self._prettyprint(l, indent)
   12.22 +
   12.23 +    def to_string(self, out):
   12.24 +        out.start_macro(self.name, self.args, self.nodes)
   12.25 +        if self.nodes:
   12.26 +            self._to_string(out)
   12.27 +        out.end_macro()
   12.28 +
   12.29  class Monospace(Inline):
   12.30  
   12.31      "Monospaced text."
   12.32 @@ -531,23 +553,6 @@
   12.33      def to_string(self, out):
   12.34          out.break_()
   12.35  
   12.36 -class Macro(Node):
   12.37 -
   12.38 -    "Macro details."
   12.39 -
   12.40 -    def __init__(self, name, args):
   12.41 -        self.name = name
   12.42 -        self.args = args
   12.43 -
   12.44 -    def __repr__(self):
   12.45 -        return "Macro(%r, %r)" % (self.name, self.args)
   12.46 -
   12.47 -    def prettyprint(self, indent=""):
   12.48 -        return "%sMacro: name=%r args=%r" % (indent, self.name, self.args)
   12.49 -
   12.50 -    def to_string(self, out):
   12.51 -        out.macro(self.name, self.args)
   12.52 -
   12.53  class Rule(Node):
   12.54  
   12.55      "A horizontal rule."
    13.1 --- a/tests/test_parser.py	Tue Jul 24 15:53:44 2018 +0200
    13.2 +++ b/tests/test_parser.py	Tue Jul 24 23:37:44 2018 +0200
    13.3 @@ -12,7 +12,7 @@
    13.4      if split(parent)[1] == "MoinLight":
    13.5          sys.path.append(parent)
    13.6  
    13.7 -from moinformat import all_parsers, get_serialiser, parse, serialise
    13.8 +from moinformat import get_serialiser, parse, serialise
    13.9  from moinformat.tree.moin import Container
   13.10  from glob import glob
   13.11  
   13.12 @@ -185,7 +185,7 @@
   13.13          tree_filename = "%s.tree" % filename.rsplit(".", 1)[0]
   13.14  
   13.15          s = readfile(filename)
   13.16 -        d = parse(s, all_parsers)
   13.17 +        d = parse(s)
   13.18  
   13.19          if exists(tree_filename):
   13.20              ts = readfile(tree_filename)