« Previous  |  Next »

Fixing Underscore Assignments

12 August 2012

Goran Krampe's HttpView2 web programming framework has a nice utility called MacroProcessor, which provides PHP-ish expansions, as per the class comment:

| proc |
proc := MacroProcessor block: [ :x | (Compiler evaluate: '3+4') asString
            between: ''.
proc process: 'This code: 3+4 evaluates to: .'.

The above code produces the string 'This code: 3+4 evaluates to: 7.'

I've previously used MacroProcessor to good effect to generate Linux- and Windows-specific Makefiles from templates.

MacroProcessor's code contains a sprinkling of underscores as assignment operators. The code base is small, so it is possible to convert underscores to the ":=" assignment operator by hand, but what fun is that?

What is fun is to do that in Smalltalk. The following is done in Pharo 1.2, which is where I have a copy of MacroProcessor. The starting points were Compiler, RBParser, Decompiler, etc. After poking around - running code in workspaces and exploring their results, looking at test cases - I come to RBConfigurableFormatter>>acceptAssignmentNode: which looks like this:

acceptAssignmentNode: anAssignmentNode
    self visitNode: anAssignmentNode variable.
    codeStream space; nextPutAll: anAssignmentNode assignmentOperator; space.
    self visitNode: anAssignmentNode value

Ok... And assignmentOperator turns out to be RBAssignmentNode>>assignmentOperator, which is:

    ^ (self assignmentPosition notNil and: [ self source notNil and: [ (self source at: self assignmentPosition ifAbsent: [ nil ]) = $_ ]])
        ifTrue: [ '_' ]
        ifFalse: [ ':=' ]

Aha! The next step is obvious:

RBConfigurableFormatter subclass: #PNUnderscoreRewriter
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'PN-Tools'

acceptAssignmentNode: anAssignmentNode 
    self visitNode: anAssignmentNode variable.
    codeStream space; nextPutAll: ':='; space.
    self visitNode: anAssignmentNode value

With the above, and a bit more exploratory programming in workspaces, I end up with the following:

MacroProcessor selectorsDo: [ :s |
    | formatter method oldSrc newSrc |
        formatter := PNUnderscoreRewriter new.
        method := MacroProcessor compiledMethodAt: s.
        oldSrc := MacroProcessor sourceCodeAt: s.
        newSrc := formatter format: (RBParser parseMethod: oldSrc).
        MacroProcessor compile: newSrc classified: method catergory ]

Figure 1: Here's what a method looked like originally.

Figure 2: Here's what the method looks like afterwards.

It remains to file MacroProcessor out from the Pharo 1.2 image, then file it into my current working Pharo 1.4 image.

Blog comments powered by Disqus