Spec-Zone.ru › Elixir 1.18

Исходный код Код

Утилиты для управления компиляцией, вычислением и загрузкой кода.

Этот модуль дополняет модуль Erlang's :code, добавляя поведение, специфичное для Elixir. Для функций по манипулированию AST Elixir (вместо его вычисления), обратитесь к модулю Macro.

Работа с файлами

Этот модуль содержит три функции для компиляции и вычисления файлов. Вот краткое описание их и их поведения:

  • require_file/2 - компилирует файл и отслеживает его имя. Не компилирует файл повторно, если он был ранее загружен.

  • compile_file/2 - компилирует файл без отслеживания его имени. Компилирует файл несколько раз при многократном вызове.

  • eval_file/2 - вычисляет содержимое файла без отслеживания его имени. Возвращает результат последнего выражения в файле, вместо определённых в нём модулей. Вычисленные файлы не активируют отслеживатели компиляции, описанные в следующем разделе.

Вкратце, первая функция должна использоваться, когда вам нужно отслеживать файлы, обрабатываемые системой, чтобы избежать повторной компиляции одного и того же файла. Это обычно используется в скриптах.

compile_file/2 необходимо использовать, когда вы заинтересованы в модулях, определённых в файле, без отслеживания. eval_file/2 следует использовать, когда вы заинтересованы в результате вычисления файла, а не в определённых в нём модулях.

Перечисленные выше функции работают с исходным кодом Elixir. Если вы хотите работать с модулями, скомпилированными в байткод, имеющими расширение .beam и обычно расположенными в каталоге _build проекта Mix, обратитесь к функциям в модуле Erlang's :code.

Загрузка кода в Erlang VM

Erlang имеет два режима загрузки кода: интерактивный и встроенный.

По умолчанию Erlang VM работает в интерактивном режиме, где модули загружаются по мере необходимости. Во встроенном режиме происходит обратное, так как все модули должны быть загружены заранее или явно.

Вы можете использовать ensure_loaded/1 (а также ensure_loaded?/1 и ensure_loaded!/1), чтобы проверить, загружен ли модуль перед его использованием и выполнить соответствующие действия.

ensure_compiled/1 и ensure_compiled!/1

Elixir также включает функции ensure_compiled/1 и ensure_compiled!/1, которые являются супермножеством ensure_loaded/1.

Поскольку компиляция Elixir происходит параллельно, в некоторых ситуациях вам может потребоваться использовать модуль, который ещё не был скомпилирован, поэтому его даже нельзя загрузить.

При вызове ensure_compiled/1 и ensure_compiled!/1 компиляция вызывающего модуля приостанавливается до тех пор, пока модуль не станет доступным. Обратите внимание на разницу между ensure_compiled/1 и ensure_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 — модуль, из которого происходит импорт, за которым следуют name и arity импортированной функции/макроса. Событие :remote_function/:remote_macro всё ещё может быть выпущено для импортированного модуля/имени/арности.

  • {:imported_quoted, meta, module, name, [arity]} - отслеживается всякий раз, когда импортированная функция или макрос обрабатываются внутри quote/2. meta — это метаданные вызова AST, module — модуль, из которого происходит импорт, за которым следуют name и список arities импортированной функции/макроса.

  • {:alias, meta, alias, as, opts} - отслеживается всякий раз, когда alias псевдонимизируется в as. meta — это метаданные псевдонима AST, а opts — параметры псевдонима.

  • {:alias_expansion, meta, as, alias} отслеживается всякий раз, когда происходит расширение псевдонима для ранее определённого alias, т. е. когда пользователь пишет as, что расширяется до alias. 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 — вызванный модуль, за которым следуют name и arity.

  • {:local_function, meta, name, arity} и {:local_macro, meta, name, arity} - отслеживаются всякий раз, когда ссылаются на локальную функцию или макрос. meta — это метаданные вызова AST, за которым следуют name и arity.

  • {:compile_env, app, path, return} - отслеживается всякий раз, когда вызываются Application.compile_env/3 или Application.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

Обзор

Типы

binding()

Список со всеми переменными и их значениями.

diagnostic(severity)

Диагностика, возвращаемая компилятором и оценкой кода.

line()

Строка. 0 указывает на отсутствие строки.

position()

