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:
assignmentOperator
^ (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.
Tags: metaprogramming