檢視原始碼 巨集 (Elixir v1.16.2)

用於處理 AST 和實作巨集的函式。

巨集是在編譯時接收 Elixir 的 AST 作為輸入,並傳回 Elixir 的 AST 作為輸出的建構。

這個模組中的許多函式存在,就是為了處理 Elixir AST,以遍歷、查詢和轉換它。

我們來看一個簡單的範例,說明函式和巨集之間的差異

defmodule Example do
  defmacro macro_inspect(value) do
    IO.inspect(value)
    value
  end

  def fun_inspect(value) do
    IO.inspect(value)
    value
  end
end

現在我們來試試看

import Example

macro_inspect(1)
#=> 1
#=> 1

fun_inspect(1)
#=> 1
#=> 1

到目前為止,它們的行為相同,因為我們傳遞一個整數作為引數。但我們來看一下當我們傳遞一個表達式時會發生什麼事

macro_inspect(1 + 2)
#=> {:+, [line: 3], [1, 2]}
#=> 3

fun_inspect(1 + 2)
#=> 3
#=> 3

巨集接收作為引數傳遞的程式碼表示,而函式接收作為引數傳遞的程式碼結果。巨集必須傳回程式碼表示的超集。請參閱 input/0output/0 以取得更多資訊。

若要深入了解 Elixir 的 AST 以及如何以程式方式建構它們,請參閱 quote/2

評估程式碼

這個模組中的函式不會評估程式碼。事實上,從巨集中評估程式碼通常是一種反模式。對於程式碼評估,請參閱 Code 模組。

摘要

類型

以 &Mod.fun/arity 格式擷取的遠端函式

巨集的輸入

AST 元資料的關鍵字清單。

巨集的輸出

t()

抽象語法樹 (AST)

函數

將給定的字串轉換為 CamelCase 格式。

根據可能的 AST 位置對 atom 進行分類。

caller 中編譯時套用 modfunctionargs

將區域或遠端呼叫分解為其遠端部分(如果提供)、函數名稱和參數清單。

遞迴轉譯值,以便可以將其插入語法樹中。

接收 AST 節點並擴充它,直到無法再擴充為止。

使用給定的 env 擴充 ast 中的所有文字。

使用給定的 accfun 擴充 ast 中的所有文字。

接收 AST 節點並擴充一次。

使用 Macro.var/2 為給定數量的必要參數變數產生 AST 節點。

使用 Macro.unique_var/2 為給定數量的必要參數變數產生 AST 節點。

根據不同的來源格式檢查 atom

如果給定的名稱和元數是運算子,則傳回 true

傳回 ast 中節點的路徑,fun 為其傳回真值。

expr 導向給定 positioncall_args

此函數的行為類似於 prewalk/2,但對引號表達式執行深度優先、後序遍歷。

此函數的行為類似於 prewalk/3,但使用累加器對引號表達式執行深度優先、後序遍歷。

傳回一個可列舉的物件,以深度優先、後序遍歷方式遍歷 ast

以深度優先、前序遍歷方式遍歷引號表達式。

以深度優先、前序遍歷方式遍歷引號表達式,並使用累加器。

傳回一個可列舉的物件,以深度優先、前序遍歷方式遍歷 ast

如果給定的引號表達式表示引號文字,則傳回 true

如果給定的名稱和元數是特殊形式,則傳回 true

在給定的 env 中擴充 module 給出的結構。

將給定的表達式 AST 轉換為字串。

將給定的表達式 AST 轉換為字串。

以深度優先方式遍歷引號表達式,並使用累加器。

將給定的參數轉換為使用底線斜線格式的字串。

取消轉義字串中的字元。

根據給定的對應取消轉義字串中的字元。

產生一個 AST 節點,表示由原子 varcontext 給出的唯一變數。

將管線表達式分解成一個清單。

如果節點元資料包含一個值,則對該值套用給定的函式。

驗證給定的表達式是否為有效的引號表達式。

產生一個 AST 節點,表示由原子 varcontext 給出的變數。

類型

連結到此類型

captured_remote_function()

檢視原始碼
@type captured_remote_function() :: (... -> any())

以 &Mod.fun/arity 格式擷取的遠端函式

@type input() ::
  input_expr() | {input(), input()} | [input()] | atom() | number() | binary()

巨集的輸入

@type metadata() :: keyword()

AST 元資料的關鍵字清單。

Elixir AST 中的元資料是值的關鍵字清單。任何鍵都可以使用,而編譯器的不同部分可能會使用不同的鍵。例如,巨集所接收的 AST 將永遠包含 :line 註解,而 quote/2 所發出的 AST 只有在提供 :line 選項時才會具有 :line 註解。

