How to write a simple Code Rewrite for x -> self x?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

How to write a simple Code Rewrite for x -> self x?

Tim M
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


Reply | Threaded
Open this post in threaded view
|

Re: How to write a simple Code Rewrite for x -> self x?

John Brant
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


Reply | Threaded
Open this post in threaded view
|

Re: How to write a simple Code Rewrite for x -> self x?

Tim M
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


Reply | Threaded
Open this post in threaded view
|

Re: How to write a simple Code Rewrite for x -> self x?

John Brant
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


Reply | Threaded
Open this post in threaded view
|

Re: How to write a simple Code Rewrite for x -> self x?

Tim M
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
>