Позиция диагностики.

Функции

append_path(path, opts \\ [])

Добавляет путь в список путей к коду Erlang VM.

append_paths(paths, opts \\ [])

Добавляет список paths в список путей к коду Erlang VM.

available_compiler_options()

Возвращает список всех доступных опций компилятора.

can_await_module_compilation?()

Возвращает true, если текущий процесс может ожидать компиляции модуля.

compile_file(file, relative_to \\ nil)

Компилирует указанный файл.

compile_quoted(quoted, file \\ "nofile")

Компилирует выражение в кавычках.

compile_string(string, file \\ "nofile")

Компилирует заданную строку.

compiler_options()

Получает все опции компиляции из сервера кода.

compiler_options(opts)

Сохраняет все заданные опции компиляции.

delete_path(path)

Удаляет путь из списка путей к коду Erlang VM.

delete_paths(paths)

Удаляет список путей из списка путей к коду Erlang VM.

ensure_all_loaded(modules)

Обеспечивает загрузку указанных модулей.

ensure_all_loaded!(modules)

То же, что и ensure_all_loaded/1, но вызывает исключение, если какой-либо из модулей не может быть загружен.

ensure_compiled(module)

Аналогично ensure_compiled!/1, но указывает, что можно продолжить без указанного модуля.

ensure_compiled!(module)

Обеспечивает компиляцию и загрузку указанного модуля.

ensure_loaded(module)

Обеспечивает загрузку указанного модуля.

ensure_loaded!(module)

То же, что и ensure_loaded/1, но вызывает исключение, если модуль не может быть загружен.

ensure_loaded?(module)

Обеспечивает загрузку указанного модуля.

env_for_eval(env_or_opts)

Возвращает среду для вычислений.

eval_file(file, relative_to \\ nil)

Вычисляет указанный файл.

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

Вычисляет содержимое в кавычках.

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

Вычисляет заданное quoted содержимое с binding и env.

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

Вычисляет содержимое, заданное string.

fetch_docs(module_or_path)

Возвращает документацию для указанного модуля или пути к файлу .beam.

format_file!(file, opts \\ [])

Форматирует файл.

format_string!(string, opts \\ [])

Форматирует предоставленный код string.

get_compiler_option(key)

Возвращает значение заданной опции компилятора.

loaded?(module)

Возвращает true если модуль загружен.

prepend_path(path, opts \\ [])

Добавляет путь в начало списка путей к коду Erlang VM.

prepend_paths(paths, opts \\ [])

Добавляет список paths в начало списка путей к коду Erlang VM.

print_diagnostic(diagnostic, opts \\ [])

Выводит диагностику в стандартный поток ошибок.

purge_compiler_modules()

Очищает модули компилятора.

put_compiler_option(key, value)

Сохраняет опцию компиляции.

quoted_to_algebra(quoted, opts \\ [])

Преобразует выражение в кавычках в алгебраическое представление с использованием правил форматирования Elixir.

require_file(file, relative_to \\ nil)

Использует указанный file.

required_files()

Список всех необходимых файлов.

string_to_quoted(string, opts \\ [])

Преобразует данную строку в её кавычковое представление.

string_to_quoted!(string, opts \\ [])

Преобразует данную строку в её кавычковое представление.

string_to_quoted_with_comments(string, opts \\ [])

Преобразует заданную строку в её кавычковое представление и список комментариев.

string_to_quoted_with_comments!(string, opts \\ [])

Преобразует заданную строку в её кавычковое представление и список комментариев.

unrequire_files(files)

Удаляет файлы из списка необходимых файлов.

with_diagnostics(opts \\ [], fun)

Выполняет заданную fun и захватывает все диагностические сообщения.

Типы

binding()Source

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

Список со всеми переменными и их значениями.

Ключи связывания обычно являются атомами, но могут быть кортежами для переменных, определенных в другом контексте.

diagnostic(severity)Source

@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 может содержать .eex шаблоны из lib/foo/bar.eex. Синтаксическая ошибка в шаблоне EEx будет указывать на файл lib/foo/bar.eex, но исходный код — lib/foo.ex.

line()Source

@type line() :: non_neg_integer()

Номер строки. 0 указывает, что строка отсутствует.

position()Source

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

Позиция диагностики.

