bartblast
How to get the AST that would be generated by the given macro?
Suppose we’ve got the following module:
defmodule TestModule do
defmacro test_macro(param1, param2, param3) do
quote do
param1 + param2 + unqoute(param3)
end
end
end
How can I get the AST that would be generated by the given macro?
Let’s say that param1 = 1, param2 = 2, param3 = 3
Given those params I’d like to get the following AST:
{:+, [context: Elixir, import: Kernel],
[
{:+, [context: Elixir, import: Kernel],
[{:param1, [], Elixir}, {:param2, [], Elixir}]},
3
]}
I tried to solve this problem with Macro.expand/2 and Code.eval_quoted/3 but wasn’t able to.
Can anyone let me know how to do this or give some hints?
Marked As Solved
ityonemo
I did a little bit of investigation… When you do a defmacro, elixir will turn your macro into a function using the following convention:
defmacro foo(a) do...
becomes the function :"MACRO-foo"/2 under the hood. First argument is what becomes __CALLER__ which should be of type Macro.Env.t; second argument is a (or generally the macro arguments concatenated). This is how __CALLER__ gets into the macro system. Output is the ast (which is what you are looking for) So, in theory you could do a fair job of reproducing even nested macros, if you carefully keep track of ENV. But at that point you are basically rebuilding the elixir ast parser =P, and there are a lot of things that are deliberately kept as “private” aka reserved fields in ENV that have zero forward compatibility guarantees.
Also Liked
ityonemo
If you are doing an elixir to js transpiler, I recommend using the BEAM bytecode. You can obtain bytecode by doing :beam_disasm on the module binary.
hauleth
What you are looking for is Macro.expand_once/2 or Macro.expand/2 if you want to expand it fully. Just beware, that it is no safer than Code.eval_quoted/3, so do not use it on untrusted input.
ityonemo
the problem is that macros are HIGHLY contextual and the “middle” of the macro code (everything between the defmacro and the resulting quoted retval), may not be able to evaluated without having access to a bunch of internal information, if for example, functions like Module.get_attribute (see note “This function can only be used on modules that have not yet been compiled”) and then if you have a macro inside of a macro, you will need to track the rolling environment, and then there’s the magic __CONTEXT__ special form, which quite frankly I’m not entirely sure how it works under the hood. I guess that’s why you’re trying to obtain the AST information at the point of egress, but note that even the AST emitted by module ITSELF might have macros inside of it, so even if you obtain the AST in a late-interception fashion, you still need something that can “execute” macros.
I think this complexity might have been what killed ElixirScript – elixir_script v0.32.1, which I have a ton of respect for. If you decide to go with reading bytecode, which honestly is IMO way simpler than elixir AST, let me know, I am very much intereseted in resurrecting these elixir on the FE type projects. I am thinking about tinkering with a WASM-based bytecode interpreter, (not to be confused with lumen, which is a beam-to-wasm compiler).
Last Post!
ityonemo
yes, but do keep in mind that any macro that uses Module.get_attribute (and that is a common use case, esp. for using macros that aren’t “abuse of using”) will fall flat.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex









