Spec-Zone.ru › Elixir 1.16

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

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

Этот модуль дополняет модуль 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 всё ещё может быть отправлено для импортированного модуля/имени/арности.

  • {: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} - (с версии 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.

append_paths(paths, opts \\ [])

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

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.

delete_paths(paths)

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

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.

prepend_paths(paths, opts \\ [])

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

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 VM: это означает, что такой код может представлять угрозу для системы (например, путём выполнения системных команд). Не используйте 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 VM. Для хранения отдельных параметров и описания всех параметров см. put_compiler_option/2.

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

Примеры

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

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

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

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

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

Дополнительную информацию о загрузке кода см. в документации по модулю.

ensure_loaded(module)Source

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

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

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

Если модуль успешно загружается, возвращается {: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 - строка, с которой начинается скрипт

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()}

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

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

См. 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()}

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

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

Опции

Опции могут быть:

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

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

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

Возвращает кортеж вида {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]
END_OF_DOCUMENT_MARKER

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()}}
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, представляющую отформатированный код в соответствии с предварительно определенными правилами.

Параметры

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

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

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

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

  • :force_do_end_blocks (с версии v1.9.0) — когда true, преобразует все встроенные использования do: ..., else: ... и аналогичных элементов в блоки do-end. По умолчанию false. Обратите внимание, что этот параметр является сходящимся: после того, как вы установите его в true, все ключевые слова будут преобразованы. Если вы установите его в false позже, блоки do-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 сигилы, например 'foo' становится ~c"foo". По умолчанию true. Этот параметр изменяет AST.

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

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

Во-первых, форматтер никогда не изменяет семантику кода. Это означает, что входной и выходной AST почти всегда эквивалентны. Единственные случаи, когда форматтер изменит AST, — это когда входной AST вызовет предупреждения компилятора, а выходной 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), в некоторых ситуациях форматтер кода рассматривает комментарии как неоднозначные. Например, комментарий в анонимной функции ниже

fn
  arg1 ->
    body1
    # comment

  arg2 ->
    body2
end

и в этом

fn
  arg1 ->
    body1

  # comment
  arg2 ->
    body2
end

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

Новые строки

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

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.

Это список каталогов, которые 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.

Это список каталогов, которые 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, сохраняет отладочную информацию в скомпилированном модуле. По умолчанию true. Это позволяет инструментам статического анализа частично восстановить исходный код. Поэтому отключение :debug_info не рекомендуется, так как оно лишает компилятор Elixir и других инструментов возможности предоставлять обратную связь. Если вы хотите удалить :debug_info при развертывании, инструменты, такие как mix 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/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

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 - (с версии 1.11.0) начальная колонка анализируемой строки. По умолчанию 1.

  • :columns - при true, добавляет ключ :column к метаданным цитируемого выражения. По умолчанию false.

  • :unescape (с версии 1.10.0) - при false, сохраняет экранированные последовательности. Например, "null byte\\t\\x00" останется как есть вместо преобразования в литерал битового массива. Обратите внимание, что если вы установите этот параметр в false, полученный AST больше не является допустимым, но это может быть полезно для анализа/преобразования исходного кода, обычно в сочетании с quoted_to_algebra/2. По умолчанию true.

  • :existing_atoms_only - при true, генерирует ошибку при обнаружении несуществующих атомов токенизатором. По умолчанию false.

  • :token_metadata (с версии 1.10.0) - при true, включает метаданные, связанные с маркерами, в AST выражения, такие как метаданные для маркеров do и end, для маркеров закрытия, конца выражений, а также разделителей для сигил. Смотрите Macro.metadata/0. По умолчанию false.

  • :literal_encoder (с версии 1.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 (с версии 1.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. Она не будет вызываться для следующих атомов:

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

  • ключевые слова синтаксиса (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 в случае отсутствия маркера (обычно из-за незаконченного выражения) или 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 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)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.32.2) для языка программирования Elixir

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

Spec-Zone.ru

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