# HG changeset patch # User Paul Boddie # Date 1333237228 -7200 # Node ID 825a3e78d952e74c8067919c6342de30249a073d # Parent 14a41cb069bf26def25e05f6a66239df0c6a38eb Changed the section nesting syntax to use the general convention described on the HelpOnParsers page where {{{...}}} sections can be nested within sections employing more { and } characters in their start and end markers respectively. Revised the tests for the new syntax. Added a help page for the extension. diff -r 14a41cb069bf -r 825a3e78d952 ImprovedTableParser.py --- a/ImprovedTableParser.py Sat Mar 31 21:20:52 2012 +0200 +++ b/ImprovedTableParser.py Sun Apr 01 01:40:28 2012 +0200 @@ -16,15 +16,11 @@ # Regular expressions. syntax = { - # For section markers. - "markers" : (r"(?P\\+)(?P{|})(?P=n)(?P=b)(?P=n)(?P=b)", re.MULTILINE), - "marker" : (r"(\\+)", 0), - # At start of line: "rows" : (r"^==(?!.*?==$)", re.MULTILINE), # == not-heading # Within text: - "sections" : (r"({{{.*?}}})", re.MULTILINE | re.DOTALL), # {{{ ... }}} + "markers" : (r"([{]{3,}|[}]{3,})", re.MULTILINE | re.DOTALL), # {{{... or }}}... "columns" : (r"\|\|[ \t]*", 0), # || ws-excl-nl # At start of column text: @@ -50,8 +46,6 @@ "Parse 's', returning a table definition." - s = replaceMarkers(s) - table_attrs = {} rows = [] @@ -67,21 +61,24 @@ # Process exposed text and sections. - exposed = True + marker = None + is_region = True # Initially, start a new row. row_continued = False - for region in patterns["sections"].split(s): + for match_text in patterns["markers"].split(s): - # Only look for table features in exposed text. + # Only look for table features in exposed text. Where a section is + # defined, a marker will have been read and all regions before the + # closing marker will not be exposed. - if exposed: + if is_region and not marker: # Extract each row from the definition. - for row_text in patterns["rows"].split(region): + for row_text in patterns["rows"].split(match_text): # Only create a new row when a boundary has been found. @@ -183,12 +180,31 @@ row_continued = True - # Write any section into the current column. + else: + + # Handle section markers. + + if not is_region: + + # Interpret the given marker, closing the current section if the + # given marker is the corresponding end marker for the current + # section. - else: - columns[columnnumber][1] += region + if marker: + if match_text.startswith("}") and len(marker) == len(match_text): + marker = None + + # Without a current marker, start a section if an appropriate marker + # is given. - exposed = not exposed + elif match_text.startswith("{"): + marker = match_text + + # Markers and section text are incorporated into the current column. + + columns[columnnumber][1] += match_text + + is_region = not is_region # Complete any final row. @@ -252,37 +268,6 @@ table_attrs[name] = value del attrs[name] -def replaceMarkers(s): - - "Convert the section notation in 's'." - - l = [] - last = 0 - - # Get each marker and convert it. - - for match in patterns["markers"].finditer(s): - start, stop = match.span() - l.append(s[last:start]) - - # Convert the marker. - - marker = [] - brace = True - for text in patterns["marker"].split(match.group()): - if brace: - marker.append(text) - else: - marker.append(text[:-1]) - brace = not brace - - l.append("".join(marker)) - last = stop - else: - l.append(s[last:]) - - return "".join(l) - def parseAttributes(s, escape=True): """ diff -r 14a41cb069bf -r 825a3e78d952 pages/HelpOnImprovedTableParser --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pages/HelpOnImprovedTableParser Sun Apr 01 01:40:28 2012 +0200 @@ -0,0 +1,422 @@ +#format wiki +#language en + +== ImprovedTableParser == + +The !ImprovedTableParser extension for !MoinMoin offers support for an alternative table syntax that is not as restrictive as the [[HelpOnTables|default syntax for tables]] in terms of the way tables are written as Wiki text, and in terms of the formatting possibilities available. For example, the default syntax insists that table rows are written on a single line - this can be challenging for rows with many columns or containing a lot of content - whereas the improved syntax allows each column to appear on one or many lines. + +=== The Improved Table Syntax === + +The principal syntax rules are described as follows. + +==== Making a Table ==== + +To make a table, make a [[HelpOnParsers|section]] and use a `#!table` declaration to indicate that it represents a table: + +{{{{ +{{{#!table +... +}}} +}}}} + +The result: + +{{{#!table +... +}}} + +==== Adding Rows ==== + +Rows are separated using the `==` marker at the start of a line. Starting a new line does not by itself start a new row: + +{{{{ +{{{#!table +Row #1 +and still row #1 +== +Row #2 +}}} +}}}} + +The result: + +{{{#!table +Row #1 +and still row #1 +== +Row #2 +}}} + +==== Adding Columns ==== + +Columns are separated using the `||` marker: + +{{{{ +{{{#!table +Column #1 || Column #2 +}}} +}}}} + +The result: + +{{{#!table +Column #1 || Column #2 +}}} + +Since there is no need to indicate the start of a table or of a row, since tables are confined to sections and rows are explicitly separated, the initial and final `||` markers are not used: + +{{{{ +{{{#!table +Already in column #1 +|| Now in column #2 + until the next row or the end of the table +}}} +}}}} + +The result: + +{{{#!table +Already in column #1 +|| Now in column #2 +until the next row or the end of the table +}}} + +==== Styles and Formatting ==== + +Style and formatting options are placed at the start of a row or column inside `<` and `>` markers, with the options corresponding to HTML attributes as described for the [[HelpOnTables|default syntax for tables]] being supported. + +Table-specific options are prefixed with `table` (`tablestyle` and `tableclass` apply to the given table): + +{{{{ +{{{#!table + '''Heading''' +== +Data +}}} +}}}} + +The result: + +{{{#!table + '''Heading''' +== +Data +}}} + +Row-specific options are prefixed with `row` (`rowstyle` and `rowclass` apply to the given row): + +{{{{ +{{{#!table + Red cell +== + Green cell +}}} +}}}} + +The result: + +{{{#!table + Red cell +== + Green cell +}}} + +General options are given verbatim (`style`, `class`, `rowspan`, `colspan`). + +{{{{ +{{{#!table + Red cell || Shared cell +== + Green cell +}}} +}}}} + +The result: + +{{{#!table + Red cell || Shared cell +== + Green cell +}}} + +Note that the short options in the default syntax are not supported. It is therefore necessary to write span and justification attributes out in full. + +=== A Simple Example === + +Here is a short example of a table using the features described above: + +{{{{ +{{{#!table + '''Heading''' +== +cell 1 || cell 2 || cell 3 +== + spanning rows +|| spanning 2 columns +== + cell 2 || cell 3 +}}} +}}}} + +And here is the result: + +{{{#!table + '''Heading''' +== +cell 1 || cell 2 || cell 3 +== + spanning rows +|| spanning 2 columns +== + cell 2 || cell 3 +}}} + +=== An Advanced Example === + +Since table cells can be described on more than one line, a wider range of Wiki syntax can be used to populate tables. For example, headings and lists can be added to tables: + +{{{{ +{{{#!table +== Proper Heading! == +|| +== Another Heading! == +== + * First + * Second + * Third +|| + * Gold + * Silver + * Bronze +}}} +}}}} + +The result: + +{{{#!table +== Proper Heading! == +|| +== Another Heading! == +== + * First + * Second + * Third +|| + * Gold + * Silver + * Bronze +}}} + +Note that the usual restrictions around these features still apply, so headings, for example, must appear on a separate line. + +=== A More Advanced Example === + +Consider the following table: + +|| System || RAM || RAM<
>(official expansion limit) || ROM || Year of introduction || Price on introduction (GBP) || +|| BBC Microcomputer Model B || 32K || 32K || 1981 || 335 || +|| Acorn Electron || 32K || 32K || 1983 || 199 || +|| Commodore 64 || 64K || 20K || 1982 || 399 || +|| Sinclair ZX Spectrum 48K || 48K || 16K || 175 || +|| Amstrad CPC464 || 64K || 32K || 1984 || 249 (green screen), 349 (colour) || +|| Amstrad CPC6128 || 128K || 48K || 1985 || 299 (colour) || +|| Sinclair ZX81 || 1K || 16K || 8K || 1981 || 70 (assembled), 50 (kit) || +|| VIC-20 || 5K || 32K || 16K || 1980 || 200 (estimated) || + +Here is how it is written in the default table syntax: + +{{{ +|| System || RAM || RAM<
>(official expansion limit) || ROM || Year of introduction || Price on introduction (GBP) || +|| BBC Microcomputer Model B || 32K || 32K || 1981 || 335 || +|| Acorn Electron || 32K || 32K || 1983 || 199 || +|| Commodore 64 || 64K || 20K || 1982 || 399 || +|| Sinclair ZX Spectrum 48K || 48K || 16K || 175 || +|| Amstrad CPC464 || 64K || 32K || 1984 || 249 (green screen), 349 (colour) || +|| Amstrad CPC6128 || 128K || 48K || 1985 || 299 (colour) || +|| Sinclair ZX81 || 1K || 16K || 8K || 1981 || 70 (assembled), 50 (kit) || +|| VIC-20 || 5K || 32K || 16K || 1980 || 200 (estimated) || +}}} + +Although the lines are not too long, it is somewhat difficult to interpret and edit the text. + +Here is how the above table can be written in the improved syntax (with the section markers omitted): + +{{{{ +{{{#!table + System +|| RAM +|| RAM<
>(official expansion limit) +|| ROM +|| Year of introduction +|| Price on introduction (GBP) +== +BBC Microcomputer Model B +|| 32K +|| 32K +|| 1981 +|| 335 +== +Acorn Electron +|| 32K +|| 32K +|| 1983 +|| 199 +== +Commodore 64 +|| 64K +|| 20K +|| 1982 +|| 399 +== +Sinclair ZX Spectrum 48K +|| 48K +|| 16K +|| 175 +== +Amstrad CPC464 +|| 64K +|| 32K +|| 1984 +|| 249 (green screen), 349 (colour) +== +Amstrad CPC6128 +|| 128K +|| 48K +|| 1985 +|| 299 (colour) +== +Sinclair ZX81 || 1K || 16K || 8K || 1981 +|| 70 (assembled), 50 (kit) +== +VIC-20 || 5K || 32K || 16K || 1980 +|| 200 (estimated) +}}} +}}}} + +Without the restriction of putting everything in a row on one line, the table can be written in a way that is as concise or as verbose as you like. Here is the result: + +{{{#!table + System +|| RAM +|| RAM<
>(official expansion limit) +|| ROM +|| Year of introduction +|| Price on introduction (GBP) +== +BBC Microcomputer Model B +|| 32K +|| 32K +|| 1981 +|| 335 +== +Acorn Electron +|| 32K +|| 32K +|| 1983 +|| 199 +== +Commodore 64 +|| 64K +|| 20K +|| 1982 +|| 399 +== +Sinclair ZX Spectrum 48K +|| 48K +|| 16K +|| 175 +== +Amstrad CPC464 +|| 64K +|| 32K +|| 1984 +|| 249 (green screen), 349 (colour) +== +Amstrad CPC6128 +|| 128K +|| 48K +|| 1985 +|| 299 (colour) +== +Sinclair ZX81 || 1K || 16K || 8K || 1981 +|| 70 (assembled), 50 (kit) +== +VIC-20 || 5K || 32K || 16K || 1980 +|| 200 (estimated) +}}} + +=== Adding Table Sorting === + +In addition to a more flexible syntax, !ImprovedTableParser also allows the contents of tables to be described so that they can be manipulated and shown according to different sorting or ordering criteria. This is done by adding some directives to the table declaration. + + headers:: indicates the number of rows which are headers, describing non-sortable data + columntypes:: describes the kind of data in each column along with how the column should be ordered (ascending or descending); the syntax is reminiscent of the Unix `sort` utility; column numbering starts at 0 + sortcolumns:: describes the initial sorting or ordering of the table data; the syntax is the same as `columntypes` + name:: the HTML `id` attribute or anchor given to the table so that when the ordering is changed, the browser will refresh the page and can jump to the table's position + +To the above table, we can change the declaration as follows: + +{{{{ +{{{#!table headers=1 columntypes='2n,1nd,0,3n,4n,5n' sortcolumns='4n' name=computers +}}}} + +This indicates that the first row is not sortable data (`headers=1`), that the column types for all but the first column (`0`) are numeric (`n`) and that the second column (`1`) should be presented in descending order by default (`d`), that the initial view will sort the data according to the fifth column (`4`) by treating its cells as if they provide numeric data. Finally, the table has an anchor called `computers` associated with it. + +The result is as follows: + +{{{#!table headers=1 columntypes='2n,1nd,0,3n,4n,5n' sortcolumns='4n' name=computers + System +|| RAM +|| RAM<
>(official expansion limit) +|| ROM +|| Year of introduction +|| Price on introduction (GBP) +== +BBC Microcomputer Model B +|| 32K +|| 32K +|| 1981 +|| 335 +== +Acorn Electron +|| 32K +|| 32K +|| 1983 +|| 199 +== +Commodore 64 +|| 64K +|| 20K +|| 1982 +|| 399 +== +Sinclair ZX Spectrum 48K +|| 48K +|| 16K +|| 175 +== +Amstrad CPC464 +|| 64K +|| 32K +|| 1984 +|| 249 (green screen), 349 (colour) +== +Amstrad CPC6128 +|| 128K +|| 48K +|| 1985 +|| 299 (colour) +== +Sinclair ZX81 || 1K || 16K || 8K || 1981 +|| 70 (assembled), 50 (kit) +== +VIC-20 || 5K || 32K || 16K || 1980 +|| 200 (estimated) +}}} + +By pointing to the header cells, a pop-up dialogue allows the sorting criteria to be edited. + + * Where a column is already being used to sort the data, it will appear in the list of sort columns. + * By opening the dialogue over a sort column, the column can be removed from the criteria by selecting it in the list. (It will appear crossed out when pointed to.) + * A column can be moved or inserted into the criteria by pointing to another column or the space at the end of the list. The column should appear at its new position and can be inserted by selecting the region next to which (or within which) the new column appears, or by selecting the arrows in the new column indicator. + * The sort direction for a column can be changed by selecting the highlighted arrow next to its name. diff -r 14a41cb069bf -r 825a3e78d952 tests/test_sections.py --- a/tests/test_sections.py Sat Mar 31 21:20:52 2012 +0200 +++ b/tests/test_sections.py Sun Apr 01 01:40:28 2012 +0200 @@ -1,26 +1,29 @@ #!/usr/bin/env python -from ImprovedTableParser import replaceMarkers +from ImprovedTableParser import patterns text = """ -{{{ +{{{{{ Hello -\\{\\{\\{ +{{{{ Hello again -And also \\\\{\\\\{\\\\{ this \\\\}\\\\}\\\\} -\\\\{\\\\{\\\\{ +And also {{{ this }}} +{{{ And once again -\\\\}\\\\}\\\\} +}}} And again -\\}\\}\\} +}}}} again -}}} +}}}} """ -replaced = replaceMarkers(text) +matches = patterns["markers"].split(text) +expected = 17 print text print -print replaced +print matches +print +print len(matches) == expected, ": length is", len(matches), "==", expected # vim: tabstop=4 expandtab shiftwidth=4 diff -r 14a41cb069bf -r 825a3e78d952 tests/test_table.py --- a/tests/test_table.py Sat Mar 31 21:20:52 2012 +0200 +++ b/tests/test_table.py Sun Apr 01 01:40:28 2012 +0200 @@ -21,9 +21,11 @@ * Item #A || Not a list == -\\{\\{\\{ +{{{ Some preformatted text. -\\}\\}\\} +== +Not another row. +}}} || Preformatted text in a separate section == == Heading 2 == @@ -33,7 +35,7 @@ || And this is the second column. == -Some \\{\\{\\{preformatted text\\}\\}\\} || Some `preformatted text` || Observe the region notation inline. +Some {{{{preformatted text}}}} || Some `preformatted text` || Observe the region notation inline. == 1 || 2 || 3 ==