Справочник синтаксиса
Синтаксис Elixir был разработан для простого преобразования в абстрактное синтаксическое дерево (AST). Это означает, что синтаксис Elixir в основном однороден с небольшим количеством конструкций «синтаксического сахара», чтобы уменьшить шум в распространённых идиомах Elixir.
В этом документе рассматриваются все конструкции синтаксиса Elixir в качестве справочника, а затем обсуждается их точное представление в AST.
Зарезервированные слова
Это зарезервированные слова языка Elixir. Они подробно описаны в этом руководстве, но здесь сгруппированы для удобства:
-
true,false,nil— используются как атомы -
when,and,or,not,in— используются как операторы -
fn— используется для определения анонимных функций -
do,end,catch,rescue,after,else— используются в блоках do/end
Типы данных
Числа
Целые числа (1234) и числа с плавающей запятой (123.4) в Elixir представляются как последовательность цифр, которые могут быть разделены подчёркиваниями для повышения удобочитаемости, например, 1_000_000. Целые числа никогда не содержат точки (.) в своём представлении. Числа с плавающей запятой содержат точку и по меньшей мере одну цифру после точки. Числа с плавающей запятой также поддерживают научный формат, например 123.4e10 или 123.4E10.
Атомы
Атомы в Elixir начинаются с двоеточия (:), за которым должны следовать некомбинируемые символы Unicode и подчёркивание. Атом может продолжаться, используя последовательность символов Unicode, включая цифры, подчёркивание и @. Атомы могут заканчиваться на ! или ?. См. Синтаксис Unicode для формального описания. Для символов Unicode требуется Erlang/OTP 20.
Все операторы в Elixir также являются допустимыми атомами. Допустимые примеры — :foo, :FOO, :foo_42, :foo@bar и :++. Недопустимые примеры — :@foo (@ не разрешено в начале), :123 (цифры не разрешены в начале) и :(*) (не является допустимым оператором).
Если за двоеточием следуют двойные или одинарные кавычки, атом может состоять из любого символа, например :"++olá++".
true, false, и nil — зарезервированные слова, которые представлены атомами :true, :false и :nil соответственно.
Строки
Однострочные строки в Elixir пишутся в двойных кавычках, например "foo". Любая двойная кавычка внутри строки должна быть экранирована с помощью \. Строки поддерживают символы Unicode и хранятся как двоичные данные в кодировке UTF-8.
Многострочные строки в Elixir пишутся с помощью трёх двойных кавычек и могут содержать неэкранированные кавычки внутри них. Результирующая строка будет заканчиваться новой строкой. Отступ последнего """ используется для удаления отступа из внутренней строки. Например:
iex> test = """ ...> this ...> is ...> a ...> test ...> """ " this\n is\n a\n test\n" iex> test = """ ...> This ...> Is ...> A ...> Test ...> """ "This\nIs\nA\nTest\n"
Строки всегда представляются сами по себе в AST.
Списки символов
Списки символов в Elixir пишутся в одинарных кавычках, например 'foo'. Любая одинарная кавычка внутри строки должна быть экранирована с помощью \. Списки символов представляют собой список целых чисел, каждое из которых представляет символ Unicode.
Многострочные списки символов пишутся с помощью трёх одинарных кавычек ('''), так же как и многострочные строки.
Списки символов всегда представляются сами по себе в AST.
Списки, кортежи и бинарные данные
Структуры данных, такие как списки, кортежи и бинарные данные, помечаются соответственно разделителями [...], {...}, и <<...>>. Каждый элемент разделяется запятой. Также разрешена конечная запятая, как в [1, 2, 3,].
Карты и списки ключевых слов
Карты используют запись %{...} и каждое значение ключа задаётся парами, помеченными =>, например %{"hello" => 1, 2 => "world"}.
Как списки ключевых слов (список кортежей из двух элементов, где первый элемент — атом), так и карты с атомными ключами поддерживают нотацию ключевых слов, где символ двоеточия : перемещается в конец атома. %{hello: "world"} эквивалентно %{:hello => "world"}, а [foo: :bar] эквивалентно [{:foo, :bar}]. Эта нотация — синтаксический сахар, который генерирует то же самое представление AST. Она будет объяснена в последующих разделах.
Структуры
Структуры создаются на основе синтаксиса карты, передавая имя структуры между % и {. Например, %User{...}.
Выражения
Переменные
Переменные в Elixir должны начинаться с подчёркивания или некомбинируемого символа Unicode, который не находится в верхнем или прописном регистре. Переменная может продолжаться, используя последовательность символов Unicode, включая цифры и подчёркивание. Переменные могут заканчиваться на ? или !. См. Синтаксис Unicode для формального описания. Для символов Unicode требуется Erlang/OTP 20.
Рекомендации по именованию в Elixir рекомендуют использовать переменные в формате snake_case.
Неквалифицированные вызовы (локальные вызовы)
Неквалифицированные вызовы, такие как add(1, 2), должны начинаться с подчёркивания или некомбинируемого символа Unicode, который не находится в верхнем или прописном регистре. Вызов может продолжаться, используя последовательность символов Unicode, включая цифры и подчёркивание. Вызовы могут заканчиваться на ? или !. См. Синтаксис Unicode для формального описания. Для символов Unicode требуется Erlang/OTP 20.
Скобки для неквалифицированных вызовов необязательны, за исключением вызовов нулевой арности, которые в этом случае были бы неоднозначными с переменными. Если скобки используются, они должны следовать сразу за именем функции без пробелов. Например, add (1, 2) — синтаксическая ошибка, так как (1, 2) обрабатывается как недопустимый блок, который пытаются передать как единственный аргумент функции add.
Рекомендации по именованию в Elixir рекомендуют использовать вызовы в формате snake_case.
Операторы
Как и во многих языках программирования, Elixir также поддерживает операторы как неквалифицированные вызовы со своими правилами приоритета и ассоциативности. Конструкции, такие как =, when, & и @ просто обрабатываются как операторы. См. страницу Операторы для полного справочника.
Квалифицированные вызовы (удалённые вызовы)
Квалифицированные вызовы, такие как Math.add(1, 2), должны начинаться с подчёркивания или некомбинируемого символа Unicode, который не находится в верхнем или прописном регистре. Вызов может продолжаться, используя последовательность символов Unicode, включая цифры и подчёркивание. Вызовы могут заканчиваться на ? или !. См. Синтаксис Unicode для формального описания. Для символов Unicode требуется Erlang/OTP 20.
Рекомендации по именованию в Elixir рекомендуют использовать вызовы в формате snake_case.
Для квалифицированных вызовов Elixir также разрешает писать имя функции в двойных или одинарных кавычках, позволяя делать вызовы, такие как Math."++add++"(1, 2). Операторы могут использоваться в качестве квалифицированных вызовов без необходимости в кавычках, таких как Kernel.+(1, 2).
Скобки для квалифицированных вызовов необязательны. Если скобки используются, они должны следовать сразу за именем функции без пробелов.
Псевдонимы
Псевдонимы — это конструкции, которые расширяются до атомов во время компиляции. Псевдоним String расширяется до атома :"Elixir.String". Псевдонимы должны начинаться с заглавной латинской буквы, за которой может следовать любая латинская буква, цифра или символ подчёркивания. Символы не ASCII не поддерживаются в псевдонимах.
Рекомендации по именованию в Elixir рекомендуют использовать псевдонимы в формате CamelCase.
Блоки
Блоки — это несколько выражений Elixir, разделённые новыми строками или точкой с запятой. Новый блок может быть создан в любой момент с помощью скобок.
Стрелка слева направо
Стрелка слева направо (->) используется для установления связи между левой и правой частью. Левая часть может иметь ноль, один или несколько аргументов, правая часть — ноль, один или несколько выражений, разделённых новой строкой. -> всегда находится между одним из следующих терминаторов: do/end, fn/end или (/).
Она встречается в конструкциях case и cond между do/end:
case 1 do 2 -> 3 4 -> 5 end cond do true -> false end
Встречается в типах спецификаций между (/):
(integer(), boolean() -> integer())
Также используется между fn/end для построения анонимных функций:
fn x, y -> x + y end
Сигилы
Сигилы начинаются с ~ и следуют за буквой и одной из следующих пар:
-
(и) -
{и} -
[и] -
<и> -
"и" -
'и' -
|и| -
/и/
После закрытия пары можно указать ноль или более латинских букв в качестве модификатора. Сигилы выражаются как неквалифицированные вызовы, префиксрованные с sigil_, где первый аргумент — содержимое сигила как строка, а второй аргумент — список целых чисел как модификаторы:
Если буква сигила заглавная, интерполяция в сигиле запрещена, в противном случае его содержимое может быть динамическим. Сравните результаты сигилов ниже для получения дополнительной информации:
~s/f#{"o"}o/
~S/f#{"o"}o/ Сигилы полезны для кодирования текста со своими правилами экранирования, такими как регулярные выражения, даты и время и т. д.
Дерево абстрактного синтаксиса Elixir
Синтаксис Elixir разработан для прямого преобразования в дерево абстрактного синтаксиса (AST). Дерево AST Elixir — это обычная структура данных Elixir, состоящая из следующих элементов:
- атомы — например,
:foo - целые числа — например,
42 - вещественные числа — например,
13.1 - строки — например,
"hello" - списки — например,
[1, 2, 3] - кортежи с двумя элементами — например,
{"hello", :world} - кортежи с тремя элементами, представляющие вызовы или переменные, как поясняется далее
Основным строительным блоком AST Elixir является вызов, например:
sum(1, 2, 3)
который представлен как кортеж с тремя элементами:
{:sum, meta, [1, 2, 3]} первый элемент — это атом (или другой кортеж), второй элемент — список кортежей с двумя элементами и метаданными (например, номерами строк), а третий — список аргументов.
Мы можем получить AST для любого выражения Elixir, вызвав quote:
quote do
sum()
end
#=> {:sum, [], []} Переменные также представлены с помощью кортежа с тремя элементами и комбинацией списков и атомов, например:
quote do
sum
end
#=> {:sum, [], Elixir} Вы можете видеть, что переменные также представлены кортежем, за исключением третьего элемента, который является атомом, выражающим контекст переменной.
В следующем разделе мы рассмотрим многие конструкции синтаксиса Elixir вместе с их представлением в AST.
Операторы
Операторы обрабатываются как неквалифицированные вызовы:
quote do
1 + 2
end
#=> {:+, [], [1, 2]} Заметьте, что . также является оператором. Удаленные вызовы используют точку в AST с двумя аргументами, где второй аргумент всегда атом:
quote do
foo.bar(1, 2, 3)
end
#=> {{:., [], [{:foo, [], Elixir}, :bar]}, [], [1, 2, 3]} Вызов анонимных функций использует точку в AST с одним аргументом, отражая тот факт, что имя функции «отсутствует» справа от точки:
quote do
foo.(1, 2, 3)
end
#=> {{:., [], [{:foo, [], Elixir}]}, [], [1, 2, 3]} Псевдонимы
Псевдонимы представлены вызовом __aliases__, где каждый сегмент, разделенный точкой, является аргументом:
quote do
Foo.Bar.Baz
end
#=> {:__aliases__, [], [:Foo, :Bar, :Baz]}
quote do
__MODULE__.Bar.Baz
end
#=> {:__aliases__, [], [{:__MODULE__, [], Elixir}, :Bar, :Baz]} Все аргументы, кроме первого, гарантированно являются атомами.
Структуры данных
Помните, что списки являются литералами, поэтому они представлены самими собой в AST:
quote do [1, 2, 3] end #=> [1, 2, 3]
Кортежи имеют собственное представление, за исключением кортежей с двумя элементами, которые представлены самими собой:
quote do
{1, 2}
end
#=> {1, 2}
quote do
{1, 2, 3}
end
#=> {:{}, [], [1, 2, 3]} Бинарные данные имеют представление, аналогичное кортежам, за исключением того, что они помечены :<<>> вместо :{}:
quote do
<<1, 2, 3>>
end
#=> {:<<>>, [], [1, 2, 3]} То же самое относится к картам, где каждая пара рассматривается как список кортежей с двумя элементами:
quote do
%{1 => 2, 3 => 4}
end
#=> {:%{}, [], [{1, 2}, {3, 4}]} Блоки
Блоки представлены как вызов __block__, где каждая строка является отдельным аргументом:
quote do
1
2
3
end
#=> {:__block__, [], [1, 2, 3]}
quote do 1; 2; 3; end
#=> {:__block__, [], [1, 2, 3]} Стрелка слева направо
Стрелка слева направо (->) представлена аналогично операторам, за исключением того, что они всегда являются частью списка, его левая сторона представляет список аргументов, а правая сторона — выражение.
Например, в case и cond:
quote do
case 1 do
2 -> 3
4 -> 5
end
end
#=> {:case, [], [1, [do: [{:->, [], [[2], 3]}, {:->, [], [[4], 5]}]]]}
quote do
cond do
true -> false
end
end
#=> {:cond, [], [[do: [{:->, [], [[true], false]}]]]} Между (/):
quote do
(1, 2 -> 3
4, 5 -> 6)
end
#=> [{:->, [], [[1, 2], 3]}, {:->, [], [[4, 5], 6]}] Между fn/end:
quote do
fn
1, 2 -> 3
4, 5 -> 6
end
end
#=> {:fn, [], [{:->, [], [[1, 2], 3]}, {:->, [], [[4, 5], 6]}]} Синтаксический сахар
Все вышеперечисленные конструкции являются частью синтаксиса Elixir и имеют собственное представление в качестве части AST Elixir. В этом разделе будут рассмотрены оставшиеся конструкции, которые «разрушаются» до одной из изученных выше конструкций. Другими словами, конструкции ниже могут быть представлены в коде Elixir более чем одним способом и сохраняют эквивалентность AST.
Целые числа в других системах счисления и коды Unicode
Elixir позволяет целым числам содержать _ для разделения цифр и предоставляет удобства для представления целых чисел в других системах счисления:
1_000_000 #=> 1000000 0xABCD #=> 43981 (Hexadecimal base) 0o01234567 #=> 342391 (Octal base) 0b10101010 #=> 170 (Binary base) ?é #=> 233 (Unicode codepoint)
Эти конструкции существуют только на уровне синтаксиса. Все приведенные выше примеры представлены в AST в качестве соответствующих целых чисел.
Синтаксис доступа
Синтаксис доступа представлен как вызов Access.get/2:
quote do
opts[arg]
end
#=> {{:., [], [Access, :get]}, [], [{:opts, [], Elixir}, {:arg, [], Elixir}]} Необязательные скобки
Elixir предоставляет необязательные скобки для неквалифицированных и квалифицированных вызовов.
quote do
sum 1, 2, 3
end
#=> {:sum, [], [1, 2, 3]} Вышеперечисленное обрабатывается парсером так же, как sum(1, 2, 3).
То же самое относится к квалифицированным вызовам, например, Foo.bar(1, 2, 3), что эквивалентно Foo.bar 1, 2, 3. Однако помните, что скобки не являются необязательными для неквалифицированных вызовов без аргументов, таких как sum(). Удаление скобок для sum приводит к тому, что он представляется как переменная sum, что означает, что они больше не будут эквивалентны.
Ключевые слова
Ключевые слова в Elixir — это список кортежей из двух элементов, где первый элемент — атом. Используя базовые конструкции, они представлялись бы:
[{:foo, 1}, {:bar, 2}] Однако Elixir вводит синтаксический сахар, где ключевые слова выше могут быть записаны следующим образом:
[foo: 1, bar: 2]
Атомы с иностранными символами в их имени, такими как пробелы, должны быть заключены в кавычки. Это правило также относится к ключевым словам:
[{:"foo bar", 1}, {:"bar baz", 2}] == ["foo bar": 1, "bar baz": 2] Помните, что, поскольку списки и кортежи с двумя элементами являются литералами в кавычках, по определению ключевые слова также являются литералами (на самом деле, единственная причина, по которой кортежи с двумя элементами являются литералами в кавычках, заключается в поддержке ключевых слов как литералов).
Ключевые слова в качестве последних аргументов
Elixir также поддерживает синтаксис, где, если последний аргумент вызова является ключевым словом, то квадратные скобки можно опустить. Это означает, что следующее:
if(condition, do: this, else: that)
то же самое, что
if(condition, [do: this, else: that])
что в свою очередь эквивалентно
if(condition, [{:do, this}, {:else, that}]) do/end блоки
Последней синтаксической удобством являются блоки do/end. Блоки do/end эквивалентны ключевым словам в качестве последнего аргумента вызова функции, где содержимое блока заключено в скобки. Например:
if true do this else that end
эквивалентно:
if(true, do: (this), else: (that))
что мы рассмотрели в предыдущем разделе.
Скобки важны для поддержки нескольких выражений. Это:
if true do this that end
эквивалентно:
if(true, do: ( this that ))
Внутри блоков do/end вы можете использовать другие ключевые слова, такие как else в if выше. Поддерживаемые ключевые слова между do/end статичны и составляют:
-
after -
catch -
else -
rescue
Вы можете увидеть их использование в конструкциях, таких как receive, try, и других.
Резюме
Этот документ предоставляет справочную информацию по синтаксису Elixir, изучая его конструкции и их эквиваленты в AST.
Мы также рассмотрели несколько синтаксических удобств, предоставляемых Elixir. Эти удобства позволяют нам писать
defmodule Math do
def add(a, b) do
a + b
end
end вместо
defmodule(Math, [
{:do, def(add(a, b), [{:do, a + b}])}
]) Сопоставление между кодом и данными (основным AST) позволяет Elixir реализовывать defmodule, def, if, и другие функции в самом Elixir. Elixir делает доступными для разработчиков, желающих расширить язык до новых областей, конструкции, необходимые для построения языка.
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.7.4/syntax-reference.html