My question is not exclusively about Julia, but Julia is one language in which I see a possible solution.
What I want is to implement a natural syntax to select items from a list (actually atoms from a molecular system), using something like:
selection = select("index < 1000 and not water and (name C or name N)",atoms)
where atoms
is a vector of elements of type atom
, which might be something like:
struct Atom
name
index
element
coordinates
end
and, of course, we could define functions like iswater(atom)
to deal with with the not water
condition, for example.
I thought that I could simply parse such a statement “index < …” converting it to a Julia-code, with
something like
function convert(selection)
expr = ""
for keyword in split(selection)
if keyword == "index"
expr = expr*" atom.index "
elseif keyword == "and"
expr = expr*" && "
elseif keyword == "not"
expr = expr*" ! "
elseif keyword == "water"
expr = expr*" iswater(x) "
else
expr = expr*" $keyword "
end
end
return expr
end
such that
convert("index < 100 and not water")
returns
" atom.index < 100 && ! iswater(x) "
From that, I could use something like:
function select(atoms,selection)
expr = convert(selection)
selection = Vector{Atoms}(undef,0)
for atom in atoms
if eval(Meta.parse(expr))
append(selection,atom)
end
end
return selection
end
# Obs: actually this function does not work, because the
# interpolation of the atom variable on "eval" fails, but
# I need yet to understand that. But probably the idea of what
# I aim to obtain is clear.
So, my general question is: is this strategy (of generating meta-codes) a reasonable choice to tackle this problem? Writing an actual interpreter of the user-provided syntax seems too complicated.
The more specific question, if the approach is reasonable, is how to evaluate the generated expression for each element of an array, as it seems to work for a single element, but when inside a loop or function, the variable is no longer recognized:
julia> expr = :(x < 1);
julia> y = [ 0, 1 ];
julia> for x in y
eval(expr)
end
ERROR: UndefVarError: x not defined
(a third possibility is that someone knows of a package that does that already, I could not find one).