Spec-Zone.ru › Elixir 1.18

Исходный код Макросы

Функции для работы с AST и реализации макросов.

Макросы — это конструкции времени компиляции, которые принимают AST Elixir в качестве входных данных и возвращают AST Elixir в качестве выходных.

Многие функции в этом модуле предназначены для работы с AST Elixir, для его обхода, запросов и преобразований.

Давайте рассмотрим простой пример, который демонстрирует разницу между функциями и макросами:

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

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

Теперь давайте попробуем:

import Example

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

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

Пока они ведут себя одинаково, так как мы передаём целое число в качестве аргумента. Но давайте посмотрим, что произойдёт, если мы передадим выражение:

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

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

Макрос получает представление кода, переданного в качестве аргумента, в то время как функция получает результат кода, переданного в качестве аргумента. Макрос должен возвращать супермножество представления кода. Подробнее см. input/0 и output/0.

Чтобы узнать больше об AST Elixir и о том, как создавать их программно, см. quote/2.

Вычисление кода

Функции в этом модуле не вычисляют код. На самом деле, вычисление кода из макросов часто является антипаттерном. Для вычисления кода см. модуль Code.

Краткое описание

Типы

captured_remote_function()

Захваченная удалённая функция в формате &Mod.fun/arity

input()

Входы макроса

metadata()

Список ключевых слов метаданных AST.

output()

Выход макроса

t()

Абстрактное синтаксическое дерево (AST)

Функции

camelize(строка)

Преобразует заданную строку в формат CamelCase.

classify_atom(атом)

Классифицирует atom на основе возможного расположения в AST.

compile_apply(модуль, функция, аргументы, вызывающий)

Применяет mod, function, и args во время компиляции в caller.

dbg(код, опции, среда)

По умолчанию бэкэнд для Kernel.dbg/2.

decompose_call(ast)

Декомпозирует локальный или удалённый вызов на его удалённую часть (если она есть), имя функции и список аргументов.

escape(выражение, опции \\ [])

Рекурсивно экранирует значение, чтобы его можно было вставить в синтаксическое дерево.

expand(ast, среда)

Получает узел AST и расширяет его до тех пор, пока больше нельзя.

expand_literals(ast, среда)

Расширяет все литералы в ast с заданной env.

expand_literals(ast, сумма, функция)

Расширяет все литералы в ast с заданной acc и fun.

expand_once(ast, среда)

Получает узел AST и расширяет его один раз.

generate_arguments(количество, контекст)

Генерирует узлы AST для заданного количества требуемых переменных аргументов с использованием Macro.var/2.

generate_unique_arguments(количество, контекст)

Генерирует узлы AST для заданного количества требуемых переменных аргументов с использованием Macro.unique_var/2.

inspect_atom(формат_источника, атом, опции \\ [])

Проверяет atom в соответствии с различными форматами источников.

operator?(имя, арность)

Возвращает true если заданное имя и арность являются оператором.

path(ast, функция)

Возвращает путь к узлу в ast, для которого fun возвращает истинное значение.

pipe(выражение, аргументы_вызова, позиция)

Направляет expr в call_args в заданной position.

postwalk(ast, функция)

Эта функция ведет себя как prewalk/2, но выполняет обход по цитируемым выражениям в порядке постфиксного обхода.

postwalk(ast, сумма, функция)

Эта функция ведет себя как prewalk/3, но выполняет обход по цитируемым выражениям с использованием накопителя.

postwalker(ast)

Возвращает перечислитель, который проходит по ast в порядке постфиксного обхода.

prewalk(ast, функция)

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

prewalk(ast, сумма, функция)

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

prewalker(ast)

Возвращает перечислитель, который проходит по ast в порядке префиксного обхода.

quoted_literal?(термин)

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

special_form?(имя, арность)

Возвращает true если заданное имя и арность являются специальной формой.

struct!(модуль, среда) устарело
struct_info!(модуль, среда)

Извлекает информацию о структуре (эквивалентно вызову module.__info__(:struct)).

