Inject julia code as string inside julia function

Hi all,

I’m receiving a pice of julia code as a string and I need to evaluate this code inside a function and access the local scope. Is it possible to access local scope with eval(Meta.parse(....)) or is there another alternativ to eval a code given as a string? (performance is not an issue in my case)

Below is a minimal code snibbet I like to get working

jl_string = """println("Hello"); println(a); b=4"""

function test(jl_string)
	a = 3
	expr = Meta.parse(jl_string)
	eval(expr)
	println(b)
end

test(jl_string)

Hello @JesperMartinsson,

AFAIK, eval always executes its argument in a global scope of the current module. I’m not aware of a trivial technique that makes the local scope available to the evaluated code in general. You can, however, turn it into a function:

jl_string = """println("Hello"); println(a); 4"""

function test(jl_string)
	a = 3
	expr = Meta.parse("function(a) $(jl_string) end")
	b = invokelatest(eval(expr), a)
	println(b)
end

test(jl_string)

This makes variable access and modification more explicit, thus it is less error-prone.

That being said, passing expressions as strings and evaluating them without sanitization is generally a bad idea, both performancewise and out of security considerations. So whatever it is you are trying to achieve, there is almost certainly a better way to do it.

6 Likes

This is only possible if you effectively destroy most compiler optimizations. That string can contain assignments of arbitrarily many new variables, so local variables can’t be arranged in a fixed dense stack frame and accessed by fixed pointers. The types in that string are only known at runtime yet can affect the variables in the function, so none of the variables in the function can undergo type inference. A bit of this shows up in HanD’s strategy: although the string is wrapped into an anonymous function, the type of b is likewise uninferrable because the wrapper function only exists at runtime, far after test(::String) was compiled.

You can’t inform the compiler to stop doing its job in order to wrap string processing and runtime evaluation into a method, so you might as well just do it yourself:

jl_string = """println("Hello"); println(a); b=4"""

prefix = """
  let # stay in local scope so it doesn't fill up global scope
    a = 3
""" # newline important here

suffix = """
    # newline important here
    println(b)
  end
"""

eval(Meta.parse(prefix * jl_string * suffix))

Expression mutation is more efficient and can allow mimicry of non-string arguments for test, but it’s probably worth rethinking the goal so any compilation can be done.

Side note: HanD’s strategy currently evaluates an arbitrary number of anonymous functions whose separate types and methods persist for the whole Julia process, but that can be reduced to 1 repeatedly edited function by naming it in the global scope.

3 Likes