Skip to content

Extending the Pier Parser

Early versions of Pier included the syntax to embed Smalltalk code into a page. This was a nice feature for power users, but caused certain confusion among others. Moreover it also caused potential security issues, when the permissions weren't setup properly. Never the less, as there were some people complaining about the missing feature, I am going to show how to extend Pier and its pluggable parser to support this functionality.

1. Creating a new Node Type

First let's create a new node type, a subclass of PRText:

PRText subclass: #PRCode
  instanceVariableNames: ''
  classVariableNames: ''
  poolDictionaries: ''
  category: 'Pier-Executable'

2. Register with the Parser

Let's specify an initializer method on the class side to register it with the parser. We choose a single pipe character as the new markup:

PRCode class>>markup
  ^ '|'
PRCode class>>initialize
  PRDocumentParser textMatcher at: self markup put: self

Next we need to implement the hook method #parse: aString markup: aMarkupString with: aParser to parse the string. In our case this is simple as we just want to return a new instance of PRCode with the contents:

PRDocumentParser class>>parse: aString markup: aMarkupString with: aParser
  ^ self new text: aString

3. Plugging into the Visitors

Next we have to make sure that the new node type plays well with the visitor framework:

PRCode>>accept: aVisitor
  aVisitor visitCode: self
PRVisitor>>visitCode: anObject
  self visitText: anObject

Now we add the different #visitCode: implementations in specific visitors. First this is the visitor to serialize a parsed node back to Wiki syntax:

PRWikiWriter>>visitCode: anObject
  self nextPutAll: anObject class markup.
  self nextPutAll: anObject text.
  self nextPutAll: anObject class markup

And second, this is the XHTML visitor that should evaluate the code and display it on the resulting output stream:

PRViewRenderer>>visitCode: anObject
  html render: (Compiler
    evaluate: anObject text
    for: self component
    logged: false)

That's all for a very basic implementation. Now we can write in any document |TimeStamp current| to embed the current time. Since self refers to the current component, we can embed the current page title into the document by writing: |self context structure title|. Or, you can write Seaside rendering code to create a link that calls the counter example: | [ :html | html anchor callback: [ self call: WACounter new ]; with: 'Start Counter' ] |. The possibilites are endless.

To use the presented code in a productive environment, some error handling has to be added. Propre sandboxing would also be a requirement.