Goal
マクロを再帰的に展開しながらコンソールへ出力する。
Dev-Environment
OS: Windows8.1
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.5
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.5
Wait a minute
マクロを再帰的に展開し、
iexのコンソールへ結果を表示していくプログラムを作ります。
iexのコンソールへ結果を表示していくプログラムを作ります。
Index
Macro to trace the AST
|> Tracing AST
|> Let’s run!
|> Extra
|> Tracing AST
|> Let’s run!
|> Extra
Tracing AST
一々、マクロをexpand_once/2で展開していくのは面倒ですね。
なので、ASTを渡すと再帰的にASTを展開して、
内容を出力していくプログラムを実装します。
内容を出力していくプログラムを実装します。
Example:
defmodule AstTracer do
defmacro print(ast) do
quote do
IO.puts "\nTrace start...\n"
IO.puts "[Unexpanded]"
AstTracer.expr_print(unquote(ast))
AstTracer.ast_print(unquote(ast))
IO.puts "================================\n"
AstTracer.trace_print(
Macro.expand_once(unquote(ast), __ENV__), unquote(ast), 1)
end
end
def trace_print(ast, before_ast, count) do
cond do
ast == before_ast or is_nil(ast) ->
IO.puts "...Trace end"
true ->
IO.puts "[Expand: #{count}]"
expr_print(ast)
ast_print(ast)
IO.puts "================================\n"
trace_print(Macro.expand_once(ast, __ENV__), ast, count + 1)
end
end
def expr_print(expression_ast) do
IO.puts "========== Expression =========="
IO.puts "#{Macro.to_string(expression_ast)}"
end
def ast_print(expression_ast) do
IO.puts "============= AST =============="
IO.inspect expression_ast
end
end
Let’s run!
実際に使ってみましょう!
iexを起動して、以下をrequireして下さい。
iex> require AstTracer
nil
まずは、1 + 2を試します。
Result:
iex> quoted = quote do: 1 + 2
{:+, [context: Elixir, import: Kernel], [1, 2]}
iex> AstTracer.print(quoted)
Trace start...
[Unexpanded]
========== Expression ==========
1 + 2
============= AST ==============
{:+, [context: Elixir, import: Kernel], [1, 2]}
================================
...Trace end
:ok
展開がないので微妙ですね。
2,3回展開するものでぱっと思いつくのは・・・
unlessを展開させましょう!
Result:
iex> quoted = quote do: unless(true, do: false, else: true)
{:unless, [context: Elixir, import: Kernel], [true, [do: false, else: true]]}
iex> AstTracer.print(quoted)
Trace start...
[Unexpanded]
========== Expression ==========
unless(true) do
false
else
true
end
============= AST ==============
{:unless, [context: Elixir, import: Kernel], [true, [do: false, else: true]]}
================================
[Expand: 1]
========== Expression ==========
if(true) do
true
else
false
end
============= AST ==============
{:if, [context: Kernel, import: Kernel], [true, [do: true, else: false]]}
================================
[Expand: 2]
========== Expression ==========
case(true) do
x when x in [false, nil] ->
false
_ ->
true
end
============= AST ==============
{:case, [optimize_boolean: true],
[true,
[do: [{:->, [],
[[{:when, [],
[{:x, [counter: 59], Kernel},
{:in, [context: Kernel, import: Kernel],
[{:x, [counter: 59], Kernel}, [false, nil]]}]}], false]},
{:->, [], [[{:_, [], Kernel}], true]}]]]}
================================
...Trace end
:ok
おぉ、一杯出力された(笑)
動作的には問題ないようですね。
動作的には問題ないようですね。
Extra
Phoenix-Frameworkのweb.exを展開させてみました。
Result:
iex> require PhoenixV1_0_0Sample.Web
nil
iex> require AstTracer
nil
iex> quoted = quote do: PhoenixV1_0_0Sample.Web.__using__(:router)
{{:., [],
[{:__aliases__, [alias: false], [:PhoenixV1_0_0Sample, :Web]}, :__using__]},
[], [:router]}
iex> AstTracer.print(quoted)
Trace start...
[Unexpanded]
========== Expression ==========
PhoenixV1_0_0Sample.Web.__using__(:router)
============= AST ==============
{{:., [],
[{:__aliases__, [alias: false], [:PhoenixV1_0_0Sample, :Web]}, :__using__]},
[], [:router]}
================================
[Expand: 1]
========== Expression ==========
use(Phoenix.Router)
============= AST ==============
{:use, [context: PhoenixV1_0_0Sample.Web, import: Kernel],
[{:__aliases__, [alias: false, counter: 54], [:Phoenix, :Router]}]}
================================
[Expand: 2]
========== Expression ==========
(
require(Phoenix.Router)
Phoenix.Router.__using__([])
)
============= AST ==============
{:__block__, [],
[{:require, [context: Kernel, counter: 55], [Phoenix.Router]},
{{:., [], [Phoenix.Router, :__using__]}, [], [[]]}]}
================================
...Trace end
:ok
Speaking to oneself
必要かどうかはともかく、「やってみたかった」この一言に尽きます。
(完全に自己満足の世界でした(笑))
(完全に自己満足の世界でした(笑))
少し見辛いですかね?