Skip to content

Instantly share code, notes, and snippets.

@alco
Last active May 8, 2019 22:21
Show Gist options
  • Save alco/6449190 to your computer and use it in GitHub Desktop.
Save alco/6449190 to your computer and use it in GitHub Desktop.
iex> """ |> Erlang.module_to_core |> IO.puts
...> -file("nofile", 1).
...> -module('Elixir.M').
...>
...> -export([hello/0]).
...>
...> hello() -> hello.
...> """
module 'Elixir.M' ['hello'/0,
'module_info'/0,
'module_info'/1]
attributes []
'hello'/0 =
%% Line 6
fun () ->
'hello'
'module_info'/0 =
fun () ->
call 'erlang':'get_module_info'
('Elixir.M')
'module_info'/1 =
fun (_cor0) ->
call 'erlang':'get_module_info'
('Elixir.M', _cor0)
end
:ok
defmodule Erlang do
@doc """
Receives a module definition (a string or a quoted expression), compiles it,
and returns a string with pretty printed Erlang code.
"""
def module_to_code(string) when is_binary(string) do
to_code_dispatch(Code.eval_string(string))
end
def module_to_code(q) do
to_code_dispatch(Code.eval_quoted(q))
end
@doc """
Receives an expression (string or quote) and produces pretty-printed Erlang
code out of it.
"""
def expr_to_code(string) when is_binary(string) do
code = String.to_char_list!(string)
forms = :elixir_translator.forms!(code, 1, "nofile", [])
pretty_print_forms(forms)
end
def expr_to_code(q) do
pretty_print_forms([q])
end
@doc """
Compiles given Erlang module definition to Core Erlang.
"""
def module_to_core(string) when is_binary(string) do
{ :ok, tokens, _ } = :erl_scan.string(String.to_char_list!(string))
{ :ok, module, core }
= tokens
|> Enum.chunks_by(&match?({:dot, _}, &1))
|> Stream.filter(&not match?([{:dot, _}], &1))
|> Enum.map(fn tf ->
{ :ok, form } = :erl_parse.parse_form(tf ++ [{:dot,-1}])
form
end)
|> :compile.forms(:to_core)
String.from_char_list!(:core_pp.format(core))
end
### Private helpers
defp to_code_dispatch(code) do
case code do
{ {:module,_,beam_code,_}, _ } ->
pretty_print_beam(beam_code)
end
end
defp pretty_print_beam(beam_code) do
{:ok, {_, [{:abstract_code, {_, code}}]}}
= :beam_lib.chunks(beam_code, [:abstract_code])
String.from_char_list!(:erl_prettypr.format(:erl_syntax.form_list(code)))
end
defp pretty_print_forms(forms) do
scope = :elixir.scope_for_eval(file: "nofile")
{ exprs, _ } = :elixir_translator.translate(forms, scope)
String.from_char_list!(:erl_prettypr.format(:erl_syntax.form_list(exprs)))
end
end
iex> Erlang.expr_to_code("File.Stat[]") |> IO.puts
{'Elixir.File.Stat', undefined, undefined, undefined,
undefined, undefined, undefined, undefined, undefined,
undefined, undefined, undefined, undefined, undefined}
:ok
iex> Erlang.module_to_code(quote do
...> defmodule MM do
...> def hello, do: :hello
...> end
...> end) |> IO.puts
-compile({no_auto_import,
[{bitsize, 1}, {apply, 2}, {spawn, 2}, {spawn_link, 2},
{spawn_monitor, 3}, {spawn_opt, 2}, {spawn_opt, 3},
{spawn, 4}, {spawn_link, 4}, {spawn_opt, 4},
{spawn_opt, 5}, {nodes, 0}, {disconnect_node, 1},
{integer_to_list, 2}, {integer_to_binary, 2}, {max, 2},
{min, 2}, {port_control, 3}, {port_connect, 2},
{port_command, 2}, {port_command, 3}, {port_close, 1},
{spawn_monitor, 1}, {spawn, 1}, {load_module, 2},
{spawn_link, 1}, {binary_to_float, 1},
{float_to_binary, 2}, {float_to_binary, 1},
{list_to_integer, 2}, {integer_to_binary, 1},
{binary_to_integer, 2}, {binary_to_integer, 1},
{check_old_code, 1}, {binary_part, 3}, {binary_part, 2},
{binary_to_term, 2}, {binary_to_existing_atom, 2},
{binary_to_atom, 2}, {atom_to_binary, 2},
{bitstring_to_list, 1}, {list_to_bitstring, 1},
{bit_size, 1}, {byte_size, 1}, {tuple_size, 1},
{is_bitstring, 1}, {list_to_existing_atom, 1},
{iolist_to_binary, 1}, {iolist_size, 1},
{is_boolean, 1}, {is_record, 3}, {is_record, 2},
{is_function, 2}, {is_function, 1}, {is_binary, 1},
{is_reference, 1}, {is_port, 1}, {is_pid, 1},
{is_number, 1}, {is_integer, 1}, {is_float, 1},
{is_tuple, 1}, {is_list, 1}, {is_atom, 1}, {error, 2},
{error, 1}, {is_process_alive, 1}, {demonitor, 2},
{demonitor, 1}, {monitor, 2}, {whereis, 1},
{unregister, 1}, {unlink, 1}, {tuple_to_list, 1},
{trunc, 1}, {tl, 1}, {time, 0}, {throw, 1},
{term_to_binary, 2}, {term_to_binary, 1},
{statistics, 1}, {split_binary, 2}, {spawn_link, 3},
{spawn, 3}, {size, 1}, {setelement, 3}, {self, 0},
{round, 1}, {registered, 0}, {register, 2}, {put, 2},
{purge_module, 1}, {processes, 0}, {process_info, 2},
{process_info, 1}, {process_flag, 3}, {process_flag, 2},
{pre_loaded, 0}, {pid_to_list, 1}, {open_port, 2},
{now, 0}, {nodes, 1}, {node, 0}, {node, 1},
{monitor_node, 2}, {module_loaded, 1}, {make_ref, 0},
{list_to_tuple, 1}, {list_to_pid, 1},
{list_to_integer, 1}, {list_to_float, 1},
{list_to_binary, 1}, {list_to_atom, 1}, {link, 1},
{length, 1}, {is_alive, 0}, {integer_to_list, 1},
{hd, 1}, {halt, 2}, {halt, 1}, {halt, 0},
{group_leader, 2}, {group_leader, 0}, {get_keys, 1},
{get, 1}, {get, 0}, {garbage_collect, 1},
{garbage_collect, 0}, {float_to_list, 2},
{float_to_list, 1}, {float, 1}, {exit, 2}, {exit, 1},
{erase, 1}, {erase, 0}, {element, 2},
{delete_module, 1}, {date, 0}, {check_process_code, 2},
{binary_to_term, 1}, {binary_to_list, 3},
{binary_to_list, 1}, {atom_to_list, 1}, {apply, 3},
{abs, 1}]}).
-file("nofile", 1).
-module('Elixir.MM').
-export(['__info__'/1, hello/0]).
'__info__'(functions) -> [{hello, 0}];
'__info__'(macros) -> [];
'__info__'(docs) -> [{{hello, 0}, 1, def, [], nil}];
'__info__'(moduledoc) -> {1, nil};
'__info__'(module) -> 'Elixir.MM';
'__info__'(atom) -> module_info(atom).
hello() -> hello.
:ok
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment