Spec-Zone.ru › Elixir 1.3

Макросы

Удобства для работы с макросами.

Пользовательские знаки

Чтобы создать пользовательский знак, определите функцию с именем sigil_{identifier}, которая принимает два аргумента. Первый аргумент — строка, второй — список символов, содержащих любые модификаторы. Если знак является строчной буквой (например, sigil_x), то аргумент строки позволит интерполяцию. Если знак является заглавной буквой (например, sigil_X), то строка не будет интерполироваться.

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

Модуль, содержащий пользовательский знак, должен быть импортирован, прежде чем синтаксис знака может быть использован.

Примеры

defmodule MySigils do
  defmacro sigil_x(term, [?r]) do
    quote do
      unquote(term) |> String.reverse()
    end
  end
  defmacro sigil_x(term, _modifiers) do
    term
  end
  defmacro sigil_X(term, [?r]) do
    quote do
      unquote(term) |> String.reverse()
    end
  end
  defmacro sigil_X(term, _modifiers) do
    term
  end
end

import MySigils

~x(with #{"inter" <> "polation"})
#=>"with interpolation"

~x(with #{"inter" <> "polation"})r
#=>"noitalopretni htiw"

~X(without #{"interpolation"})
#=>"without \#{"interpolation"}"

~X(without #{"interpolation"})r
#=>"}\"noitalopretni\"{# tuohtiw"

Обзор

Типы

expr()
t()

Дерево абстрактного синтаксиса (AST)

Функции

camelize(строка)

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

decompose_call(ast)

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

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

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

expand(дерево, среда)

Принимает узел AST и расширяет его до тех пор, пока это не станет невозможно

expand_once(ast, среда)

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

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

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

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

Выполняет обход дерева, постфиксный обход, дерева выражений в кавычках

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

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

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

Выполняет обход дерева, префиксный обход, дерева выражений в кавычках

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

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

to_string(дерево, функция \\ fn _ast, строка -> строка end)

Преобразует заданное выражение в двоичное

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

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

underscore(атом)

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

unescape_string(символы)

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

unescape_string(символы, карта)

Деэскэпирует заданные символы в соответствии с предоставленной картой

unescape_tokens(токени)

Деэскэпирует заданные токены в соответствии с карте по умолчанию

unescape_tokens(токени, карта)

Деэскэпирует заданные токены в соответствии с предоставленной картой

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

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

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

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

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

Проверяет, являются ли заданные выражения корректными выражениями в кавычках

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

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

Типы

expr()

expr() :: {expr | atom, Keyword.t, atom | [t]}

t()

t ::
  expr |
  {t, t} |
  atom |
  number |
  binary |
  pid |
  (... -> any) |
  [t]

Дерево абстрактного синтаксиса (AST)

Функции

camelize(строка)

camelize(String.t) :: String.t

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

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

Примеры

iex> Macro.camelize "foo_bar"
"FooBar"

decompose_call(ast)

decompose_call(Macro.t) ::
  {atom, [Macro.t]} |
  {Macro.t, atom, [Macro.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

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

escape(term, Keyword.t) :: Macro.t

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

Можно передать unquote: true в escape/2, что оставляет unquote/1 выражения не эскэпированными, эффективно отменяя квотирование содержимого при эскэпировании.

Примеры

iex> Macro.escape(:foo)
:foo

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

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

expand(дерево, среда)

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

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

expand_once(ast, среда)

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

Ниже перечислены расширяемые элементы:

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

Если выражение не может быть расширено, оно возвращает само выражение. Обратите внимание, что 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

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

pipe(Macro.t, Macro.t, integer) :: Macro.t | no_return

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

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

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

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

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

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

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

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

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

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

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

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

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

to_string(дерево, функция \\ fn _ast, строка -> строка end)

to_string(Macro.t, (Macro.t, String.t -> String.t)) :: String.t

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

Примеры

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

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

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

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

underscore(атом)

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

Если задан атом, предполагается, что это модуль 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"

unescape_string(chars)

unescape_string(String.t) :: String.t

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

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

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

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

Примеры

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

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

unescape_string(chars, map)

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

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

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

Карта

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

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(?x), do: true
def unescape_map(?u), do: true
def unescape_map(e),  do: e

Если функция unescape_map возвращает false. Символ не экранируется и \ сохраняется в списке символов. Шестнадцатеричные и кодовые точки Юникода будут экранированы, если функция отображения возвращает true для ?x. Кодовые точки Юникода, если функция отображения возвращает true для ?u. ## Примеры Использование функции unescape_map , определенной выше, очень просто: Macro.unescape_string “example\n”, &unescape_map(&1)

unescape_tokens(tokens)

unescape_tokens([Macro.t]) :: [Macro.t]

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

Проверьте unescape_string/1 и unescape_string/2 для получения дополнительной информации о распаковке.

Только токены, являющиеся бинарными, распаковываются, все остальные игнорируются. Эта функция полезна при реализации собственных сигилов. Проверьте реализацию Kernel.sigil_s/2 для примеров.

unescape_tokens(tokens, map)

unescape_tokens([Macro.t], (non_neg_integer -> non_neg_integer | false)) :: [Macro.t]

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

Проверьте unescape_tokens/1 и unescape_string/2 для получения дополнительной информации.

unpipe(expr)

unpipe(Macro.t) :: [Macro.t]

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

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

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

В примере выше, конвейер |> — это правый конвейер; 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)

update_meta(t, (Keyword.t -> Keyword.t)) :: 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)

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

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

Проверяет type:Macro.t спецификации валидного выражения с цитированием.

Возвращает :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)

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

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

Примеры

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

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

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

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

© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.3.4/Macro.html

Spec-Zone.ru

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