Может быть либо номером строки, либо {line, column}. Номера строк и столбцов — с единицы. Позиция 0 обозначает неизвестное значение.

Функции

append_path(path, opts \\ [])Source

@spec append_path(Path.t(), [{:cache, boolean()}]) :: true | false

Добавляет путь в список путей к коду виртуальной машины Erlang.

Это список каталогов, используемых виртуальной машиной Erlang для поиска кода модулей. Список файлов управляется по узлу виртуальной машины Erlang.

Путь расширяется с помощью 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 \\ [])Source

@spec append_paths([Path.t()], [{:cache, boolean()}]) :: :ok

Добавляет список paths в список путей к коду виртуальной машины Erlang.

Это список каталогов, используемых виртуальной машиной Erlang для поиска кода модулей. Список файлов управляется по узлу виртуальной машины Erlang.

Все пути расширяются с помощью Path.expand/1 перед добавлением. Добавляются только существующие пути. Эта функция всегда возвращает :ok, независимо от того, сколько путей было добавлено. Используйте append_path/1, если вам нужен больший контроль.

Примеры

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

Параметры

  • :cache - при значении true, путь к коду кешируется при первом проходе, чтобы уменьшить количество операций с файловой системой. Требуется Erlang/OTP 26, в противном случае это бесполезно.

available_compiler_options()Source

@spec available_compiler_options() :: [atom()]

Возвращает список всех доступных параметров компилятора.

Описание всех параметров см. в put_compiler_option/2.

Примеры

Code.available_compiler_options()
#=> [:docs, :debug_info, ...]

can_await_module_compilation?()Source

@spec can_await_module_compilation?() :: boolean()

Возвращает true, если текущий процесс может ожидать компиляции модуля.

При компиляции кода Elixir через Kernel.ParallelCompiler, который используется Mix и elixirc, обращение к модулю, который еще не скомпилирован, заблокирует вызывающий процесс до тех пор, пока модуль не станет доступным. Выполнение скриптов Elixir, например, передача имени файла в elixir, не ожидает.

compile_file(file, relative_to \\ nil)Source

@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")Source

@spec compile_quoted(Macro.t(), binary()) :: [{module(), binary()}]

Компилирует выражение в виде цитаты.

Возвращает список кортежей, где первый элемент — имя модуля, а второй — его байткод (как двоичное значение). В качестве второго аргумента может быть задан file, который будет использоваться для вывода предупреждений и ошибок.

compile_string(string, file \\ "nofile")Source

@spec compile_string(List.Chars.t(), binary()) :: [{module(), binary()}]

Компилирует заданную строку.

Возвращает список кортежей, где первый элемент — имя модуля, а второй — его байткод (как двоичное значение). В качестве второго аргумента может быть передан file, который будет использоваться для вывода предупреждений и ошибок.

Предупреждение: string может содержать любой код Elixir, и код может выполняться с такими же привилегиями, как и виртуальная машина Erlang: это означает, что такой код может представлять угрозу для машины (например, выполняя системные команды). Не используйте compile_string/2 с ненадежными данными (например, строками, полученными из сети).

compiler_options()Source

@spec compiler_options() :: map()

Получает все параметры компиляции из сервера кода.

Для получения отдельных параметров см. get_compiler_option/1. Описание всех параметров см. в put_compiler_option/2.

Примеры

Code.compiler_options()
#=> %{debug_info: true, docs: true, ...}

compiler_options(opts)Source

@spec compiler_options(Enumerable.t({atom(), term()})) :: %{
  optional(atom()) => term()
}

Сохраняет все заданные параметры компиляции.

Изменение параметров компиляции влияет на все процессы, работающие в узле виртуальной машины Erlang. Для хранения отдельных параметров и описания всех параметров см. put_compiler_option/2.

Возвращает карту с предыдущими значениями.

Примеры

Code.compiler_options(infer_signatures: false)
#=> %{infer_signatures: true}

delete_path(path)Source

@spec delete_path(Path.t()) :: boolean()

Удаляет путь из списка путей к коду виртуальной машины Erlang.

Это список каталогов, используемых виртуальной машиной Erlang для поиска кода модулей. Список файлов управляется по узлу виртуальной машины Erlang.

