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
FunctionDefinition
s 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:
Post a Comment