檢視原始碼 程式碼 (Elixir v1.16.2)

用於管理程式碼編譯、程式碼評估和程式碼載入的公用程式。

此模組補充 Erlang 的 :code 模組,以新增專屬於 Elixir 的行為。如要處理 Elixir 的 AST(而非評估它)的函式,請參閱 Macro 模組。

處理檔案

此模組包含三個用於編譯和評估檔案的函式。以下是它們的摘要和行為

  • require_file/2 - 編譯檔案並追蹤其名稱。如果檔案之前已需要,它不會再次編譯檔案。

  • compile_file/2 - 編譯檔案,但不追蹤其名稱。當多次呼叫時,會多次編譯檔案。

  • eval_file/2 - 評估檔案內容,但不追蹤其名稱。它傳回檔案中最後一個表達式的結果,而不是其中定義的模組。已評估的檔案不會觸發下一個區段中描述的編譯追蹤器。

簡而言之,當您想要追蹤系統處理的檔案,以避免重複編譯同一個檔案時,必須使用第一個函式。這在腳本中很常見。

當您有興趣取得檔案中定義的模組,但不追蹤時,必須使用 compile_file/2。當您有興趣取得評估檔案的結果,而不是它定義的模組時,應使用 eval_file/2

上述函式會處理 Elixir 原始碼。如果您想要處理編譯成位元組碼的模組,其副檔名為 .beam,而且通常會出現在 Mix 專案的 _build 目錄底下,請參閱 Erlang 的 :code 模組中的函式。

在 Erlang VM 上載入程式碼

Erlang 有兩種載入程式碼的模式:互動式和嵌入式。

Erlang VM 預設會以互動式模式執行,其中會視需要載入模組。在嵌入式模式中,情況相反,因為所有模組都需要預先或明確載入。

您可以在使用模組之前使用 ensure_loaded/1(以及 ensure_loaded?/1ensure_loaded!/1)來檢查模組是否已載入,並採取行動。

ensure_compiled/1ensure_compiled!/1

Elixir 也包含 ensure_compiled/1ensure_compiled!/1 函式,它們是 ensure_loaded/1 的超集。

由於 Elixir 編譯會並行進行,在某些情況下,你可能需要使用尚未編譯的模組,因此甚至無法載入。

呼叫時,ensure_compiled/1ensure_compiled!/1 會暫停呼叫者的編譯,直到模組可用為止。請注意 ensure_compiled/1ensure_compiled!/1 之間的區別很重要:如果你使用 ensure_compiled!/1,表示你告訴編譯器,只有在該模組可用時,你才能繼續。

如果你使用 Code.ensure_compiled/1,表示你暗示你可以繼續而不需要該模組,因此 Elixir 可以在模組尚未可用(但稍後可能會可用)的情況下傳回 {:error, :unavailable}

由於這些原因,開發人員通常必須使用 Code.ensure_compiled!/1。特別是,不要這樣做

case Code.ensure_compiled(module) do
  {:module, _} -> module
  {:error, _} -> raise ...
end

最後,請注意你只需要 ensure_compiled!/1 來檢查在同一個專案中定義的模組。它不適用於依賴項中的模組,因為依賴項總是會預先編譯。

在大多數情況下,ensure_loaded/1 就足夠了。 ensure_compiled!/1 必須在罕見的情況下使用,通常涉及需要呼叫模組以取得回呼資訊的巨集。使用 ensure_compiled/1 的可能性更低。

編譯追蹤器

Elixir 支援編譯追蹤器,讓模組可以在編譯檔案時觀察 Elixir 編譯器處理的建構。追蹤器是一個實作 trace/2 函式的模組。此函式會接收事件名稱作為第一個引數,Macro.Env 作為第二個引數,並且必須傳回 :ok。對於追蹤器而言,同步執行越少的工作越好,並將大部分工作分派到一個獨立的程序。追蹤器執行速度過慢會拖慢編譯速度

你可以透過 put_compiler_option/2 來設定追蹤器清單。追蹤器可以使用下列事件

  • :start -(自 v1.11.0 起)每當編譯器開始追蹤新的詞彙環境時呼叫。編譯新檔案或在函式中定義模組時,會啟動詞彙環境。請注意,已評估的程式碼不會啟動新的詞彙環境(因為它們不會追蹤未使用的別名、匯入等),但如果在已評估的程式碼中定義模組,則會啟動新的詞彙環境。

    請注意,此事件可能會並行發出,其中多個檔案/模組會呼叫 :start 並同時執行。巨集環境的 lexical_tracker 的值雖然不透明,但可用於唯一識別環境。

  • :stop -(自 v1.11.0 起)每當編譯器停止追蹤新的詞彙環境(例如新檔案)時呼叫。

  • {:import, meta, module, opts} - 每當匯入 module 時追蹤。 meta 是匯入 AST 元資料,opts 是匯入選項。

  • {:imported_function, meta, module, name, arity}{:imported_macro, meta, module, name, arity} - 每當呼叫匯入的函式或巨集時追蹤。 meta 是呼叫 AST 元資料,module 是匯入的模組,後面接著匯入函式/巨集的 namearity。匯入的模組/名稱/arity 仍可能會發出 :remote_function/:remote_macro 事件。

  • {:alias, meta, alias, as, opts} - 每當將 alias 別名設為 as 時追蹤。 meta 是別名 AST 元資料,opts 是別名選項。

  • {:alias_expansion, meta, as, alias} 每當先前定義的 alias 有別名擴充時追蹤,亦即當使用者撰寫擴充為 aliasas 時。 meta 是別名擴充 AST 元資料。

  • {:alias_reference, meta, module} - 每當程式碼中出現別名時,都會追蹤,也就是說,不論是否已展開,每當使用者在程式碼中寫入 MyModule.Foo.Bar 時,都會追蹤。

  • {:require, meta, module, opts} - 每當 module 被需要時,都會追蹤。 meta 是需要 AST 的元資料,而 opts 是需要選項。如果 meta 選項包含 :from_macro,則表示模組是由巨集內部呼叫,因此必須視為編譯時期依賴項。

  • {:struct_expansion, meta, module, keys} - 每當 module 的結構展開時,都會追蹤。 meta 是結構 AST 的元資料,而 keys 是展開中使用的金鑰

  • {:remote_function, meta, module, name, arity}{:remote_macro, meta, module, name, arity} - 每當參考遠端函式或巨集時,都會追蹤。 meta 是呼叫 AST 的元資料,module 是呼叫的模組,後面接著 namearity

  • {:local_function, meta, name, arity}{:local_macro, meta, name, arity} - 每當參考區域函式或巨集時,都會追蹤。 meta 是呼叫 AST 的元資料,後面接著 namearity

  • {:compile_env, app, path, return} - 每當呼叫 Application.compile_env/3Application.compile_env!/2 時,都會追蹤。 app 是原子,path 是在應用程式環境中要遍歷的鍵清單,而 return{:ok, value}:error

  • :defmodule - (自 v1.16.2 起) 一開始定義模組時就會追蹤。這會在模組生命週期早期呼叫,Module.open?/1 仍會針對此類追蹤傳回 false

  • {:on_module, bytecode, _ignore} - (自 v1.13.0 起) 每當定義模組時,都會追蹤。這等同於 @after_compile 回呼,並在給定模組中的任何 @after_compile 之後呼叫。第三個元素目前為 :none,但未來可能會提供更多元資料。目前最好忽略它。請注意,在發出此事件時,仍然可以使用預期尚未編譯模組的 Module 函式 (例如 Module.definitions_in/1)。

