2008-06-17

Book feature just started working!

Oh yes I just committed the Book feature and it looks good! A book file looks like this:
insert file:relative/path/part1.nlp

section
  This Section was defined from inside a Book file!

insert file:part2.nlp
This means: "fetch a pair of Part files and define some Section between them". After launching the HTTP Daemon you can see resulting document with that URL: http://localhost:8080/samples/book.html. First it attempts to load a samples/book.nlb file ("nlb" like NoveLang Book). If the file doesn't exist, it attempts to load its .nlp counterpart ("nlp" like NoveLang Part). If the .nlp file doesn't exit then the preview fails with an error message. The syntax for Book files is easy to expand, as functions like section or insert are not defined inside the grammar. Instead, they are Java classes that receive arguments and apply to a document tree. For Novelang, a Book is made of function calls. The general form of a function call is a function name, then a URL or an identifier or a paragraph body (all optional), then an unlimited number of arguments wich can be identifiers, or words preceded with a dollar sign ("$"). Here is the formal definition as an ANTLR grammar fragment:
functionCall
  : word 
     (   smallBreak url 
       | smallBreak headerIdentifier 
       | WHITESPACE? SOFTBREAK WHITESPACE? paragraphBody
     )?
    ( mediumBreak valuedArgument )*
  ; 

valuedArgument
  : ( PLUS_SIGN? blockIdentifier )
  | ( DOLLAR_SIGN word )
  ;
In order to make the grammar simple, there must be a line break between the function name and paragraph body. This constraint is ok as it makes the Book file more readable. A paragraph body is the same as for a Part: a sequence of words, punctuation signs, and blocks made of quotes, parenthesis, square brackets and interpolated clauses (all can be nested). A header identifier is a reference to some block of text defined elsewhere (more on this here). The dollar-hinted words indicate some other options. On the Java side, functions are defined by implementing the FunctionDefinition interface. This interface defines the contract for instantiating a FunctionCall which captures all the values for a given call (like file:part2.nlp parameter value in the example above). The FunctionRegistry knows FunctionDefinitions by their names. As a Book evaluates itself, it receives the result of the parsing of a Book file: basically an Abstract Syntax Tree with FUNCTION_CALL nodes. For each nodes it attempts to get a FunctionDefinition instance, then a FunctionCall object. The FunctionDefinition checks parameters sanity and consistency, for a fail-fast approach. The FunctionCall operate on an immutable tree-like structure called Treepath. A Treepath indicates the relative position of a Tree inside another "bigger" Tree. It solves the problem of changing the value of an immutable Tree (by creating an evolved copy) while original Tree is the child of some other immutable Tree. Immutable objects make easier to get safer and cleaner code. Before implementing more functions, I'll play a bit with the insert and section functions and investigate error cases like having a broken Part file.

No comments: