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.