Путь расширяется с помощью Path.expand/1 перед удалением. Если путь не существует, функция возвращает false.

Примеры

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

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

delete_paths(paths)Source

@spec delete_paths([Path.t()]) :: :ok

Удаляет список путей из списка путей к коду виртуальной машины Erlang.

Это список каталогов, используемых виртуальной машиной Erlang для поиска кода модулей. Список файлов управляется по узлу виртуальной машины Erlang.

Путь расширяется с помощью Path.expand/1 перед удалением. Если путь не существует, функция возвращает false.

ensure_all_loaded(modules)Source

@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)Source

@spec ensure_all_loaded!([module()]) :: :ok

То же, что и ensure_all_loaded/1, но генерирует исключение, если какой-либо из модулей не может быть загружен.

ensure_compiled(module)Source

@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)Source

@spec ensure_compiled!(module()) :: module()

Обеспечивает компиляцию и загрузку данного модуля.

Если модуль уже загружен, функция работает как no-op. Если модуль ещё не был скомпилирован, ensure_compiled!/1 приостанавливает компиляцию вызывающего кода до тех пор, пока модуль, указанный в ensure_compiled!/1, не станет доступен или не будут скомпилированы все файлы текущего проекта. Если компиляция завершается, а модуль недоступен или находится в тупике, возникает ошибка.

Поскольку эта функция приостанавливает компиляцию, используйте её осторожно. В частности, избегайте её использования для угадывания, какие модули присутствуют в системе. Чрезмерное использование этой функции также может привести к тупикам, когда два модуля одновременно проверяют, скомпилирован ли другой. Эта функция возвращает специфический код ошибки недоступности, когда мы не можем успешно проверить, доступен ли модуль или нет.

См. документацию модуля для получения дополнительной информации о загрузке кода.

ensure_loaded(module)Source

@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)Source

@spec ensure_loaded!(module()) :: module()

То же, что и ensure_loaded/1, но вызывает исключение, если модуль не может быть загружен.

ensure_loaded?(module)Source

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

Обеспечивает загрузку данного модуля.

Аналогично ensure_loaded/1, но возвращает true если модуль уже загружен или был успешно загружен. В противном случае возвращает false.

Примеры

iex> Code.ensure_loaded?(String)
true

env_for_eval(env_or_opts)Source

Возвращает среду для оценки.

Принимает либо Macro.Env, который затем обрезается и подготавливается, либо список опций. Возвращает среду, готовую к оценке.

Большинство функций в этом модуле автоматически подготавливают заданную среду для оценки, поэтому вам не нужно явно вызывать эту функцию, за исключением eval_quoted_with_env/3, которая была разработана именно для вызова в цикле, для реализации функций, таких как интерактивные оболочки или любые другие с множественными оценками.

Опции

Если среда не задана, опции могут быть:

  • :file - файл, который следует рассматривать при оценке

  • :line - строка, с которой начинается скрипт

  • :module - модуль, на котором следует запустить среду

eval_file(file, relative_to \\ nil)Source

@spec eval_file(binary(), nil | binary()) :: {term(), binding()}

Вычисляет заданный файл.

Принимает relative_to в качестве аргумента, чтобы указать расположение файла.

В то время как require_file/2 и compile_file/2 возвращают загруженные модули и их байт-код, eval_file/2 просто выполняет содержимое файла и возвращает результат выполнения и его привязку (точно такой же возвращаемый результат, как и у eval_string/3).

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

@spec eval_quoted(Macro.t(), binding(), Macro.Env.t() | keyword()) ::
  {term(), binding()}

Вычисляет содержимое в формате quote.

Предупреждение: Вызов этой функции внутри макроса считается плохой практикой, так как она попытается оценить значения во время выполнения во время компиляции. Аргументы макроса обычно преобразуются путем их извлечения в возвращаемых выражениях с помощью unquote (вместо оценки).

См. eval_string/3 для описания binding и opts.

Примеры

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 \\ [])Source

@spec eval_quoted_with_env(Macro.t(), binding(), Macro.Env.t(), keyword()) ::
  {term(), binding(), Macro.Env.t()}

Вычисляет данное содержимое в формате quote с quoted и binding.