to_string(дерево)

Преобразует заданное выражение AST в строку.

to_string(дерево, функция) устарело

Преобразует заданное выражение AST в строку.

traverse(ast, сумма, префикс, постфикс)

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

underscore(атом_или_строка)

Преобразует заданный аргумент в строку с форматом подчеркивание-спуск.

unescape_string(строка)

Раскрашивает символы в строке.

unescape_string(строка, карта)

Раскрашивает символы в строке в соответствии с заданной картой.

unique_var(переменная, контекст)

Генерирует узел AST, представляющий уникальную переменную, заданную атомами var и context.

unpipe(выражение)

Разбивает выражение-поток в список.

update_meta(цитируемое, функция)

Применяет заданную функцию к метаданным узла, если они содержатся.

validate(выражение)

Проверяет, являются ли заданные выражения допустимыми цитируемыми выражениями.

var(переменная, контекст)

Генерирует узел AST, представляющий переменную, заданную атомами var и context.

END_OF_DOCUMENT_MARKER

Типы

captured_remote_function()Source

@type captured_remote_function() :: fun()

Захваченная удалённая функция в формате &Mod.fun/arity

input()Source

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

Входные данные макроса

metadata()Source

@type metadata() :: keyword()

Список ключевых слов метаданных AST.

Метаданные в Elixir AST — это список ключевых слов значений. Можно использовать любой ключ, и разные части компилятора могут использовать разные ключи. Например, AST, полученный макросом, всегда включает аннотацию :line, в то время как AST, выпущенный quote/2, будет содержать аннотацию :line только в том случае, если предоставлен параметр :line.

Следующие ключи метаданных являются общедоступными:

  • :context — определяет контекст, в котором был сгенерирован AST. Например, quote/2 будет включать модуль, вызывающий quote/2, в качестве контекста. Это часто используется для различения обычного кода от кода, сгенерированного макросом или quote/2.

  • :counter — счётчик переменных, используемый для гигиены переменных. С точки зрения компилятора, каждая переменная идентифицируется комбинацией name и metadata[:counter], или name и context.

  • :from_brackets — используется для определения, происходит ли вызов Access.get/3 из синтаксиса в квадратных скобках.

  • :from_interpolation — используется для определения, происходит ли вызов Kernel.to_string/1 из интерполяции.

  • :generated — указывает, считается ли код сгенерированным компилятором. Это означает, что компилятор и инструменты, такие как Dialyzer, могут не генерировать определённые предупреждения.

  • :if_undefined — как расширить неопределённую переменную. Установите её в значение :apply, если вы хотите, чтобы переменная стала вызовом без аргументов без предупреждения, или :raise.

  • :keep — используется quote/2 с параметром location: :keep, чтобы добавить аннотацию файла и номера строки цитируемого исходного кода.

  • :line — номер строки узла AST. Обратите внимание, что информация о строках удаляется из цитируемого кода, но её можно восстановить с помощью параметра :line.

Следующие ключи метаданных разрешены Code.string_to_quoted/2:

  • :closing — содержит метаданные об закрывающей паре, такой как } в кортеже или в карте, или закрывающей ) в вызове функции с круглыми скобками (когда :token_metadata равно true). Если к вызову функции прикреплён блок do-end, его метаданные находятся в метаданных :do и :end.

  • :column — номер столбца узла AST (когда :columns равно true). Обратите внимание, что информация о столбцах всегда удаляется из цитируемого кода.

  • :delimiter — содержит открывающий разделитель для сигилов, строк и списков символов в виде строки (например, "{", "/", "'", и тому подобное).

  • :format — устанавливается в :keyword когда атом определён как ключевое слово.

  • :do — содержит метаданные о позиции do в вызове функции с блоками do-end (когда :token_metadata равно true).

  • :end — содержит метаданные о позиции end в вызове функции с блоками do-end (когда :token_metadata равно true).

  • :end_of_expression — обозначает, когда фактически происходит окончание выражения (когда :token_metadata равно true). Это доступно только для выражений внутри «блоков кода», которые являются прямыми потомками __block__ или правой частью ->. У последнего выражения блока нет метаданных, если за ним не следует символ конца строки (новая строка или ;).

  • :indentation — отступ сигильного heredoc.

