1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/ImprovedTableParser.py Sun Jan 15 23:37:14 2012 +0100
1.3 @@ -0,0 +1,166 @@
1.4 +# -*- coding: iso-8859-1 -*-
1.5 +"""
1.6 + MoinMoin - ImprovedTableParser library
1.7 +
1.8 + @copyright: 2012 by Paul Boddie <paul@boddie.org.uk>
1.9 + @license: GNU GPL (v2 or later), see COPYING.txt for details.
1.10 +"""
1.11 +
1.12 +from MoinMoin import wikiutil
1.13 +from shlex import shlex
1.14 +from StringIO import StringIO
1.15 +import re
1.16 +
1.17 +# Regular expressions.
1.18 +
1.19 +syntax = {
1.20 + # At start of line:
1.21 + "rows" : (r"^==", re.MULTILINE), # ==
1.22 + # Within text:
1.23 + "columns" : (r"\|\|[ \t]*", 0), # || whitespace
1.24 + # At start of column text:
1.25 + "column" : (r"^\s*<(.*?)>\s*(.*)", re.DOTALL), # whitespace < attributes > whitespace
1.26 + }
1.27 +
1.28 +patterns = {}
1.29 +for name, (value, flags) in syntax.items():
1.30 + patterns[name] = re.compile(value, re.UNICODE | flags)
1.31 +
1.32 +# Functions.
1.33 +
1.34 +def parse(s):
1.35 +
1.36 + "Parse 's', returning a table definition."
1.37 +
1.38 + rows = []
1.39 + table_attrs = {}
1.40 +
1.41 + # Extract each row from the definition.
1.42 +
1.43 + for row_text in patterns["rows"].split(s):
1.44 + columns = []
1.45 +
1.46 + # Extract each column from the row.
1.47 +
1.48 + for text in patterns["columns"].split(row_text):
1.49 +
1.50 + # Extract the attribute and text sections.
1.51 +
1.52 + match = patterns["column"].search(text)
1.53 + if match:
1.54 + attribute_text, text = match.groups()
1.55 + columns.append((parseAttributes(attribute_text, True), text))
1.56 + else:
1.57 + columns.append(({}, text))
1.58 +
1.59 + # Extract row- and table-level attributes.
1.60 +
1.61 + row_attrs = {}
1.62 +
1.63 + if columns:
1.64 + attrs, column = columns[0]
1.65 +
1.66 + for name, value in attrs.items():
1.67 + if name.startswith("row"):
1.68 + row_attrs[name] = value
1.69 + del attrs[name]
1.70 + elif name.startswith("table"):
1.71 + table_attrs[name] = value
1.72 + del attrs[name]
1.73 +
1.74 + rows.append((row_attrs, columns))
1.75 +
1.76 + return table_attrs, rows
1.77 +
1.78 +def parseAttributes(s, escape=True):
1.79 +
1.80 + """
1.81 + Parse the table attributes string 's', returning a mapping of names to
1.82 + values. If 'escape' is set to a true value, the attributes will be suitable
1.83 + for use with the formatter API.
1.84 + """
1.85 +
1.86 + attrs = {}
1.87 + f = StringIO(s)
1.88 + name = None
1.89 + need_value = False
1.90 +
1.91 + for token in shlex(f):
1.92 +
1.93 + # Capture the name if needed.
1.94 +
1.95 + if name is None:
1.96 + name = escape and wikiutil.escape(token) or token
1.97 +
1.98 + # Detect either an equals sign or another name.
1.99 +
1.100 + elif not need_value:
1.101 + if token == "=":
1.102 + need_value = True
1.103 + else:
1.104 + attrs[name.lower()] = escape and "true" or True
1.105 + name = wikiutil.escape(token)
1.106 +
1.107 + # Otherwise, capture a value.
1.108 +
1.109 + else:
1.110 + # Quoting of attributes done similarly to parseAttributes.
1.111 +
1.112 + if escape and token:
1.113 + if token[0] in ("'", '"'):
1.114 + token = wikiutil.escape(token)
1.115 + else:
1.116 + token = '"%s"' % wikiutil.escape(token, 1)
1.117 +
1.118 + attrs[name.lower()] = token
1.119 + name = None
1.120 + need_value = False
1.121 +
1.122 + return attrs
1.123 +
1.124 +# Formatting of embedded content.
1.125 +# NOTE: Borrowed from EventAggregator.
1.126 +
1.127 +def getParserClass(request, format):
1.128 +
1.129 + """
1.130 + Return a parser class using the 'request' for the given 'format', returning
1.131 + a plain text parser if no parser can be found for the specified 'format'.
1.132 + """
1.133 +
1.134 + try:
1.135 + return wikiutil.searchAndImportPlugin(request.cfg, "parser", format or "plain")
1.136 + except wikiutil.PluginMissingError:
1.137 + return wikiutil.searchAndImportPlugin(request.cfg, "parser", "plain")
1.138 +
1.139 +def formatText(text, request, fmt):
1.140 +
1.141 + "Format the given 'text' using the specified 'request' and formatter 'fmt'."
1.142 +
1.143 + parser_cls = getParserClass(request, request.page.pi["format"])
1.144 + parser = parser_cls(text, request, line_anchors=False)
1.145 + return request.redirectedOutput(parser.format, fmt, inhibit_p=True)
1.146 +
1.147 +# Common formatting functions.
1.148 +
1.149 +def formatTable(text, request, fmt):
1.150 +
1.151 + "Format the given 'text' using the specified 'request' and formatter 'fmt'."
1.152 +
1.153 + attrs, table = parse(text)
1.154 +
1.155 + request.write(fmt.table(1, attrs))
1.156 +
1.157 + for row_attrs, columns in table:
1.158 + request.write(fmt.table_row(1, row_attrs))
1.159 +
1.160 + for column_attrs, column_text in columns:
1.161 + request.write(fmt.table_cell(1, column_attrs))
1.162 + request.write(formatText(column_text, request, fmt))
1.163 + request.write(fmt.table_cell(0))
1.164 +
1.165 + request.write(fmt.table_row(0))
1.166 +
1.167 + request.write(fmt.table(0))
1.168 +
1.169 +# vim: tabstop=4 expandtab shiftwidth=4