下列元資料鍵是公開的

  • :context - 定義產生 AST 的內容。例如,quote/2 會將呼叫 quote/2 的模組包含為內容。這通常用於區分一般程式碼和巨集或 quote/2 產生的程式碼。

  • :counter - 用於變數衛生的變數計數器。在編譯器中,每個變數都透過 namemetadata[:counter]namecontext 的組合來識別。

  • :from_brackets - 用於判斷對 Access.get/3 的呼叫是否來自方括號語法。

  • :from_interpolation - 用於判斷對 Kernel.to_string/1 的呼叫是否來自內插。

  • :generated - 程式碼是否應視為由編譯器產生。這表示編譯器和 Dialyzer 等工具可能不會發出特定警告。

  • :if_undefined - 如何展開未定義的變數。如果您希望變數變成沒有警告的空元呼叫,請將其設定為 :apply,或 :raise

  • :keep - quote/2 使用選項 location: :keep 來註解引述來源的檔案和行號。

  • :line - AST 節點的行號。請注意,引述程式碼會捨棄行資訊,但可透過 :line 選項重新啟用。

以下的元資料金鑰由 Code.string_to_quoted/2 啟用

  • :closing - 包含有關關閉配對的元資料,例如元組或映射中的 },或帶有括號的函式呼叫中的關閉 )(當 :token_metadata 為 true 時)。如果函式呼叫附加了 do-end 區塊,其元資料會出現在 :do:end 元資料中

  • :column - AST 節點的欄位號碼(當 :columns 為 true 時)。請注意,引述程式碼會始終捨棄欄位資訊。

  • :delimiter - 包含符號、字串和字元清單的開頭分隔符號,為字串(例如 "{""/""'" 等)

  • :format - 當原子定義為關鍵字時,設定為 :keyword

  • :do - 包含函數呼叫中 do 位置的元資料,並帶有 do-end 區塊(當 :token_metadata 為 true 時)

  • :end - 包含函數呼叫中 end 位置的元資料,並帶有 do-end 區塊(當 :token_metadata 為 true 時)

  • :end_of_expression - 表示表達式實際發生結束的時間(當 :token_metadata 為 true 時)。這僅適用於 __block__ 的直接子節點,且為換行符號或 ; 字元的所在位置。 __block__ 的最後一個表達式沒有這個元資料。

  • :indentation - 符號 heredoc 的縮排

下列元資料金鑰為私人

  • :alias - 用於別名衛生。
  • :ambiguous_op - 用於編譯器中改善的錯誤訊息。
  • :imports - 用於匯入衛生。
  • :var - 用於未定義變數的改善錯誤訊息。

不要依賴它們,因為它們可能會在語言的未來版本中變更或完全移除。它們通常由 quote/2 和編譯器使用,以提供衛生、更好的錯誤訊息等功能。

如果您在 AST 元資料中引入自訂金鑰,請務必在金鑰前面加上您的程式庫或應用程式的名稱,這樣它們才不會與編譯器未來可能引入的金鑰衝突。

@type output() ::
  output_expr()
  | {output(), output()}
  | [output()]
  | atom()
  | number()
  | binary()
  | captured_remote_function()
  | pid()

巨集的輸出

@type t() :: input()

抽象語法樹 (AST)

函數

@spec camelize(String.t()) :: String.t()

將給定的字串轉換為 CamelCase 格式。

此函式設計用於將語言識別碼/代碼轉換為駝峰式大小寫,因此它屬於 Macro 模組。請勿將它用作將字串轉換為駝峰式大小寫的一般機制,因為它不支援 Unicode 或 Elixir 識別碼中無效的字元。

範例

iex> Macro.camelize("foo_bar")
"FooBar"

iex> Macro.camelize("foo/bar")
"Foo.Bar"

如果存在大寫字元,它們不會以任何方式修改,作為保留縮寫的機制

iex> Macro.camelize("API.V1")
"API.V1"
iex> Macro.camelize("API_SPEC")
"API_SPEC"
連結到此函數

classify_atom(atom)

檢視原始碼 (自 1.14.0 起)
@spec classify_atom(atom()) :: :alias | :identifier | :quoted | :unquoted

根據可能的 AST 位置對 atom 進行分類。

它會傳回下列原子之一

  • :alias - 原子表示別名

  • :identifier - 原子可用作變數或區域函式呼叫 (以及作為未引用的原子)

  • :unquoted - 原子可用於其未引用的形式,包括運算子及包含 @ 的原子

  • :quoted - 所有其他原子,只能用於其引用的形式

