Replacing ast nodes

742 views
Skip to first unread message

Jesse van den Kieboom

unread,
Sep 23, 2013, 3:45:38 AM9/23/13
to golan...@googlegroups.com
I'm trying to replace one ast node with another. Is it possible to have a function like this:

func replace(e ast.Expr) {
}

and replace the actual expression in e in-place? I was trying something with reflect, but I'm not sure it would work anyway. I've also tried looking at the implementation of gofmt but didn't quite understand how it worked.

Kyle Lemons

unread,
Sep 23, 2013, 4:21:37 AM9/23/13
to Jesse van den Kieboom, golang-nuts
Having not seen the context, I can only speculate, but it seems like you could do:

func replace(e ast.Expr) ast.Expr { ... }

and always call it like

e = replace(e)

(think append)


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

gordon...@gmail.com

unread,
Sep 23, 2013, 4:26:28 AM9/23/13
to golan...@googlegroups.com, Jesse van den Kieboom
Since the aim is to modify the Expr, why not do:

func replace(e *ast.Expr) { ... }

?

Jesse van den Kieboom

unread,
Sep 23, 2013, 4:27:18 AM9/23/13
to golan...@googlegroups.com, Jesse van den Kieboom
I should probably have been more clear. For the moment, at the point where I have to replace the ast expression, I only have access to the expression I need to replace and not, let's say, the parent of the expression. So I wanted to know if it was possible to replace the expression value in-place. I'm thinking now that it's probably not possible unless it would be the exact same type of ast node (which it is not).

On Monday, September 23, 2013 10:21:37 AM UTC+2, Kyle Lemons wrote:

Robert Griesemer

unread,
Sep 23, 2013, 4:17:06 PM9/23/13
to Jesse van den Kieboom, golang-nuts
Replacing the expression value in-place is not possible; what you want to do is to replace it with a new node. But that requires that you fix the parent of that node to point to your new node.

You can use ast.Walk to find a parent for a given ast.Expr and then change its children. But at the moment there's no easy way (i.e., w/o writing quite a bit of code) to do this: You'd have to write your own visitor at the moment since Walk and (ast.)Inspect provide Nodes not *Nodes (so changing the parent is not easily supported).

- gri

Jesse van den Kieboom

unread,
Sep 23, 2013, 5:11:50 PM9/23/13
to golan...@googlegroups.com, Jesse van den Kieboom
On Monday, September 23, 2013 10:17:06 PM UTC+2, gri wrote:
Replacing the expression value in-place is not possible; what you want to do is to replace it with a new node. But that requires that you fix the parent of that node to point to your new node.

You can use ast.Walk to find a parent for a given ast.Expr and then change its children. But at the moment there's no easy way (i.e., w/o writing quite a bit of code) to do this: You'd have to write your own visitor at the moment since Walk and (ast.)Inspect provide Nodes not *Nodes (so changing the parent is not easily supported).

Thanks, that's actually what I ended up doing. I basically just copied ast.Walk and changed it so it replaces nodes instead of only visiting them. I could probably go the gofmt way instead, which uses reflection, but it seemed more complicated to do.

tsuna

unread,
Mar 21, 2015, 1:52:19 AM3/21/15
to Robert Griesemer, Jesse van den Kieboom, golang-nuts
(resurrecting an old thread)

I just found myself in a similar situation. I have a large code base
in which I’m trying to change a lot of &foo{a: 1, b: “two”} to
newFoo(1, “two”). In just 100 lines I was able to write a Go program
that does almost that – really cool!

The “almost” part is because I match on an UnaryExpr that is made of
Op == “&” and CompositeLit with Name == “foo”. But I can’t replace
the UnaryExpr itself. So my program rewrites &foo{a: 1, b: “two”} to
&newFoo(1, “two”) … bleh.

I didn’t find any way to get rid of the “&”, so I had to use my
program to rewrite the code base and then sed the extra ampersand out.

This made me think that an elegant solution to this problem would be
to have another version of ast.Walk() that is implemented slightly
differently. Walk() is basically just a big switch on the node type
to recursively invoke the Visitor. But if we had a Visitor that
returned (Node, Visitor) and simply re-inserted the returned Node in
the original AST, where the original was found, then we could rewrite
the entire AST as we go.

I implemented this idea here, based on the original code from
ast.Walk(): https://github.com/tsuna/gorewrite
I easily migrated my code from Visitor + Walk() to Rewriter +
Rewrite() – basically just had to change a method name and return the
modified Node, and it worked just fine.
Benoit "tsuna" Sigoure

minux

unread,
Mar 22, 2015, 5:21:02 AM3/22/15
to tsuna, golang-nuts
On Sat, Mar 21, 2015 at 1:51 AM, tsuna <tsun...@gmail.com> wrote:
(resurrecting an old thread)

I just found myself in a similar situation.  I have a large code base
in which I’m trying to change a lot of &foo{a: 1, b: “two”} to
newFoo(1, “two”).  In just 100 lines I was able to write a Go program
that does almost that – really cool

Have you tried the example based Go refactor tool eg?

Nick Craig-Wood

unread,
Mar 23, 2015, 1:47:45 PM3/23/15
to tsuna, Robert Griesemer, Jesse van den Kieboom, golang-nuts
On 21/03/15 05:51, tsuna wrote:
> (resurrecting an old thread)
>
> I just found myself in a similar situation. I have a large code base
> in which I’m trying to change a lot of &foo{a: 1, b: “two”} to
> newFoo(1, “two”). In just 100 lines I was able to write a Go program
> that does almost that – really cool!
>
> The “almost” part is because I match on an UnaryExpr that is made of
> Op == “&” and CompositeLit with Name == “foo”. But I can’t replace
> the UnaryExpr itself. So my program rewrites &foo{a: 1, b: “two”} to
> &newFoo(1, “two”) … bleh.
>
> I didn’t find any way to get rid of the “&”, so I had to use my
> program to rewrite the code base and then sed the extra ampersand out.
>
> This made me think that an elegant solution to this problem would be
> to have another version of ast.Walk() that is implemented slightly
> differently. Walk() is basically just a big switch on the node type
> to recursively invoke the Visitor. But if we had a Visitor that
> returned (Node, Visitor) and simply re-inserted the returned Node in
> the original AST, where the original was found, then we could rewrite
> the entire AST as we go.
>
> I implemented this idea here, based on the original code from
> ast.Walk(): https://github.com/tsuna/gorewrite
> I easily migrated my code from Visitor + Walk() to Rewriter +
> Rewrite() – basically just had to change a method name and return the
> modified Node, and it worked just fine.

Yes that would have been useful to me in gotemplate.

What I wanted to so was replace an ast.Node with a different one.

In fact you can see from this branch that it takes a huge amount of code
to rewrite the ast, which is probably why I never go round to merging it!

https://github.com/ncw/gotemplate/compare/ast-arguments
Nick Craig-Wood <ni...@craig-wood.com> -- http://www.craig-wood.com/nick
Reply all
Reply to author
Forward
0 new messages