Description
Discussed in #52
One of the pain points of :recon
output to Elixirists is that it logs trace events in erlang syntax. Our trace APIs are well-equipped to insert a custom logger, and the extensive effort in Matcha
to convert code through Elixir -> Erlang -> MS makes us ideally placed to support something like ex_to_erl
natively.
A solid abstraction could convert arbitrary MS back to erl, which we could use to convert erl to elixir——useful for Matcha.Spec
rendering purposes in general, let alone trace output.
Original Discussion
Originally posted by christhekeele September 7, 2022
An idea proposed in the Q&A portion of my Matcha presentation during ElixirConf US 2022:
Provide an API to convert valid match specifications source AST into an Elixir code representation.
This would be a VERY handy feature for Elixir devs to better understand existing match specs in their codebases, and even provide a path to migrating to Matcha (existing ms -> elixir code -> Matcha.spec).
I see this as an API living in Matcha.Source
, which is the module that concerns itself with (non-Matcha-wrapped, erlang-ready) match spec AST. Possibly with a signature like Matcha.Source.to_elixir_string(Matcha.Source.t) :: binary()
to mirror Macro.to_string/1
?
The implementation should mostly call out to Matcha.Rewrite
, which is the module that concerns itself with converting between elixir and match spec source AST. This would be the first instance of "decompiling" source AST back into elixir, but I don't think it would be too involved! A lot of the work in compilation is expanding Elixir code in the right contexts and handling contexts for valid function calls. Decompilation should be able to do something similar, without worrying about expanions:
- convert context function calls into bare local function call AST
- preserve guard calls–possibly translating erlangy ones into Elixir? or just prefacing with
:erlang.
calls? One virtue of Matcha is that it does not memorize elixir -> erlang guard translation, leveraging the compiler instead, so I'm reluctant to hard-code the reverse - convert all other function calls into
:erlang
calls - rewrite the rest of the spec into a clause AST representation delimited by
pattern when guard -> body
stuff
An alternative to this would be something like bringing in erl2ex that does a more professional job of it, though I struggle to to find a library like this that is well maintained. It also goes against our goals of being dependency-free, but we could always only conditionally compile these helper functions if such a dependency is manually specified in a project's own mix.exs
.
Worth noting that this generate Elixir representation would not handle more complicated expansions elegantly, like custom macros and in/2
operator expansions; this is less concerning considering that people trying to use this feature are likely not using match specs generated by such means.