大多數運算子將會是 :unquoted,例如 :+,但有些例外會傳回 :quoted,因為有歧義,例如 :"::"。使用 operator?/2 來檢查給定的原子是否為運算子。

範例

iex> Macro.classify_atom(:foo)
:identifier
iex> Macro.classify_atom(Foo)
:alias
iex> Macro.classify_atom(:foo@bar)
:unquoted
iex> Macro.classify_atom(:+)
:unquoted
iex> Macro.classify_atom(:Foo)
:unquoted
iex> Macro.classify_atom(:"with spaces")
:quoted
連結到此函數

compile_apply(mod, fun, args, caller)

檢視原始碼 (自 1.16.0 起)

caller 中編譯時套用 modfunctionargs

當您要在編譯時以程式方式呼叫巨集時,會使用這個。

連結到此函數

dbg(code, options, env)

檢視原始碼 (自 1.14.0 起)
@spec dbg(t(), t(), Macro.Env.t()) :: t()

Kernel.dbg/2 的預設後端。

此函式提供 Kernel.dbg/2 的預設後端。請參閱 Kernel.dbg/2 文件以取得更多資訊。

此函式

  • 列印關於給定 env 的資訊
  • 列印關於 code 及其傳回值 (使用 opts 來檢查項目) 的資訊
  • 傳回評估 code 所傳回的值

您可以直接呼叫此函式來建立回歸到此函式的 Kernel.dbg/2 後端。

此函式會在給定的 env 的內容為 :match:guard 時引發錯誤。

@spec decompose_call(t()) :: {atom(), [t()]} | {t(), atom(), [t()]} | :error

將區域或遠端呼叫分解為其遠端部分(如果提供)、函數名稱和參數清單。

在提供無效的呼叫語法時,會傳回 :error

範例

iex> Macro.decompose_call(quote(do: foo))
{:foo, []}

iex> Macro.decompose_call(quote(do: foo()))
{:foo, []}

iex> Macro.decompose_call(quote(do: foo(1, 2, 3)))
{:foo, [1, 2, 3]}

iex> Macro.decompose_call(quote(do: Elixir.M.foo(1, 2, 3)))
{{:__aliases__, [], [:Elixir, :M]}, :foo, [1, 2, 3]}

iex> Macro.decompose_call(quote(do: 42))
:error

iex> Macro.decompose_call(quote(do: {:foo, [], []}))
:error
@spec escape(
  term(),
  keyword()
) :: t()

遞迴轉譯值,以便可以將其插入語法樹中。

範例

iex> Macro.escape(:foo)
:foo

iex> Macro.escape({:a, :b, :c})
{:{}, [], [:a, :b, :c]}

iex> Macro.escape({:unquote, [], [1]}, unquote: true)
1

選項

  • :unquote - 為 true 時,此函式會保留 unquote/1unquote_splicing/1 陳述式未經過 escape,並在 escape 時有效地取消內容的引用。此選項僅在 escape 可能包含引號片段的 AST 時才有用。預設為 false。

  • :prune_metadata - 為 true 時,會移除已 escape 的 AST 節點的元資料。請注意,此選項會變更已 escape 的程式碼的語意,且僅應在 escape AST 時使用。預設為 false。

    舉例來說,ExUnit 會儲存每個斷言的 AST,因此當斷言失敗時,我們可以向使用者顯示程式碼片段。沒有此選項時,每次編譯測試模組時,我們會取得模組位元組碼不同的 MD5,因為 AST 包含特定於編譯環境的元資料,例如計數器。透過修剪元資料,我們可以確保模組是確定性的,並減少 ExUnit 需要保留的資料量。僅保留最少的元資料,例如 :line:no_parens

quote/2 的比較

escape/2 函式有時會與 quote/2 混淆,因為上述範例在兩個函式下行為相同。關鍵差異最能從將要 escape 的值儲存在變數中時看出。

iex> Macro.escape({:a, :b, :c})
{:{}, [], [:a, :b, :c]}
iex> quote do: {:a, :b, :c}
{:{}, [], [:a, :b, :c]}

iex> value = {:a, :b, :c}
iex> Macro.escape(value)
{:{}, [], [:a, :b, :c]}

iex> quote do: value
{:value, [], __MODULE__}

iex> value = {:a, :b, :c}
iex> quote do: unquote(value)
{:a, :b, :c}

escape/2 用於 escape (直接傳遞或變數繫結),而 quote/2 會產生表達式的語法樹。

@spec expand(input(), Macro.Env.t()) :: output()

接收 AST 節點並擴充它,直到無法再擴充為止。

請注意,此函式不會遍歷 AST,只會展開根節點。

此函式在幕後使用 expand_once/2。請查看以取得更多資訊和範例。

連結到此函數

expand_literals(ast, env)

檢視原始碼 (自 1.14.1 起)
@spec expand_literals(input(), Macro.Env.t()) :: output()

使用給定的 env 擴充 ast 中的所有文字。

此函式主要用於移除 AST 節點的編譯時期相依性。在這種情況下,給定的環境通常會被操作,以表示一個函式

Macro.expand_literals(ast, %{env | function: {:my_code, 1}})

目前,AST 中唯一可擴充的文字節點是別名,因此此函式只會擴充別名(而且會在文字中的任何位置擴充)。

不過,在移除模組之間的編譯時期相依性時要小心。如果移除它們,但仍會在編譯時期呼叫模組,Elixir 將無法在模組變更時正確重新編譯模組。

連結到此函數

expand_literals(ast, acc, fun)

檢視原始碼 (自 1.14.1 起)
@spec expand_literals(t(), acc, (t(), acc -> {t(), acc})) :: t() when acc: term()

使用給定的 accfun 擴充 ast 中的所有文字。

fun 會使用可擴充的 AST 節點和 acc 呼叫,且必須傳回一個包含 acc 的新節點。這是 expand_literals/2 的一般版本,它支援自訂擴充函式。請查看 expand_literals/2 的使用案例和陷阱。

@spec expand_once(input(), Macro.Env.t()) :: output()

接收 AST 節點並擴充一次。

會擴充以下內容

如果無法擴充表達式,它會傳回表達式本身。此函式不會遍歷 AST,只會擴充根節點。

expand_once/2 只會執行一次擴充。請查看 expand/2 以執行擴充,直到無法再擴充節點為止。

範例

在以下範例中,我們有一個巨集,它會產生一個模組,其中有一個名為 name_length 的函式,會傳回模組名稱的長度。此函式的值會在編譯時計算,而非在執行時。

考慮以下實作

defmacro defmodule_with_length(name, do: block) do
  length = length(Atom.to_charlist(name))

  quote do
    defmodule unquote(name) do
      def name_length, do: unquote(length)
      unquote(block)
    end
  end
end

當像這樣呼叫時

defmodule_with_length My.Module do
  def other_function, do: ...
end

編譯會失敗,因為 My.Module 在引用時不是一個原子,而是一個語法樹,如下所示

{:__aliases__, [], [:My, :Module]}

也就是說,我們需要將上述別名的節點擴充為一個原子,才能擷取其長度。擴充節點並不容易,因為我們也需要擴充呼叫者的別名。例如

alias MyHelpers, as: My

defmodule_with_length My.Module do
  def other_function, do: ...
end

最後的模組名稱會是 MyHelpers.Module,而不是 My.Module。使用 Macro.expand/2 時,會考慮到這些別名。本機和遠端巨集也會被擴充。我們可以將上述巨集改寫為使用此函式,如下所示

defmacro defmodule_with_length(name, do: block) do
  expanded = Macro.expand(name, __CALLER__)
  length = length(Atom.to_charlist(expanded))

  quote do
    defmodule unquote(name) do
      def name_length, do: unquote(length)
      unquote(block)
    end
  end
end
連結到此函數

generate_arguments(amount, context)

檢視原始碼 (自 1.5.0 起)
@spec generate_arguments(0, context :: atom()) :: []
@spec generate_arguments(pos_integer(), context) :: [{atom(), [], context}, ...]
when context: atom()

使用 Macro.var/2 為給定數量的必要參數變數產生 AST 節點。

請注意,引數並非唯一的。如果你稍後想要存取相同的變數,你可以使用相同的輸入呼叫此函式。使用 generate_unique_arguments/2 來產生無法覆寫的唯一引數。

範例

iex> Macro.generate_arguments(2, __MODULE__)
[{:arg1, [], __MODULE__}, {:arg2, [], __MODULE__}]
連結到此函數

generate_unique_arguments(amount, context)

檢視原始碼 (自 1.11.3 起)
@spec generate_unique_arguments(0, context :: atom()) :: []
@spec generate_unique_arguments(pos_integer(), context) :: [
  {atom(), [{:counter, integer()}], context},
  ...
]
when context: atom()

使用 Macro.unique_var/2 為給定數量的必要參數變數產生 AST 節點。

範例

iex> [var1, var2] = Macro.generate_unique_arguments(2, __MODULE__)
iex> {:arg1, [counter: c1], __MODULE__} = var1
iex> {:arg2, [counter: c2], __MODULE__} = var2
iex> is_integer(c1) and is_integer(c2)
true
連結到此函數

inspect_atom(source_format, atom)

