Roman Numbers

Imagine the requirement of supporting roman numbers within a programming language. We are looking for an approach that keeps the complete syntax of the host language and avoids to add any boilerplate code. We would like to be able to write expressions like:

self assert: VII = III + IV.

In the following example we assume that there is a converter method #romanToArabic in the class String answering the decimal representation of a roman number or nil, if invalid.

We add the following transformation rule to the system:

RomanDSL class>>transformation

    ^ DSLTreePattern new 
        expression: '`var'; 
        action: [ :context | 
            | arabic | 
            arabic := context node name romanToArabic. 
            arabic notNil 
                ifTrue: [ context node swapWith: arabic lift ] ]

Line by line explanation:

String interpolation

Adding string interpolation to Smalltalk is a matter of adding a simple method that transforms string literals to a more complex AST after the initial parsing:

StringInterpolationDSL class>>stringInterpolation

    ^ DSLTreePattern new
        verification: [ :node | node value isString and: [ node value isSymbol not ] ];
        expression: '`#literal' do: [ :context | 
            context node swapWith: (self interpolate: context node value) ];

The transformation itself is defined in a separate helper method:

StringInterpolationDSL class>>interpolate: aString
    | stream parts string |
    stream := aString readStream.
    parts := OrderedCollection new.
    [ stream atEnd ] whileFalse: [
        string := stream upTo: ${.
        string isEmpty 
            ifFalse: [ parts add: string lift ].
        string := stream upTo: $}.
        string isEmpty
            ifFalse: [ parts add: (``(`,(Parser parseExpression: string) asString) ] ].
    ^ parts fold: [ :a :b | ``(`,a , `,b) ]

This method makes use of quasiquoting, a mechanism that is particularly well known in Scheme and other functional languages. Quasiquoting is merely syntactic sugar to make it easier to build parse tree nodes. For example ``(1 + a) represents the parse tree of 1 + a, as opposed to the expression itself. This language extension was built using HELVETIA as well.

The test case below demonstrates the new interpolation functionality:

    self assert: 'Result: {1 + 2}' = 'Result: 3'.
    self assert: '10! = {10 factorial}' = '10! = 3628800'