Spec-Zone.ru › Elixir 1.5

Макрос

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

Пользовательские Сигилы

Чтобы создать пользовательский сигил, определите функцию с именем 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()

Представляет выражения в АСТ

literal()

Представляет литералы в АСТ

t()

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

Функции

camelize(строка)

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

decompose_call(аст)

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

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

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

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

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

expand_once(аст, среда)

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

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

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

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

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

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

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

postwalk(аст, аккумулятор, функция)

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

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

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

prewalk(аст, аккумулятор, функция)

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

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

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

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

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

underscore(атом)

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

unescape_string(символы)

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

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

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

unescape_tokens(токены)

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

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

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

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

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

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

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

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

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

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

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

Типы

expr()

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

Представляет выражения в АСТ

literal()

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

Представляет литералы в АСТ

t()

t() :: expr() | literal()

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

Функции

camelize(строка)

camelize(String.t()) :: String.t()

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

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

Примеры

iex> Macro.camelize "foo_bar"
"FooBar"

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

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

decompose_call(аст)

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()) :: 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(дерево, среда)

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

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

expand_once(аст, среда)

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

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

  • Макросы (локальные или удалённые)
  • Псевдонимы расширяются (если возможно) и возвращают атомы
  • Макросы среды компиляции (__ENV__/0, __MODULE__/0 и __DIR__/0)
  • Чтение атрибутов модуля (@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

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

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

Примеры

iex> Macro.generate_arguments(2, __MODULE__)
[{:var1, [], __MODULE__}, {:var2, [], __MODULE__}]

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

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

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

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

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

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

postwalk(аст, аккумулятор, функция)

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

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

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

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

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

prewalk(аст, аккумулятор, функция)

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

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

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

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

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

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

Примеры

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

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

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

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

underscore(atom)

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

Если задан атом, предполагается, что это модуль 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, а коды Unicode – как \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/1 возвращает false, символ не экранируется, и обратная косая черта сохраняется в строке.

Шестнадцатеричные числа и коды Unicode будут экранированы, если функция отображения возвращает true для ?x. Коды Unicode, если функция отображения возвращает true для ?u.

Примеры

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

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

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)

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

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

Примеры

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

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.5.3/Macro.html

Spec-Zone.ru

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