檢視原始碼 (自 1.14.0 起)
@spec inspect_atom(:literal | :key | :remote_call, atom()) :: binary()

根據不同的來源格式檢查 atom

可以根據原子在 AST 中出現的三種不同格式來檢查原子:文字 (:literal)、金鑰 (:key) 或遠端呼叫的函式名稱 (:remote_call)。

範例

作為文字

文字包括一般原子、引用原子、運算子、別名,以及特殊原子 niltruefalse

iex> Macro.inspect_atom(:literal, nil)
"nil"
iex> Macro.inspect_atom(:literal, :foo)
":foo"
iex> Macro.inspect_atom(:literal, :<>)
":<>"
iex> Macro.inspect_atom(:literal, :Foo)
":Foo"
iex> Macro.inspect_atom(:literal, Foo.Bar)
"Foo.Bar"
iex> Macro.inspect_atom(:literal, :"with spaces")
":\"with spaces\""

做為一個鍵

檢查一個原子做為一個關鍵字清單或一個映射的鍵。

iex> Macro.inspect_atom(:key, :foo)
"foo:"
iex> Macro.inspect_atom(:key, :<>)
"<>:"
iex> Macro.inspect_atom(:key, :Foo)
"Foo:"
iex> Macro.inspect_atom(:key, :"with spaces")
"\"with spaces\":"

做為一個遠程呼叫

檢查一個原子做為一個遠程呼叫的函數名稱。

iex> Macro.inspect_atom(:remote_call, :foo)
"foo"
iex> Macro.inspect_atom(:remote_call, :<>)
"<>"
iex> Macro.inspect_atom(:remote_call, :Foo)
"\"Foo\""
iex> Macro.inspect_atom(:remote_call, :"with spaces")
"\"with spaces\""
連結到此函數

operator?(name, arity)

檢視原始碼 (自 1.7.0 起)
@spec operator?(name :: atom(), arity()) :: boolean()

如果給定的名稱和元數是運算子,則傳回 true

範例

