On Sat, Nov 21, 2015 at 7:07 AM, Thierry Goubier <thierry.goubier@gmail.com> wrote:
Hi Mariano,
Le 20/11/2015 20:58, Mariano Martinez Peck a écrit :
Thierry,
Sorry to bother again. Unfortunately, RB does not work out of the box in GemStone .... only the formatter part. grrr.
There is maybe a message to send to the maintainer of RB in Gemstone :)
Thanks Thierry. Yes, I talked with Dale and the only "official" usage/port of RB is actually the formatter ;)
Anyway, I am trying to make it work, and it's failing in this easy example:
/| tree rewriter |/ /tree := RBParser parseMethod: 'DoIt ^ [:proxy | proxy at: #oldSelector. ]'./ /rewriter := RBParseTreeRewriter new./ /rewriter / /replace: ('`#oldSelector `{:node | node value isSymbol and: [node value matchesRegex: ''.*oldSelector.*''] }')/ /with: ('`{RBLiteralNode value: (`#oldSelector value copyReplaceAll: ''oldSelector'' with: ''newSelector'') asSymbol }')./ /rewriter executeTree: tree./ /rewriter tree newSource/ And the problem is the line:
/with: ('`{RBLiteralNode value: (`#oldSelector value copyReplaceAll: ''oldSelector'' with: ''newSelector'') asSymbol }')./
which gets translated to:
[ :aDictionary | RBLiteralNode value: ((*self* lookupMatchFor: '`#oldSelector' in: aDictionary) value copyReplaceAll: 'oldSelector' with: 'newSelector') asSymbol ]
The problem in GemStone is that that "self" is binded to nil and not to the receiver instance (RBPatternBlockNode). And so I get a Nil dnu.. *And I don't know how that closure is finally generated.*
The message node:
/(*self* lookupMatchFor: '`#oldSelector' in: aDictionary) /
Its generated in:
RBPatternBlockNode >> constructLookupNodeFor: aString in: aRBBlockNode | argumentNode | argumentNode := RBLiteralNode value: aString.
^RBMessageNode receiver:* (RBVariableNode named: 'self')* selector: #lookupMatchFor:in: arguments: (Array with: argumentNode with: aRBBlockNode arguments last)
Any ideas?
Yes. I think it is possible to work around it by using the dictionary itself, storing the node selector in it in the replace: pattern and retrieving it in the with: pattern.
rewriter replace: ('`#oldSelector `{:node :dic | (node value isSymbol and: [node value matchesRegex: ''.*oldSelector.*'']) ifTrue: [dic at: #selector put: node value. true] ifFalse: [false] }') with: ('`{:dic | RBLiteralNode value: ((dic at: #selector) copyReplaceAll: ''oldSelector'' with: ''newSelector'') asSymbol }').
Wow, that's very cool. I wasn't aware that I could pass arguments (like the dict in this case). This is powerful. Thank you RB Jedi.
Now, maybe correcting RB in Gemstone is something to do, as well.
Yes. So with the dict workaround you said, it DID work, but when I continue a little more and I found out where was the closure being generated, and that is RBPatternBlockNode >> #createBlockFor: And yeah, the way it was being generated the closure ("source evalauted") was binding "self" to nil. I found a way to make it bind the real self and that worked too. So...time to fork RB for GemStone and commit the fix :) BTW, do you image other cases like this? Thanks! Regards,
Thierry
thanks in advance,
On Mon, Nov 9, 2015 at 2:39 PM, Thierry Goubier <thierry.goubier@gmail.com <mailto:thierry.goubier@gmail.com>> wrote:
Le 09/11/2015 17:38, Mariano Martinez Peck a écrit :
Thierry, I have a last question. My last requirement will basically be to find ANY node that would match the regex *oldSelector* and rename it to *newSelector* being that.. a literal string, a literal symbol, a message send, a tempVar name, a comment, etc... I guess there is a simpler way that defining rewrite for each type of node right? I can always do a "(methodSource substrings detect: [:each | each matches: '*oldSelector'] ifNone: [#()] ) each2: [:each2 | copReplace. .... ]
I think that if it is really everywhere, then it may works better to just do text replace (methodSource copyReplaceAll: 'oldSelector' with: 'newSelector').
But remember that it will touch everything (pragmas, varNames, comments, etc.)
but RB is probably better.
It gives you more control to what you will change. But then you will have to write rewrites for all types of nodes (around 6 or 7?).
Any clues in this last requirement?
Well, one thing which may be interesting would be to consider encapsulating the rewrites in refactoring commands, and bring them as a composite change, so that you could launch a rewrite, recover all those method changes, apply (execute) them, check the result with the possibility of undoing them.
But I'm not familiar with that part of RB. I do consider it could become significant with EPICEA. It has been a long term project to have AltBrowser make all coding actions as refactoring commands so that EPICEA could log them properly, but its only partly there (and has been for years now)... and EPICEA is not yet here as well (but could be soon).
Thierry
-- Mariano http://marianopeck.wordpress.com
-- Mariano http://marianopeck.wordpress.com