Следующие ключи метаданных являются закрытыми:

  • :alias — используется для гигиены псевдонимов.
  • :ambiguous_op — используется для улучшения сообщений об ошибках в компиляторе.
  • :imports — используется для гигиены импорта.
  • :var — используется для улучшения сообщений об ошибках для неопределённых переменных.

Не полагайтесь на них, так как они могут измениться или быть полностью удалены в будущих версиях языка. Они часто используются quote/2 и компилятором для обеспечения таких функций, как гигиена, улучшенные сообщения об ошибках и так далее.

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

output()Source

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

Вывод макроса

t()Source

@type t() :: input()

Абстрактное синтаксическое дерево (AST)

Функции

camelize(string)Source

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

Преобразует заданную строку в формат CamelCase.

Эта функция была разработана для преобразования идентификаторов/токенов языка, поэтому она принадлежит модулю Macro. Не используйте её как общий механизм преобразования строк в CamelCase, так как она не поддерживает Unicode или символы, которые не являются допустимыми в идентификаторах Elixir.

Примеры

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

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

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

iex> Macro.camelize("API.V1")
"API.V1"
iex> Macro.camelize("API_SPEC")
"API_SPEC"

classify_atom(atom)Source

@spec classify_atom(atom()) :: :alias | :identifier | :quoted | :unquoted

Классифицирует atom на основе возможного расположения в AST.

Возвращает один из следующих атомов:

  • :alias — атом представляет алиас

  • :identifier — атом может использоваться в качестве переменной или локального вызова функции (а также быть нецитируемым атомом)

  • :unquoted — атом может использоваться в нецитируемой форме, включает операторы и атомы с @ в них

  • :quoted — все остальные атомы, которые могут использоваться только в цитируемой форме

Большинство операторов будут :unquoted, такие как :+, за некоторыми исключениями, возвращающими :quoted из-за неоднозначности, например, :"::". Используйте operator?/2, чтобы проверить, является ли данный атом оператором.

Примеры

iex> Macro.classify_atom(:foo)
:identifier
iex> Macro.classify_atom(Foo)
:alias
iex> Macro.classify_atom(:foo@bar)
:unquoted
iex> Macro.classify_atom(:+)
:unquoted
iex> Macro.classify_atom(:Foo)
:unquoted
iex> Macro.classify_atom(:"with spaces")
:quoted

compile_apply(mod, fun, args, caller)Source

Применяет mod, function, и args во время компиляции в caller.

Используется, когда вы хотите программно вызвать макрос во время компиляции.

dbg(code, options, env)Source

@spec dbg(t(), t(), Macro.Env.t()) :: t()

По умолчанию бэкенд для Kernel.dbg/2.

Эта функция предоставляет бэкенд по умолчанию для Kernel.dbg/2. Для получения дополнительной информации см. документацию к Kernel.dbg/2.

Эта функция:

  • выводит информацию о заданном env
  • выводит информацию о code и его возвращаемом значении (используя opts для проверки терминов)
  • возвращает значение, возвращённое при вычислении code

Вы можете напрямую вызывать эту функцию для создания бэкендов Kernel.dbg/2, которые обращаются к этой функции по умолчанию.

Функция вызывает исключение, если контекст данного env является :match или :guard.

decompose_call(ast)Source

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

Разбивает локальный или удалённый вызов на удалённую часть (если она предоставлена), имя функции и список аргументов.

Возвращает :error при предоставлении некорректного синтаксиса вызова.

Примеры

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

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

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

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

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

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

escape(expr, opts \\ [])Source

@spec escape(
  term(),
  keyword()
) :: t()

Рекурсивно экранирует значение, чтобы его можно было вставить в дерево синтаксического анализа.

Примеры

iex> Macro.escape(:foo)
:foo

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

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