Эта функция предназначена для вызова в цикле, чтобы реализовать функции, такие как интерактивные оболочки или любые другие с множественными вычислениями. Поэтому в первый раз при вызове этой функции необходимо вычислить начальную среду с помощью env_for_eval/1. Остальные вызовы должны передавать среду, возвращённую этой функцией.

Опции

  • :prune_binding - (с версии v1.14.2) обрезать привязку, чтобы сохранить только переменные, читаемые или записываемые вычисляемым кодом. Обратите внимание, что переменные, используемые модулями, всегда обрезаются, даже если используются позднее модулями. Вы можете отслеживать событие :on_module и получить доступ к переменным, используемым модулем, из его среды.

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

@spec eval_string(List.Chars.t(), binding(), Macro.Env.t() | keyword()) ::
  {term(), binding()}

Вычисляет содержимое, заданное string.

Аргумент binding — список всех переменных и их значений. Аргумент opts — список опций среды в формате ключевое слово-значение.

Предупреждение: string может быть любым кодом Elixir и будет выполняться с теми же правами, что и Erlang VM: это означает, что такой код может скомпрометировать машину (например, выполняя системные команды). Не используйте eval_string/3 с ненадежными данными (например, строками, полученными из сети).

Опции

Принимает те же опции, что и env_for_eval/1. Кроме того, можно передать среду как второй аргумент, чтобы выполнение происходило в этой среде.

Возвращает кортеж вида {value, binding}, где value — значение, возвращённое при вычислении string. Если при вычислении string произойдёт ошибка, будет вызвано исключение.

binding — список имён всех переменных и их значений после вычисления string. Ключи привязки обычно атомы, но могут быть кортежами для переменных, определённых в другом контексте. Имена не упорядочены.

Примеры

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 и все импорты, require и алиасы, определённые в текущей среде, будут автоматически перенесены:

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)Source

@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()}
     | :invalid_beam}
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 \\ [])Source

@spec format_file!(
  binary(),
  keyword()
) :: iodata()

Форматирует файл.

См. format_string!/2 для получения дополнительной информации о форматировании кода и доступных параметрах.

format_string!(string, opts \\ [])Source

@spec format_string!(
  binary(),
  keyword()
) :: iodata()

Форматирует предоставленный код string.

Форматтер получает строку, представляющую код Elixir, и возвращает iodata, представляющую отформатированный код в соответствии с предварительно определёнными правилами.

Параметры

Стандартные параметры (не изменяют AST):

  • :file — файл, содержащий строку, используемый для отчёта об ошибках

  • :line — строка, с которой начинается строка, используемая для отчёта об ошибках

  • :line_length — длина строки, к которой следует стремиться при форматировании документа. По умолчанию 98. Это значение указывает, когда выражение должно быть разбито на несколько строк, но не гарантирует этого. См. раздел «Длина строки» ниже для получения дополнительной информации

  • :locals_without_parens — список ключевых слов пар «имя-арность», которые должны сохраняться без скобок по возможности. Арность может быть атомом :*, что подразумевает все арности данного имени. Форматтер уже включает список функций, и этот параметр дополняет этот список.

  • :force_do_end_blocks (с версии 1.9.0) — когда true, преобразует все встроенные использования do: ..., else: ... и т. п. в блоки do-end. По умолчанию false. Обратите внимание, что этот параметр конвергентный: как только вы установите его в значение true, все ключевые слова будут преобразованы. Если вы установите его в значение false позже, блоки do-end не будут преобразованы обратно в ключевые слова.

Параметры миграции (изменяют AST), см. раздел «Форматирование миграции» ниже:

  • :migrate (с версии 1.18.0) — когда true, устанавливает все другие параметры миграции по умолчанию в значение true. По умолчанию false.

  • :migrate_bitstring_modifiers (с версии 1.18.0) — когда true, удаляет лишние скобки в известных модификаторах битовых строк модификаторах, например <<foo::binary()>> становится <<foo::binary>>, или добавляет скобки для пользовательских модификаторов, где <<foo::custom_type>> становится <<foo::custom_type()>>. По умолчанию значение параметра :migrate . Этот параметр изменяет AST.

  • :migrate_charlists_as_sigils (с версии 1.18.0) — когда true, форматирует списки символов как ~c сигилы, например 'foo' становится ~c"foo". По умолчанию значение параметра :migrate . Этот параметр изменяет AST.

  • :migrate_unless (с версии 1.18.0) — когда true, переписывает выражения unless с использованием if с отрицаемым условием, например unless foo, do: становится if !foo, do:. По умолчанию значение параметра :migrate . Этот параметр изменяет AST.

