2016-04-14 15 views
1

以下のマルコに関する質問があります。私はここに、呼び出し側モジュールに機能を注入ライン上のテストマクロマクロを使用した動的関数定義

def unquote(test_func)(), do: unquote(test_block) 

defmodule MathTest do 
    use Assertion 
    test "integers can be added and subtracted" do 
    assert 2 + 3 == 5 
    assert 5 - 5 == 10 
    end 
end 

ルック:

defmodule Assertion do 

    defmacro __using__(_options) do 
    quote do 
     import unquote(__MODULE__) 
     Module.register_attribute __MODULE__, :tests, accumulate: true 
     @before_compile unquote(__MODULE__) 
    end 
    end 

    defmacro __before_compile__(_env) do 
    IO.puts "before compile" 
    quote do 
     def run, do: Assertion.Test.run(@tests, __MODULE__)  
    end 
    end 

    defmacro test(description, do: test_block) do 
    test_func = String.to_atom(description) 
    quote do 
     @tests {unquote(test_func), unquote(description)} 
     def unquote(test_func)(), do: unquote(test_block) 
    end 
    end 

    defmacro assert({operator, _, [lhs, rhs]}) do 
     IO.puts "assert" 
    quote bind_quoted: [operator: operator, lhs: lhs, rhs: rhs] do 
     Assertion.Test.assert(operator, lhs, rhs) 
    end 
    end 
end 

defmodule Assertion.Test do 
    def run(tests, module) do        
    Enum.each tests, fn {test_func, description} -> 
     case apply(module, test_func, []) do 
     :ok    -> IO.write "." 
     {:fail, reason} -> IO.puts """ 

      =============================================== 
      FAILURE: #{description} 
      =============================================== 
      #{reason} 
      """ 
     end 
    end 
    end              

    def assert(:==, lhs, rhs) when lhs == rhs do 
    :ok 
    end 
    def assert(:==, lhs, rhs) do 
    {:fail, """ 
     Expected:  #{lhs} 
     to be equal to: #{rhs} 
     """ 
    } 
    end 

    def assert(:>, lhs, rhs) when lhs > rhs do 
    :ok 
    end 
    def assert(:>, lhs, rhs) do 
    {:fail, """ 
     Expected:   #{lhs} 
     to be greater than: #{rhs} 
     """ 
    } 
    end 
end 

と、次のモジュールのマクロを使用します。次のコードスニペットを考えてみましょうintegers can be added and subtractedという名前で、それが原子に変換される前に。

スペースを持つ関数にどのように名前を付けることができますか? def unquote(test_func)の後に()とは何ですか?

答えて

2

quoteunquoteは、抽象構文木(AST)を作成します。見てみると比較することができます

我々は名前が原子

として格納されている、つまり、関数の名前が{:foo, [context: Elixir], []}としてASTに格納されて見ることができます

iex(1)> quote do def foo(), do: :ok end 
{:def, [context: Elixir, import: Kernel], 
[{:foo, [context: Elixir], []}, [do: :ok]]} 

通常の関数定義名前はpreviouslとしてタプルの名前の一部とASTに格納され、ここでunquote

iex(2)> foovar=:'foo var'    
:"foo var" 
iex(3)> quote do def unquote(foovar)(), do: :ok end 
{:def, [context: Elixir, import: Kernel], 
[{:"foo var", [context: Elixir], []}, [do: :ok]]} 

を使用して関数の定義あなたはapply


()はオプションで組み込み関数を使用して機能のこれらの並べ替えを呼び出すことができます:"foo var"

yの定義された変数、。自分自身を含めて、一貫性のためにすべての関数定義と関数呼び出しでそれらを使う方が好きです。


注:私はErlangの構文についての方が好きなものの

一つは、関数名と原子が同じように見える、と彼らは一種のと同じであるということです。それはapply(Module, :foo, [])のためだけにModule.fooと言うことができます。エリクシール関数名では、変数のように見えますが、オプションのかっこと組み合わせると、コードを読みやすく理解しにくくなります。

関連する問題