Параметры

  • :unquote - если true, эта функция оставляет выражения unquote/1 и unquote_splicing/1 неэкранированными, фактически снимая цитирование содержимого при экранировании. Этот параметр полезен только при экранировании AST, которые могут содержать цитированные фрагменты. По умолчанию false.

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

    Например, для :prune_metadata, ExUnit сохраняет AST каждого утверждения, поэтому, когда утверждение не выполняется, мы можем показать фрагменты кода пользователям. Без этого параметра каждый раз, когда модуль теста компилируется, мы получим разный MD5 байтового кода модуля, потому что AST содержит метаданные, такие как счётчики, специфичные для среды компиляции. Удаляя метаданные, мы обеспечиваем детерминированность модуля и уменьшаем объём данных, который ExUnit должен хранить. Сохраняется только минимальный объём метаданных, такие как :line, :no_parens и :delimiter.

Сравнение с quote/2

Функция escape/2 иногда путают с quote/2, потому что примеры выше ведут себя одинаково с обеими функциями. Ключевое различие лучше всего иллюстрируется, когда значение для экранирования хранится в переменной.

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

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

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

iex> value = {:a, :b, :c}
iex> quote do: unquote(value)
** (ArgumentError) tried to unquote invalid AST: {:a, :b, :c}
Did you forget to escape term using Macro.escape/1?

escape/2 используется для экранирования значений (либо непосредственно переданных, либо привязанных к переменной), в то время как quote/2 создаёт деревья синтаксического анализа для выражений.

expand(ast, env)Source

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

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

Обратите внимание, что эта функция не обходит AST, а только расширяет корневой узел.

Эта функция использует expand_once/2 в подпрограммах. Ознакомьтесь с ней для получения дополнительной информации и примеров.

expand_literals(ast, env)Source

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

Расширяет все литералы в ast с использованием данного env.

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

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

На данный момент единственными расширяемыми литеральными узлами в AST являются алиасы, поэтому эта функция расширяет только алиасы (и делает это повсюду в литерале).

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

expand_literals(ast, acc, fun)Source

@spec expand_literals(t(), acc, (t(), acc -> {t(), acc})) :: t() when acc: term()

Расширяет все литералы в ast с использованием заданной acc и fun.

fun будет вызвана с расширяемым узлом AST и acc и должна вернуть новый узел с acc. Это обобщённая версия expand_literals/2, которая поддерживает пользовательскую функцию расширения. Пожалуйста, обратитесь к expand_literals/2 для использования и потенциальных проблем.

END_OF_DOCUMENT_MARKER

expand_once(ast, env)Source

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

Принимает узел AST и расширяет его один раз.

Следующие содержимые расширяются:

  • Макросы (локальные или удалённые)
  • Псевдонимы расширяются (если возможно) и возвращают атомы
  • Макросы среды компиляции (__CALLER__/0, __DIR__/0, __ENV__/0 и __MODULE__/0)
  • Чтение атрибутов модуля (@foo)

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

expand_once/2 выполняет расширение только один раз. Проверьте expand/2, чтобы выполнить расширение до тех пор, пока узел больше не сможет быть расширен.

Примеры

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

Рассмотрим реализацию ниже:

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

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

При вызове так:

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

Компиляция завершится ошибкой, потому что My.Module при цитировании не является атомом, а деревом синтаксического анализа, как показано ниже:

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

Это означает, что нам нужно расширить узел псевдонима выше до атома, чтобы мы могли получить его длину. Расширение узла не является тривиальным, потому что нам также нужно расширить псевдонимы вызывающего лица. Например:

alias MyHelpers, as: My

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

Итоговое имя модуля будет MyHelpers.Module, а не My.Module. С Macro.expand/2 такие псевдонимы учитываются. Также расширяются локальные и удалённые макросы. Мы могли бы переписать наш макрос выше, используя эту функцию следующим образом:

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

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

generate_arguments(amount, context)Source

@spec generate_arguments(0, context :: atom()) :: []
@spec generate_arguments(pos_integer(), context) :: [{atom(), [], context}, ...]
when context: atom()

