julia eval an expression including an escape variable - julia

Using eval against the following expression is straightforward:
exp = :(x + 1)
x = 1
eval(exp)
I cannot figure out how to evaluate the following expression that involves an escaped variable:
x = 1
exp = :($(esc(x)) + 1)
A dump of the exp gives:
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Expr
head: Symbol escape
args: Array{Any}((1,))
1: Int64 1 <-- current value of x
3: Int64 1
I figured out how to replace the escaped variable value like this:
exp.args[2].args[1] = 2
dump(exp) is now:
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Expr
head: Symbol escape
args: Array{Any}((1,))
1: Int64 2 <-- Replaced value
3: Int64 1
I am missing one last step that I could not find in spite of searching the docs and stackoverflow.

I am not exactly sure what you want to accomplish, however esc normally makes sense only inside macros, so if have object like this you need to wrap it around a macro.
julia> exp = :($(esc(x)) + 1)
:($(Expr(:escape, 33)) + 1)
julia> macro call_exp(); exp; end
#call_exp (macro with 1 method)
julia> x=33;
julia> #call_exp
34

Related

left arrow operator in Julia

I have a vector
a = collect(1:4)
What is the meaning of a <- a in Julia [result - false]
As per the documentation <- is not an assignment operator in Julia.
Here is a way to check it (so that in the future you will be able to solve similar problems more easily):
julia> dump(:(a <- a))
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol <
2: Symbol a
3: Expr
head: Symbol call
args: Array{Any}((2,))
1: Symbol -
2: Symbol a
So as you can see a <- a is the same as a < (-a) thus you are comparing, using the < operator the a vector with the -a vector.

Generic function for stripping `LineNumberNode` in `Expr`(should be able to deal with :macrocalls)?

