Abhinav Sharma
Abhinav Sharma

Reputation: 137

How to unparse a Julia expression

I've been trying to understand the Julia from a meta-programming viewpoint and often I find myself in the position where I wish to generate the user facing Julia syntax from an Expr.

Searching through the source code on GitHub I came across the "deparse" function defined in femtolisp. But it doesn't seem to be exposed at all.

What are the ways I can generate a proper Julia expression using just the internal representation?

P. S. There ought to be some sort of prettifying tool for the generated Julia code, do you know of some such (un/registered) pkg?

~#~#~#~#~

UPDATE

I've stored all the Meta.show_sexprof a julia source file into a different file.

# This function is identical to create_adder implementation above.
function create_adder(x)
    y -> x + y
end

# You can also name the internal function, if you want
function create_adder(x)
    function adder(y)
        x + y
    end
    adder
end

add_10 = create_adder(10)
add_10(3) # => 13

is converted to

  (:line, 473, :none),
    (:function, (:call, :create_adder, :x), (:block,
        (:line, 474, :none),
        (:function, (:call, :adder, :y), (:block,
            (:line, 475, :none),
            (:call, :+, :x, :y)
          )),
        (:line, 477, :none),
        :adder
      )),
    (:line, 480, :none),
    (:(=), :add_10, (:call, :create_adder, 10)),
    (:line, 481, :none),
    (:call, :add_10, 3))

Now, Wish to evaluate these in julia.

Upvotes: 2

Views: 516

Answers (1)

Tasos Papastylianou
Tasos Papastylianou

Reputation: 22245

Here's an example of a function that takes an "s_expression" in tuple form, and generates the corresponding Expr object:

"""rxpe_esrap: parse expr in reverse :p """
function rpxe_esrap(S_expr::Tuple)
  return Expr( Tuple( isa(i, Tuple) ? rpxe_esrap(i) : i for i in S_expr )... );
end

Demo

Let's generate a nice s_expression tuple to test our function.
(Unfortunately Meta.show_sexpr doesn't generate a string, it just prints to an IOStream, so to get its output as a string that we can parse / eval, we either need to get it from a file, or print straight into something like an IOBuffer)

B     = IOBuffer();              # will use to 'capture' the s_expr in
Expr1 = :(1 + 2 * 3);            # the expr we want to generate an s_expr for
Meta.show_sexpr(B, Expr1);       # push s_expr into buffer B
seek(B, 0);                      # 'rewind' buffer
SExprStr = read(B, String);      # get buffer contents as string
close(B);                        # please to be closink after finished, da?
SExpr = parse(SExprStr) |> eval; # final s_expr in tuple form

resulting in the following s_expression:

julia> SExpr
(:call, :+, 1, (:call, :*, 2, 3))

Now let's test our function:

julia> rpxe_esrap(SExpr)
:(1 + 2 * 3)                     # Success!

Notes:
1. This is just a bare-bones function to demonstrate the concept, obviously this would need appropriate sanity checks if to be used on serious projects.
2. This implementation just takes a single "s_expr tuple" argument; your example shows a string that corresponds to a sequence of tuples, but presumably you could tokenise such a string first to obtain the individual tuple arguments, and run the function on each one separately.
3. The usual warnings regarding parse / eval and scope apply. Also, if you wanted to pass the s_expr string itself as the function argument, rather than an "s_expr tuple", then you could modify this function to move the parse / eval step inside the function. This may be a better choice, since you can check what the string contains before evaluating potentially dangerous code, etc etc.
4. I'm not saying there isn't an official function that does this. Though if there is one, I'm not aware of it. This was fun to write though.

Upvotes: 1

Related Questions