Is it possible to modify ast by itself?

Hi everyone, I am trying to write a julia debugger.
I am wondering if it is possible to modify a block’s ast by itself.

I tried like this:

julia> s = "function test1()
               println(1)
               println(2)
               println(3)
               println(4)
       end"
"function test1()\n        println(1)\n        println(2)\n        println(3)\n        println(4)\nend"

julia> ast = Meta.parse(s)
:(function test1()
      #= none:2 =#
      println(1)
      #= none:3 =#
      println(2)
      #= none:4 =#
      println(3)
      #= none:5 =#
      println(4)
  end)

julia> eval(ast)
test1 (generic function with 1 method)

julia> function helloworld()
         print("helloworld")
       end
helloworld (generic function with 1 method)

julia> function testReviseSelf(ast)
         ast.args[2].args[5] = Expr(:call, helloworld)
       end
testReviseSelf (generic function with 1 method)

julia> ast.args[2].args[3] = Expr(:call, helloworld)
:((helloworld)())

julia> ast
:(function test1()
      #= none:2 =#
      println(1)
      (helloworld)()
      println(2)
      #= none:4 =#
      println(3)
      #= none:5 =#
      println(4)
  end)

I want to use inter() to modify ast

julia> function inter()
         global ast
         testReviseSelf(ast)
       end
inter (generic function with 1 method)

julia> ast.args[2].args[1] = Expr(:call, inter)
:((inter)())

julia> ast
:(function test1()
      (inter)()
      println(1)
      (helloworld)()
      println(2)
      #= none:4 =#
      println(3)
      #= none:5 =#
      println(4)
  end)

However it seems I should run the block twice to get it changed

julia> test1()
1
helloworld2
3
4

julia> ast
:(function test1()
      (inter)()
      println(1)
      (helloworld)()
      println(2)
      (helloworld)()
      println(3)
      #= none:5 =#
      println(4)
  end)

julia> eval(ast)
test1 (generic function with 1 method)

julia> test1()
1
helloworld2
helloworld3
4

I am wondering if it is possible to modify the ast while eval the ast.
Thanks!

The function test1 is in no way connected to the variable ast so no. Not in anyway similar to what you have.

It might work as a recursive Base.invokelatest(eval(ast), "args") if you can avoid it looping forever

Well, part of my “in anyway similar to what you have” assumes that the test1 should not be completely rewritten. If that’s acceptable, there’s infinite many ways to change the target of a function call. Just use a callback or a non-const variable to store the function. What the OP wanted to do is never going to be type stable anyway.

1 Like