Генерирует узлы AST для заданного количества переменных аргументов, используя Macro.var/2.

Обратите внимание, что аргументы не уникальны. Если впоследствии вы хотите обратиться к тем же переменным, вы можете вызвать эту функцию с теми же входными данными. Используйте generate_unique_arguments/2, чтобы сгенерировать уникальные аргументы, которые нельзя перезаписать.

Примеры

iex> Macro.generate_arguments(2, __MODULE__)
[{:arg1, [], __MODULE__}, {:arg2, [], __MODULE__}]

generate_unique_arguments(amount, context)Source

@spec generate_unique_arguments(0, context :: atom()) :: []
@spec generate_unique_arguments(pos_integer(), context) :: [
  {atom(), [{:counter, integer()}], context},
  ...
]
when context: atom()

Генерирует узлы AST для заданного количества необходимых переменных аргументов, используя Macro.unique_var/2.

Примеры

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

inspect_atom(source_format, atom, opts \\ [])Source

@spec inspect_atom(:literal | :key | :remote_call, atom(), keyword()) :: binary()

Отображает atom в соответствии с различными форматами исходного кода.

Атом можно отобразить в соответствии с тремя различными форматами, в которых он появляется в AST: как литерал (:literal), как ключ (:key) или как имя функции удалённого вызова (:remote_call).

Параметры

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

Примеры

В качестве литерала

Литералы включают обычные атомы, цитируемые атомы, операторы, псевдонимы и специальные nil, true, и false атомы.

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

В качестве ключа

Отобразите атом как ключ списка ключевых слов или карты.

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

В качестве имени удалённого вызова

Отобразите атом как имя функции удалённого вызова.

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

operator?(name, arity)Source

@spec operator?(name :: atom(), arity()) :: boolean()

Возвращает true если данное имя и арность — оператор.

Примеры