Принципы проектирования

Форматтер был разработан на основе трёх принципов.

Во-первых, форматтер по умолчанию никогда не изменяет семантики кода. Это означает, что входной и выходной AST почти всегда эквивалентны.

Второй принцип — предоставить как можно меньше конфигурации. Это облегчает принятие форматтера, устраняя точки разногласий, и гарантирует, что единый стиль поддерживается всем сообществом в целом.

Форматтер не жёстко кодирует имена. Форматтер не будет вести себя особым образом, потому что функция называется defmodule, def, или подобным образом. Этот принцип отражает цель 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). Хотя форматировщик может сохранять комментарии к коду между выражениями и аргументами функций, в настоящее время он не может сохранять их вокруг операторов. Например, следующий код переместит комментарии к коду перед использованием оператора:

foo() ||
  # also check for bar
  bar()

В некоторых ситуациях комментарии к коду могут рассматриваться форматировщиком как неоднозначные. Например, комментарий в анонимной функции ниже

fn
  arg1 ->
    body1
    # comment

  arg2 ->
    body2
end

и в этом

fn
  arg1 ->
    body1

  # comment
  arg2 ->
    body2
end

считаются эквивалентными (вложенность отбрасывается вместе с большей частью форматирования пользователя). В таких случаях форматировщик кода всегда будет форматировать по последнему варианту.

Новые строки

Форматировщик преобразует все новые строки в коде из \r\n в \n.

Форматирование миграции

В рамках цикла выпуска Elixir вводятся устаревшие функции, что генерирует предупреждения, которые могут потребовать изменения существующего кода. Для уменьшения нагрузки на разработчиков при обновлении Elixir до следующей версии форматировщик предоставляет некоторые опции, по умолчанию отключенные, для автоматизации этого процесса.

Эти опции должны охватить большинство типичных случаев использования, но, учитывая, что они вносят изменения в AST, существует ненулевой риск для проектов с интенсивным метапрограммированием, которые полагались на определенный AST, или проектов, переопределяющих функции из Kernel. В таких случаях миграции нельзя применять бездумно, и могут потребоваться дополнительные изменения для устранения предупреждений об устаревании.

get_compiler_option(key)Source

@spec get_compiler_option(atom()) :: term()

Возвращает значение заданного параметра компилятора.

Описание всех параметров см. в put_compiler_option/2.

Примеры

Code.get_compiler_option(:debug_info)
#=> true

loaded?(module)Source

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

Возвращает true, если модуль загружен.

Эта функция не пытается загрузить модуль. Для такого поведения можно использовать ensure_loaded?/1.

Примеры

iex> Code.loaded?(Atom)
true

iex> Code.loaded?(NotYetLoaded)
false

prepend_path(path, opts \\ [])Source

@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 \\ [])Source

@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, в противном случае это бесполезная операция.

print_diagnostic(diagnostic, opts \\ [])Source

@spec print_diagnostic(
  diagnostic(:warning | :error),
  keyword()
) :: :ok

Выводит диагностическую информацию в стандартный поток ошибок.

Диагностика возвращается либо функцией Kernel.ParallelCompiler, либо функцией Code.with_diagnostics/2.

Параметры

  • :snippet - нужно ли читать фрагмент кода в месте диагностики. Поскольку это может повлиять на производительность, использование не рекомендуется во время выполнения. По умолчанию true.

purge_compiler_modules()Source

@spec purge_compiler_modules() :: {:ok, non_neg_integer()}

Очистка модулей компилятора.

Компилятор использует временные модули для компиляции кода. Например, elixir_compiler_1, elixir_compiler_2, и так далее. В случае, если скомпилированный код хранит ссылки на анонимные функции или аналогичное, компилятор Elixir может не иметь возможности вернуть эти модули, удерживая необоснованное количество кода в памяти и, в конечном итоге, приводя к появлению модулей, таких как elixir_compiler_12345.

