1.1 --- a/moinformat/parsers/common.py Tue Jul 24 10:47:29 2018 +0200
1.2 +++ b/moinformat/parsers/common.py Tue Jul 24 12:58:43 2018 +0200
1.3 @@ -20,7 +20,7 @@
1.4 """
1.5
1.6 from collections import defaultdict
1.7 -from moinformat.tree import Block, Region, Text
1.8 +from moinformat.tree.moin import Block, Region, Text
1.9 import re
1.10
1.11 # Pattern management.
2.1 --- a/moinformat/parsers/moin.py Tue Jul 24 10:47:29 2018 +0200
2.2 +++ b/moinformat/parsers/moin.py Tue Jul 24 12:58:43 2018 +0200
2.3 @@ -20,13 +20,15 @@
2.4 """
2.5
2.6 from moinformat.parsers.common import ParserBase, get_patterns, \
2.7 - excl, expect, group, optional, recur, repeat
2.8 + excl, expect, group, optional, recur, \
2.9 + repeat
2.10 from moinformat.serialisers import serialise
2.11 -from moinformat.tree import Break, DefItem, DefTerm, FontStyle, Heading, \
2.12 - Larger, Link, List, ListItem, Macro, Monospace, \
2.13 - Region, Rule, Smaller, Strikethrough, Subscript, \
2.14 - Superscript, Table, TableAttr, TableAttrs, \
2.15 - TableCell, TableRow, Text, Underline
2.16 +from moinformat.tree.moin import Break, DefItem, DefTerm, FontStyle, Heading, \
2.17 + Larger, Link, List, ListItem, Macro, \
2.18 + Monospace, Region, Rule, Smaller, \
2.19 + Strikethrough, Subscript, Superscript, Table, \
2.20 + TableAttr, TableAttrs, TableCell, TableRow, \
2.21 + Text, Underline
2.22
2.23 join = "".join
2.24
3.1 --- a/moinformat/parsers/table.py Tue Jul 24 10:47:29 2018 +0200
3.2 +++ b/moinformat/parsers/table.py Tue Jul 24 12:58:43 2018 +0200
3.3 @@ -22,8 +22,8 @@
3.4 from moinformat.parsers.common import get_patterns, \
3.5 excl, expect, group
3.6 from moinformat.parsers.moin import MoinParser
3.7 -from moinformat.tree import Continuation, Table, TableAttrs, TableCell, \
3.8 - TableRow, Text
3.9 +from moinformat.tree.moin import Table, TableAttrs, TableCell, TableRow, Text
3.10 +from moinformat.tree.table import Continuation
3.11
3.12 join = "".join
3.13
4.1 --- a/moinformat/tree.py Tue Jul 24 10:47:29 2018 +0200
4.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
4.3 @@ -1,621 +0,0 @@
4.4 -#!/usr/bin/env python
4.5 -
4.6 -"""
4.7 -Moin wiki format document tree nodes.
4.8 -
4.9 -Copyright (C) 2017, 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 Container:
4.26 -
4.27 - "A container of document nodes."
4.28 -
4.29 - def __init__(self, nodes):
4.30 - self.nodes = nodes
4.31 -
4.32 - # In principle, allow blocks within containers. Some nodes may forbid
4.33 - # them to simplify the document structure.
4.34 -
4.35 - self.allow_blocks = True
4.36 -
4.37 - def append(self, node):
4.38 - self.nodes.append(node)
4.39 -
4.40 - def append_many(self, nodes):
4.41 - for node in nodes:
4.42 - self.append(node)
4.43 -
4.44 - add = append
4.45 -
4.46 - append_inline = append
4.47 -
4.48 - def append_inline_many(self, nodes):
4.49 - for node in nodes:
4.50 - self.append_inline(node)
4.51 -
4.52 - def empty(self):
4.53 - return not self.nodes
4.54 -
4.55 - def node(self, index):
4.56 - try:
4.57 - return self.nodes[index]
4.58 - except IndexError:
4.59 - return None
4.60 -
4.61 - def normalise(self):
4.62 -
4.63 - "Combine adjacent text nodes."
4.64 -
4.65 - nodes = self.nodes
4.66 - self.nodes = []
4.67 - text = None
4.68 -
4.69 - for node in nodes:
4.70 -
4.71 - # Open a text node or merge text into an open node.
4.72 -
4.73 - if isinstance(node, Text):
4.74 - if not text:
4.75 - text = node
4.76 - else:
4.77 - text.merge(node)
4.78 -
4.79 - # Close any open text node and append the current node.
4.80 -
4.81 - else:
4.82 - if text:
4.83 - self.append(text)
4.84 - text = None
4.85 - self.append(node)
4.86 -
4.87 - # Add any open text node.
4.88 -
4.89 - if text:
4.90 - self.append(text)
4.91 -
4.92 - def __str__(self):
4.93 - return self.prettyprint()
4.94 -
4.95 - def _prettyprint(self, l, indent=""):
4.96 - for node in self.nodes:
4.97 - l.append(node.prettyprint(indent + " "))
4.98 - return "\n".join(l)
4.99 -
4.100 - def _to_string(self, out):
4.101 - for node in self.nodes:
4.102 - node.to_string(out)
4.103 -
4.104 -class Region(Container):
4.105 -
4.106 - "A region of the page."
4.107 -
4.108 - def __init__(self, nodes, level=0, indent=0, type=None, transparent=True, extra=None):
4.109 - Container.__init__(self, nodes)
4.110 - self.level = level
4.111 - self.indent = indent
4.112 - self.type = type
4.113 - self.transparent = transparent
4.114 - self.extra = extra
4.115 -
4.116 - def add(self, node):
4.117 - last = self.node(-1)
4.118 - if last and last.empty():
4.119 - self.nodes[-1] = node
4.120 - else:
4.121 - self.append(node)
4.122 -
4.123 - def append_inline(self, node):
4.124 - if self.transparent:
4.125 - self.nodes[-1].append(node)
4.126 - else:
4.127 - self.append(node)
4.128 -
4.129 - def have_end(self, s):
4.130 - return self.level and s.startswith("}") and self.level == len(s)
4.131 -
4.132 - def __repr__(self):
4.133 - return "Region(%r, %r, %r, %r, %r, %r)" % (self.nodes, self.level,
4.134 - self.indent, self.type, self.transparent, self.extra)
4.135 -
4.136 - def prettyprint(self, indent=""):
4.137 - l = ["%sRegion: level=%d indent=%d type=%s extra=%r" % (indent,
4.138 - self.level, self.indent, self.type, self.extra)]
4.139 - return self._prettyprint(l, indent)
4.140 -
4.141 - def to_string(self, out):
4.142 - out.start_region(self.level, self.indent, self.type, self.extra)
4.143 -
4.144 - # Obtain a serialiser for the region, if appropriate.
4.145 -
4.146 - serialiser = out.formats and out.formats.get(self.type)
4.147 - region_out = serialiser and serialiser(out.out, out.formats) or out
4.148 -
4.149 - # Serialise the region.
4.150 -
4.151 - self._to_string(region_out)
4.152 -
4.153 - out.end_region(self.level, self.indent, self.type, self.extra)
4.154 -
4.155 -
4.156 -
4.157 -# Block nodes.
4.158 -
4.159 -class Block(Container):
4.160 -
4.161 - "A block in the page."
4.162 -
4.163 - def __repr__(self):
4.164 - return "Block(%r)" % self.nodes
4.165 -
4.166 - def prettyprint(self, indent=""):
4.167 - l = ["%sBlock" % indent]
4.168 - return self._prettyprint(l, indent)
4.169 -
4.170 - def to_string(self, out):
4.171 - out.start_block()
4.172 - self._to_string(out)
4.173 - out.end_block()
4.174 -
4.175 -class DefItem(Container):
4.176 -
4.177 - "A definition item."
4.178 -
4.179 - def __init__(self, nodes, pad, extra):
4.180 - Container.__init__(self, nodes)
4.181 - self.pad = pad
4.182 - self.extra = extra
4.183 -
4.184 - def __repr__(self):
4.185 - return "DefItem(%r, %r, %r)" % (self.nodes, self.pad, self.extra)
4.186 -
4.187 - def prettyprint(self, indent=""):
4.188 - l = ["%sDefItem: pad=%r extra=%r" % (indent, self.pad, self.extra)]
4.189 - return self._prettyprint(l, indent)
4.190 -
4.191 - def to_string(self, out):
4.192 - out.start_defitem(self.pad, self.extra)
4.193 - self._to_string(out)
4.194 - out.end_defitem(self.pad, self.extra)
4.195 -
4.196 -class DefTerm(Container):
4.197 -
4.198 - "A definition term."
4.199 -
4.200 - def __init__(self, nodes, pad):
4.201 - Container.__init__(self, nodes)
4.202 - self.pad = pad
4.203 -
4.204 - def __repr__(self):
4.205 - return "DefTerm(%r, %r)" % (self.nodes, self.pad)
4.206 -
4.207 - def prettyprint(self, indent=""):
4.208 - l = ["%sDefTerm: pad=%r" % (indent, self.pad)]
4.209 - return self._prettyprint(l, indent)
4.210 -
4.211 - def to_string(self, out):
4.212 - out.start_defterm(self.pad)
4.213 - self._to_string(out)
4.214 - out.end_defterm(self.pad)
4.215 -
4.216 -class FontStyle(Container):
4.217 -
4.218 - "Emphasised and/or strong text."
4.219 -
4.220 - def __init__(self, nodes, emphasis=False, strong=False):
4.221 - Container.__init__(self, nodes)
4.222 - self.emphasis = emphasis
4.223 - self.strong = strong
4.224 -
4.225 - def close_emphasis(self):
4.226 - if self.strong:
4.227 - span = FontStyle(self.nodes, emphasis=True)
4.228 - self.nodes = [span]
4.229 - self.emphasis = False
4.230 - return self.strong
4.231 -
4.232 - def close_strong(self):
4.233 - if self.emphasis:
4.234 - span = FontStyle(self.nodes, strong=True)
4.235 - self.nodes = [span]
4.236 - self.strong = False
4.237 - return self.emphasis
4.238 -
4.239 - def __repr__(self):
4.240 - return "FontStyle(%r, %r, %r)" % (self.nodes, self.emphasis, self.strong)
4.241 -
4.242 - def prettyprint(self, indent=""):
4.243 - l = ["%sFontStyle: emphasis=%r strong=%r" % (indent, self.emphasis, self.strong)]
4.244 - return self._prettyprint(l, indent)
4.245 -
4.246 - def to_string(self, out):
4.247 - if self.emphasis:
4.248 - out.start_emphasis()
4.249 - elif self.strong:
4.250 - out.start_strong()
4.251 - self._to_string(out)
4.252 - if self.emphasis:
4.253 - out.end_emphasis()
4.254 - elif self.strong:
4.255 - out.end_strong()
4.256 -
4.257 -class Heading(Container):
4.258 -
4.259 - "A heading."
4.260 -
4.261 - def __init__(self, nodes, level, start_extra="", start_pad="", end_pad="", end_extra=""):
4.262 - Container.__init__(self, nodes)
4.263 - self.level = level
4.264 - self.start_extra = start_extra
4.265 - self.start_pad = start_pad
4.266 - self.end_pad = end_pad
4.267 - self.end_extra = end_extra
4.268 -
4.269 - def __repr__(self):
4.270 - return "Heading(%r, %d, %r, %r, %r, %r)" % (
4.271 - self.nodes, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra)
4.272 -
4.273 - def prettyprint(self, indent=""):
4.274 - l = ["%sHeading: level=%d start_extra=%r start_pad=%r end_pad=%r end_extra=%r" % (
4.275 - indent, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra)]
4.276 - return self._prettyprint(l, indent)
4.277 -
4.278 - def to_string(self, out):
4.279 - out.start_heading(self.level, self.start_extra, self.start_pad)
4.280 - self._to_string(out)
4.281 - out.end_heading(self.level, self.end_pad, self.end_extra)
4.282 -
4.283 -class List(Container):
4.284 -
4.285 - "A list."
4.286 -
4.287 - def __init__(self, nodes, indent, marker, num):
4.288 - Container.__init__(self, nodes)
4.289 - self.indent = indent
4.290 - self.marker = marker
4.291 - self.num = num
4.292 -
4.293 - def __repr__(self):
4.294 - return "List(%r, %r, %r, %r)" % (self.nodes, self.indent, self.marker, self.num)
4.295 -
4.296 - def prettyprint(self, indent=""):
4.297 - l = ["%sList: indent=%d marker=%r num=%r" % (indent, self.indent, self.marker, self.num)]
4.298 - return self._prettyprint(l, indent)
4.299 -
4.300 - def to_string(self, out):
4.301 - out.start_list(self.indent, self.marker, self.num)
4.302 - self._to_string(out)
4.303 - out.end_list(self.indent, self.marker, self.num)
4.304 -
4.305 -class ListItem(Container):
4.306 -
4.307 - "A list item."
4.308 -
4.309 - def __init__(self, nodes, indent, marker, space, num):
4.310 - Container.__init__(self, nodes)
4.311 - self.indent = indent
4.312 - self.marker = marker
4.313 - self.space = space
4.314 - self.num = num
4.315 -
4.316 - # Forbid blocks within list items for simpler structure.
4.317 -
4.318 - self.allow_blocks = False
4.319 -
4.320 - def __repr__(self):
4.321 - return "ListItem(%r, %r, %r, %r, %r)" % (self.nodes, self.indent, self.marker, self.space, self.num)
4.322 -
4.323 - def prettyprint(self, indent=""):
4.324 - l = ["%sListItem: indent=%d marker=%r space=%r num=%r" % (indent, self.indent, self.marker, self.space, self.num)]
4.325 - return self._prettyprint(l, indent)
4.326 -
4.327 - def to_string(self, out):
4.328 - out.start_listitem(self.indent, self.marker, self.space, self.num)
4.329 - self._to_string(out)
4.330 - out.end_listitem(self.indent, self.marker, self.space, self.num)
4.331 -
4.332 -class TableAttrs(Container):
4.333 -
4.334 - "A collection of table attributes."
4.335 -
4.336 - def __repr__(self):
4.337 - return "TableAttrs(%r)" % self.nodes
4.338 -
4.339 - def prettyprint(self, indent=""):
4.340 - l = ["%sTableAttrs:" % indent]
4.341 - return self._prettyprint(l, indent)
4.342 -
4.343 - def to_string(self, out):
4.344 - out.start_table_attrs()
4.345 - self._to_string(out)
4.346 - out.end_table_attrs()
4.347 -
4.348 -class Table(Container):
4.349 -
4.350 - "A table."
4.351 -
4.352 - def __repr__(self):
4.353 - return "Table(%r)" % self.nodes
4.354 -
4.355 - def prettyprint(self, indent=""):
4.356 - l = ["%sTable:" % indent]
4.357 - return self._prettyprint(l, indent)
4.358 -
4.359 - def to_string(self, out):
4.360 - out.start_table()
4.361 - self._to_string(out)
4.362 - out.end_table()
4.363 -
4.364 -class TableCell(Container):
4.365 -
4.366 - "A table cell."
4.367 -
4.368 - def __init__(self, nodes, attrs=None):
4.369 - Container.__init__(self, nodes)
4.370 - self.attrs = attrs
4.371 -
4.372 - def __repr__(self):
4.373 - return "TableCell(%r, %r)" % (self.nodes, self.attrs)
4.374 -
4.375 - def prettyprint(self, indent=""):
4.376 - l = ["%sTableCell:" % indent]
4.377 - return self._prettyprint(l, indent)
4.378 -
4.379 - def to_string(self, out):
4.380 - out.start_table_cell(self.attrs)
4.381 - for node in self.nodes:
4.382 - if node is not self.attrs:
4.383 - node.to_string(out)
4.384 - out.end_table_cell()
4.385 -
4.386 -class TableRow(Container):
4.387 -
4.388 - "A table row."
4.389 -
4.390 - def __init__(self, nodes, trailing=""):
4.391 - Container.__init__(self, nodes)
4.392 - self.trailing = trailing
4.393 -
4.394 - def __repr__(self):
4.395 - return "TableRow(%r, %r)" % (self.nodes, self.trailing)
4.396 -
4.397 - def prettyprint(self, indent=""):
4.398 - l = ["%sTableRow: trailing=%r" % (indent, self.trailing)]
4.399 - return self._prettyprint(l, indent)
4.400 -
4.401 - def to_string(self, out):
4.402 - out.start_table_row()
4.403 - self._to_string(out)
4.404 - out.end_table_row(self.trailing)
4.405 -
4.406 -
4.407 -
4.408 -# Inline nodes with children.
4.409 -
4.410 -class Inline(Container):
4.411 -
4.412 - "Generic inline formatting."
4.413 -
4.414 - def __repr__(self):
4.415 - return "%s(%r)" % (self.__class__.__name__, self.nodes)
4.416 -
4.417 - def prettyprint(self, indent=""):
4.418 - l = ["%s%s" % (indent, self.__class__.__name__)]
4.419 - return self._prettyprint(l, indent)
4.420 -
4.421 -class Larger(Inline):
4.422 -
4.423 - "Larger text."
4.424 -
4.425 - def to_string(self, out):
4.426 - out.start_larger()
4.427 - self._to_string(out)
4.428 - out.end_larger()
4.429 -
4.430 -class Link(Container):
4.431 -
4.432 - "Link details."
4.433 -
4.434 - def __init__(self, nodes, target):
4.435 - Container.__init__(self, nodes)
4.436 - self.target = target
4.437 -
4.438 - def __repr__(self):
4.439 - return "Link(%r, %r)" % (self.nodes, self.target)
4.440 -
4.441 - def prettyprint(self, indent=""):
4.442 - l = ["%sLink: target=%r" % (indent, self.target)]
4.443 - return self._prettyprint(l, indent)
4.444 -
4.445 - def to_string(self, out):
4.446 - out.start_link(self.target)
4.447 - if self.nodes:
4.448 - out.start_linktext()
4.449 - self._to_string(out)
4.450 - out.end_linktext()
4.451 - out.end_link()
4.452 -
4.453 -class Monospace(Inline):
4.454 -
4.455 - "Monospaced text."
4.456 -
4.457 - def to_string(self, out):
4.458 - out.start_monospace()
4.459 - self._to_string(out)
4.460 - out.end_monospace()
4.461 -
4.462 -class Smaller(Inline):
4.463 -
4.464 - "Smaller text."
4.465 -
4.466 - def to_string(self, out):
4.467 - out.start_smaller()
4.468 - self._to_string(out)
4.469 - out.end_smaller()
4.470 -
4.471 -class Strikethrough(Inline):
4.472 -
4.473 - "Crossed-out text."
4.474 -
4.475 - def to_string(self, out):
4.476 - out.start_strikethrough()
4.477 - self._to_string(out)
4.478 - out.end_strikethrough()
4.479 -
4.480 -class Subscript(Inline):
4.481 -
4.482 - "Subscripted text."
4.483 -
4.484 - def to_string(self, out):
4.485 - out.start_subscript()
4.486 - self._to_string(out)
4.487 - out.end_subscript()
4.488 -
4.489 -class Superscript(Inline):
4.490 -
4.491 - "Superscripted text."
4.492 -
4.493 - def to_string(self, out):
4.494 - out.start_superscript()
4.495 - self._to_string(out)
4.496 - out.end_superscript()
4.497 -
4.498 -class Underline(Inline):
4.499 -
4.500 - "Underlined text."
4.501 -
4.502 - def to_string(self, out):
4.503 - out.start_underline()
4.504 - self._to_string(out)
4.505 - out.end_underline()
4.506 -
4.507 -
4.508 -
4.509 -# Nodes without children.
4.510 -
4.511 -class Node:
4.512 -
4.513 - "A document node without children."
4.514 -
4.515 - def empty(self):
4.516 - return False
4.517 -
4.518 -class Break(Node):
4.519 -
4.520 - "A paragraph break."
4.521 -
4.522 - def __repr__(self):
4.523 - return "Break()"
4.524 -
4.525 - def prettyprint(self, indent=""):
4.526 - return "%sBreak" % indent
4.527 -
4.528 - def to_string(self, out):
4.529 - out.break_()
4.530 -
4.531 -class Continuation(Node):
4.532 -
4.533 - "Continuation padding for table content."
4.534 -
4.535 - def __init__(self, text):
4.536 - self.text = text
4.537 -
4.538 - def __repr__(self):
4.539 - return "Continuation(%r)" % self.text
4.540 -
4.541 - def prettyprint(self, indent=""):
4.542 - return "%sContinuation: %r" % (indent, self.text)
4.543 -
4.544 - def to_string(self, out):
4.545 - out.continuation(self.text)
4.546 -
4.547 -class Macro(Node):
4.548 -
4.549 - "Macro details."
4.550 -
4.551 - def __init__(self, name, args):
4.552 - self.name = name
4.553 - self.args = args
4.554 -
4.555 - def __repr__(self):
4.556 - return "Macro(%r, %r)" % (self.name, self.args)
4.557 -
4.558 - def prettyprint(self, indent=""):
4.559 - return "%sMacro: name=%r args=%r" % (indent, self.name, self.args)
4.560 -
4.561 - def to_string(self, out):
4.562 - out.macro(self.name, self.args)
4.563 -
4.564 -class Rule(Node):
4.565 -
4.566 - "A horizontal rule."
4.567 -
4.568 - def __init__(self, length):
4.569 - self.length = length
4.570 -
4.571 - def __repr__(self):
4.572 - return "Rule(%d)" % self.length
4.573 -
4.574 - def prettyprint(self, indent=""):
4.575 - return "%sRule: length=%d" % (indent, self.length)
4.576 -
4.577 - def to_string(self, out):
4.578 - out.rule(self.length)
4.579 -
4.580 -class TableAttr(Node):
4.581 -
4.582 - "A table attribute."
4.583 -
4.584 - def __init__(self, name, value=None, concise=False, quote=None):
4.585 - self.name = name
4.586 - self.value = value
4.587 - self.concise = concise
4.588 - self.quote = quote
4.589 -
4.590 - def __repr__(self):
4.591 - return "TableAttr(%r, %r, %r, %r)" % (self.name, self.value, self.concise, self.quote)
4.592 -
4.593 - def prettyprint(self, indent=""):
4.594 - return "%sTableAttr: name=%r value=%r concise=%r quote=%r" % (indent, self.name, self.value, self.concise, self.quote)
4.595 -
4.596 - def to_string(self, out):
4.597 - out.table_attr(self.name, self.value, self.concise, self.quote)
4.598 -
4.599 -class Text(Node):
4.600 -
4.601 - "A text node."
4.602 -
4.603 - def __init__(self, s):
4.604 - self.s = s
4.605 -
4.606 - def empty(self):
4.607 - return not self.s
4.608 -
4.609 - def multiline(self):
4.610 - return "\n" in self.s
4.611 -
4.612 - def merge(self, text):
4.613 - self.s += text.s
4.614 -
4.615 - def __repr__(self):
4.616 - return "Text(%r)" % self.s
4.617 -
4.618 - def prettyprint(self, indent=""):
4.619 - return "%sText: %r" % (indent, self.s)
4.620 -
4.621 - def to_string(self, out):
4.622 - out.text(self.s)
4.623 -
4.624 -# vim: tabstop=4 expandtab shiftwidth=4
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/moinformat/tree/__init__.py Tue Jul 24 12:58:43 2018 +0200
5.3 @@ -0,0 +1,22 @@
5.4 +#!/usr/bin/env python
5.5 +
5.6 +"""
5.7 +A package of modules containing document tree nodes.
5.8 +
5.9 +Copyright (C) 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 +# vim: tabstop=4 expandtab shiftwidth=4
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/moinformat/tree/moin.py Tue Jul 24 12:58:43 2018 +0200
6.3 @@ -0,0 +1,605 @@
6.4 +#!/usr/bin/env python
6.5 +
6.6 +"""
6.7 +Moin wiki format document tree nodes.
6.8 +
6.9 +Copyright (C) 2017, 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 +class Container:
6.26 +
6.27 + "A container of document nodes."
6.28 +
6.29 + def __init__(self, nodes):
6.30 + self.nodes = nodes
6.31 +
6.32 + # In principle, allow blocks within containers. Some nodes may forbid
6.33 + # them to simplify the document structure.
6.34 +
6.35 + self.allow_blocks = True
6.36 +
6.37 + def append(self, node):
6.38 + self.nodes.append(node)
6.39 +
6.40 + def append_many(self, nodes):
6.41 + for node in nodes:
6.42 + self.append(node)
6.43 +
6.44 + add = append
6.45 +
6.46 + append_inline = append
6.47 +
6.48 + def append_inline_many(self, nodes):
6.49 + for node in nodes:
6.50 + self.append_inline(node)
6.51 +
6.52 + def empty(self):
6.53 + return not self.nodes
6.54 +
6.55 + def node(self, index):
6.56 + try:
6.57 + return self.nodes[index]
6.58 + except IndexError:
6.59 + return None
6.60 +
6.61 + def normalise(self):
6.62 +
6.63 + "Combine adjacent text nodes."
6.64 +
6.65 + nodes = self.nodes
6.66 + self.nodes = []
6.67 + text = None
6.68 +
6.69 + for node in nodes:
6.70 +
6.71 + # Open a text node or merge text into an open node.
6.72 +
6.73 + if isinstance(node, Text):
6.74 + if not text:
6.75 + text = node
6.76 + else:
6.77 + text.merge(node)
6.78 +
6.79 + # Close any open text node and append the current node.
6.80 +
6.81 + else:
6.82 + if text:
6.83 + self.append(text)
6.84 + text = None
6.85 + self.append(node)
6.86 +
6.87 + # Add any open text node.
6.88 +
6.89 + if text:
6.90 + self.append(text)
6.91 +
6.92 + def __str__(self):
6.93 + return self.prettyprint()
6.94 +
6.95 + def _prettyprint(self, l, indent=""):
6.96 + for node in self.nodes:
6.97 + l.append(node.prettyprint(indent + " "))
6.98 + return "\n".join(l)
6.99 +
6.100 + def _to_string(self, out):
6.101 + for node in self.nodes:
6.102 + node.to_string(out)
6.103 +
6.104 +class Region(Container):
6.105 +
6.106 + "A region of the page."
6.107 +
6.108 + def __init__(self, nodes, level=0, indent=0, type=None, transparent=True, extra=None):
6.109 + Container.__init__(self, nodes)
6.110 + self.level = level
6.111 + self.indent = indent
6.112 + self.type = type
6.113 + self.transparent = transparent
6.114 + self.extra = extra
6.115 +
6.116 + def add(self, node):
6.117 + last = self.node(-1)
6.118 + if last and last.empty():
6.119 + self.nodes[-1] = node
6.120 + else:
6.121 + self.append(node)
6.122 +
6.123 + def append_inline(self, node):
6.124 + if self.transparent:
6.125 + self.nodes[-1].append(node)
6.126 + else:
6.127 + self.append(node)
6.128 +
6.129 + def have_end(self, s):
6.130 + return self.level and s.startswith("}") and self.level == len(s)
6.131 +
6.132 + def __repr__(self):
6.133 + return "Region(%r, %r, %r, %r, %r, %r)" % (self.nodes, self.level,
6.134 + self.indent, self.type, self.transparent, self.extra)
6.135 +
6.136 + def prettyprint(self, indent=""):
6.137 + l = ["%sRegion: level=%d indent=%d type=%s extra=%r" % (indent,
6.138 + self.level, self.indent, self.type, self.extra)]
6.139 + return self._prettyprint(l, indent)
6.140 +
6.141 + def to_string(self, out):
6.142 + out.start_region(self.level, self.indent, self.type, self.extra)
6.143 +
6.144 + # Obtain a serialiser for the region, if appropriate.
6.145 +
6.146 + serialiser = out.formats and out.formats.get(self.type)
6.147 + region_out = serialiser and serialiser(out.out, out.formats) or out
6.148 +
6.149 + # Serialise the region.
6.150 +
6.151 + self._to_string(region_out)
6.152 +
6.153 + out.end_region(self.level, self.indent, self.type, self.extra)
6.154 +
6.155 +
6.156 +
6.157 +# Block nodes.
6.158 +
6.159 +class Block(Container):
6.160 +
6.161 + "A block in the page."
6.162 +
6.163 + def __repr__(self):
6.164 + return "Block(%r)" % self.nodes
6.165 +
6.166 + def prettyprint(self, indent=""):
6.167 + l = ["%sBlock" % indent]
6.168 + return self._prettyprint(l, indent)
6.169 +
6.170 + def to_string(self, out):
6.171 + out.start_block()
6.172 + self._to_string(out)
6.173 + out.end_block()
6.174 +
6.175 +class DefItem(Container):
6.176 +
6.177 + "A definition item."
6.178 +
6.179 + def __init__(self, nodes, pad, extra):
6.180 + Container.__init__(self, nodes)
6.181 + self.pad = pad
6.182 + self.extra = extra
6.183 +
6.184 + def __repr__(self):
6.185 + return "DefItem(%r, %r, %r)" % (self.nodes, self.pad, self.extra)
6.186 +
6.187 + def prettyprint(self, indent=""):
6.188 + l = ["%sDefItem: pad=%r extra=%r" % (indent, self.pad, self.extra)]
6.189 + return self._prettyprint(l, indent)
6.190 +
6.191 + def to_string(self, out):
6.192 + out.start_defitem(self.pad, self.extra)
6.193 + self._to_string(out)
6.194 + out.end_defitem(self.pad, self.extra)
6.195 +
6.196 +class DefTerm(Container):
6.197 +
6.198 + "A definition term."
6.199 +
6.200 + def __init__(self, nodes, pad):
6.201 + Container.__init__(self, nodes)
6.202 + self.pad = pad
6.203 +
6.204 + def __repr__(self):
6.205 + return "DefTerm(%r, %r)" % (self.nodes, self.pad)
6.206 +
6.207 + def prettyprint(self, indent=""):
6.208 + l = ["%sDefTerm: pad=%r" % (indent, self.pad)]
6.209 + return self._prettyprint(l, indent)
6.210 +
6.211 + def to_string(self, out):
6.212 + out.start_defterm(self.pad)
6.213 + self._to_string(out)
6.214 + out.end_defterm(self.pad)
6.215 +
6.216 +class FontStyle(Container):
6.217 +
6.218 + "Emphasised and/or strong text."
6.219 +
6.220 + def __init__(self, nodes, emphasis=False, strong=False):
6.221 + Container.__init__(self, nodes)
6.222 + self.emphasis = emphasis
6.223 + self.strong = strong
6.224 +
6.225 + def close_emphasis(self):
6.226 + if self.strong:
6.227 + span = FontStyle(self.nodes, emphasis=True)
6.228 + self.nodes = [span]
6.229 + self.emphasis = False
6.230 + return self.strong
6.231 +
6.232 + def close_strong(self):
6.233 + if self.emphasis:
6.234 + span = FontStyle(self.nodes, strong=True)
6.235 + self.nodes = [span]
6.236 + self.strong = False
6.237 + return self.emphasis
6.238 +
6.239 + def __repr__(self):
6.240 + return "FontStyle(%r, %r, %r)" % (self.nodes, self.emphasis, self.strong)
6.241 +
6.242 + def prettyprint(self, indent=""):
6.243 + l = ["%sFontStyle: emphasis=%r strong=%r" % (indent, self.emphasis, self.strong)]
6.244 + return self._prettyprint(l, indent)
6.245 +
6.246 + def to_string(self, out):
6.247 + if self.emphasis:
6.248 + out.start_emphasis()
6.249 + elif self.strong:
6.250 + out.start_strong()
6.251 + self._to_string(out)
6.252 + if self.emphasis:
6.253 + out.end_emphasis()
6.254 + elif self.strong:
6.255 + out.end_strong()
6.256 +
6.257 +class Heading(Container):
6.258 +
6.259 + "A heading."
6.260 +
6.261 + def __init__(self, nodes, level, start_extra="", start_pad="", end_pad="", end_extra=""):
6.262 + Container.__init__(self, nodes)
6.263 + self.level = level
6.264 + self.start_extra = start_extra
6.265 + self.start_pad = start_pad
6.266 + self.end_pad = end_pad
6.267 + self.end_extra = end_extra
6.268 +
6.269 + def __repr__(self):
6.270 + return "Heading(%r, %d, %r, %r, %r, %r)" % (
6.271 + self.nodes, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra)
6.272 +
6.273 + def prettyprint(self, indent=""):
6.274 + l = ["%sHeading: level=%d start_extra=%r start_pad=%r end_pad=%r end_extra=%r" % (
6.275 + indent, self.level, self.start_extra, self.start_pad, self.end_pad, self.end_extra)]
6.276 + return self._prettyprint(l, indent)
6.277 +
6.278 + def to_string(self, out):
6.279 + out.start_heading(self.level, self.start_extra, self.start_pad)
6.280 + self._to_string(out)
6.281 + out.end_heading(self.level, self.end_pad, self.end_extra)
6.282 +
6.283 +class List(Container):
6.284 +
6.285 + "A list."
6.286 +
6.287 + def __init__(self, nodes, indent, marker, num):
6.288 + Container.__init__(self, nodes)
6.289 + self.indent = indent
6.290 + self.marker = marker
6.291 + self.num = num
6.292 +
6.293 + def __repr__(self):
6.294 + return "List(%r, %r, %r, %r)" % (self.nodes, self.indent, self.marker, self.num)
6.295 +
6.296 + def prettyprint(self, indent=""):
6.297 + l = ["%sList: indent=%d marker=%r num=%r" % (indent, self.indent, self.marker, self.num)]
6.298 + return self._prettyprint(l, indent)
6.299 +
6.300 + def to_string(self, out):
6.301 + out.start_list(self.indent, self.marker, self.num)
6.302 + self._to_string(out)
6.303 + out.end_list(self.indent, self.marker, self.num)
6.304 +
6.305 +class ListItem(Container):
6.306 +
6.307 + "A list item."
6.308 +
6.309 + def __init__(self, nodes, indent, marker, space, num):
6.310 + Container.__init__(self, nodes)
6.311 + self.indent = indent
6.312 + self.marker = marker
6.313 + self.space = space
6.314 + self.num = num
6.315 +
6.316 + # Forbid blocks within list items for simpler structure.
6.317 +
6.318 + self.allow_blocks = False
6.319 +
6.320 + def __repr__(self):
6.321 + return "ListItem(%r, %r, %r, %r, %r)" % (self.nodes, self.indent, self.marker, self.space, self.num)
6.322 +
6.323 + def prettyprint(self, indent=""):
6.324 + l = ["%sListItem: indent=%d marker=%r space=%r num=%r" % (indent, self.indent, self.marker, self.space, self.num)]
6.325 + return self._prettyprint(l, indent)
6.326 +
6.327 + def to_string(self, out):
6.328 + out.start_listitem(self.indent, self.marker, self.space, self.num)
6.329 + self._to_string(out)
6.330 + out.end_listitem(self.indent, self.marker, self.space, self.num)
6.331 +
6.332 +class TableAttrs(Container):
6.333 +
6.334 + "A collection of table attributes."
6.335 +
6.336 + def __repr__(self):
6.337 + return "TableAttrs(%r)" % self.nodes
6.338 +
6.339 + def prettyprint(self, indent=""):
6.340 + l = ["%sTableAttrs:" % indent]
6.341 + return self._prettyprint(l, indent)
6.342 +
6.343 + def to_string(self, out):
6.344 + out.start_table_attrs()
6.345 + self._to_string(out)
6.346 + out.end_table_attrs()
6.347 +
6.348 +class Table(Container):
6.349 +
6.350 + "A table."
6.351 +
6.352 + def __repr__(self):
6.353 + return "Table(%r)" % self.nodes
6.354 +
6.355 + def prettyprint(self, indent=""):
6.356 + l = ["%sTable:" % indent]
6.357 + return self._prettyprint(l, indent)
6.358 +
6.359 + def to_string(self, out):
6.360 + out.start_table()
6.361 + self._to_string(out)
6.362 + out.end_table()
6.363 +
6.364 +class TableCell(Container):
6.365 +
6.366 + "A table cell."
6.367 +
6.368 + def __init__(self, nodes, attrs=None):
6.369 + Container.__init__(self, nodes)
6.370 + self.attrs = attrs
6.371 +
6.372 + def __repr__(self):
6.373 + return "TableCell(%r, %r)" % (self.nodes, self.attrs)
6.374 +
6.375 + def prettyprint(self, indent=""):
6.376 + l = ["%sTableCell:" % indent]
6.377 + return self._prettyprint(l, indent)
6.378 +
6.379 + def to_string(self, out):
6.380 + out.start_table_cell(self.attrs)
6.381 + for node in self.nodes:
6.382 + if node is not self.attrs:
6.383 + node.to_string(out)
6.384 + out.end_table_cell()
6.385 +
6.386 +class TableRow(Container):
6.387 +
6.388 + "A table row."
6.389 +
6.390 + def __init__(self, nodes, trailing=""):
6.391 + Container.__init__(self, nodes)
6.392 + self.trailing = trailing
6.393 +
6.394 + def __repr__(self):
6.395 + return "TableRow(%r, %r)" % (self.nodes, self.trailing)
6.396 +
6.397 + def prettyprint(self, indent=""):
6.398 + l = ["%sTableRow: trailing=%r" % (indent, self.trailing)]
6.399 + return self._prettyprint(l, indent)
6.400 +
6.401 + def to_string(self, out):
6.402 + out.start_table_row()
6.403 + self._to_string(out)
6.404 + out.end_table_row(self.trailing)
6.405 +
6.406 +
6.407 +
6.408 +# Inline nodes with children.
6.409 +
6.410 +class Inline(Container):
6.411 +
6.412 + "Generic inline formatting."
6.413 +
6.414 + def __repr__(self):
6.415 + return "%s(%r)" % (self.__class__.__name__, self.nodes)
6.416 +
6.417 + def prettyprint(self, indent=""):
6.418 + l = ["%s%s" % (indent, self.__class__.__name__)]
6.419 + return self._prettyprint(l, indent)
6.420 +
6.421 +class Larger(Inline):
6.422 +
6.423 + "Larger text."
6.424 +
6.425 + def to_string(self, out):
6.426 + out.start_larger()
6.427 + self._to_string(out)
6.428 + out.end_larger()
6.429 +
6.430 +class Link(Container):
6.431 +
6.432 + "Link details."
6.433 +
6.434 + def __init__(self, nodes, target):
6.435 + Container.__init__(self, nodes)
6.436 + self.target = target
6.437 +
6.438 + def __repr__(self):
6.439 + return "Link(%r, %r)" % (self.nodes, self.target)
6.440 +
6.441 + def prettyprint(self, indent=""):
6.442 + l = ["%sLink: target=%r" % (indent, self.target)]
6.443 + return self._prettyprint(l, indent)
6.444 +
6.445 + def to_string(self, out):
6.446 + out.start_link(self.target)
6.447 + if self.nodes:
6.448 + out.start_linktext()
6.449 + self._to_string(out)
6.450 + out.end_linktext()
6.451 + out.end_link()
6.452 +
6.453 +class Monospace(Inline):
6.454 +
6.455 + "Monospaced text."
6.456 +
6.457 + def to_string(self, out):
6.458 + out.start_monospace()
6.459 + self._to_string(out)
6.460 + out.end_monospace()
6.461 +
6.462 +class Smaller(Inline):
6.463 +
6.464 + "Smaller text."
6.465 +
6.466 + def to_string(self, out):
6.467 + out.start_smaller()
6.468 + self._to_string(out)
6.469 + out.end_smaller()
6.470 +
6.471 +class Strikethrough(Inline):
6.472 +
6.473 + "Crossed-out text."
6.474 +
6.475 + def to_string(self, out):
6.476 + out.start_strikethrough()
6.477 + self._to_string(out)
6.478 + out.end_strikethrough()
6.479 +
6.480 +class Subscript(Inline):
6.481 +
6.482 + "Subscripted text."
6.483 +
6.484 + def to_string(self, out):
6.485 + out.start_subscript()
6.486 + self._to_string(out)
6.487 + out.end_subscript()
6.488 +
6.489 +class Superscript(Inline):
6.490 +
6.491 + "Superscripted text."
6.492 +
6.493 + def to_string(self, out):
6.494 + out.start_superscript()
6.495 + self._to_string(out)
6.496 + out.end_superscript()
6.497 +
6.498 +class Underline(Inline):
6.499 +
6.500 + "Underlined text."
6.501 +
6.502 + def to_string(self, out):
6.503 + out.start_underline()
6.504 + self._to_string(out)
6.505 + out.end_underline()
6.506 +
6.507 +
6.508 +
6.509 +# Nodes without children.
6.510 +
6.511 +class Node:
6.512 +
6.513 + "A document node without children."
6.514 +
6.515 + def empty(self):
6.516 + return False
6.517 +
6.518 +class Break(Node):
6.519 +
6.520 + "A paragraph break."
6.521 +
6.522 + def __repr__(self):
6.523 + return "Break()"
6.524 +
6.525 + def prettyprint(self, indent=""):
6.526 + return "%sBreak" % indent
6.527 +
6.528 + def to_string(self, out):
6.529 + out.break_()
6.530 +
6.531 +class Macro(Node):
6.532 +
6.533 + "Macro details."
6.534 +
6.535 + def __init__(self, name, args):
6.536 + self.name = name
6.537 + self.args = args
6.538 +
6.539 + def __repr__(self):
6.540 + return "Macro(%r, %r)" % (self.name, self.args)
6.541 +
6.542 + def prettyprint(self, indent=""):
6.543 + return "%sMacro: name=%r args=%r" % (indent, self.name, self.args)
6.544 +
6.545 + def to_string(self, out):
6.546 + out.macro(self.name, self.args)
6.547 +
6.548 +class Rule(Node):
6.549 +
6.550 + "A horizontal rule."
6.551 +
6.552 + def __init__(self, length):
6.553 + self.length = length
6.554 +
6.555 + def __repr__(self):
6.556 + return "Rule(%d)" % self.length
6.557 +
6.558 + def prettyprint(self, indent=""):
6.559 + return "%sRule: length=%d" % (indent, self.length)
6.560 +
6.561 + def to_string(self, out):
6.562 + out.rule(self.length)
6.563 +
6.564 +class TableAttr(Node):
6.565 +
6.566 + "A table attribute."
6.567 +
6.568 + def __init__(self, name, value=None, concise=False, quote=None):
6.569 + self.name = name
6.570 + self.value = value
6.571 + self.concise = concise
6.572 + self.quote = quote
6.573 +
6.574 + def __repr__(self):
6.575 + return "TableAttr(%r, %r, %r, %r)" % (self.name, self.value, self.concise, self.quote)
6.576 +
6.577 + def prettyprint(self, indent=""):
6.578 + return "%sTableAttr: name=%r value=%r concise=%r quote=%r" % (indent, self.name, self.value, self.concise, self.quote)
6.579 +
6.580 + def to_string(self, out):
6.581 + out.table_attr(self.name, self.value, self.concise, self.quote)
6.582 +
6.583 +class Text(Node):
6.584 +
6.585 + "A text node."
6.586 +
6.587 + def __init__(self, s):
6.588 + self.s = s
6.589 +
6.590 + def empty(self):
6.591 + return not self.s
6.592 +
6.593 + def multiline(self):
6.594 + return "\n" in self.s
6.595 +
6.596 + def merge(self, text):
6.597 + self.s += text.s
6.598 +
6.599 + def __repr__(self):
6.600 + return "Text(%r)" % self.s
6.601 +
6.602 + def prettyprint(self, indent=""):
6.603 + return "%sText: %r" % (indent, self.s)
6.604 +
6.605 + def to_string(self, out):
6.606 + out.text(self.s)
6.607 +
6.608 +# vim: tabstop=4 expandtab shiftwidth=4
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/moinformat/tree/table.py Tue Jul 24 12:58:43 2018 +0200
7.3 @@ -0,0 +1,40 @@
7.4 +#!/usr/bin/env python
7.5 +
7.6 +"""
7.7 +Extended table syntax document tree nodes.
7.8 +
7.9 +Copyright (C) 2018 Paul Boddie <paul@boddie.org.uk>
7.10 +
7.11 +This program is free software; you can redistribute it and/or modify it under
7.12 +the terms of the GNU General Public License as published by the Free Software
7.13 +Foundation; either version 3 of the License, or (at your option) any later
7.14 +version.
7.15 +
7.16 +This program is distributed in the hope that it will be useful, but WITHOUT
7.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
7.19 +details.
7.20 +
7.21 +You should have received a copy of the GNU General Public License along with
7.22 +this program. If not, see <http://www.gnu.org/licenses/>.
7.23 +"""
7.24 +
7.25 +from moinformat.tree.moin import Node
7.26 +
7.27 +class Continuation(Node):
7.28 +
7.29 + "Continuation padding for table content."
7.30 +
7.31 + def __init__(self, text):
7.32 + self.text = text
7.33 +
7.34 + def __repr__(self):
7.35 + return "Continuation(%r)" % self.text
7.36 +
7.37 + def prettyprint(self, indent=""):
7.38 + return "%sContinuation: %r" % (indent, self.text)
7.39 +
7.40 + def to_string(self, out):
7.41 + out.continuation(self.text)
7.42 +
7.43 +# vim: tabstop=4 expandtab shiftwidth=4
8.1 --- a/tests/test_parser.py Tue Jul 24 10:47:29 2018 +0200
8.2 +++ b/tests/test_parser.py Tue Jul 24 12:58:43 2018 +0200
8.3 @@ -13,7 +13,7 @@
8.4 sys.path.append(parent)
8.5
8.6 from moinformat import all_parsers, all_serialisers, parse, serialise
8.7 -from moinformat.tree import Container
8.8 +from moinformat.tree.moin import Container
8.9 from glob import glob
8.10
8.11 def test_input(d, s):