What is equivalent to Python ast.literal_eval () in Julia?

Is there anything in Julia that is equivalent to the Python literal_eval provided by the ast package (abstract syntax tree)?

Summary of his description ( literal_eval ):

This function evaluates only Python literary structures: strings, bytes, numbers, tuples, lists, dicts, sets, booleans, and None , and can be used to safely evaluate strings from untrusted sources without the need to parse values. It is not capable of evaluating arbitrarily complex expressions, such as operators or indexing.

+7
python type-conversion abstract-syntax-tree julia-lang
source share
1 answer

There is no equivalent, although you could write quite easily by analyzing the code, and then recursively ensuring that before evaluating it, you will only get syntactic forms in the resulting expression. However, unlike Python, where many basic types, their syntax and behavior are built-in and immutable, Julia "built-in" types are only user-defined types that must be defined before the system starts. Let's look at what happens, for example, when you use the vector literal syntax:

 julia> :([1,2,3]) |> dump Expr head: Symbol vect args: Array{Any}((3,)) 1: Int64 1 2: Int64 2 3: Int64 3 typ: Any julia> f() = [1,2,3] f (generic function with 2 methods) julia> @code_lowered f() CodeInfo(:(begin nothing return (Base.vect)(1, 2, 3) end)) julia> methods(Base.vect) # 3 methods for generic function "vect": vect() in Base at array.jl:63 vect(X::T...) where T in Base at array.jl:64 vect(X...) in Base at array.jl:67 

So [1,2,3] is just a syntactic form that is omitted as a call to the Base.vect function, i.e. Base.vect(1,2,3) . Now we can give the opportunity to "seal" some functions in the future, so that it is impossible to add any sub-methods or to somehow rewrite their behavior, but at the moment it is possible to change the behavior of Base.vect for a certain set of arguments

 julia> function Base.vect(a::Int, b::Int, c::Int) warn("SURPRISE!") return invoke(Base.vect, Tuple{Any,Any,Any}, a, b, c) end julia> [1,2,3] WARNING: SURPRISE! 3-element Array{Int64,1}: 1 2 3 

Since the array literal is overloaded in Julia, this is not just literal syntax. Of course, I do not recommend doing what I just did - "SURPRISE!" this is not what you want to see in the middle of your program, but it is possible, and therefore the syntax is not "safe" in the sense of this question. Some other constructs expressed by literals in Python or JavaScript (or most scripting languages) are explicitly function calls in Julia, such as building dictionaries:

 julia> Dict(:foo => 1, :bar => 2, :baz => 42) Dict{Symbol,Int64} with 3 entries: :baz => 42 :bar => 2 :foo => 1 

This is just a function call for a Dict type with three arguments to a pair of objects, not a literal syntax. The very syntax a => b is also the special syntax for calling a function to the => operator, which is an alias for the Pair type:

 julia> dump(:(a => b)) Expr head: Symbol call args: Array{Any}((3,)) 1: Symbol => 2: Symbol a 3: Symbol b typ: Any julia> :foo => 1.23 :foo=>1.23 julia> => Pair julia> Pair(:foo, 1.23) :foo=>1.23 

What about whole literals? Of course it's safe! Well, yes and no. Small integer literals are currently safe because they are converted directly to Int values โ€‹โ€‹in the analyzer without any overloaded entry points (which may change in the future, however, allowing the user code to choose different types of behavior for integer literals). However, large enough literals are omitted before macro calls, for example:

 julia> :(18446744073709551616) :(@int128_str "18446744073709551616") 

An Integer literal that is too large for an Int64 type is omitted as a macro call with a string argument containing integers, allowing the macro to parse the string and return the corresponding integer object - in this case a Int128 value - to merge into an abstract syntax tree. But you can define new behavior for these macros:

 julia> macro int128_str(s) warn("BIG SUPRISE!") 9999999999999999 end julia> 18446744073709551616 WARNING: BIG SUPRISE! 9999999999999999 

Essentially, there is no meaningful โ€œsafe literal subsetโ€ of Julia. Philosophically, Julia is very different from Python: instead of creating types in a fixed set of special features that are not available for custom types, Julia includes quite powerful mechanisms in a language in which the language can be built from within itself - a process known as "self-tuning". These powerful language engines are just as accessible to Julia programmers as are Julia programmers. It is here that the bulk of Juliaโ€™s flexibility and power takes place. But with great power comes great responsibility and all that ... so in fact I do not do anything of what I did in this answer, unless you have a really good reason :)

To get back to the original problem, it might be best to create a parser to build a safe literal using Julia syntax to implement a parser for a subset of Julia, giving literals their usual meaning in a way that cannot be overloaded. This safe subset of syntax can include, for example, numeric literals, string literals, array literals, and Dict constructors. But it would probably be more practical to just use the JSON syntax and parse it using the Julia JSON package.

+12
source share

All Articles