編譯器選項 :tracers 可與編譯器選項 :parser_options 結合使用,以豐富上述追蹤事件的元資料。

未來隨時可能會新增新事件,因此建議 trace/2 函數具備「全域性」子句。

以下是一個追蹤器範例,用於列印所有遠端函數呼叫

defmodule MyTracer do
  def trace({:remote_function, _meta, module, name, arity}, env) do
    IO.puts "#{env.file}:#{env.line} #{inspect(module)}.#{name}/#{arity}"
    :ok
  end

  def trace(_event, _env) do
    :ok
  end
end

摘要

類型

包含所有變數及其值的清單。

編譯器和程式碼評估傳回的診斷資料。

行號。0 表示沒有行號。

診斷資料的位置。

函數

將路徑附加到 Erlang VM 程式碼路徑清單。

paths 清單附加到 Erlang VM 程式碼路徑清單。

傳回清單,其中包含所有可用的編譯器選項。

如果目前程序可以等待模組編譯,則傳回 true

編譯指定的檔案。

編譯引用的表達式。

編譯指定的字串。

從程式碼伺服器取得所有編譯選項。

儲存所有指定的編譯選項。

從 Erlang VM 程式碼路徑清單中刪除路徑。

從 Erlang VM 程式碼路徑清單中刪除路徑清單。

確保載入指定的模組。

ensure_all_loaded/1 相同,但如果無法載入任何模組,則會引發例外。

類似於 ensure_compiled!/1,但表示您可以在沒有該模組的情況下繼續執行。

確保已編譯並載入指定的模組。

確保載入指定的模組。

ensure_loaded/1 相同,但如果無法載入模組,則會引發例外。

確保載入指定的模組。

傳回評估環境。

評估給定的檔案。

使用 bindingenv 評估給定的 quoted 內容。

評估 string 給定的內容。

傳回給定模組或路徑至 .beam 檔案的說明文件。

格式化檔案。

格式化給定的程式碼 string

傳回給定編譯器選項的值。

如果模組已載入,傳回 true

將路徑新增到 Erlang VM 程式碼路徑清單的最前面。

paths 清單新增到 Erlang VM 程式碼路徑清單的最前面。

將診斷訊息印出到標準錯誤輸出。

清除編譯器模組。

儲存編譯選項。

使用 Elixir 的格式化規則,將引號中的表達式轉換為代數文件。

需要給定的 file

列出所有需要的檔案。

將給定的字串轉換為其引號形式。

將給定的字串轉換為其引號形式。

將給定的字串轉換為其引號形式和註解清單。

將給定的字串轉換為其引號形式和註解清單。

從需要的檔案清單中移除檔案。

執行給定的 fun 並擷取所有診斷訊息。

類型

@type binding() :: [{atom() | tuple(), any()}]

包含所有變數及其值的清單。

繫結鍵通常是原子,但對於在不同內容中定義的變數,它們可能是元組。

@type diagnostic(severity) :: %{
  :source => Path.t() | nil,
  :file => Path.t() | nil,
  :severity => severity,
  :message => String.t(),
  :position => position(),
  :stacktrace => Exception.stacktrace(),
  :span => {line :: pos_integer(), column :: pos_integer()} | nil,
  optional(:details) => term(),
  optional(any()) => any()
}

編譯器和程式碼評估傳回的診斷資料。

檔案和位置與診斷應顯示的位置有關。如果存在檔案和位置,則診斷是精確的,您可以使用給定的檔案和位置來產生片段、IDE 註解等。診斷結束的行和欄可使用一個選用的跨距。

否則,可能會提供堆疊追蹤,您可以放置自己的啟發法來提供更好的報告。

source 欄位指向編譯器追蹤錯誤的來源檔案。例如,檔案 lib/foo.ex 可能嵌入來自 lib/foo/bar.eex.eex 範本。EEx 範本的語法錯誤將指向檔案 lib/foo/bar.eex,但來源是 lib/foo.ex

@type line() :: non_neg_integer()

行號。0 表示沒有行號。

@type position() :: line() | {line :: pos_integer(), column :: pos_integer()}

診斷資料的位置。

可以是行號或 {line, column}。行和欄號從 1 開始。位置 0 代表未知。

函式

連結到此函式

append_path(path, opts \\ [])

檢視原始碼
@spec append_path(Path.t(), [{:cache, boolean()}]) :: true | false

將路徑附加到 Erlang VM 程式碼路徑清單。

這是 Erlang VM 用於尋找模組程式碼的目錄清單。檔案清單會針對每個 Erlang VM 節點進行管理。

在附加之前,會使用 Path.expand/1 來擴充路徑。它需要路徑存在。傳回一個布林值,表示是否已成功新增路徑。

範例

Code.append_path(".")
#=> true

Code.append_path("/does_not_exist")
#=> false

選項

  • :cache - (自 v1.15.0 起) 為 true 時,會在第一次遍歷程式碼路徑時快取該路徑,以減少檔案系統作業。它需要 Erlang/OTP 26,否則它是一個空操作。
連結到此函式

append_paths(paths, opts \\ [])

檢視原始碼 (自 1.15.0 起)
@spec append_paths([Path.t()], [{:cache, boolean()}]) :: :ok

paths 清單附加到 Erlang VM 程式碼路徑清單。

這是 Erlang VM 用於尋找模組程式碼的目錄清單。檔案清單會針對每個 Erlang VM 節點進行管理。

在附加之前,會使用 Path.expand/1 來擴充所有路徑。只會附加現有的路徑。此函式總是傳回 :ok,無論附加了多少路徑。如果您需要更多控制,請使用 append_path/1

範例

Code.append_paths([".", "/does_not_exist"])
#=> :ok

選項

  • :cache - 當為 true 時,會在第一次遍歷時快取程式碼路徑,以減少檔案系統操作。需要 Erlang/OTP 26,否則為無操作。
連結到此函式

available_compiler_options()

檢視原始碼
@spec available_compiler_options() :: [atom()]

傳回清單,其中包含所有可用的編譯器選項。

所有選項的說明,請參閱 put_compiler_option/2

範例

Code.available_compiler_options()
#=> [:docs, :debug_info, ...]
連結到此函式

can_await_module_compilation?()

檢視原始碼 (自 1.11.0 起)
@spec can_await_module_compilation?() :: boolean()

如果目前程序可以等待模組編譯,則傳回 true

透過 Kernel.ParallelCompiler 編譯 Elixir 程式碼時,Mix 和 elixirc 會使用它,呼叫尚未編譯的模組會封鎖呼叫者,直到模組可用為止。執行 Elixir 腳本,例如傳遞檔名給 elixir,不會等待。