Эта функция очищает все модули, которые в настоящее время хранятся компилятором, позволяя повторно использовать старые имена модулей компилятора. Если какие-либо процессы выполняют код из таких модулей, они также будут завершены.

Эта функция предназначена только для вызова, если у вас есть узел с длительным выполнением, который постоянно оценивает код.

Она возвращает {:ok, number_of_modules_purged}.

put_compiler_option(key, value)Source

@spec put_compiler_option(atom(), term()) :: :ok

Сохраняет опцию компиляции.

Изменение опций компиляции влияет на все процессы, работающие в узле данного Erlang VM.

Доступные опции:

  • :docs - при true, сохраняет документацию в скомпилированном модуле. По умолчанию true.

  • :debug_info - при true, сохраняет отладочную информацию в скомпилированном модуле. Данную опцию также можно переопределить на уровне модуля, используя директиву @compile. По умолчанию true.

    Это позволяет инструментам частично восстановить исходный код, например, для выполнения статического анализа кода. Поэтому отключение :debug_info не рекомендуется, так как оно лишает возможность Elixir-компилятору и другим инструментам предоставлять обратную связь. Если вы хотите убрать :debug_info при развертывании, инструменты, такие как mix release, уже делают это по умолчанию.

    Другие среды, такие как mix test, автоматически отключают это через конфигурацию проекта :test_elixirc_options, так как обычно нет необходимости сохранять отладочные данные для файлов тестов.

  • :ignore_already_consolidated (с версии v1.10.0) - при true, не выводит предупреждение, когда протокол уже объединён, а добавлена новая реализация. По умолчанию false.

  • :ignore_module_conflict - при true, не выводит предупреждение, когда модуль уже определён. По умолчанию false.

  • :infer_signatures (с версии v1.18.0) - при false, отключает локальный вывод сигнатуры модуля, используемый при проверке типов удалённых вызовов скомпилированного модуля. Проверка типов будет выполняться независимо от значения этой опции. По умолчанию true.

    mix test автоматически отключает эту опцию через конфигурацию проекта :test_elixirc_options, так как обычно нет необходимости сохранять сигнатуры вывода для файлов тестов.

  • :relative_paths - при true, использует относительные пути в цитируемых узлах, предупреждениях и ошибках, генерируемых компилятором. Обратите внимание, что отключение этой опции не повлияет на предупреждения и ошибки во время выполнения. По умолчанию true.

  • :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/2 и compile_file/2, но не string_to_quoted/2 и аналогичные, так как последняя используется для других целей, помимо компиляции.

  • :on_undefined_variable (с версии v1.15.0) - либо :raise, либо :warn. При :raise (по умолчанию) неопределённые переменные вызовут ошибку компиляции. Вы можете установить значение :warn, если вы хотите, чтобы неопределённые переменные генерировали предупреждение и расширялись в локальный вызов функции с нулевым числом аргументов с тем же именем (например, node будет расширено как node()). Это поведение :warn существует только для совместимости при работе со старыми зависимостями, его использование не рекомендуется, и оно будет удалено в будущих версиях.

Всегда возвращает :ok. Возвращает ошибку для недопустимых опций.

Примеры

Code.put_compiler_option(:debug_info, true)
#=> :ok

quoted_to_algebra(quoted, opts \\ [])Source

@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. Если опция :unescape была установлена в false при использовании string_to_quoted/2, установка этой опции в false предотвратит двойное экранирование последовательностей. По умолчанию true.

  • :locals_without_parens - список пар имя-арность, которые следует сохранять без скобок, когда это возможно. Арность может быть атомом :*, что подразумевает все арности этого имени. Форматировщик уже включает список функций, а эта опция дополняет этот список.

  • :syntax_colors - список цветов для цветного вывода. См. Inspect.Opts для получения дополнительной информации.

require_file(file, relative_to \\ nil)Source

@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()Source

@spec required_files() :: [binary()]

Отображает все использованные файлы.

Примеры

Code.require_file("../eex/test/eex_test.exs")
List.first(Code.required_files()) =~ "eex_test.exs"
#=> true
END_OF_DOCUMENT_MARKER

string_to_quoted(string, opts \\ [])Source

