Spec-Zone.ru › Elixir 1.16

Source Macro

Функции для работы с 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(string)

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

classify_atom(atom)

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

compile_apply(mod, fun, args, caller)

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

dbg(code, options, env)

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

decompose_call(ast)

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

escape(expr, opts \\ [])

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

expand(ast, env)

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

expand_literals(ast, env)

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

expand_literals(ast, acc, fun)

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

expand_once(ast, env)

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

generate_arguments(amount, context)

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

generate_unique_arguments(amount, context)

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

inspect_atom(source_format, atom)

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

operator?(name, arity)

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

path(ast, fun)

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

pipe(expr, call_args, position)

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

postwalk(ast, fun)

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

postwalk(ast, acc, fun)

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

postwalker(ast)

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

prewalk(ast, fun)

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

prewalk(ast, acc, fun)

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

prewalker(ast)

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

quoted_literal?(term)

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

special_form?(name, arity)

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

struct!(module, env)

Расширяет структуру, заданную module в заданном env.

to_string(tree)

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

to_string(tree, fun) deprecated

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

traverse(ast, acc, pre, post)

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

underscore(atom_or_string)

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

unescape_string(string)

Деэкранирует символы в строке.

unescape_string(string, map)

Деэкранирует символы в строке в соответствии с заданным отображением.

unique_var(var, context)

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

unpipe(expr)

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

update_meta(quoted, fun)

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

validate(expr)

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

var(var, context)

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

END_OF_DOCUMENT_MARKER

Типы

captured_remote_function()Source

@type captured_remote_function() :: (... -> any())

Захваченная удалённая функция в формате &Модуль.функция/арность

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. Примечание: информация о строке игнорируется для кода, полученного с помощью quote, но может быть включена обратно с помощью параметра :line.

Следующие ключи метаданных включаются функцией Code.string_to_quoted/2:

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

  • :column - номер столбца узла AST (когда :columns равно true). Примечание: информация о столбце всегда игнорируется для кода, полученного с помощью quote.

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

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

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

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

  • :end_of_expression - обозначает, когда фактически происходит окончание выражения (когда :token_metadata равно true). Это доступно только для непосредственных дочерних элементов __block__, и это либо позиция новой строки, либо символ ;. Последнее выражение в __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.

Эта функция была разработана для преобразования идентификаторов/токенов языка в 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.

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

Сравнение с 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)
{:a, :b, :c}

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 для случаев использования и потенциальных проблем.

expand_once(ast, env)Source

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

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

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

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

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

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

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

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

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

Примеры

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

Литералы включают обычные атомы, атомы в кавычках, операторы, псевдонимы и специальные атомы 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

@spec struct!(module, Macro.Env.t()) :: %{
  :__struct__ => module,
  optional(atom()) => any()
}
when module: module()

Расширяет структуру, заданную module в данном env.

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

Вызовет CompileError если структура недоступна. Начиная с Elixir v1.12, вызов этой функции также добавляет зависимость экспорта на данную структуру.

to_string(tree)Source

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

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

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

Примеры

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(module() | atom() | String.t()) :: String.t()

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

Аргумент должен быть либо атомом, либо строкой. Если задан атом, предполагается, что это модуль 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.32.2) для языка программирования Elixir

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

Spec-Zone.ru

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