2010-11-13

Grammar pattern: twin delimiters

This post describes a tricky point of Novelang’s grammar design: how to handle twin delimiters like // in a non-ambiguous manner for an ANTLR grammar. It’s a useful refresh before adding long-awaited ** (asterisk pair) delimiter.

The problem

For paired delimiters like ( and ) or [ and ] it’s easy to know when to “open” or “close” a block, and support nested blocks. In contrast, a twin delimiter is an opening one if not preceded by a closing one inside the same block, regardless of what happens in subblocks. This is a complicated way to say we support this kind of nesting:

// block-1 ( block-2 //block-3// ) //

+ block-inside-solidus-pairs
    block-1
  + block-inside-parenthesis
      block-2
    + block-inside-solidus-pairs
        block-3

We also support this:

block-1 // block-2 // block-3 // block-4 //

  block-1
+ block-inside-solidus-pairs
    block-2
  block-3
+ block-inside-solidus-pairs
    block-4

(We have only one level of nesting here. 2 levels of nesting is counter-intuitive and would have required very complex lookahead.)

The pattern

The pattern is to define special grammatical elements when inside a block defined by a twin delimiter, to propagate this element cannot appear again, unless inside some other subblock.

Taking “XXX” for the name of some twin delimiter, here is a simplified version of the grammar for spreadblocks. The term “spreadblock” stands for a block that may spread on several lines (containing single line breaks).

paragraph
  : ... mixedDelimitedSpreadblock
  ;

mixedDelimitedSpreadblock
  : word ( punctuationSign | delimitedSpreadblock ) ...

delimitedSpreadblock
  : xxxSpreadblock
  : parenthesizedSpreadblock
  | squareBracketsSpreadblock
  | doubleQuotedSpreadblock
  | hyphenPairSpreadblock
  ;

parenthesizedSpreadblock  
  : '(' spreadblockBody ')' // Same for other paired delimiters.
  ;

spreadblockBody
  : ... mixedDelimitedSpreadblock
  ;

xxxSpreadblock
  : XXX spreadblockBodyNoXxx XXX
  ;

spreadblockBodyNoXxx
  : ... mixedDelimitedSpreadblockNoXxx ...
  ;

mixedDelimitedSpreadblockNoXxx
  : ... delimitedSpreadblockNoXxx ...
  ;

delimitedSpreadblockNoXxx
  : parenthesizedSpreadblock
  | squareBracketsSpreadblock
  | doubleQuotedSpreadblock
  | hyphenPairSpreadblock
  ;

This is more or less the same for tightblocks. “Tightblocks” stand for blocks containing no line breaks, like cells and embedded lists.

acell  // Same for embedded list items.
  : ... mixedDelimitedTightblock ..
  ;

mixedDelimitedTightblock
  : word ( punctuationSign | delimitedTightblock | ... ) ...
  : word ( punctuationSign | delimitedSpreadblock | ... ) ...
  ;

delimitedTightblock
  : xxxTightblock
  | parenthesizedTightblock
  | squareBracketsTightblock
  | doubleQuotedTightblock
  | hyphenPairTightblock
  ;

xxxTightblock
  : XXX tightblockBodyNoXxx XXX
  ;

tightblockBodyNoXxx
  : ... mixedDelimitedTightblockNoXxx ...
  ;

mixedDelimitedTightblockNoXxx
  : word ( punctuationSign | delimitedTightblockNoXxx ) ...
  ;

delimitedTightblockNoXxx
  : parenthesizedTightblock 
  | squarebracketsTightblock
  | doubleQuotedTightblock
  | hyphenPairTightblock
  ; // That's all.

Thought it is over? There is another kind of block, the delimitedTightblockNoSeparator used inside the subblockAfterTilde which reflects each block inside ~x~y~z! But at this point you probably got the idea.

Yes this makes the grammar quite verbose, but factoring it would reduce ANTLR’s ability to check for inconsistencies. Anyways, the slightest addition brings the need of writing test cases for every logical path inside each ANTLR grammar rule.

