I am stumped on how to use the code rewriter - I've read some of John
and Don's tutorials but I can't get simple stuff to work? e.g. if I have the method: executeSomethingElse self show: 'owner = ' , owner; cr; show: 'child = ' , child And I want all direct references to instance vars to be "self ivar" I thought I could write: `var and in the replace pane self var The result I get is: executeSomethingElse (self var) show: 'owner = ' , self var; cr; show: 'child = ' , self var So I'm wondering - after "var" has matched a variable, how I can reference it in the replace pane? I also hadn't anticpated getting a match on the initial "self" reference - this seems to make things more complicated as I have to filter out variables named "self". Anyone have any tips on using the Code Rewriter? Tim |
TimM wrote:
> I am stumped on how to use the code rewriter - I've read some of John > and Don's tutorials but I can't get simple stuff to work? > > e.g. if I have the method: > > executeSomethingElse > self > show: 'owner = ' , owner; > cr; > show: 'child = ' , child > > And I want all direct references to instance vars to be "self ivar" I > thought I could write: > > `var > > and in the replace pane > > self var > > > The result I get is: > > executeSomethingElse > (self var) > show: 'owner = ' , self var; > cr; > show: 'child = ' , self var > > > > So I'm wondering - after "var" has matched a variable, how I can > reference it in the replace pane? The rewriter is good about matching arbitrary nodes and moving them around. It is less useful for transforming arbitrary nodes into nodes of other types. In your example, you are trying to convert an arbitrary variable node into a message send node. If you would explicitly specify the variable you are looking for then it get fairly simple. For example, you could use "owner" and "self owner" to replace the owner variable. Why don't you use the "Abstract" variable refactoring? It will safely convert the explicit variable references into message sends to accessor methods. > I also hadn't anticpated getting a match on the initial "self" > reference - this seems to make things more complicated as I have to > filter out variables named "self". You also have to worry about globals, local variable references, variable references on the left hand side of assignments, etc. If you really want to use the rewriter for this task, you could use a search pattern like: `var `{:node | ((#('self' 'super') includes: node name) or: [(Smalltalk includesKey: node name asSymbol) or: [(node whoDefines: node name) notNil or: [node parent isAssignment and: [node parent variable == node]]]]) not} and a replace pattern of: `{RBMessageNode receiver: (RBVariableNode named: 'self') selector: `var name asSymbol} The `{} syntax allows you to evaluate Smalltalk code in the pattern matcher. BTW, the search pattern doesn't work for class or pool variables. John Brant |
Thanks John,
I was just putting a toe in the water with a baby problem (I guess I picked the wrong toe ;-). I am kind of suprised by the replace pattern complexity. For example given how SmaCC lets me easily name expressions and then use that name in an action, I was kind of expecting the same from the Code Rewriter? Is this just something I wouldn't normally need to do - or is it just that you never thought of this? I can't imagine that its technically hard - but maybe I wouldn't normally need to do it. Anyway - thanks for the swift reply. Tim |
TimM wrote:
> I am kind of suprised by the replace pattern complexity. For example > given how SmaCC lets me easily name expressions and then use that name > in an action, I was kind of expecting the same from the Code Rewriter? > Is this just something I wouldn't normally need to do - or is it just > that you never thought of this? I can't imagine that its technically > hard - but maybe I wouldn't normally need to do it. The problem is that the code rewriter works with parse tree nodes not strings. You can't use a variable parse node as the selector of a message node. Selectors must be symbols. I have worked on another system that used the rewriter's searching, but the replacement expression was string based. When you reference a matched node, it would just print the matched code in the output. This would allow you to convert a variable node to a message by using something like "`var" -> "self `var". This approach seems simpler, but doesn't work for cascaded messages -- the matched node might not be contiguous for a string replacement. Also, it is common to forget to add parentheses in the replacement expression. For example, if you search for "`@foo bang: `@bar" and replace it with "`@foo ! `@bar", you would get invalid results when the argument is a binary message (e.g., "var bang: 1 + 2" becomes "var ! 1 + 2" instead of "var ! (1 + 2)"). If we consider your example, you don't really want to convert all references to variables to be messages sends. You only want to convert the instance variable references. Instead of using the code rewriter in the browser, you could use it directly: ------------- | class rule | class := SmaCCParser. rule := SmalllintChecker runRule: (TransformationRule rewrite: (class instVarNames collect: [:each | Array with: each , ' := ``@a' with: 'self ' , each, ': ``@a']) , (class instVarNames collect: [:each | Array with: each with: 'self ' , each]) methods: false name: 'Convert inst vars refs to messages') onEnvironment: (BrowserEnvironment new forClassHierarchyFrom: class). RewriteChangesBrowser showOn: rule changes ------------- This will convert all instance variable references in SmaCCParser and its subclasses to go through accessors (of course, you'll need to define them first). Instead of looking at all variable references, this is only looking at variables defined in SmaCCParser. John Brant |
Hi John,
Thanks for taking the time to show an example of using a TransformationRule - I was doing a little internal presentation on refactoring and parse nodes so it was really cool to reference your paper and show your response. People commented that they were inspired by the topic - which is rewarding. Thanks! Tim > Instead of using the code rewriter > in > the browser, you could use it directly: > ------------- > | class rule | > class := SmaCCParser. > rule := SmalllintChecker > runRule: (TransformationRule > rewrite: > (class instVarNames collect: [:each | Array with: each , ' := ``@a' > with: 'self ' , each, ': ``@a']) , > (class instVarNames collect: [:each | Array with: each with: 'self > ' > , each]) > methods: false > name: 'Convert inst vars refs to messages') > onEnvironment: (BrowserEnvironment new forClassHierarchyFrom: class). > RewriteChangesBrowser showOn: rule changes > ------------- > This will convert all instance variable references in SmaCCParser and > its subclasses to go through accessors (of course, you'll need to > define > them first). Instead of looking at all variable references, this is > only > looking at variables defined in SmaCCParser. > John Brant > |
Free forum by Nabble | Edit this page |