I’m still busy with a yecc parser, that means I’m in erlang, and I wonder how to produce elixir structures.
I know that maps like %{k: v, ...} are just elixir syntactic sugar on top of [{k, v} ...], but what about structs?
for example, I have this production, where I recognize a string as a domain, and the value I want to associate to a domain is a Ecto.Query.FromExpr. I only know how to return a string, while I wish to have %Ecto.Query.FromExpr{source: {$1, nil}}.
domain -> word : list_to_binary(extract_value('$1')).
extract_value({_Token, _Line, Value}) -> Value.
Maps are not syntactic sugar over lists, they are just plain maps, in Erlang #{}.
Structs are just maps with the :__struct__ key set to a module. From elixir side they are constructed to always have all specified keys set to their defaults.
All elixir modules, which includes structs, have an Elixir. prefix. You don’t see this when you are writing literal modules in Elixir, but you’ll need to specify it from Erlang.
I mean that you should file a bug at the ecto repository, asking for a API that can be used to create queries at runtime (function based) rather the current macro-based API which does construction only during compile time.
The API for query creation is exactly what we have today. You are thinking about using the internals but it is not necessary to fiddle with them all. Our query api supports both interpolation and dynamic values. For example, imagine you have to parse “foo:43” and convert it to a query. You can do this:
query = "table" # or Schema
[field, value] = String.split("foo:43", ":")
field = String.to_existing_atom(field)
query = from q in querry, field(q, ^field) == ^value
And so on. Ben mentioned the correct way to solve this in the forum here: How to produce executable code using yecc? - In a nutshell, you shouldn’t build the query inside the parser because the parser goal is to return an AST. Then, in Elixir code, you traverse the AST, building the Ecto query.
For example, for “foo:43”, your parser should return something like:
{:where, :foo, "43"}
Then you can traverse this AST and convert it into an Ecto query.
As a good exercise, I would suggest for you to implement a calculator using yecc. Do not have yecc perform the computations. Instead you should have yecc return an AST (so 1 + 2 returns {:+, 1, 2}) and then you traverse this AST in Elixir computing the final result.