2010-11-07

Novelang-0.51.1 released!

Just released Novelang-0.51.1!

Summary of changes:

  • Upgraded from FOP-0.95 to FOP-1.0. FOP is the library for generating PDF documents.
  • Various other library upgrades that shouldn’t affect normal users.

Download it from here.

Enjoy!

2010-11-06

Novelang-0.51.0 released!

Just released Novelang-0.51.0!

Summary of changes:

  • Fixed: list with double hyphen and number sign was using a “plus sign” everywhere (source documents and XML elements). This might break existing documents and stylesheet using this brand new feature.

Download it from here.

Enjoy!

Novelang-0.50.2 released!

Just released Novelang-0.50.2!

Summary of changes:

  • Fixed: support paragraphs as lists (n:list-with-triple-hyphen and n:list-with-double-hyphen-and-plus-sign) inside n:paragraphs-inside-angled-bracket-pairs.

Download it from here.

Enjoy!

Novelang-0.50.1 released!

Just released Novelang-0.50.1!

Summary of changes:

  • Minor fix on JavaShell for cleaner shutdown when there is no default JmxKit. This only may affect users of Novelang-attirail subproject.
  • Fixed documentation generation where release notes for SNAPSHOT versions appeared for non-SNAPSHOT versions.

Download it from here.

Enjoy!

2010-11-05

Novelang-0.50.0 released!

Just released Novelang-0.50.0!

Summary of changes:

  • Embedded numbered lists (n:embedded-list-with-number-sign).
  • Paragraphs as numbered lists (n:list-with-double-hyphen-and-plus-sign).
  • Switched to Maven 3. This required no change but future build features may not work with formerly-used Maven 2.2.1.

Download it from here.

Enjoy!

2010-10-30

Novelang-0.49.0 released!

Just released Novelang-0.49.0!

Summary of changes:

  • In default stylesheet for HTML and PDF, the first n:cell-row element renders as a table header, if there is more than one. This might break existing documents.

Download it from here.

Enjoy!

Maven cheat sheet (update)

This is an update of previous Maven cheat sheet.

Release

mvn -e --batch-mode clean release:prepare -Dnovelang.build.distribution -DreleaseVersion=M.m.f > build-release-prepare.log

mvn release:perform -Dnovelang.build.distribution -DreleaseVersion=M.m.f > build-release-perform.log

2010-10-25

Novelang-0.48.0 released!

Just released Novelang-0.48.0!

Summary of changes:

  • Fixed startup option in documentation.
  • Tags and location for lines of literal. Required an intermediate n:raw-lines element nested inside n:lines-of-literal. This might break existing stylesheets.
  • Location for cell rows with vertical line.

Download it from here.

Enjoy!

2010-10-13

Random text generator for French

This is a cool one. As it takes random phrases from classical French litteracy, punctuation signs and "typographic grey" look natural.

2010-10-07

Cheatsheet template

Wikipedia definitely offers the right template for a cheat sheet. Short and easy to render with Novelang.

2010-10-04

Novelang-0.47.0 released!

Just released Novelang-0.47.0!

Summary of changes:

  • Feature removal: relative identifier. Never used, and would make multipage rendering much more complicated.
  • Small enhancements to Novelang-attirail, the reusable library.

Download it from here.

Enjoy!

2010-09-20

Technical study: multi-page HTML rendering

What we need

How hard would that be to render a single Novelang document over multiple HTML pages? Better ask: how cool would that be? Think about Novelang documentation taking one single huge page. This makes non-linear reading quite uncomfortable. Of course, multi-page rendering should work for both batch and interactive mode.

Technical implications

For batch rendering, there can be a simple approach. Xalan (XSLT rendering engine) offers the redirect extension for redirecting output into a given file.

<xsl:template match="/doc/foo">
  <redirect:write select="@file">
    <foo-out>
      <xsl:apply-templates/>
    </foo-out>
  </redirect:write>