iex> Macro.operator?(:not_an_operator, 3)
false
iex> Macro.operator?(:.., 0)
true
iex> Macro.operator?(:+, 1)
true
iex> Macro.operator?(:++, 2)
true
iex> Macro.operator?(:..//, 3)
true
連結到此函數

path(ast, fun)

檢視原始碼 (自 1.14.0 起)
@spec path(t(), (t() -> as_boolean(term()))) :: [t()] | nil

傳回 ast 中節點的路徑,fun 為其傳回真值。

路徑是一個清單,從 fun 回傳真值節點開始,接著是它的所有父節點。

如果 fun 只回傳假值,則回傳 nil

當你想要在 AST 中找到一個特定節點及其上下文,然後對它做某種斷言時,計算路徑可能是一個有效率的操作。

範例

iex> Macro.path(quote(do: [1, 2, 3]), & &1 == 3)
[3, [1, 2, 3]]

iex> Macro.path(quote(do: [1, 2]), & &1 == 5)
nil

iex> Macro.path(quote(do: Foo.bar(3)), & &1 == 3)
[3, quote(do: Foo.bar(3))]

iex> Macro.path(quote(do: %{foo: [bar: :baz]}), & &1 == :baz)
[
  :baz,
  {:bar, :baz},
  [bar: :baz],
  {:foo, [bar: :baz]},
  {:%{}, [], [foo: [bar: :baz]]}
]
連結到此函數

pipe(expr, call_args, position)

檢視原始碼
@spec pipe(t(), t(), integer()) :: t()

expr 導向給定 positioncall_args

這個函數可以用來實作類似 |> 的功能。例如,|> 本身是這樣實作的

defmacro left |> right do
  Macro.pipe(left, right, 0)
end

expr 是表達式的 AST。 call_args 必須是 呼叫 的 AST,否則這個函數會引發錯誤。舉例來說,考慮管道運算子 |>/2,它使用這個函數來建立管道。

即使表達式被管道到 AST 中,並不表示 AST 一定是有效的。例如,你可以將一個參數管道到 div/2,實際上將它變成對 div/3 的呼叫,而 div/3 函數在預設情況下是不存在的。除非 div/3 函數在本地被定義,否則程式碼會引發錯誤。

@spec postwalk(t(), (t() -> t())) :: t()

此函數的行為類似於 prewalk/2,但對引號表達式執行深度優先、後序遍歷。

@spec postwalk(t(), any(), (t(), any() -> {t(), any()})) :: {t(), any()}

此函數的行為類似於 prewalk/3,但使用累加器對引號表達式執行深度優先、後序遍歷。

連結到此函數

postwalker(ast)

檢視原始碼 (自 1.13.0 起)
@spec postwalker(t()) :: Enumerable.t()

傳回一個可列舉的物件,以深度優先、後序遍歷方式遍歷 ast

範例

iex> ast = quote do: foo(1, "abc")
iex> Enum.map(Macro.postwalker(ast), & &1)
[1, "abc", {:foo, [], [1, "abc"]}]
@spec prewalk(t(), (t() -> t())) :: t()

以深度優先、前序遍歷方式遍歷引號表達式。

回傳一個新的 AST,其中每個節點都是呼叫 fun 對應於 ast 的每個節點的結果。

範例

iex> ast = quote do: 5 + 3 * 7
iex> {:+, _, [5, {:*, _, [3, 7]}]} = ast
iex> new_ast = Macro.prewalk(ast, fn
...>   {:+, meta, children} -> {:*, meta, children}
...>   {:*, meta, children} -> {:+, meta, children}
...>   other -> other
...> end)
iex> {:*, _, [5, {:+, _, [3, 7]}]} = new_ast
iex> Code.eval_quoted(ast)
{26, []}
iex> Code.eval_quoted(new_ast)
{50, []}
@spec prewalk(t(), any(), (t(), any() -> {t(), any()})) :: {t(), any()}

以深度優先、前序遍歷方式遍歷引號表達式,並使用累加器。

回傳一個二元組,其中第一個元素是一個新的 AST,其中每個節點都是呼叫 fun 對應於每個節點的結果,而第二個元素是最後的累加器。

範例

iex> ast = quote do: 5 + 3 * 7
iex> {:+, _, [5, {:*, _, [3, 7]}]} = ast
iex> {new_ast, acc} = Macro.prewalk(ast, [], fn
...>   {:+, meta, children}, acc -> {{:*, meta, children}, [:+ | acc]}
...>   {:*, meta, children}, acc -> {{:+, meta, children}, [:* | acc]}
...>   other, acc -> {other, acc}
...> end)
iex> {{:*, _, [5, {:+, _, [3, 7]}]}, [:*, :+]} = {new_ast, acc}
iex> Code.eval_quoted(ast)
{26, []}
iex> Code.eval_quoted(new_ast)
{50, []}
連結到此函數

prewalker(ast)

檢視原始碼 (自 1.13.0 起)
@spec prewalker(t()) :: Enumerable.t()

傳回一個可列舉的物件,以深度優先、前序遍歷方式遍歷 ast

範例

iex> ast = quote do: foo(1, "abc")
iex> Enum.map(Macro.prewalker(ast), & &1)
[{:foo, [], [1, "abc"]}, 1, "abc"]
連結到此函數

quoted_literal?(term)

檢視原始碼 (自 1.7.0 起)
@spec quoted_literal?(t()) :: boolean()

如果給定的引號表達式表示引號文字,則傳回 true

原子和數字永遠是文字。二進制、清單、元組、映射和結構只有在它們的所有條款都是文字時才是文字。

範例

iex> Macro.quoted_literal?(quote(do: "foo"))
true
iex> Macro.quoted_literal?(quote(do: {"foo", 1}))
true
iex> Macro.quoted_literal?(quote(do: {"foo", 1, :baz}))
true
iex> Macro.quoted_literal?(quote(do: %{foo: "bar"}))
true
iex> Macro.quoted_literal?(quote(do: %URI{path: "/"}))
true
iex> Macro.quoted_literal?(quote(do: URI.parse("/")))
false
iex> Macro.quoted_literal?(quote(do: {foo, var}))
false
連結到此函數

special_form?(name, arity)

檢視原始碼 (自 1.7.0 起)
@spec special_form?(name :: atom(), arity()) :: boolean()

如果給定的名稱和元數是特殊形式,則傳回 true

連結到此函數

struct!(module, env)

檢視原始碼 (自 1.8.0 起)
@spec struct!(module, Macro.Env.t()) :: %{
  :__struct__ => module,
  optional(atom()) => any()
}
when module: module()

在給定的 env 中擴充 module 給出的結構。

當結構需要在編譯時展開,且被展開的結構可能已編譯或尚未編譯時,這項功能非常有用。此功能還能展開在正在編譯的模組下定義的結構。

如果結構不可用,它會引發 CompileError。從 Elixir v1.12 開始,呼叫此功能也會新增對給定結構的匯出相依性。

@spec to_string(t()) :: String.t()

將給定的表達式 AST 轉換為字串。

這是將 AST 轉換為字串的便利功能,它會捨棄原始程式碼的所有格式,並在 98 個字元周圍換行。請參閱 Code.quoted_to_algebra/2,它是一個較低層級的功能,能更進一步控制格式。

範例

iex> Macro.to_string(quote(do: foo.bar(1, 2, 3)))
"foo.bar(1, 2, 3)"
此功能已棄用。請改用 Macro.to_string/1。
@spec to_string(t(), (t(), String.t() -> String.t())) :: String.t()

將給定的表達式 AST 轉換為字串。

給定的 fun 會在 AST 的每個節點呼叫,並帶有兩個參數:要列印的節點的 AST,以及該節點的字串表示形式。此功能的回傳值會用作該 AST 節點的最終字串表示形式。

此功能會捨棄原始程式碼的所有格式。

範例

Macro.to_string(quote(do: 1 + 2), fn
  1, _string -> "one"
  2, _string -> "two"
  _ast, string -> string
end)
#=> "one + two"
連結到此函數

traverse(ast, acc, pre, post)

檢視原始碼
@spec traverse(t(), any(), (t(), any() -> {t(), any()}), (t(), any() -> {t(), any()})) ::
  {t(), any()}

以深度優先方式遍歷引號表達式,並使用累加器。

傳回一個元組,其中第一個元素是新的 AST,第二個元素是最終累加器。新的 AST 是在預序階段對 ast 的每個節點呼叫 pre,以及在後序階段呼叫 post 的結果。

範例

iex> ast = quote do: 5 + 3 * 7
iex> {:+, _, [5, {:*, _, [3, 7]}]} = ast
iex> {new_ast, acc} =
...>  Macro.traverse(
...>    ast,
...>    [],
...>    fn
...>      {:+, meta, children}, acc -> {{:-, meta, children}, [:- | acc]}
...>      {:*, meta, children}, acc -> {{:/, meta, children}, [:/ | acc]}
...>      other, acc -> {other, acc}
...>    end,
...>    fn
...>      {:-, meta, children}, acc -> {{:min, meta, children}, [:min | acc]}
...>      {:/, meta, children}, acc -> {{:max, meta, children}, [:max | acc]}
...>      other, acc -> {other, acc}
...>    end
...>  )
iex> {:min, _, [5, {:max, _, [3, 7]}]} = new_ast
iex> [:min, :max, :/, :-] = acc
iex> Code.eval_quoted(new_ast)
{5, []}
連結到此函數

underscore(atom_or_string)

檢視原始碼
@spec underscore(module() | atom() | String.t()) :: String.t()

將給定的參數轉換為使用底線斜線格式的字串。

參數必須是原子或字串。如果給定原子,假設為 Elixir 模組,因此會轉換為字串,然後處理。

此函數設計為使用底線斜線格式來格式化語言識別碼/代碼,這就是它屬於 Macro 模組的原因。請勿將其用作底線字串的一般機制,因為它不支援 Unicode 或 Elixir 識別碼中無效的字元。

範例

iex> Macro.underscore("FooBar")
"foo_bar"

iex> Macro.underscore("Foo.Bar")
"foo/bar"

iex> Macro.underscore(Foo.Bar)
"foo/bar"

一般而言,underscore 可以視為 camelize 的反向操作,然而,在某些情況下,格式化可能會遺失

iex> Macro.underscore("SAPExample")
"sap_example"

iex> Macro.camelize("sap_example")
"SapExample"

iex> Macro.camelize("hello_10")
"Hello10"

iex> Macro.camelize("foo/bar")
"Foo.Bar"
@spec unescape_string(String.t()) :: String.t()

取消轉義字串中的字元。

這是 Elixir 單引號和雙引號字串中預設使用的取消跳脫行為。請查看 unescape_string/2,以取得如何自訂跳脫對應的資訊。

在此設定中,Elixir 會跳脫下列項目:\0\a\b\d\e\f\n\r\s\t\v。位元組可透過 \xNN 以十六進位表示,Unicode 碼點則可透過 \uNNNN 跳脫表示。

此函數通常用於 sigil 實作(例如 ~r~s 等),它會接收原始的未跳脫字串,且可用於任何需要模擬 Elixir 如何剖析字串的地方。

範例

iex> Macro.unescape_string("example\\n")
"example\n"

在上述範例中,我們傳遞一個跳脫 \n 的字串,並傳回未跳脫的版本。

連結到此函數

unescape_string(string, map)

檢視原始碼
@spec unescape_string(String.t(), (non_neg_integer() -> non_neg_integer() | false)) ::
  String.t()

根據給定的對應取消轉義字串中的字元。

如果您想使用與 Elixir 單引號和雙引號字串相同的對應,請查看 unescape_string/1

對應函數

對應函數接收一個整數,表示它想要取消跳脫的字元的碼點。另外還有特殊原子 :newline:unicode:hex,分別控制換行、Unicode 和跳脫。

以下是 Elixir 實作的預設對應函數

def unescape_map(:newline), do: true
def unescape_map(:unicode), do: true
def unescape_map(:hex), do: true
def unescape_map(?0), do: ?0
def unescape_map(?a), do: ?\a
def unescape_map(?b), do: ?\b
def unescape_map(?d), do: ?\d
def unescape_map(?e), do: ?\e
def unescape_map(?f), do: ?\f
def unescape_map(?n), do: ?\n
def unescape_map(?r), do: ?\r
def unescape_map(?s), do: ?\s
def unescape_map(?t), do: ?\t
def unescape_map(?v), do: ?\v
def unescape_map(e), do: e

如果 unescape_map/1 函數傳回 false,則不會跳脫字元,且反斜線會保留在字串中。

範例

使用上面定義的 unescape_map/1 函式很簡單

Macro.unescape_string("example\\n", &unescape_map(&1))
連結到此函數

unique_var(var, context)

檢視原始碼 (自 1.11.3 起)
@spec unique_var(var, context) :: {var, [{:counter, integer()}], context}
when var: atom(), context: atom()

產生一個 AST 節點,表示由原子 varcontext 給出的唯一變數。

使用相同的參數呼叫此函式將會產生另一個變數,並具有其自己的唯一計數器。請參閱 var/2 以取得替代方案。

範例

iex> {:foo, [counter: c], __MODULE__} = Macro.unique_var(:foo, __MODULE__)
iex> is_integer(c)
true
@spec unpipe(t()) :: [t()]

將管線表達式分解成一個清單。

管線 (一系列 |>/2 應用) 的 AST 與一系列二元運算子或函式應用的 AST 類似:最上層的表達式是最右邊的 :|> (這是最後一個要執行的),而其左手邊和右手邊則為其參數

quote do: 100 |> div(5) |> div(2)
#=> {:|>, _, [arg1, arg2]}

在上面的範例中,|>/2 管線是最右邊的管線;arg1100 |> div(5) 的 AST,而 arg2div(2) 的 AST。

通常會需要將此類管線的 AST 作為函式應用清單。此函式執行這項工作

Macro.unpipe(quote do: 100 |> div(5) |> div(2))
#=> [{100, 0}, {{:div, [], [5]}, 0}, {{:div, [], [2]}, 0}]

我們會取得一個直接遵循管線的清單:首先是 100,接著是 div(5) (更精確地說,是其 AST),然後是 div(2)。在組成的第二個元素中,0 是管線中前一個元素在目前函式應用中的位置:{{:div, [], [5]}, 0} 表示前一個元素 (100) 將會插入為 div/2 函式的第 0 個 (第一個) 參數,因此該函式的 AST 將會變成 {:div, [], [100, 5]} (div(100, 5))。

@spec update_meta(t(), (keyword() -> keyword())) :: t()

如果節點元資料包含一個值,則對該值套用給定的函式。

這通常會在與 Macro.prewalk/2 搭配使用時很有用,用於移除表達式中的資訊,例如行數和衛生計數器,以進行儲存或比較。

範例

iex> quoted = quote line: 10, do: sample()
{:sample, [line: 10], []}
iex> Macro.update_meta(quoted, &Keyword.delete(&1, :line))
{:sample, [], []}
@spec validate(term()) :: :ok | {:error, term()}

驗證給定的表達式是否為有效的引號表達式。

請查看類型 Macro.t/0 以取得有效引號表達式的完整規格。

如果表達式有效,則會傳回 :ok。否則,會傳回一個格式為 {:error, remainder} 的組成,其中 remainder 是引號表達式中無效的部分。

範例

iex> Macro.validate({:two_element, :tuple})
:ok
iex> Macro.validate({:three, :element, :tuple})
{:error, {:three, :element, :tuple}}

iex> Macro.validate([1, 2, 3])
:ok
iex> Macro.validate([1, 2, 3, {4}])
{:error, {4}}
@spec var(var, context) :: {var, [], context} when var: atom(), context: atom()

產生一個 AST 節點,表示由原子 varcontext 給出的變數。

請注意,此變數並非唯一的。如果您稍後想要存取同一個變數,您可以再次使用相同的參數呼叫 var/2。請使用 unique_var/2 來產生一個無法覆寫的唯一變數。

範例

為了建立變數,預期會有一個內容。大部分時間,為了保持衛生,內容必須是 __MODULE__/0

iex> Macro.var(:foo, __MODULE__)
{:foo, [], __MODULE__}

不過,如果需要存取使用者變數,可以提供 nil

iex> Macro.var(:foo, nil)
{:foo, [], nil}