Problem with Macro expression evaluation

Hello,

this is my first post and i need a little help with my macro problem

macro foo(f, x)
    Expr(:call, f, x)
end

macro foo(exp)
    quote
        $(esc(exp))
    end
end

function test(f)
    exp = Meta.parse(f)
    return @foo(exp)
end

p=@foo(cos, 30)
println(p, " : ", typeof(p))

p=test("cos(30)")
println(p, " : ", typeof(p))

The output from @foo is what i want and shows me the correct value
But the output from test returns only the expression cos(30) and no value.

How can i change that without using eval?

best regards

Michael

But isn’t that literally want you want to do? You want to take a string of arbitrary julia code, parse it to an expression and then evaluate that expression. That is done using eval.

2 Likes

Thanks for your reply

No i want the value of the expression like in the first example with @foo(cos, 30). Only that the value should returned by the function without the slow eval.

Everytime i read about julia macros i should not use eval because the macro do that supposedly. I don’t know

best regards

Michael

I changed the code a bit and escaped the parameters. That does the job what i want

macro foo(f, x)
    Expr(:call, esc(f), esc(x))
end

function test(f::Symbol, x)
    return @foo(f, x)
end

@time p=@foo(cos, 30)
println(p, " : ", typeof(p))

@time p=test(length, "justanotherText")
println(p, " : ", typeof(p))

best regards

Michael

Try running that code in a fresh Julia session. It actually throws an error on the call to test():

julia> @time p=test(length, "justanotherText")
ERROR: MethodError: no method matching test(::typeof(length), ::String)
Closest candidates are:
  test(::Symbol, ::Any) at REPL[2]:2
Stacktrace:
 [1] top-level scope at util.jl:156

That makes sense, because your function test() is only defined on ::Symbol, but length is a Function. I suspect that you’re actually calling some other version of test() which was defined earlier in your Julia session.

1 Like

Hi,

thanks for your answer :slight_smile:

Yes i have the same problem here with a new Session!
You must delete the ::Symbol definition in test and then it should go.

best regards
Michael

I’m not sure I understand what you want to achieve here… Could you please explain how the last code you posted relates to the title of this thread? Or maybe take a step back and explain what your bigger problem is?

Also, consider your proposed solution:

macro foo(f, x)
    Expr(:call, esc(f), esc(x))
end

function test(f, x)
    return @foo(f, x)
end

which benchmarks like this (BenchmarkTools, which is more reliable than just @time since it avoids measuring compilation times):

julia> @btime test(length, "justanotherText");
  15.426 ns (0 allocations: 0 bytes)

julia> p=test(length, "justanotherText");
julia> println(p, " : ", typeof(p))
15 : Int64

Is this not a rather complicated way of applying a function to a value, like for example:

function apply(f, x)
    f(x)
end
julia> @btime apply(length, "justanotherText");
  15.062 ns (0 allocations: 0 bytes)

julia> p = apply(length, "justanotherText");
julia> println(p, " : ", typeof(p))
15 : Int64

or even a plain

julia> @btime length("justanotherTest")
  15.421 ns (0 allocations: 0 bytes)
15

These ways of expressing things have the same performance as your proposed solution, which is arguably more difficult to understand; wouldn’t you think?

1 Like

Hello,

I am currently working on a oldschool basic interpreter for fun, where every detail in a line is a string array.
Example of a function

10 x = Mid ("justanothertext", 5, 3)
becomes
line = ["10", "x", "Mid", "\"justanothertext\"", "5", "3"]

if the function is recognized as existing, I create an expression with e = Expr (: call, symbol (“MID”), line, pos).
pos points to the first parameter in my function.
It looks like this

function MID (line :: Array {String, 1}, pos :: Int) # s = Mid ("NocheinText", 5,3) => ein
    s = ""; start = 0; num = 0; erg = ""
    try
        s = getCheckedValue (MEMDICT, line, pos)
        start = getCheckedValue (MEMDICT, line, pos + 1)
        num = getCheckedValue (MEMDICT, line, pos + 2)
        erg = s [start: start + num-1]
    catch e
        printError (e, line)
    end

    return pos + 2, erg
end

Will then be executed with eval (e). Works well too …
MEMDICT is my variable memory as a dictionary
x = 5 => MEMDICT [“x”] = 5
getCheckedValue checks if variable exists or a value(number…)!

Couldn’t you have some sort of dictionary storing all recognised functions? You probably already have some sort of dynamic data structure listing existing functions in order to recognize legit calls… (or you could use the same MEMDICT as for variables)

This would allow to avoid expressions, macros and eval altogether, and do simple things like:

foo(line) = "foo($(line[2]))"
bar(line) = "bar($(line[2]))"

const functions = Dict("FOO" => foo,
                       "BAR" => bar)

interpret(line) = functions[uppercase(line[1])](line)
julia> interpret(["foo", "arg"])
"foo(arg)"
1 Like

Yes you are right. Have a separate dictionary for almost everything.
Jump table for GOTO and FOR … NEXT. For IF … THEN I take the Dictionary of GOTO
Thank you for your suggestions.

best regards

Michael