can I get Elixir to execute quoted code matched to a variable?

144 views Asked by At

maybe it’s not the way to go, but this was my initial guess and I'm open to hints and corrections.

I have been writing a parser for a reduced query dialect that I have partially inherited and much expanded, allowing users write things like:

plant where accession.code='2018.0047'

it’s not ready, but the missing intermediate steps are clear, except the final one: how do I have the result executed?

I am targeting as result the quote representation of the equivalent Ecto.Query.from query. for the above example the equivalent as far as I am concerned would be:

from(p in "plant", 
  select: [:id], 
  join: a in "accession", 
  on: a.id==p.accession_id, 
  where: a.code=="2018.0047")

I have been looking into the structures returned by the __schema__ functions, and all looks quite doable, I mean I know how to extract the table name from the modules, and owner and related modules and keys from the association given its name, so let’s assume that my parser does return this value:

{:from, [context: Elixir, import: Ecto.Query],
 [
   {:in, [context: Elixir, import: Kernel], [{:p, [], Elixir}, "plant"]},
   [
     select: [:id],
     join: {:in, [context: Elixir, import: Kernel],
      [{:a, [], Elixir}, "accession"]},
     on: {:==, [context: Elixir, import: Kernel],
      [
        {{:., [], [{:a, [], Elixir}, :id]}, [], []},
        {{:., [], [{:p, [], Elixir}, :accession_id]}, [], []}
      ]},
     where: {:==, [context: Elixir, import: Kernel],
      [{{:., [], [{:a, [], Elixir}, :code]}, [], []}, "2018.0047"]}
   ]
 ]}

how do I get Ecto to execute it?

or what's the best way to produce Elixir code from a yecc parser?

1

There are 1 answers

0
mariotomo On

short answer: no, we can't get Elixir to execute "quoted" code.

if I correctly understand the comments received on the elixir forum, the quoted format is only meant for macros, that is, for things to be defined at compile time.

back to the example:

from(p in "plant", 
  select: [:id], 
  join: a in "accession", 
  on: a.id==p.accession_id, 
  where: a.code=="2018.0047")

the result of this is a value, a Ecto.Query structure, and although it's not immediate to see which are the fields that are being defined, having a look at the sources, I figured out I can produce the same value step by step, like this:

q = %Ecto.Query{}
q = %{ q | from: %Ecto.Query.FromExpr{source: {"plant", nil}}}
q = %{ q | select: %Ecto.Query.SelectExpr{
  expr: [{{:., [], [{:&, [], [0]}, :id]}, [], []}]}}
q = %{ q | joins: [
  %Ecto.Query.JoinExpr{
    source: {"accession", nil}, 
    qual: :inner,
    on: %Ecto.Query.QueryExpr{
      expr: {:==, [], [
        {{:., [], [{:&, [], [0]}, :accession_id]}, [], []},
        {{:., [], [{:&, [], [1]}, :id]}, [], []}
      ]}}}]}
q = %{ q | wheres: [
  %Ecto.Query.BooleanExpr{
    op: :and, 
    expr: {:==, [], [{{:., [], [{:&, [], [1]}, :code]}, [], []}, "2018.0047"]}}]}

I know that it does not look any easier, but I can use this in my grammar productions.