@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 выражения, такие как метаданные для do и end токенов, для закрывающих токенов, конца выражений, а также разделителей для сигилов. См. Macro.metadata/0. По умолчанию false.

  • :literal_encoder (с версии v1.10.0) - способ кодирования литералов в AST. Он должен быть функцией, которая принимает два аргумента: литерал и его метаданные, и должна возвращать {:ok, ast :: Macro.t} или {:error, reason :: binary}. Если вы вернёте что-то другое, чем сам литерал в качестве term, то AST больше не будет валидным. Этот параметр может быть всё ещё полезен для текстового анализа исходного кода.

  • :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: &my_encoder/2 передаётся в качестве аргумента, my_encoder/2 вызывается каждый раз, когда токенизатор должен создать «статический» атом. Статические атомы — это атомы в AST, которые работают как псевдонимы, удалённые вызовы, локальные вызовы, имена переменных, обычные атомы и списки ключевых слов.

Функция кодирования получит имя атома (как двоичную строку) и список ключевых слов с текущим файлом, строкой и столбцом. Она должна вернуть {:ok, token :: term} | {:error, reason :: binary}.

Функция кодирования должна создать атом из заданной строки. Для создания валидного AST необходимо вернуть {:ok, term}, где term — атом. Возвращение значения, отличного от атома, возможно, но в этом случае AST больше не будет «валидным», так как его нельзя будет использовать для компиляции или оценки кода Elixir. Пример применения этого — если вы хотите использовать парсер Elixir в пользовательском интерфейсе, но не хотите исчерпать таблицу атомов.

Функция кодирования атомов не вызывается для всех атомов, присутствующих в AST. Она не будет вызвана для следующих атомов:

  • операторы (:+, :-, и так далее)

  • ключевые слова синтаксиса (fn, do, else, и так далее)

  • атомы, содержащие интерполяцию (:"#{1 + 1} is two"), так как эти атомы создаются во время выполнения

  • атомы, используемые для представления сигилов с одной буквой, таких как :sigil_X (но сигилы с несколькими буквами, такие как :sigil_XYZ кодируются).

string_to_quoted!(string, opts \\ [])Source

@spec string_to_quoted!(
  List.Chars.t(),
  keyword()
) :: Macro.t()

Преобразует заданную строку в её строковое представление.

Возвращает AST при успешном выполнении, в противном случае генерирует исключение. Исключение является TokenMissingError в случае отсутствия токена (обычно, потому что выражение неполное), MismatchedDelimiterError (в случае несоответствия открывающего и закрывающего разделителей) и SyntaxError в остальных случаях.

См. string_to_quoted/2 для информации о параметрах.

string_to_quoted_with_comments(string, opts \\ [])Source

@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 \\ [])Source

@spec string_to_quoted_with_comments!(
  List.Chars.t(),
  keyword()
) :: {Macro.t(), [map()]}

Преобразует заданную строку в её строковое представление и список комментариев.

Возвращает AST и список комментариев при успешном выполнении, в противном случае генерирует исключение. Исключение является TokenMissingError в случае отсутствия токена (обычно, потому что выражение неполное), SyntaxError в остальных случаях.

См. string_to_quoted/2 для информации о параметрах.

unrequire_files(files)Source

@spec unrequire_files([binary()]) :: :ok

Удаляет файлы из списка требуемых файлов.

Модули, определённые в файле, не удаляются; вызов этой функции только удаляет их из списка, позволяя их снова потребовать.

Список файлов управляется по узлу виртуальной машины Erlang.

Примеры

# 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)Source

@spec with_diagnostics(
  keyword(),
  (-> result)
) :: {result, [diagnostic(:warning | :error)]}
when result: term()

Выполняет заданную fun и собирает все диагностические сообщения.

Диагностические сообщения — это предупреждения и ошибки, выводимые во время оценки кода или компиляции одного файла, и функциями, такими как IO.warn/2.

Если вы используете mix compile или Kernel.ParallelCompiler, обратите внимание, что они уже собирают и возвращают диагностические сообщения.

Параметры

  • :log - если диагностические сообщения должны регистрироваться по мере их возникновения. По умолчанию false.

Обработка ошибок

with_diagnostics/2 не обрабатывает исключения автоматически. Вы можете перехватить их, добавив try/1 в fun:

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

Загрузить версию ePub

Создано с помощью ExDoc (v0.36.1) для программного языка Elixir

© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.18.1/Code.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API