Quote and Unquote Questions

Hi,

I have some question related to quote and unquote to generate AST.

Let’s assume this code as a references.

quote do: sum(1, 2, 3)  # {:sum, [], [1, 2, 3]}
:sum         #=> Atoms
1.0          #=> Numbers
[1, 2]       #=> Lists
"strings"    #=> Strings
{key, value} #=> Tuples with two elements

and

iex(1)> quote do
…(1)> defmodule A do
…(1)> def a do
…(1)> a
…(1)> end
…(1)> end
…(1)> end
{:defmodule, [context: Elixir, import: Kernel],
[{:aliases, [alias: false], [:A]},
[do: {:def, [context: Elixir, import: Kernel],
[{:a, [context: Elixir], Elixir}, [do: {:a, [], Elixir}]]}]]}

My question is if I have File called A.ex like following -

#a.ex    
defmodule A do
    def a do
    a
    end
end

How would I read this file and gets a result of quoting that whole file.
I have tried

text = File.read("a.ex")
#1
 quote do: text
#2 
 quote do: unquote(text)

Both of the methods didn’t work.
The first one returns {:text, [], My}
and the second one return “defmodule Test do\n def a do\n a\n end\nend\n”
Whereas it should return this instead

 {:defmodule, [context: Elixir, import: Kernel],
  [{:__aliases__, [alias: false], [:A]},
   [do: {:def, [context: Elixir, import: Kernel],
   [{:a, [context: Elixir], Elixir}, [do: {:a, [], Elixir}]]}]]}

Thank you for your help
Dev.

Code.string_to_quoted File.read("a.ex")

7 Likes

Thank you very much @josevalim for your help.

Code.string_to_quoted File.read!("a.ex") 
#File.read! #with the bang! works

While trying things out I am also seeing metadata other than :line, such as :context, :import also.
Could you recommend where to study more about this I would like to know about other metadata.

I am looking into :elixir_tokenizer’s erlang module and elixir.hrl. I think this could be the starting point where I see everything.

I am researching in elixir AST. I want to build something similar to wrangler refactoring in erlang. but this for elixir. for my master degree thesis.

Thank you very much
Dev.

I did found out about other meta data here -

-record(elixir_scope, {
context=nil, %% can be match, guards or nil
extra=nil, %% extra information about the context, like pin_guard and map_key
super=false, %% when true, it means super was invoked
caller=false, %% when true, it means caller was invoked
module=nil, %% the current module
function=nil, %% the current function
vars=#{}, %% a map of defined variables and their alias
backup_vars=nil, %% a copy of vars to be used on ^var
match_vars=nil, %% a set of all variables defined in a particular match
export_vars=nil, %% a dict of all variables defined in a particular clause
extra_guards=nil, %% extra guards from args expansion
counter=#{}, %% a map counting the variables defined
file=(<<“nofile”>>) %% the current scope filename
}).

I think I am in right direction here.

1 Like

They are used by different places in the compiler.

:context is the module that generated the quoted expression. :import is used to annotate functions that were imported from another module. For example:

iex(1)> quote do
...(1)> is_atom true
...(1)> end
{:is_atom, [context: Elixir, import: Kernel], [true]}

is_atom was generated at the root context (Elixir) and it was imported from Kernel.

:keep is used to hold the original line numbers in case you want to keep the original line number information and so on.

1 Like

Thank you so much @josevalim

Dev