連結到此函式

compile_file(file, relative_to \\ nil)

檢視原始碼 (自 1.7.0 起)
@spec compile_file(binary(), nil | binary()) :: [{module(), binary()}]

編譯指定的檔案。

接受 relative_to 作為引數,以告知檔案所在位置。

傳回一個元組清單,其中第一個元素是模組名稱,第二個元素是其位元組碼 (作為二進位)。與 require_file/2 相反,它不會追蹤已編譯檔案的檔名。

如果您想取得評估檔案的結果,而不是定義在其中的模組,請參閱 eval_file/2

如要同時編譯多個檔案,請參閱 Kernel.ParallelCompiler.compile/2

連結到此函式

compile_quoted(quoted, file \\ "nofile")

檢視原始碼
@spec compile_quoted(Macro.t(), binary()) :: [{module(), binary()}]

編譯引用的表達式。

傳回一個元組清單,其中第一個元素是模組名稱,第二個元素是其位元組碼 (作為二進位)。可以將 file 作為第二個引數提供,它將用於報告警告和錯誤。

連結到此函式

compile_string(string, file \\ "nofile")

檢視原始碼
@spec compile_string(List.Chars.t(), binary()) :: [{module(), binary()}]

編譯指定的字串。

傳回一個元組清單,其中第一個元素是模組名稱,第二個元素是其位元組碼 (作為二進位)。可以將 file 作為第二個引數提供,它將用於報告警告和錯誤。

警告字串可以是任何 Elixir 程式碼,而且程式碼可以執行與 Erlang VM 相同的權限:這表示此類程式碼可能會危害機器(例如透過執行系統指令)。請勿將 compile_string/2 與不受信任的輸入(例如來自網路的字串)一起使用。

@spec compiler_options() :: map()

從程式碼伺服器取得所有編譯選項。

如要取得個別選項,請參閱 get_compiler_option/1。如要取得所有選項的說明,請參閱 put_compiler_option/2

範例

Code.compiler_options()
#=> %{debug_info: true, docs: true, ...}
@spec compiler_options(Enumerable.t({atom(), term()})) :: %{
  optional(atom()) => term()
}

儲存所有指定的編譯選項。

變更編譯選項會影響在特定 Erlang VM 節點中執行的所有程序。如要儲存個別選項以及取得所有選項的說明,請參閱 put_compiler_option/2

傳回包含先前值的對應。

範例

Code.compiler_options(warnings_as_errors: true)
#=> %{warnings_as_errors: false}
@spec delete_path(Path.t()) :: boolean()

從 Erlang VM 程式碼路徑清單中刪除路徑。

這是 Erlang VM 用於尋找模組程式碼的目錄清單。檔案清單會針對每個 Erlang VM 節點進行管理。

在刪除路徑之前,會使用 Path.expand/1 展開路徑。如果路徑不存在,此函式會傳回 false

範例

Code.prepend_path(".")
Code.delete_path(".")
#=> true

Code.delete_path("/does_not_exist")
#=> false
連結到此函式

delete_paths(paths)

檢視原始碼 (自 1.15.0 起)
@spec delete_paths([Path.t()]) :: :ok

從 Erlang VM 程式碼路徑清單中刪除路徑清單。

這是 Erlang VM 用於尋找模組程式碼的目錄清單。檔案清單會針對每個 Erlang VM 節點進行管理。

在刪除路徑之前,會使用 Path.expand/1 展開路徑。如果路徑不存在,此函式會傳回 false

連結到此函式

ensure_all_loaded(modules)

檢視原始碼 (自 1.15.0 起)
@spec ensure_all_loaded([module()]) :: :ok | {:error, [{module(), reason}]}
when reason: :badfile | :nofile | :on_load_failure

確保載入指定的模組。

類似於 ensure_loaded/1,但接受模組清單(而非單一模組),並載入所有模組。

如果所有模組都載入成功,會傳回 :ok。否則,會傳回 {:error, errors},其中 errors 是由模組和載入失敗原因所組成的元組清單。

範例

iex> Code.ensure_all_loaded([Atom, String])
:ok

iex> Code.ensure_all_loaded([Atom, DoesNotExist])
{:error, [{DoesNotExist, :nofile}]}
連結到此函式

ensure_all_loaded!(modules)

檢視原始碼 (自 1.15.0 起)
@spec ensure_all_loaded!([module()]) :: :ok

ensure_all_loaded/1 相同,但如果無法載入任何模組,則會引發例外。

@spec ensure_compiled(module()) ::
  {:module, module()}
  | {:error, :embedded | :badfile | :nofile | :on_load_failure | :unavailable}

類似於 ensure_compiled!/1,但表示您可以在沒有該模組的情況下繼續執行。

ensure_compiled!/1 指示 Elixir 編譯器表示只有在該模組可用時才能繼續時,此函式表示可以在沒有該模組的情況下繼續編譯。

如果成功載入模組,它會傳回 {:module, module}。如果沒有,會傳回 {:error, reason},並附上錯誤原因。如果正在檢查的模組目前處於編譯死結,這個函式會傳回 {:error, :unavailable}。無法使用不代表模組不存在,只是代表它目前無法使用,但它(可能)會在未來可以使用。

因此,如果你只能在模組可用時繼續,請改用 ensure_compiled!/1。特別是,不要這樣做

case Code.ensure_compiled(module) do
  {:module, _} -> module
  {:error, _} -> raise ...
end

有關程式碼載入的更多資訊,請參閱模組文件。

連結到此函式

ensure_compiled!(module)

檢視原始碼 (自 1.12.0 起)
@spec ensure_compiled!(module()) :: module()

確保已編譯並載入指定的模組。

如果模組已載入,它會作為 no-op 運作。如果模組尚未編譯,ensure_compiled!/1 會暫停呼叫者的編譯,直到傳遞給 ensure_compiled!/1 的模組可用,或已編譯目前專案的所有檔案。如果編譯完成,但模組不可用或處於死結,則會引發錯誤。

由於這個函式會暫停編譯,請小心使用它。特別是,避免使用它來猜測系統中的模組。過度使用這個函式也可能導致死結,也就是兩個模組同時檢查另一個模組是否已編譯。這會傳回特定的無法使用錯誤碼,我們無法成功驗證模組是否可用。

有關程式碼載入的更多資訊,請參閱模組文件。

@spec ensure_loaded(module()) ::
  {:module, module()}
  | {:error, :embedded | :badfile | :nofile | :on_load_failure}

確保載入指定的模組。

如果模組已載入,它會作為 no-op 運作。如果模組尚未載入,它會嘗試載入它。

如果成功載入模組,它會傳回 {:module, module}。如果沒有,會傳回 {:error, reason},並附上錯誤原因。

有關程式碼載入的更多資訊,請參閱模組文件。

範例

iex> Code.ensure_loaded(Atom)
{:module, Atom}

iex> Code.ensure_loaded(DoesNotExist)
{:error, :nofile}
連結到此函式

ensure_loaded!(module)

