Search code examples
pharo

RBParser message nodes and targeting the receiver and the argument?


Trying to get some of my old code up and running in Pharo. Some method names are different but after some hardship I managed to find equivalents that work.

I am parsing my code and I'd like to check if the receiver or any of the arguments is aSymbol in an effort to match them to supported alternatives. I've managed to do this to selectors, by analysing RBMessageNode s

aNode selector == aSymbol ifTrue: [ aNode selector: replacementSymbol ].

How can this be done to arguments and receivers? Is there a comprehensive guide on RBParser somewhere?


Solution

  • By direct manipulation

    Assuming that you are looking for cases like this:

    aSymbol message: aSymbol message: aSymbol
    

    For receiver you should do:

    (aNode isMessage and: [
    aNode receiver isVariable and: [ 
    aNode receiver name = 'aSymbol' ]]) ifTrue: [
       "do your job here" ]
    

    Here is another example on how to replace #aSymbol arguments with #newSymbol:

    messageNode arguments: (messageNode arguments collect: [ :arg |
        (arg isLiteralNode and: [ arg value = #aSymbol ])
            ifFalse: [ arg ]
            ifTrue: [ | newNode |
                newNode := #aNewSymbol asLiteralNode.
                arg replaceSourceWith: newNode.
                newNode ] ]).
    
    methodClass compile: ast newSource
    

    The replaceSourceWith: makes sure that just a source will be replaced, but for newSource to actually return a new source you also need to swap the nodes themselves, that's why I'm doing a collect on arguments and return the new ones where needed.

    You can view help about RBParser in Word Menu > Help > Help Browser > Refactoring Framework.

    You can also play around by inspecting

    RBParser parseExpression: 'aSymbol message: aSymbol message: aSymbol'
    

    and looking at its contents

    By Parse Tree Transformation

    You can use pattern code to match and replace certain code. For example to change the symbol argument of a perform: message you can do this:

    ast := yourMethod parseTree.
    
    rewriter := RBParseTreeRewriter new 
            replace: '`receiver perform: #aSymbol'
            with: '`receiver perform: #newSelector'.
    
    (rewriter executeTree: ast) ifTrue: [
        yourMethod class compile: ast newSource ]
    

    You can learn more about the pattern matching syntax in the help topic Word Menu > Help > Help Browser > Refactoring Framework > Refactoring Engine > RBPatternParser …. I thing that MatchTool from pharo catalog can greatly help you in testing the match expressions (it also has a dedicated help topic about the matching syntax) while RewriteTool can help you to preview how your code will be transformed.