iex> Macro.operator?(:not_an_operator, 3)
false
iex> Macro.operator?(:.., 0)
true
iex> Macro.operator?(:+, 1)
true
iex> Macro.operator?(:++, 2)
true
iex> Macro.operator?(:..//, 3)
true

path(ast, fun)Source

@spec path(t(), (t() -> as_boolean(term()))) :: [t()] | nil

Возвращает путь к узлу в ast, для которого fun возвращает значение, истинное для логического выражения.

Путь — список, начинающийся с узла, в котором fun возвращает истинное значение, за которым следуют все его родительские узлы.

Возвращает nil если fun возвращает только ложные значения.

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

Примеры

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

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

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

iex> Macro.path(quote(do: %{foo: [bar: :baz]}), & &1 == :baz)
[
  :baz,
  {:bar, :baz},
  [bar: :baz],
  {:foo, [bar: :baz]},
  {:%{}, [], [foo: [bar: :baz]]}
]

pipe(expr, call_args, position)Source

@spec pipe(t(), t(), integer()) :: t()

Перенаправляет expr в call_args в заданной position.

Эта функция может использоваться для реализации функциональности типа «потока». Например, сам |> реализован как:

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

expr — AST выражения. call_args должен быть AST вызова, в противном случае эта функция вызовет ошибку. Например, рассмотрите оператор потока |>/2, который использует эту функцию для построения конвейеров.

Даже если выражение перенаправлено в AST, это не обязательно означает, что AST валиден. Например, вы можете перенаправить аргумент в div/2, фактически превращая его в вызов div/3, который по умолчанию не является функцией. Код вызовет ошибку, если функция div/3 не определена локально.

postwalk(ast, fun)Source

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

Эта функция ведет себя как prewalk/2, но выполняет обход выражений в цитировании по глубине, в порядке постфиксной записи.

postwalk(ast, acc, fun)Source

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

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

postwalker(ast)Source

@spec postwalker(t()) :: Enumerable.t()

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

Примеры

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

prewalk(ast, fun)Source

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

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

Возвращает новый AST, где каждый узел является результатом вызова fun для каждого соответствующего узла ast.

Примеры

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

prewalk(ast, acc, fun)Source

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

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

Возвращает кортеж, где первый элемент — новый AST, где каждый узел является результатом вызова fun для каждого соответствующего узла, а второй — конечный накопитель.

Примеры

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

prewalker(ast)Source

@spec prewalker(t()) :: Enumerable.t()

Возвращает перечислимый объект, который обходит ast в глубину, в порядке обхода по предшествующим узлам.

Примеры

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

quoted_literal?(term)Source

@spec quoted_literal?(t()) :: boolean()

Возвращает true, если данное выражение с цитированием представляет собой цитируемую литерал.

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

Примеры

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

special_form?(name, arity)Source

@spec special_form?(name :: atom(), arity()) :: boolean()

Возвращает true, если данное имя и арность являются специальной формой.

struct!(module, env)Source

Данная функция устарела. Используйте Macro.struct_info!/2 вместо неё.

struct_info!(module, env)Source

@spec struct_info!(module(), Macro.Env.t()) :: [
  %{field: atom(), required: boolean(), default: term()}
]

Извлекает информацию о структуре (эквивалентно вызову module.__info__(:struct)).

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

Она вызовет исключение ArgumentError, если структура недоступна.

to_string(tree)Source

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

Преобразует данное выражение AST в строку.

Это удобная функция для преобразования AST в строку, которая отбрасывает все форматирование исходного кода и заключает новые строки в 98 символов. См. Code.quoted_to_algebra/2 как функцию более низкого уровня с большим контролем над форматированием.

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

Примеры

iex> Macro.to_string(quote(do: foo.bar(1, 2, 3)))
"foo.bar(1, 2, 3)"

to_string(tree, fun)Source

Данная функция устарела. Используйте Macro.to_string/1 вместо неё.
@spec to_string(t(), (t(), String.t() -> String.t())) :: String.t()

Преобразует данное выражение AST в строку.

Указанная fun вызывается для каждого узла в AST с двумя аргументами: AST узла, который печатается, и строковое представление того же узла. Возвращаемое значение этой функции используется в качестве окончательного строкового представления этого узла AST.

Эта функция отбрасывает всё форматирование исходного кода.

Примеры

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

traverse(ast, acc, pre, post)Source

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

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

Возвращает кортеж, где первый элемент — новый AST, а второй — конечный аккумулятор. Новый AST является результатом вызова pre для каждого узла ast во время фазы обхода по предшествующим узлам и post во время фазы обхода по последующим узлам.

Примеры

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

underscore(atom_or_string)Source

@spec underscore(atom() | String.t()) :: String.t()

Преобразует заданный аргумент в строку с форматом подчёркивания-слэша.

Аргумент должен быть либо атомом, представляющим модуль Elixir, либо строкой, представляющей модуль без Elixir. префикса.

Эта функция была разработана для форматирования идентификаторов/токенов языка с форматом подчёркивание-слэш, поэтому она принадлежит модулю Macro. Не используйте её в качестве общего механизма подчёркивания строк, так как она не поддерживает Unicode или символы, которые не являются допустимыми в идентификаторах Elixir.

Примеры

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

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

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

В общем случае, underscore можно рассматривать как обратное преобразование camelize, однако в некоторых случаях форматирование может быть потеряно:

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

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

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

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

unescape_string(string)Source

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

Распаковывает символы в строке.

Это поведение распаковки, используемое по умолчанию в одинарных и двойных кавычках строк Elixir. См. unescape_string/2 для получения информации о том, как настроить карту экранирования.

В этом случае Elixir будет экранировать следующие: \0, \a, \b, \d, \e, \f, \n, \r, \s, \t и \v. Байты могут быть заданы в шестнадцатеричном формате с помощью \xNN, а коды Unicode — с помощью \uNNNN экранирования.

Эта функция часто используется в реализациях сигил (например, ~r, ~s и других), которые получают исходную, неэкранированную строку, и её можно использовать везде, где нужно имитировать обработку строк Elixir.

Примеры

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

В примере выше мы передаём строку с \n экранированием и возвращаем версию без него.

unescape_string(string, map)Source

@spec unescape_string(String.t(), (non_neg_integer() -> non_neg_integer() | false)) ::
  String.t()

Распаковывает символы в строке в соответствии с заданной схемой.

См. unescape_string/1, если вы хотите использовать ту же схему, что и одинарные и двойные кавычки строк Elixir.

Функция сопоставления

Функция сопоставления получает целое число, представляющее код символа, который нужно распаковать. Также есть специальные атомы :newline, :unicode, и :hex, которые управляют переходом на новую строку, кодами Unicode и экранированием соответственно.

Вот функция сопоставления по умолчанию, реализованная в Elixir:

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

Если функция unescape_map/1 возвращает false, символ не экранирован, и обратная косая черта сохраняется в строке.

Примеры

Использование функции unescape_map/1 определённой выше просто:

Macro.unescape_string("example\\n", &unescape_map(&1))

unique_var(var, context)Source

@spec unique_var(var, context) :: {var, [{:counter, integer()}], context}
when var: atom(), context: atom()

Генерирует узел AST, представляющий уникальную переменную, заданную атомами var и context.

Вызов этой функции с теми же аргументами сгенерирует другую переменную со своим уникальным счётчиком. См. var/2 для альтернативы.

Примеры

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

unpipe(expr)Source

@spec unpipe(t()) :: [t()]

Разбивает выражение конвейера на список.

AST для конвейера (последовательности применений |>/2) похож на AST последовательности бинарных операторов или применений функций: выражение верхнего уровня — это правое :|> (которое выполняется последним), а его левая и правая части — его аргументы:

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

В примере выше, труба |>/2 — правая труба; arg1 — AST для 100 |> div(5), а arg2 — AST для div(2).

Часто бывает полезно иметь AST такого конвейера в виде списка применений функций. Эта функция делает именно это:

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

Мы получаем список, который непосредственно следует за конвейером: сначала 100, затем div(5) (точнее, его AST), затем div(2). 0 как второй элемент кортежа — это позиция предыдущего элемента в конвейере внутри текущего применения функции: {{:div, [], [5]}, 0} означает, что предыдущий элемент (100) будет вставлен в качестве 0-го (первого) аргумента функции div/2, так что AST для этой функции станет {:div, [], [100, 5]} (div(100, 5)).

update_meta(quoted, fun)Source

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

Применяет заданную функцию к метаданным узла, если они содержатся.

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

Примеры

iex> quoted = quote line: 10, do: sample()
{:sample, [line: 10], []}
iex> Macro.update_meta(quoted, &Keyword.delete(&1, :line))
{:sample, [], []}

validate(expr)Source

@spec validate(term()) :: :ok | {:error, term()}

Проверяет, являются ли данные выражения допустимыми цитируемыми выражениями.

См. тип Macro.t/0 для полного описания допустимого цитируемого выражения.

Возвращает :ok если выражение допустимо. В противном случае возвращает кортеж в формате {:error, remainder}, где remainder — недопустимая часть цитируемого выражения.

Примеры

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

iex> Macro.validate([1, 2, 3])
:ok
iex> Macro.validate([1, 2, 3, {4}])
{:error, {4}}

var(var, context)Source

@spec var(var, context) :: {var, [], context} when var: atom(), context: atom()

Генерирует узел AST, представляющий переменную, заданную атомами var и context.

Обратите внимание, что эта переменная не уникальна. Если позже вам нужно получить доступ к той же переменной, вы можете снова вызвать var/2 с теми же аргументами. Используйте unique_var/2 для генерации уникальной переменной, которую нельзя переопределить.

Примеры

Для построения переменной требуется контекст. В большинстве случаев для сохранения гигиены контекст должен быть __MODULE__/0:

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

Однако, если есть необходимость получить доступ к пользовательской переменной, можно указать nil:

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

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

Создано с помощью ExDoc (v0.36.1) для Elixir programming language

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

Spec-Zone.ru

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