檢視原始碼 (自 1.12.0 起)
@spec ensure_loaded!(module()) :: module()

ensure_loaded/1 相同,但如果無法載入模組,則會引發例外。

@spec ensure_loaded?(module()) :: boolean()

確保載入指定的模組。

類似於 ensure_loaded/1,但如果模組已載入或已成功載入,則傳回 true。否則,傳回 false

範例

iex> Code.ensure_loaded?(String)
true
連結到此函式

env_for_eval(env_or_opts)

檢視原始碼 (自 1.14.0 起)

傳回評估環境。

它接受 Macro.Env,然後進行修剪和準備,或選項清單。它傳回準備好評估的環境。

此模組中的大多數函式會自動準備指定的環境以進行評估,因此您不需要明確呼叫此函式,但 eval_quoted_with_env/3 除外,它被設計為在迴圈中呼叫,以實作互動式 shell 或其他具有多重評估功能。

選項

如果未提供 env,選項可以是

  • :file - 評估中要考慮的文件

  • :line - 腳本開始的行

連結到此函式

eval_file(file, relative_to \\ nil)

檢視原始碼
@spec eval_file(binary(), nil | binary()) :: {term(), binding()}

評估給定的檔案。

接受 relative_to 作為引數,以告知檔案所在位置。

雖然 require_file/2compile_file/2 傳回載入的模組及其位元組碼,但 eval_file/2 僅評估檔案內容,並傳回評估結果及其繫結(與 eval_string/3 完全相同的傳回值)。

連結到此函式

eval_quoted(quoted, binding \\ [], env_or_opts \\ [])

檢視原始碼
@spec eval_quoted(Macro.t(), binding(), Macro.Env.t() | keyword()) ::
  {term(), binding()}

評估引號中的內容。

警告:在巨集內呼叫此函式被視為不良做法,因為它會嘗試在編譯時評估執行時期值。巨集引數通常會透過將其展開為傳回的引號表達式(而不是評估)來轉換。

請參閱 eval_string/3 以取得 bindingopts 的說明。

範例

iex> contents = quote(do: var!(a) + var!(b))
iex> {result, binding} = Code.eval_quoted(contents, [a: 1, b: 2], file: __ENV__.file, line: __ENV__.line)
iex> result
3
iex> Enum.sort(binding)
[a: 1, b: 2]

為方便起見,您可以將 __ENV__/0 作為 opts 引數傳遞,所有選項將自動從目前環境中擷取

iex> contents = quote(do: var!(a) + var!(b))
iex> {result, binding} = Code.eval_quoted(contents, [a: 1, b: 2], __ENV__)
iex> result
3
iex> Enum.sort(binding)
[a: 1, b: 2]
連結到此函式

eval_quoted_with_env(quoted, binding, env, opts \\ [])

檢視原始碼 (自 1.14.0 起)
@spec eval_quoted_with_env(Macro.t(), binding(), Macro.Env.t(), keyword()) ::
  {term(), binding(), Macro.Env.t()}

使用 bindingenv 評估給定的 quoted 內容。

此函式旨在迴圈中呼叫,以實作互動式 shell 或任何其他具有多重評估功能之項目。因此,您第一次呼叫此函式時,必須使用 env_for_eval/1 計算初始環境。其餘呼叫必須傳遞此函式所傳回的環境。

選項

  • :prune_binding - (自 v1.14.2 起) 修剪繫結,僅保留已評估程式碼讀取或寫入的變數。請注意,模組所使用的變數總是會被修剪,即使稍後由模組使用。您可以提交至 :on_module 追蹤事件,並從其環境中存取模組所使用的變數。
連結到此函式

eval_string(string, binding \\ [], opts \\ [])

檢視原始碼
@spec eval_string(List.Chars.t(), binding(), Macro.Env.t() | keyword()) ::
  {term(), binding()}

評估 string 給定的內容。

binding 參數是所有變數及其值的清單。opts 參數是環境選項的關鍵字清單。

警告string 可以是任何 Elixir 程式碼,且將以與 Erlang VM 相同的權限執行:這表示此類程式碼可能會危害機器(例如,透過執行系統指令)。請勿對來自不受信任輸入(例如來自網路的字串)使用 eval_string/3

選項

選項可以是

  • :file - 評估中要考慮的文件

  • :line - 腳本開始的行

此外,您也可以將環境作為第二個參數傳遞,以便在該環境中進行評估。

傳回形式為 {value, binding} 的元組,其中 value 是評估 string 傳回的值。如果在評估 string 時發生錯誤,將會引發例外狀況。

binding 是評估 string 後所有變數名稱及其值的清單。binding 鍵通常是原子,但對於在不同內容中定義的變數,它們可能是元組。這些名稱沒有特定的順序。

範例

iex> {result, binding} = Code.eval_string("a + b", [a: 1, b: 2], file: __ENV__.file, line: __ENV__.line)
iex> result
3
iex> Enum.sort(binding)
[a: 1, b: 2]

iex> {result, binding} = Code.eval_string("c = a + b", [a: 1, b: 2], __ENV__)
iex> result
3
iex> Enum.sort(binding)
[a: 1, b: 2, c: 3]

iex> {result, binding} = Code.eval_string("a = a + b", [a: 1, b: 2])
iex> result
3
iex> Enum.sort(binding)
[a: 3, b: 2]

為了方便起見,您可以傳遞 __ENV__/0 作為 opts 參數,且在目前環境中定義的所有匯入、需求和別名將會自動傳遞

iex> {result, binding} = Code.eval_string("a + b", [a: 1, b: 2], __ENV__)
iex> result
3
iex> Enum.sort(binding)
[a: 1, b: 2]
連結到此函式

fetch_docs(module_or_path)

檢視原始碼 (自 1.7.0 起)
@spec fetch_docs(module() | String.t()) ::
  {:docs_v1, annotation, beam_language, format, module_doc :: doc_content,
   metadata, docs :: [doc_element]}
  | {:error, :module_not_found | :chunk_not_found | {:invalid_chunk, binary()}}
when annotation: :erl_anno.anno(),
     beam_language: :elixir | :erlang | atom(),
     doc_content: %{optional(binary()) => binary()} | :none | :hidden,
     doc_element:
       {{kind :: atom(), function_name :: atom(), arity()}, annotation,
        signature, doc_content, metadata},
     format: binary(),
     signature: [binary()],
     metadata: map()

傳回給定模組或路徑至 .beam 檔案的說明文件。

當給定模組名稱時,它會找出其 BEAM 程式碼並從中讀取文件。

當給定 .beam 檔案的路徑時,它會直接從該檔案載入文件。

它傳回儲存在文件區塊中的術語,其格式由 EEP 48 定義,或如果區塊不可用,則傳回 {:error, reason}

範例

# Module documentation of an existing module
iex> {:docs_v1, _, :elixir, _, %{"en" => module_doc}, _, _} = Code.fetch_docs(Atom)
iex> module_doc |> String.split("\n") |> Enum.at(0)
"Atoms are constants whose values are their own name."