</xsl:template>

Unfortunately, this is not suitable for interactive rendering. For interactive rendering, the endering process must known both:

  • The requested page, through a URL aware of the page (as sub-part of the whole document).
  • The whole document, because we may need to render links to other chapters or whatever.

The same need arises for batch rendering but with Xalan’s Redirect extension mentioned above, the whole logic gets buried inside the XSLT (which probably makes it quite complex).

Obviously, we need a Renderer to work the same way for batch and interactive rendering, e. g. there should be no special handling of interactive or batch rendering in the XSL stylesheet. (But multi-page rendering would require a special stylesheet anyways, at least for generating navigation.)

While XSLT-based rendering is the most common case in Novelang, it’s better to think about the general contract of a org.novelang.rendering.Renderer. As it already does, the Renderer should spit bytes into a java.io.OutputStream with no knowledge wether it is a file or a socket. The job of creating the output (which means chosing a file name in the case of batch rendering) is left to some upstream object opening the OutputStream. Currently, this is done by org.novelang.batch.DocumentGenerator or org.novelang.daemon.DocumentHandler which both end by calling DocumentProducer, passing it the OutputStream.

So rendering stage needs additional logic. Interactive rendering implies to extract the requested page from the URL. Batch rendering implies to find the list of pages to create corresponding files on the filesystem.

New Renderer contract

There can’t be unique way to split a document into pages, so we have new responsabilties for our Renderer:Given a document tree (as a org.novelang.common.SyntacticTree) it calculates a list of page identifiers.Given the same document tree, plus a page identifier, it renders the corresponding page to an OutputStream.With something like a single empty page identifier, we should get the same single-page rendering as we have now.

For an XSLT-based Renderer, we should embed page identifiers generation in the same XSL stylesheet (as a part of already-discussed stylesheet metadata ):

<xsl:stylesheet [namespaces blah blah] >

  <nlm:multipage>
    <!-- 
      Some XSL tranformations here,
      starting from  element. 
    -->
  </nlm:multipage>

  ...

Before page rendering occurs, Novelang asks the Renderer for page identifiers. The default XSL-based Renderer applies the content of the element (if there is one) as a stylesheet on the whole document tree. Then it obtains a list of page identifiers as follows:

<pages>
  <page name="Home" >/n:opus
  <page name="ChapterOne" >/n:opus/n:level[1]
  <page name="ChapterTwo" >/n:opus/n:level[2]
<pages>

Of course each page name is unique. In order to achieve this with no tweak, the document tree may embed unique identifiers by extending the semantic of n:implicit-identifier or by adding a new n:unique-identifier element. Node paths seem easy to generate .

Now for each page, Novelang creates the corresponding file out of the page name. If the stylesheet in the did chose filesystem-friendly names, those will be used verbatim (otherwise we may apply some variant of URL encoding). And, for each page, Novelang calls the Renderer with the whole document tree again, and passes additional metadata elements to tell the Renderer which page it is rendering. Input XML looks like this:

<n:opus>
  <n:meta>
    <n:page-name>ChapterOne
    <n:page-path>/n:opus/n:level[1]
  </n:meta>
</n:opus>

This should be enough for the Renderer to figure how to render only the page of interest. It might need to peek elsewhere in the document tree (like for a footer with a copyright notice, or find other chapter names for a navigation bar).

Mix with other features (present or future)

There is an additional role for node identifiers: they might help to “enhance” internal links by adding the prefix corresponding to the target page. (The internal link feature is yet in inception phase. It just seems easier to implement it right after multipage rendering.)

Unique page names

Novelang’s Fragment Identifier is the perfect candidate to generate page identifers. Unfortunately, composite identifier contain the \ character. Should we escape it, or mix it with some weird pseudo-directory feature? But maybe it’s time to remove relative identifiers which never proved useful, and don’t guarantee identifer uniqueness, anyways.

