Макрос
Макросы — это конструкции времени компиляции, которые вызываются со входными данными в виде абстрактного синтаксического дерева Elixir и возвращают выходные данные, являющиеся надмножеством абстрактного синтаксического дерева 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.
Чтобы узнать больше об абстрактном синтаксическом дереве Elixir и о том, как создавать их программно, см. quote/2.
Пользовательские сигилы
Макросы также часто используются для реализации пользовательских сигилов. Для создания пользовательского сигила определите макрос с именем 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" Сводка
Типы
- captured_remote_function()
Захваченная удалённая функция в формате &Mod.fun/arity
- input()
Входные данные макроса
- metadata()
Список ключевых слов метаданных AST.
- output()
Выходные данные макроса
- t()
Абстрактное синтаксическое дерево (AST)
Функции
- camelize(строка)
Преобразует заданную строку в формат CamelCase.
- decompose_call(ast)
Декомпозирует локальный или удалённый вызов на его удалённую часть (если она есть), имя функции и список аргументов.
- escape(выражение, opts \\ [])
Рекурсивно экранирует значение, чтобы его можно было вставить в синтаксическое дерево.
- expand(ast, env)
Принимает узел AST и расширяет его до тех пор, пока он больше не может быть расширен.
- expand_once(ast, env)
Принимает узел AST и расширяет его один раз.
- generate_arguments(количество, контекст)
Генерирует узлы AST для заданного количества требуемых переменных аргументов, используя
Macro.var/2.- generate_unique_arguments(количество, контекст)
Генерирует узлы AST для заданного количества требуемых переменных аргументов, используя
Macro.unique_var/2.- operator?(имя, арность)
Возвращает
true, если заданное имя и арность — оператор.- pipe(выражение, аргументы_вызова, позиция)
Направляет
exprвcall_argsв указаннойposition.- postwalk(ast, fun)
Выполняет обход в глубину, постфиксный обход выражений с цитированием.
- postwalk(ast, acc, fun)
Выполняет обход в глубину, постфиксный обход выражений с цитированием с использованием аккумулятора.
- postwalker(ast)
Возвращает перечислимый объект, который обходит
astв постфиксном обходе в глубину.- prewalk(ast, fun)
Выполняет обход в глубину, префиксный обход выражений с цитированием.
- prewalk(ast, acc, fun)
Выполняет обход в глубину, префиксный обход выражений с цитированием с использованием аккумулятора.
- prewalker(ast)
Возвращает перечислимый объект, который обходит
astв префиксном обходе в глубину.- quoted_literal?(термин)
Возвращает
true, если данное цитируемое выражение представляет собой цитируемую литерал.- special_form?(имя, арность)
Возвращает
true, если данное имя и арность — специальная форма.- struct!(модуль, env)
Расширяет структуру, заданную
moduleв заданномenv.- to_string(дерево)
Преобразует данное выражение AST в строку.
- to_string(дерево, fun) устаревший
Преобразует данное выражение AST в строку.
- traverse(ast, acc, pre, post)
Выполняет обход в глубину выражений с цитированием с использованием аккумулятора.
- underscore(атом)
Преобразует данный атом или двоичное значение в нижний регистр с использованием нижнего подчеркивания.
- unescape_string(строка)
Деэкранирует заданные символы.
- unescape_string(строка, карта)
Деэкранирует заданные символы в соответствии с заданной картой.
- unique_var(переменная, контекст)
Генерирует узел AST, представляющий уникальную переменную, заданную атомами
varиcontext.- unpipe(выражение)
Разбивает выражение конвейера на список.
- update_meta(цитируемое, fun)
Применяет заданную функцию к метаданным узла, если они присутствуют.
- validate(выражение)
Проверяет, являются ли данные выражения допустимыми цитируемыми выражениями.
- var(переменная, контекст)
Генерирует узел AST, представляющий переменную, заданную атомами
varиcontext.
Типы
captured_remote_function()Source
@type captured_remote_function() :: (... -> any())
Захваченная удалённая функция в формате &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. -
:generated— указывает, должен ли код рассматриваться как сгенерированный компилятором или нет. Это означает, что компилятор и инструменты, такие как Dialyzer, могут не выдавать определённые предупреждения. -
:keep— используетсяquote/2с параметромlocation: :keepдля аннотирования файла и номера строки исходного кода. -
:line— номер строки узла AST.
Следующие ключи метаданных включены Code.string_to_quoted/2:
-
:closing— содержит метаданные о закрывающей паре, например, о}в кортеже или в карте, или о закрывающей)в вызове функции с круглыми скобками.:closingне определяет конец выражения, если существуют метаданные:doи:end(когда:token_metadataистинно). -
:column— номер столбца узла AST (если:columnsистинно). -
:delimiter— содержит открывающий разделитель для сигилов, строк и списков символов в виде строки (например,"{","/","'"и т. п.). -
:format— устанавливается в:keywordпри определении атома как ключевого слова. -
:do— содержит метаданные о расположенииdoв вызове функции с блокамиdo-end(если:token_metadataистинно). -
:end— содержит метаданные о расположенииendв вызове функции с блокамиdo-end(если:token_metadataистинно). -
:end_of_expression— обозначает, когда фактически происходит конец выражения. Доступно для всех выражений, кроме последнего внутри__block__(если:token_metadataистинно). -
:indentation— отступ сигилового heredoc.
Следующие ключи метаданных являются закрытыми:
-
:alias— используется для гигиены псевдонимов. -
:ambiguous_op— используется для улучшения сообщений об ошибках в компиляторе. -
:import— используется для гигиены импорта. -
: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(строка)Исходный код
@spec 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(ast)Исходный код
@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(выражение, опции \\ [])Исходный код
@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не экранированными, фактически раскрывая содержимое при экранировании. Этот параметр полезен только при экранировании деревьев синтаксического анализа, которые могут содержать фрагменты в кавычках. По умолчанию значение false.-
:prune_metadata- если значение true, удаляет метаданные из экранированных узлов дерева синтаксического анализа. Обратите внимание, что этот параметр изменяет семантику экранированного кода, и его следует использовать только при экранировании AST. По умолчанию значение false.Например,
ExUnitхранит AST каждого утверждения, поэтому при сбое утверждения мы можем показать фрагменты кода пользователям. Без этого параметра каждый раз при компиляции модуля теста мы получаем разный MD5 модуля байткода, потому что AST содержит метаданные, такие как счётчики, специфичные для среды компиляции. Удаляя метаданные, мы обеспечиваем детерминированность модуля и сокращаем объём данных, которыеExUnitдолжен хранить. Сохраняется только минимальный объём метаданных, например:lineи:no_parens.
Сравнение с Kernel.SpecialForms.quote/2
Функция escape/2 иногда путают с Kernel.SpecialForms.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 используется для экранирования значений (либо напрямую передаваемых, либо связанных переменной), в то время как Kernel.SpecialForms.quote/2 генерирует деревья синтаксического анализа для выражений.
expand(ast, env)Исходный код
Принимает узел AST и расширяет его, пока он может быть расширен.
Обратите внимание, что эта функция не обходит AST, только корневой узел расширяется.
Эта функция использует expand_once/2 внутри. Обратите внимание на неё для получения дополнительной информации и примеров.
expand_once(ast, env)Исходный код
Принимает узел 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(количество, контекст)Исходный код
@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(количество, контекст)Исходный код
@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 operator?(имя, арность)Исходный код
@spec operator?(name :: atom(), arity()) :: boolean()
Возвращает true если заданное имя и арность являются оператором.
Примеры
iex> Macro.operator?(:not_an_operator, 3) false iex> Macro.operator?(:+, 1) true iex> Macro.operator?(:++, 2) true iex> Macro.operator?(:..//, 3) true
pipe(выражение, аргументы_вызова, позиция)Исходный код
@spec pipe(t(), t(), integer()) :: t()
Перенаправляет expr в call_args в заданной position.
postwalk(ast, функция)Исходный код
@spec postwalk(t(), (t() -> t())) :: t()
Выполняет обход выражений с кавычками в постфиксном порядке, по глубине.
postwalk(ast, сумма, функция)Исходный код
@spec postwalk(t(), any(), (t(), any() -> {t(), any()})) :: {t(), any()} Выполняет обход выражений с кавычками в постфиксном порядке, по глубине, используя накопитель.
postwalker(ast)Исходный код
Возвращает перечислитель, который проходит по ast в постфиксном порядке, по глубине.
Примеры
iex> ast = quote do: foo(1, "abc")
iex> Enum.map(Macro.postwalker(ast), & &1)
[1, "abc", {:foo, [], [1, "abc"]}] prewalk(ast, функция)Исходный код
@spec prewalk(t(), (t() -> t())) :: t()
Выполняет обход выражений с кавычками в префиксном порядке, по глубине.
Возвращает новый ast, где каждый узел является результатом вызова fun для каждого соответствующего узла ast.
Примеры
iex> ast = quote do: 5 + 3 * 7
iex> new_ast = Macro.prewalk(ast, fn
...> {:+, meta, children} -> {:*, meta, children}
...> {:*, meta, children} -> {:+, meta, children}
...> other -> other
...> end)
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()} Выполняет обход цитируемых выражений в порядке следования с использованием аккумулятора.
prewalker(ast)Source
Возвращает перечислимый объект, который обходит 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 в строку.
Эта функция отбрасывает все форматирование исходного кода. См. 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
@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()} Выполняет обход цитируемых выражений в глубину с использованием аккумулятора.
underscore(atom)Source
@spec underscore(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" 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 и других), которые получают исходную, неэкранированную строку.
Примеры
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.
Карта
Карта должна быть функцией. Функция получает целое число, представляющее код символа, который нужно развёрнуть. Вот функция отображения по умолчанию, реализованная 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, символ не экранируется, и обратная косая черта сохраняется в строке.
Символы новой строки, Unicode и шестнадцатеричные коды будут экранироваться, если карта возвращает true соответственно для :newline, :unicode, и :hex.
Примеры
Использование функции 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.
Примеры
iex> {:foo, [counter: c], __MODULE__} = Macro.unique_var(:foo, __MODULE__)
iex> is_integer(c)
true unpipe(expr)Source
@spec unpipe(t()) :: [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)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}
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.13.4/Macro.html