# A module that doesn't exist
iex> Code.fetch_docs(ModuleNotGood)
{:error, :module_not_found}
連結到此函式

format_file!(file, opts \\ [])

檢視原始碼 (自 1.6.0 起)
@spec format_file!(
  binary(),
  keyword()
) :: iodata()

格式化檔案。

參閱 format_string!/2 以取得更多關於程式碼格式化和可用選項的資訊。

連結到此函式

format_string!(string, opts \\ [])

檢視原始碼 (自 1.6.0 起)
@spec format_string!(
  binary(),
  keyword()
) :: iodata()

格式化給定的程式碼 string

格式化程式會接收一個表示 Elixir 程式碼的字串,並傳回一個表示格式化程式碼的 iodata,根據預先定義的規則。

選項

  • :file - 包含字串的檔案,用於錯誤回報

  • :line - 字串開始的行,用於錯誤回報

  • :line_length - 格式化文件時要達到的行長。預設為 98。請注意,此值用作準則,但在某些情況下並未強制執行。有關更多資訊,請參閱下方的「行長」區段

  • :locals_without_parens - 一個關鍵字清單,包含名稱和應盡可能不帶括號的元組對。元組可以是原子 :*,表示該名稱的所有元組。格式化程式已包含一個函式清單,此選項會擴充此清單。

  • :force_do_end_blocks (自 v1.9.0 起) - 當 true 時,將 do: ...else: ... 和相關項目的所有內嵌用法轉換為 do-end 區塊。預設為 false。請注意,此選項是收斂的:一旦您將其設定為 true所有關鍵字 都會被轉換。如果您稍後將其設定為 falsedo-end 區塊不會轉換回關鍵字。

  • :normalize_bitstring_modifiers (自 v1.14.0 起) - 當 true 時,移除已知位元字串 修改符號 中不必要的括號,例如 <<foo::binary()>> 會變成 <<foo::binary>>,或為自訂修改符號新增括號,其中 <<foo::custom_type>> 會變成 <<foo::custom_type()>>。預設為 true。此選項會變更 AST。

  • :normalize_charlists_as_sigils(自 v1.15.0 起) - 當 true 時,將字元清單格式化為 ~c sigil,例如 'foo' 會變成 ~c"foo"。預設為 true。此選項會變更 AST。

設計原則

格式化程式是在三個原則下設計的。

首先,格式化程式絕不會變更程式碼的語意。這表示輸入 AST 和輸出 AST 幾乎總是等值的。格式化程式會變更 AST 的唯一情況是,當輸入 AST 會導致編譯器警告,而輸出 AST 則不會。如果需要,可以透過格式化選項停用格式化程式變更 AST 的情況。

第二個原則是提供盡可能少的組態。這透過移除爭議點來簡化格式化程式的採用,同時確保整個社群一致遵循單一風格。

格式化程式不會硬式編碼名稱。格式化程式不會因為函式命名為 defmoduledef 等而有特別的行為。此原則反映了 Elixir 成為可擴充語言的目標,開發人員可以使用新的建構來擴充語言,就像它們是語言的一部分一樣。當絕對有必要根據名稱變更行為時,此行為應該是可組態的,例如 :locals_without_parens 選項。

執行格式化程式

格式化程式會盡可能將大部分內容放入單一行中,並在無法放入時盡可能加入換行符號。

在某些情況下,這可能會導致不想要的格式。因此,格式化程式產生的某些程式碼可能在美觀上不討喜,且可能需要開發人員明確介入。這就是我們不建議在現有程式碼庫中盲目執行格式化程式的緣故。您應該格式化並檢查每個已格式化的檔案。

例如,格式化程式可能會將長函式定義拆成多個子句

def my_function(
  %User{name: name, age: age, ...},
  arg1,
  arg2
) do
  ...
end

雖然以上的程式碼完全有效,但您可能比較喜歡在函式主體內比對結構變數,以將定義保留在單一行中

def my_function(%User{} = user, arg1, arg2) do
  %{name: name, age: age, ...} = user
  ...
end

在某些情況下,您可以利用格式化程式不會產生優雅程式碼的事實作為重構的提示。請看以下程式碼

def board?(board_id, %User{} = user, available_permissions, required_permissions) do
  Tracker.OrganizationMembers.user_in_organization?(user.id, board.organization_id) and
    required_permissions == Enum.to_list(MapSet.intersection(MapSet.new(required_permissions), MapSet.new(available_permissions)))
end

以上的程式碼有很長的程式行,而執行格式化程式並不會解決這個問題。事實上,格式化程式可能會讓您更明顯地看出您有複雜的表達式

def board?(board_id, %User{} = user, available_permissions, required_permissions) do
  Tracker.OrganizationMembers.user_in_organization?(user.id, board.organization_id) and
    required_permissions ==
      Enum.to_list(
        MapSet.intersection(
          MapSet.new(required_permissions),
          MapSet.new(available_permissions)
        )
      )
end

將此類案例視為建議,表示您的程式碼應重新整理

def board?(board_id, %User{} = user, available_permissions, required_permissions) do
  Tracker.OrganizationMembers.user_in_organization?(user.id, board.organization_id) and
    matching_permissions?(required_permissions, available_permissions)
end

defp matching_permissions?(required_permissions, available_permissions) do
  intersection =
    required_permissions
    |> MapSet.new()
    |> MapSet.intersection(MapSet.new(available_permissions))
    |> Enum.to_list()

  required_permissions == intersection
end

總而言之:由於格式化程式無法變更程式碼的語意,有時需要調整或重新整理程式碼以取得最佳格式化。為協助更了解如何控制格式化程式,我們在下一節中說明格式化程式保留使用者編碼的情況,以及如何控制多行表達式。

行長

關於格式化程式的另一點是,:line_length 組態是準則。在許多情況下,格式化程式無法將程式碼分開,這表示它會超過行長。例如,如果您有長字串

"this is a very long string that will go over the line length"

格式化程式不知道如何在不變更底層語法表示的情況下將其分開,因此由您介入

"this is a very long string " <>
   "that will go over the line length"

字串串接使程式碼符合單行,並提供更多選項給格式化程式。

這也可能出現在 do/end 區塊中,其中 do 關鍵字 (或 ->) 可能超過行長,因為格式化程式沒有機會以可讀方式加入換行符號。例如,如果您執行

case very_long_expression() do
end

而只有 do 關鍵字在行長之上,Elixir 不會發出此項

case very_long_expression()
do
end

因此,它寧願完全不觸碰該行,並將 do 保留在行長限制之上。

保留使用者的格式化

格式化程式在某些情況下會尊重輸入格式。這些情況如下所列

  • 數字中的不顯著位數會保持原樣。但是,格式化程式始終會為超過 5 位數的十進制數字插入底線,並將十六進制數字轉換為大寫

  • 字串、字元清單、原子和符號會保持原樣。不會自動跳脫或取消跳脫任何字元。也會尊重輸入中的分隔符選擇

  • 區塊內的換行符號會保持與輸入相同,但下列情況除外

    1. 佔用多行的表達式在前後都會有一個空行,以及 2) 空行會始終壓縮成單一空行
  • 使用者可以選擇使用 :do 關鍵字或 do-end 區塊

  • 如果清單、元組、位元字串、映射、結構和函式呼叫在開頭括號後換行,並在結尾括號前換行,則會分成多行

  • 在某些運算子(例如管道運算子)和其它運算子(例如比較運算子)之前換行