It’s easy to create a new element by adding a simple counter to a colliding identifier. The value for some given document fragment may change across several generations, when adding fragments with colliding identifiers. This won’t be a problem for internal links (links defined by the document itself) prohibit usage of unique identifier. Remember: unique identifiers are only for pure HTML links.

If there is a chance that a foreign HTML documents links to the HTML anchor defined by the unique identifier (in a pure WWWW – World Wide Web Way) then document author should use explicit identifiers.

New URL scheme

With single-page rendering, the rendered document has the same name as the source document (with the difference of the extension). Multi-page adds a new “dimension”. Because the name of the page may collide with another document’s name, the name of the originating document prefixes the page name. Let’s look at different options:

/main/documentation~syntax.html
/main/documentation!syntax.html
/main/documentation,syntax.html
/main/documentation^syntax.html
/main/documentation--syntax.html

Let’s see which character we could use (only checked on Mac OS X, to do: check on Windows):

Character Escaped? Comments
~ No Already used for Novelang meta pages.
- No Already used for Novelang identifiers.
^ No Meaningless in that context.
# No Fragment in URL.
, No Hard to distinguish from full stop . character.
! No Hard to read.
_ No Too common in file names.
+ No Used in URL encoding. Usage unrelated to “plus” meaning.
% No Used in URL encoding.
= Yes Used in URL encoding. Usage unrelated to “equality” meaning.
$ Yes Overused.
; Yes Hard to read.
| Yes Hard to read.
' Yes Hard to read.
& Yes Already used for URL parameters.
? Yes Already used for URL parameters, DOS wildcard.
@ Yes Inverted meaning if page name appears second.
{ Yes Weird because unpaired. Meaningful otherwise.
§ Yes Mac OS X console doesn’t like it.
: - Path separator on Unix.

The “Escaped?” column means, it requires escaping on Mac OS X console.

Finally, it turns out that -- looks the best, especially with a variable-width font like in Mac OS X Finder or Windows Explorer.

Special case: if the page identifier was blank, the page separator doesn’t appear so we would still have:

/main/documentation.html

This naming scheme also implies that all pages appear flatly in the same directory. This should help when resolving resource names.

2010-09-04

Novelang-0.46.1 released!

Just released Novelang-0.46.1!

Summary of changes:

  • Added source packaging for Novelang-attirail subproject.

Download it from here.

Enjoy!

2010-08-29

Novelang-0.46.0 released!

Just released Novelang-0.46.0!

Summary of changes:

New experimental features for code reuse:

  • Novelang-attirail subproject aggregating various tools. It’s not part of standard distribution, by now it requires separate rebuild.
  • Pluggable logging implementation.
  • Java code all under org.novelang package (was novelang).

Download it from here.

Enjoy!

2010-08-27

Novelang-0.45.0 released!

Just released Novelang-0.45.0!

Summary of changes:

  • Added Greek and Polish characters to the grammar.

Download it from here.

Enjoy!

2010-08-08

Novelang-0.44.5 released!

Just released Novelang-0.44.5!

Summary of changes:

  • Fixed release notes generation.

Download it from here.

Enjoy!

Novelang-0.44.4 released!

Just released Novelang-0.44.4!

Summary of changes:

  • Fixed a few references to old "Part" and "Book" terms, and file suffixes as well.

Download it from here.

Enjoy!

Script for renaming to new extensions

Here is a Bash script (tested on Mac OS X) that renames every .nlp into .novella and .nlb into .opus. It also changes file content. Use with care.

#!/bin/sh

SED='s/\.nlp/\.novella/g;s/\.nlb/\.opus/g'
for file in `find src modules \( -name *.nlp -o -name *.nlb \) `
do
  newfile=` echo "$file" | sed $SED `
  echo "$file -> $newfile"
  sed $SED < $file > $newfile
  rm $file
done

2010-08-07

Novelang-0.44.3 released!

Just released Novelang-0.44.3!

Summary of changes:

  • Fixed Nhovestone report generation.

Download it from here.

Enjoy!