Исходный код Код
Утилиты для управления компиляцией, оценкой и загрузкой кода.
Этот модуль дополняет модуль Erlang :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 :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}— отслеживается при вызове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 имела «всеобъемлющий» (catch-all) случай.
Ниже приведён пример трассера, который выводит все вызовы удалённых функций:
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 \\ [])
Вычисляет данное содержимое в кавычках с
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 и т. д. Доступен необязательный span с указанием строки и столбца, где заканчивается диагностика.
В противном случае может быть указан стек вызовов, с которым вы можете использовать собственные эвристики для лучшего отображения сообщений об ошибках.
Поле 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}. Номера строки и столбца основаны на индексе 1. Позиция 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(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- (с версии 1.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: это означает, что такой код может нанести ущерб машине (например, выполняя системные команды). Не используйте 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] 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, представляющую отформатированный код в соответствии с предварительно определёнными правилами.
Параметры
: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не будут преобразованы обратно в ключевые слова.:normalize_bitstring_modifiers(с версии 1.14.0) - когдаtrue, удаляет лишние скобки в известных модификаторах bitstring модификаторах, например,<<foo::binary()>>становится<<foo::binary>>, или добавляет скобки для пользовательских модификаторов, где<<foo::custom_type>>становится<<foo::custom_type()>>. По умолчаниюtrue. Этот параметр изменяет AST.:normalize_charlists_as_sigils(с версии 1.15.0) - когдаtrue, форматирует charlists как~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 цифрами и преобразует шестнадцатеричные цифры в верхний регистр.
Строки, charlists, атомы и сигилы сохраняются как есть. Ни один символ не экранируется или экранируется автоматически. Выбор разделителя также сохраняется из входных данных.
-
Пробелы внутри блоков сохраняются как во входных данных, за исключением:
- выражения, занимающие несколько строк, всегда будут иметь пустую строку перед и после, и 2) пустые строки всегда будут объединены в одну пустую строку
Выбор между ключевым словом
:doи блокамиdo-endостаётся на усмотрение пользователяСписки, кортежи, bitstrings, карты, структуры и вызовы функций будут разбиты на несколько строк, если после открывающей скобки и перед закрывающей скобкой следуют новые строки.
Пробелы перед определёнными операторами (такими как операторы конвейера) и перед другими операторами (такими как операторы сравнения).
Вышеперечисленные особенности не гарантируются. В будущем мы можем удалить или добавить новые правила. Цель их документирования – обеспечить лучшее понимание ожидаемого поведения форматтера.
Многострочные списки, карты, кортежи и т. п.
Вы можете принудительно форматировать списки, кортежи, битовые строки, карты, структуры и вызовы функций так, чтобы каждый элемент был на отдельной строке, добавив новую строку после открывающей скобки и новой строки перед закрывающей скобкой. Например:
[ foo, bar ]
Если вокруг скобок нет новых строк, форматтер попытается разместить все на одной строке, так что фрагмент кода ниже
[foo, bar]
будет отформатирован как
[foo, bar]
Вы также можете принудительно отображать вызовы функций и ключевые слова на нескольких строках, размещая каждый элемент на своей строке:
defstruct name: nil,
age: 0
В этом коде форматтер будет сохранять каждый элемент ключевого слова на отдельной строке. Чтобы этого избежать, просто поместите все на одной строке.
Скобки и отсутствие скобок в вызовах функций
В Elixir есть два синтаксиса для вызовов функций: со скобками и без них. По умолчанию Elixir добавляет скобки ко всем вызовам, за исключением:
- вызовов, имеющих блоки
do-end - локальных вызовов без скобок, где имя и арность локального вызова также указаны в
: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 использует для поиска кода модулей. Список файлов управляется по каждому узлу виртуальной машины Erlang.
Путь расширяется с помощью 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 использует для поиска кода модулей. Список файлов управляется по каждому узлу виртуальной машины Erlang.
Все пути расширяются с помощью 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.
Доступные параметры:
: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.
Учет особенностей форматирования
AST Elixir не содержит метаданных для литералов, таких как строки, списки или кортежи с двумя элементами, что означает, что сгенерированный документ алгебры не будет учитывать все пользовательские настройки, а комментарии могут быть неправильно размещены. Для получения лучших результатов можно использовать опции :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 не является валидным. Если его оценить, он не будет иметь таких же семантик, как обычный AST Elixir, из-за опций :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.
См. 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- (с версии v1.11.0) начальный столбец анализируемой строки. Значение по умолчанию 1.:columns- приtrue, добавляет ключ:columnк метаданным цитаты. Значение по умолчаниюfalse.:unescape(с версии v1.11.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- функция кодирования статических атомов, см. раздел "Функция:static_atoms_encoder" ниже. Обратите внимание, что этот параметр переопределяет поведение:existing_atoms_onlyдля статических атомов, но:existing_atoms_onlyпо-прежнему используется для динамических атомов, таких как атомы с интерполяцией.:emit_warnings(с версии v1.16.0) - приfalse, не генерирует предупреждения, связанные с токенизацией/разбором. Значение по умолчаниюtrue.
Macro.to_string/2
Обратная операция преобразования строки в её цитируемую форму — это Macro.to_string/2, которая преобразует цитируемую форму в строковое/двоичное представление.
Функция :static_atoms_encoder
Когда static_atoms_encoder: &my_encoder/2 передаётся в качестве аргумента, my_encoder/2 вызывается каждый раз, когда токенизатор должен создать "статический" атом. Статические атомы в AST служат псевдонимами, удалёнными вызовами, локальными вызовами, именами переменных, обычными атомами и списками ключевых слов.
Функция-кодировщик получит имя атома (как двоичную строку) и список ключевых слов с текущим файлом, строкой и столбцом. Она должна вернуть {:ok, token :: term} | {:error, reason :: binary}.
Функция-кодировщик должна создать атом из заданной строки. Для создания валидного AST необходимо вернуть {:ok, term}, где term — это атом. Можно вернуть что-то отличное от атома, однако в этом случае AST больше не является "валидным", поскольку его нельзя использовать для компиляции или оценки кода Elixir. Применение этого — если вы хотите использовать парсер Elixir в пользовательском интерфейсе, но не хотите использовать всю таблицу атомов.
Функция кодирования атомов не вызывается для всех атомов, присутствующих в AST. Она не будет вызвана для следующих атомов:
операторы (
:+,:-, и так далее)ключевые слова синтаксиса (
fn,else,do, и так далее)атомы, содержащие интерполяцию (
:"#{1 + 1} is two"), поскольку эти атомы создаются во время выполненияатомы, используемые для представления сигил из одного символа, таких как
:sigil_X, (но сигилы из нескольких символов, такие как:sigil_XYZ, кодируются).
string_to_quoted!(строка, опции \\ [])Source
@spec string_to_quoted!( List.Chars.t(), keyword() ) :: Macro.t()
Преобразует заданную строку в её представлении с использованием кавычек.
Возвращает AST, если преобразование успешно, иначе выбрасывает исключение. Исключение — TokenMissingError в случае отсутствия токена (обычно, из-за незавершенного выражения), MismatchedDelimiterError (в случае несоответствия открывающего и закрывающего разделителей) и SyntaxError в противном случае.
См. string_to_quoted/2 для информации об опциях.
string_to_quoted_with_comments(строка, опции \\ [])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!(строка, опции \\ [])Source
@spec string_to_quoted_with_comments!(
List.Chars.t(),
keyword()
) :: {Macro.t(), [map()]} Преобразует заданную строку в её представление с использованием кавычек и список комментариев.
Возвращает AST и список комментариев при успехе, иначе выбрасывает исключение. Исключение — TokenMissingError в случае отсутствия токена (обычно, из-за незавершенного выражения), SyntaxError в противном случае.
См. string_to_quoted/2 для информации об опциях.
unrequire_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(опции \\ [], функция)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)
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.17.2/Code.html