上述行為並非保證。我們可能會在未來移除或新增新的規則。記錄這些規則的目的是讓使用者更了解格式化程式的預期行為。

多行清單、映射、元組等

您可以在開頭括號後換行,並在結尾括號前換行,強制清單、元組、位元字串、映射、結構和函式呼叫每行只有一個項目。例如

[
  foo,
  bar
]

如果括號周圍沒有換行,則格式化程式會嘗試將所有內容放在單一行中,因此以下程式碼片段

[foo,
 bar]

會格式化為

[foo, bar]

您也可以透過讓每個項目在自己的行中,強制函式呼叫和關鍵字呈現在多行中

defstruct name: nil,
          age: 0

格式化程式會將上述程式碼保持為每行一個關鍵字項目。若要避免這種情況,只需將所有內容壓縮到單一行中即可。

函式呼叫中的括號和無括號

Elixir 有兩種函式呼叫語法。有括號和無括號。預設情況下,Elixir 會將括號新增到所有呼叫,但下列情況除外

  1. 具有 do-end 區塊的呼叫
  2. 沒有括號的區域呼叫,其中區域呼叫的名稱和元數也列在 :locals_without_parens 下(元數為 0 的呼叫除外,因為編譯器總是需要括號)

括號和無括號的選擇也會影響縮排。當帶有括號的函式呼叫不適合放在同一行時,格式化程式會在括號周圍換行,並使用兩個空格縮排引數

some_call(
  arg1,
  arg2,
  arg3
)

另一方面,沒有括號的函式呼叫總是會根據函式呼叫長度本身縮排,如下所示

some_call arg1,
          arg2,
          arg3

如果最後一個引數是資料結構,例如映射和清單,並且資料結構的開頭與函式呼叫放在同一行,則不會縮排,這允許類似這樣的程式碼

Enum.reduce(some_collection, initial_value, fn element, acc ->
  # code
end)

some_function_without_parens %{
  foo: :bar,
  baz: :bat
}

程式碼註解