Is there a build-in Julia function for stripping LineNumberNode in Expr? especially for macrocalls:
julia> ex = :(#foo 1)
:(#= REPL[5]:1 =# #foo 1)
julia> dump(ex)
Expr
head: Symbol macrocall
args: Array{Any}((3,))
1: Symbol #foo
2: LineNumberNode
line: Int64 1
file: Symbol REPL[5]
3: Int64 1
Tried MacroTools.striplines, but
julia> ex = :(#foo 1+1)
:(#= REPL[7]:1 =# #foo 1 + 1)
julia> MacroTools.striplines(ex) |> dump
Expr
head: Symbol macrocall
args: Array{Any}((3,))
1: Symbol #foo
2: LineNumberNode
line: Int64 1
file: Symbol REPL[7]
3: Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Int64 1
3: Int64 1
My use-case is to compare two different exprs constructed in different files(so different line number info). My current workaround is to explicitly write Expr(:macrocall, Symbol("#foo"), nothing, :(1+1)) which is a little bit verbose.
The built-in function is Base.remove_linenums!:
julia> ex = quote begin
x = 3
y = 2
z = 4
foo(x) = 3
end
end
quote
#= REPL[2]:1 =#
begin
#= REPL[2]:2 =#
x = 3
#= REPL[2]:3 =#
y = 2
#= REPL[2]:4 =#
z = 4
#= REPL[2]:5 =#
foo(x) = begin
#= REPL[2]:5 =#
3
end
end
end
julia> Base.remove_linenums!(ex)
quote
begin
x = 3
y = 2
z = 4
foo(x) = begin
3
end
end
end
Credit to Alex Arslan for reminding me of it.
Not built in, but MacroTools.jl has MacroTools.striplines(ex) which removes the LineNumberNodes from an expression.
Since your goal is to be able to compare Exprs maybe replace LineNumberNodes with nothing. This allows to make comparisons and the Exprs still work. See the example below:
julia> macro hello(world)
println("hello ",world)
end
#hello (macro with 1 method)
julia> m1 = :(#hello "world")
:(#= REPL[99]:1 =# #hello "world")
julia> m2 = :(#hello "world")
:(#= REPL[100]:1 =# #hello "world")
julia> m1 == m2
false
julia> replace!(arg -> typeof(arg) <: LineNumberNode ? nothing : arg, m1.args);
julia> replace!(arg -> typeof(arg) <: LineNumberNode ? nothing : arg, m2.args);
julia> dump(m1)
Expr
head: Symbol macrocall
args: Array{Any}((3,))
1: Symbol #hello
2: Nothing nothing
3: String "world"
julia> eval(m1)
hello world
julia> m1 == m2
true
Of course if your code is nested you will have to make the replace its elements recursively over the entire Expr's AST.
You can consider defining the following function to achieve what you want by comparing two expressions for equality ignoring line number nodes:
function cmpexpr(ex1::Expr, ex2::Expr)
ex1.head === ex2.head || return false
length(ex1.args) === length(ex2.args) || return false
for (a1, a2) in zip(ex1.args, ex2.args)
typeof(a1) === typeof(a2) || return false
if a1 isa Expr
cmpexpr(a1, a2) || return false
elseif !(a1 isa LineNumberNode)
isequal(a1, a2) || return false
end
end
return true
end

Julia : pass string to macro

suppose that I have a macro that is defined as :
macro foomacro(ex::Expr)
dump(ex)
ex
end
Currently I would like to pass my expression as a parsed string so that I may pass a rather complicated and case dependent expression that has been obtained via string concatenation.
However, trying :
#foomacro 1+2+3
gives the expected result 6 whereas
#foomacro parse("1+2+3")
returns the parsed expression :(1+2+3) instead of actually parsing it...
As far as I understand this both macros should be receiving the same expression but this is clearly not the case.
How do I get this MWE to work ?
ps: I figured out this fix but I feel like it is very dirty and "incorrect"
macro foomacro(ex::Expr)
if ex.head == :call
#in this case the user is calling the macro via a parsed string
dump(ex)
return ex
end
dump(ex)
ex
end
ps: if this is of any relevance, currently the code is running on 0.6.4 and if possible I'd rather not update to 1.0 yet since this would postpone my actual project to much...
You're mixing up levels. Let's introduce an intermediate function for clarity:
function foomacro_impl(expr)
dump(expr)
expr
end
macro foomacro(expr)
foomacro_impl(expr)
end
If run, the expression #foomacro <someexpr> will be parsed, the <someexpr> part passed to foomacro_impl, and the result treated as an expression and inserted instead of the original expression. That means that writing #foomacro 1+2+3 is equivalent to having written
let expr = :(1+2+3)
dump(expr)
expr
end
which returns
Expr
head: Symbol call
args: Array{Any}((4,))
1: Symbol +
2: Int64 1
3: Int64 2
4: Int64 3
:(1 + 2 + 3)
an Expr that evaluates to 6.
On the other hand, in #foomacro Meta.parse("1+2+3"), the whole argument, parse("1+2+3"), is used as expr:
julia> let expr = :(Meta.parse("1+2+3"))
dump(expr)
expr
end
Expr
head: Symbol call
args: Array{Any}((2,))
1: Expr
head: Symbol .
args: Array{Any}((2,))
1: Symbol Meta
2: QuoteNode
value: Symbol parse
2: String "1+2+3"
:(Meta.parse("1+2+3"))
So the result of the macro call is the expression Meta.parse("1+2+3"), which evaluates to another expression :(1 + 2 + 3), since it is a call to parse. The two forms are thus not receiving the same expression!
But there are ways to manually parse an expression and pass it to a macro:
You can do as I did, and use a separate "macro implementing function". Then, the expression returned by #foomacro bla is equivalent to foomacro_impl(Meta.parse(bla)). (This approach, BTW, is very useful for testing, and I recommend it most of the times.)
You can use the macro #eval to construct an expression, splice into it, and evaluate it immediately:
julia> #eval #foomacro $(Meta.parse("1+2+3"))
Expr
head: Symbol call
args: Array{Any}((4,))
1: Symbol +
2: Int64 1
3: Int64 2
4: Int64 3
6
(Or similarly, use eval and manually constructed Expr values.)

Julia codegeneration with global function

julia 0.5.1
I want to create a function inside a quote that can be used after the specified macro has been used. Here is an example of what I mean
macro wat()
quote
type Foo end
global bar() = begin end
global function bar2()
end
type Baaz end
end
end
#wat
Foo()
Baaz()
bar()
bar2()
Now when I run this the last line crashes, because bar2 is undefined. I do not understand why because in my understanding bar() and bar2() should be equal and bar is just syntactic sugar for bar2. But they are apparently not equal and I do not understand why the one works and other does not.
Secondly is there a way to define bar and bar2 inside that quote without the global-keyword and still being available after the macro has been executed?
My motivation for wanting the bar2 notation is that I can specify a return-type with this syntax.
global bar3()::Void = begin end
Is not allowed syntax.
In the returned expressions of Julia macros, names of local variables are replaced with unique symbols:
julia> macro foo()
quote
x = 1
global y = 2
end
end
#foo (macro with 1 method)
julia> macroexpand(:(#foo))
quote # REPL[1], line 4:
#1#x = 1
global y = 2
end
This feature is called macro hygiene and avoids accidental clashes with variables at the call site.
To escape from this behavior, one has to use esc:
julia> macro bar()
quote
x = 1
end |> esc
end
#bar (macro with 1 method)
julia> macroexpand(:(#bar))
quote # REPL[1], line 3:
x = 1
end
Often, one doesn't want to escape the whole returned expression but only specific parts of it:
julia> macro myshow(expr)
quote
x = $(esc(expr))
println($(string(expr)), " = ", x)
x
end
end
#myshow (macro with 1 method)
julia> x = pi/2
1.5707963267948966
julia> macroexpand(:(#myshow sin(x)))
quote # REPL[1], line 3:
#1#x = sin(x) # REPL[1], line 4:
(Main.println)("sin(x)", " = ", #1#x) # REPL[1], line 5:
#1#x
end
julia> #myshow sin(x)
sin(x) = 1.0
1.0
julia> x
1.5707963267948966
For details, I recommend to read the corresponding section in the manual.

Interpretation of method ASTs

I'm trying to understand the ASTs of Julia methods. An example is the following function:
function inner(o, p)
s = A1(o, p)
s = s + A2(o, p)
end
Calling show(code_lowered(inner, (Int64, Int64))[1]) will display something like
:($(Expr(:lambda, {:o,:p}, {{:#s908,:s},{{:o,:Any,0},{:p,:Any,0},{:#s908,:Any,18},{:s,:Any,2}},{}}, quote # /home/pool/projekt/julia/grouping.jl, line 7:
s = A1(o,p) # line 8:
#s908 = +(s,A2(o,p))
s = #s908
return #s908
end)))
My question is how to interpret the part {{:#s908,:s},{{:o,:Any,0},{:p,:Any,0},{:#s908,:Any,18},{:s,:Any,2}},{}} it seems to be two local variables {:#s908,:s}. After this there are a number of symbols listed, both function arguments and local variables again, each have type information and an Int, what is this for? I'm guessing it is listing variables in the local scope and the Int is some sort of attribute? What are the possible values and meanings of these? Are they documented somewhere?
If you want the true AST, you don't need to use code_lowered, just quote:
julia> ex = quote function inner(o, p)
s = A1(o, p)
s = s + A2(o, p)
end end
quote # none, line 1:
function inner(o,p) # none, line 2:
s = A1(o,p) # line 3:
s = +(s,A2(o,p))
end
end
julia>
julia> dump(ex)
Expr
head: Symbol block
args: Array(Any,(2,))
1: Expr
head: Symbol line
args: Array(Any,(2,))
1: Int64 1
2: Symbol none
typ: Any
2: Expr
head: Symbol function
args: Array(Any,(2,))
1: Expr
head: Symbol call
args: Array(Any,(3,))
typ: Any
2: Expr
head: Symbol block
args: Array(Any,(4,))
typ: Any
typ: Any
typ: Any
You're looking at the annotated AST after removing syntactic sugar.

Resources