格式化程式也會處理程式碼註解,以確保註解開頭 (#) 和下一個字元之間始終新增一個空格。

格式化程式也會將所有尾隨註解抽取到它們的前一行。例如,以下程式碼

hello #world

將會重寫為

# world
hello

因為程式碼註解是與程式碼表示法 (AST) 分開處理的,所以有些情況下程式碼註解會被程式碼格式化程式視為不明確。例如,以下匿名函式的註解

fn
  arg1 ->
    body1
    # comment

  arg2 ->
    body2
end

以及這個註解

fn
  arg1 ->
    body1

  # comment
  arg2 ->
    body2
end

被視為等價(巢狀結構和大部分使用者格式化會被捨棄)。在這種情況下,程式碼格式化程式總是會格式化成後者。

換行符

格式化程式會將程式碼中的所有換行符從 \r\n 轉換為 \n

連結到此函式

get_compiler_option(key)

檢視原始碼 (自 1.10.0 起)
@spec get_compiler_option(atom()) :: term()

傳回給定編譯器選項的值。

所有選項的說明,請參閱 put_compiler_option/2

範例

Code.get_compiler_option(:debug_info)
#=> true
連結到此函式

loaded?(module)

檢視原始碼 (自 1.15.0 起)
@spec loaded?(module()) :: boolean()

如果模組已載入,傳回 true

此函式不會嘗試載入模組。對於這種行為,可以使用 ensure_loaded?/1

範例

iex> Code.loaded?(Atom)
true

iex> Code.loaded?(NotYetLoaded)
false
連結到此函式

prepend_path(path, opts \\ [])

檢視原始碼
@spec prepend_path(Path.t(), [{:cache, boolean()}]) :: boolean()

將路徑新增到 Erlang VM 程式碼路徑清單的最前面。

這是 Erlang VM 用於尋找模組程式碼的目錄清單。檔案清單會針對每個 Erlang VM 節點進行管理。

路徑會在預先新增之前使用 Path.expand/1 進行擴充。它需要路徑存在。傳回一個布林值,表示路徑是否已成功新增。

範例

Code.prepend_path(".")
#=> true

Code.prepend_path("/does_not_exist")
#=> false

選項

  • :cache - (自 v1.15.0 起) 為 true 時,會在第一次遍歷程式碼路徑時快取該路徑,以減少檔案系統作業。它需要 Erlang/OTP 26,否則它是一個空操作。
連結到此函式

prepend_paths(paths, opts \\ [])

檢視原始碼 (自 1.15.0 起)
@spec prepend_paths([Path.t()], [{:cache, boolean()}]) :: :ok

paths 清單新增到 Erlang VM 程式碼路徑清單的最前面。

這是 Erlang VM 用於尋找模組程式碼的目錄清單。檔案清單會針對每個 Erlang VM 節點進行管理。

所有路徑會在預先新增之前使用 Path.expand/1 進行擴充。只會預先新增現有的路徑。此函式總是傳回 :ok,不論預先新增了多少路徑。如果您需要更多控制,請使用 prepend_path/1

範例

Code.prepend_paths([".", "/does_not_exist"])
#=> :ok

選項

  • :cache - 當為 true 時,會在第一次遍歷時快取程式碼路徑,以減少檔案系統操作。需要 Erlang/OTP 26,否則為無操作。
連結到此函式

purge_compiler_modules()

檢視原始碼 (自 1.7.0 起)
@spec purge_compiler_modules() :: {:ok, non_neg_integer()}

清除編譯器模組。

編譯器使用暫時模組來編譯程式碼。例如,elixir_compiler_1elixir_compiler_2 等。如果編譯的程式碼儲存對匿名函數或類似函數的參照,Elixir 編譯器可能無法回收這些模組,導致記憶體中保留過多程式碼,最後產生 elixir_compiler_12345 等模組。

此函數會清除編譯器目前保留的所有模組,讓舊的編譯器模組名稱可以重複使用。如果有任何處理序正在執行這些模組中的任何程式碼,它們也會被終止。

此函數僅供在持續評估程式碼的長期執行節點中呼叫。

它會回傳 {:ok, number_of_modules_purged}

連結到此函式

put_compiler_option(key, value)

檢視原始碼 (自 1.10.0 起)
@spec put_compiler_option(atom(), term()) :: :ok

儲存編譯選項。

變更編譯選項會影響在特定 Erlang VM 節點中執行的所有處理序。

可用的選項有

  • :docs - 當為 true 時,會在編譯的模組中保留文件。預設為 true

  • :debug_info - 當為 true 時,會在編譯的模組中保留偵錯資訊。預設為 true。這會啟用靜態分析工具,因為它允許開發人員部分重建原始程式碼。因此,不建議停用 :debug_info,因為它會移除 Elixir 編譯器和其他工具提供回饋的能力。如果您想要在部署時移除 :debug_infomix release 等工具預設會執行此動作。此外,mix test 會透過 :test_elixirc_options 專案設定選項停用它。此選項也可以使用 @compile 指令針對每個模組覆寫。

  • :ignore_already_consolidated (自 v1.10.0 起) - 當 true 時,不會在協定已整合且新增實作時發出警告。預設為 false

  • :ignore_module_conflict - 當 true 時,不會在模組已定義時發出警告。預設為 false

  • :relative_paths - 當 true 時,會在編譯器產生的引號節點、警告和錯誤中使用相對路徑。請注意,停用此選項不會影響執行時期的警告和錯誤。預設為 true

  • :warnings_as_errors - 導致在產生警告時編譯失敗。預設為 false

  • :no_warn_undefined (自 v1.10.0 起) - 模組和 {Mod, fun, arity} 元組清單,在編譯時不會發出模組或函式不存在的警告。傳遞原子 :all 以略過所有未定義函式的警告。這在進行動態編譯時很有用。預設為 []

  • :tracers (自 v1.10.0 起) - 編譯期間要使用的追蹤器 (模組) 清單。有關更多資訊,請參閱模組文件。預設為 []

  • :parser_options (自 v1.10.0 起) - 編譯檔案時傳遞給解析器的選項關鍵字清單。它接受與 string_to_quoted/2 相同的選項 (除了會變更 AST 本身的選項)。這可以與追蹤器結合使用,以擷取編譯期間發生的事件的在地化資訊。預設為 [columns: true]。此選項僅影響程式碼編譯函式,例如 compile_string/2compile_file/2,但不影響 string_to_quoted/2 和相關函式,因為後者用於編譯以外的其他目的。

  • :on_undefined_variable (自 v1.15.0 起) - :raise:warn。當為 :raise (預設) 時,未定義的變數會觸發編譯錯誤。如果您希望未定義的變數發出警告並擴充為對同名零元函式的區域呼叫 (例如,node 會擴充為 node()),則可以將其設定為 :warn。此 :warn 行為僅出於相容性原因而存在,用於處理舊相依項。

它總是傳回 :ok。對於無效的選項,會引發錯誤。

範例

Code.put_compiler_option(:debug_info, true)
#=> :ok
連結到此函式

quoted_to_algebra(quoted, opts \\ [])

檢視原始碼 (自 1.13.0 起)
@spec quoted_to_algebra(
  Macro.t(),
  keyword()
) :: Inspect.Algebra.t()

使用 Elixir 的格式化規則,將引號中的表達式轉換為代數文件。

代數文件可透過呼叫轉換成字串

doc
|> Inspect.Algebra.format(:infinity)
|> IO.iodata_to_binary()

如需執行相同功能的高階函數,請參閱 Macro.to_string/1

格式考量

Elixir AST 不包含字串、清單或具有兩個元素的元組等文字的元資料,這表示產生的代數文件不會尊重所有使用者偏好,且註解可能會放錯位置。如需獲得更好的結果,你可以使用 :token_metadata:unescape:literal_encoder 選項傳遞給 string_to_quoted/2,以提供格式化程式額外的資訊

[
  literal_encoder: &{:ok, {:__block__, &2, [&1]}},
  token_metadata: true,
  unescape: false
]

這將產生包含資訊的 AST,例如 do 區塊的開始和結束列或符號分隔符號,且透過將文字包在區塊中,它們現在可以保留元資料,例如列號、字串分隔符號和跳脫序列,或整數格式化(例如 0x2a 而不是 47)。但是,請注意此 AST 無效。如果你對其進行評估,它將不會具有與一般 Elixir AST 相同的語意,這是因為 :unescape:literal_encoder 選項。但是,如果你正在執行原始碼處理,這些選項很有用,因為保留使用者選擇和註解位置非常重要。

選項

  • :comments - 與引號表達式關聯的註解清單。預設為 []。建議將 :token_metadata:literal_encoder 選項都傳遞給 string_to_quoted_with_comments/2,以取得註解的正確位置

  • :escape - 當為 true 時,跳脫序列(例如 \n)將跳脫成 \\n。如果在使用 string_to_quoted/2 時將 :unescape 選項設定為 false,將此選項設定為 false 將防止它兩次跳脫序列。預設為 true

  • :locals_without_parens - 一個關鍵字清單,包含名稱和應盡可能不帶括號的元組對。元組可以是原子 :*,表示該名稱的所有元組。格式化程式已包含一個函式清單,此選項會擴充此清單。

  • :syntax_colors - 輸出色彩化的關鍵字清單。更多資訊請參閱 Inspect.Opts

連結到此函式

require_file(file, relative_to \\ nil)

檢視原始碼
@spec require_file(binary(), nil | binary()) :: [{module(), binary()}] | nil

需要給定的 file

接受 relative_to 作為參數,以告知檔案所在位置。如果檔案已經被載入,require_file/2 就不會執行任何動作,並傳回 nil

請注意,如果 require_file/2 被不同的程序同時呼叫,第一個呼叫 require_file/2 的程序會取得鎖定,而其餘的程序會封鎖,直到檔案可用。這表示如果 require_file/2 使用特定檔案被呼叫多次,該檔案只會被編譯一次。第一個呼叫 require_file/2 的程序會取得已載入模組的清單,其他程序會取得 nil。已載入檔案的清單會由每個 Erlang VM 節點管理。

如果您想編譯檔案而不追蹤其檔名,請參閱 compile_file/2。最後,如果您想取得評估檔案的結果,而不是定義在其中的模組,請參閱 eval_file/2

範例

如果檔案尚未被載入,它會傳回模組清單

modules = Code.require_file("eex_test.exs", "../eex/test")
List.first(modules)
#=> {EExTest.Compiled, <<70, 79, 82, 49, ...>>}

如果檔案已經被載入,它會傳回 nil

Code.require_file("eex_test.exs", "../eex/test")
#=> nil
連結到此函式

required_files()

檢視原始碼 (自 1.7.0 起)
@spec required_files() :: [binary()]

列出所有需要的檔案。

範例

Code.require_file("../eex/test/eex_test.exs")
List.first(Code.required_files()) =~ "eex_test.exs"
#=> true
連結到此函式

string_to_quoted(string, opts \\ [])

檢視原始碼
@spec string_to_quoted(
  List.Chars.t(),
  keyword()
) ::
  {:ok, Macro.t()}
  | {:error, {location :: keyword(), binary() | {binary(), binary()}, binary()}}

將給定的字串轉換為其引號形式。

如果成功,傳回 {:ok, quoted_form},否則傳回 {:error, {meta, message_info, token}}

選項

  • :file - 發生解析錯誤時要報告的檔名。預設為 "nofile"

  • :line - 要解析的字串的起始行。預設為 1。

  • :column - (自 v1.11.0 起) 正在解析的字串的起始欄位。預設為 1。

  • :columns - 若為 true,將 :column 鍵附加到有引號的元資料。預設為 false

  • :unescape (自 v1.10.0 起) - 若為 false,保留跳脫序列。例如,"null byte\\t\\x00" 將會保持原樣,而不是轉換為位元串文字。請注意,若您將此選項設定為 false,產生的 AST 將不再有效,但它可用於分析/轉換原始碼,通常與 quoted_to_algebra/2 結合使用。預設為 true

  • :existing_atoms_only - 若為 true,當分詞器找到不存在的原子時,會引發錯誤。預設為 false

  • :token_metadata (自 v1.10.0 起) - 若為 true,在表達式 AST 中包含與代幣相關的元資料,例如 doend 代幣、關閉代幣、表達式結尾以及符號分隔符的元資料。請參閱 Macro.metadata/0。預設為 false

  • :literal_encoder (自 v1.10.0 起) - 如何在 AST 中編碼文字。它必須是一個接收兩個參數的函式,文字及其元資料,並且它必須傳回 {:ok, ast :: Macro.t}{:error, reason :: binary}。如果您傳回的內容不是文字本身作為 term,則 AST 將不再有效。此選項對於原始碼的文字分析仍然有用。

  • :static_atoms_encoder - 靜態原子編碼器函式,請參閱下方的「:static_atoms_encoder 函式」區段。請注意,此選項會覆寫靜態原子的 :existing_atoms_only 行為,但 :existing_atoms_only 仍用於動態原子,例如帶有內插的原子。

  • :emit_warnings (自 v1.16.0 起) - 若為 false,不會發出與分詞/解析相關的警告。預設為 true

Macro.to_string/2

將字串轉換為其有引號形式的相反動作是 Macro.to_string/2,它將有引號形式轉換為字串/二進位表示形式。

:static_atoms_encoder 函式

static_atoms_encoder: &my_encoder/2 作為參數傳遞時,每當分詞器需要建立「靜態」原子時,就會呼叫 my_encoder/2。靜態原子是 AST 中的原子,用作別名、遠端呼叫、區域呼叫、變數名稱、一般原子和關鍵字清單。

編碼器函式將接收原子名稱(作為二進位)和一個包含目前檔案、行和欄位的關鍵字清單。它必須傳回 {:ok, token :: term} | {:error, reason :: binary}

編碼器函數應從給定的字串建立一個原子。若要產生有效的 AST,則必須傳回 {:ok, term},其中 term 是原子。不過,也可以傳回非原子,但這種情況下,AST 不再「有效」,因為無法用來編譯或評估 Elixir 程式碼。這項功能的用例是,如果你想在使用者介面情況下使用 Elixir 解析器,但不想耗盡原子表。

並非 AST 中存在的所有原子都會呼叫原子編碼器。它不會針對下列原子呼叫

  • 運算子 (:+:- 等)

  • 語法關鍵字 (fndoelse 等)

  • 包含內插的原子 (:"#{1 + 1} is two"),因為這些原子是在執行階段建構的

  • 用於表示單字母符號的原子,例如 :sigil_X(但會編碼多字母符號,例如 :sigil_XYZ)。

連結到此函式

string_to_quoted!(string, opts \\ [])

檢視原始碼
@spec string_to_quoted!(
  List.Chars.t(),
  keyword()
) :: Macro.t()

將給定的字串轉換為其引號形式。

如果成功,則傳回 AST,否則會引發例外。例外是 TokenMissingError(通常是因為表達式不完整),否則為 SyntaxError

查看 string_to_quoted/2 以取得選項資訊。

連結到此函式

string_to_quoted_with_comments(string, opts \\ [])

檢視原始碼 (自 1.13.0 起)
@spec string_to_quoted_with_comments(
  List.Chars.t(),
  keyword()
) ::
  {:ok, Macro.t(), [map()]} | {:error, {location :: keyword(), term(), term()}}

將給定的字串轉換為其引號形式和註解清單。

在對原始碼執行文字變更時,此函數很有用,同時保留註解和文字位置等資訊。

如果成功,則傳回 {:ok, quoted_form, comments},否則傳回 {:error, {line, error, token}}

註解是具有下列欄位的對應

  • :line - 原始碼的行號

  • :text - 註解的完整文字,包括開頭的 #

  • :previous_eol_count - 註解與前一個 AST 節點或註解之間有多少行尾

  • :next_eol_count - 註解與下一個 AST 節點或註解之間有多少個換行符號

查看 string_to_quoted/2 以取得選項資訊。

範例

iex> Code.string_to_quoted_with_comments("""
...> :foo
...>
...> # Hello, world!
...>
...>
...> # Some more comments!
...> """)
{:ok, :foo, [
  %{line: 3, column: 1, previous_eol_count: 2, next_eol_count: 3, text: "# Hello, world!"},
  %{line: 6, column: 1, previous_eol_count: 3, next_eol_count: 1, text: "# Some more comments!"},
]}

iex> Code.string_to_quoted_with_comments(":foo # :bar")
{:ok, :foo, [
  %{line: 1, column: 6, previous_eol_count: 0, next_eol_count: 0, text: "# :bar"}
]}
連結到此函式

string_to_quoted_with_comments!(string, opts \\ [])

檢視原始碼 (自 1.13.0 起)
@spec string_to_quoted_with_comments!(
  List.Chars.t(),
  keyword()
) :: {Macro.t(), [map()]}

將給定的字串轉換為其引號形式和註解清單。

如果成功,會傳回 AST 和註解清單,否則會引發例外。例外狀況為 TokenMissingError(通常是因為表達式不完整),否則為 SyntaxError

查看 string_to_quoted/2 以取得選項資訊。

連結到此函式

unrequire_files(files)

檢視原始碼 (自 1.7.0 起)
@spec unrequire_files([binary()]) :: :ok

從需要的檔案清單中移除檔案。

檔案中定義的模組不會被移除;呼叫此函式只會將它們從清單中移除,讓它們可以再次被 require。

檔案清單會在每個 Erlang VM 節點中管理。

範例

# Require EEx test code
Code.require_file("../eex/test/eex_test.exs")

# Now unrequire all files
Code.unrequire_files(Code.required_files())

# Note that modules are still available
function_exported?(EExTest.Compiled, :before_compile, 0)
#=> true
連結到此函式

with_diagnostics(opts \\ [], fun)

檢視原始碼 (自 1.15.0 起)
@spec with_diagnostics(
  keyword(),
  (-> result)
) :: {result, [diagnostic(:warning | :error)]}
when result: term()

執行給定的 fun 並擷取所有診斷訊息。

診斷是程式碼評估或單一檔案編譯期間發出的警告和錯誤,以及 IO.warn/2 等函式發出的警告和錯誤。

如果使用 mix compileKernel.ParallelCompiler,請注意它們已經擷取並傳回診斷。

選項

  • :log - 如果診斷應該在發生時記錄下來。預設為 false

救援錯誤

with_diagnostics/2 不會自動處理例外。你可以透過在 fun 中加入 try/1 來擷取例外。

{result, all_errors_and_warnings} =
  Code.with_diagnostics(fn ->
    try do
      {:ok, Code.compile_quoted(quoted)}
    rescue
      err -> {:error, err}
    end
  end)