Справочник по синтаксису
Синтаксис 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.
Атомы
Необрамленные атомы начинаются с двоеточия (:), за которым сразу следует буква Unicode или знак подчеркивания. Атом может продолжаться с помощью последовательности букв Unicode, цифр, подчеркиваний и @. Атомы могут заканчиваться на ! или ?. Действительные необрамленные атомы: :ok, :ISO8601, и :integer?.
Если двоеточие сразу же следует за парой двойных или одинарных кавычек, окружающих имя атома, атом считается обрамленным. В отличие от необрамленного атома, этот атом может состоять из любого символа Unicode (а не только букв), например, :'🌢 Elixir', :"++olá++", и :"123".
Обрамленные и необрамленные атомы с одинаковым именем считаются эквивалентными, поэтому :atom, :"atom", и :'atom' представляют один и тот же атом. Единственное исключение состоит в том, что компилятор выведет предупреждение, когда кавычки используются в атомах, которые не должны быть обрамлены.
Все операторы в Elixir также являются допустимыми атомами. Примеры включают :foo, :FOO, :foo_42, :foo@bar, и :++. Недопустимые примеры включают :@foo (@ не разрешено в начале), :123 (цифры не разрешены в начале) и :(*) (недействительный оператор).
true, false, и nil — зарезервированные слова, которые представлены атомами :true, :false и :nil соответственно.
Чтобы узнать больше о всех символах Unicode, разрешенных в атомах, см. документ Синтаксис Unicode.
Строки
Однострочные строки в 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.
Для более подробной информации см. раздел «Списки символов» в модуле List.
Списки, кортежи и бинарные данные
Структуры данных, такие как списки, кортежи и бинарные данные, обозначаются соответственно разделителями [...], {...}, и <<...>>. Каждый элемент разделяется запятой. Разрешена конечная запятая, например, в [1, 2, 3,].
Карты и списки ключевых слов
Карты используют запись %{...} и каждый ключ-значение задаётся парами, помеченными =>, например, %{"hello" => 1, 2 => "world"}.
Как списки ключевых слов (список кортежей из двух элементов, где первый элемент — атом), так и карты с атомными ключами поддерживают запись с ключевыми словами, где символ двоеточия : перемещается в конец атома. %{hello: "world"} эквивалентно %{:hello => "world"}, а [foo: :bar] эквивалентно [{:foo, :bar}]. Эта запись является синтаксическим сахаром, который генерирует то же самое представление AST. Она будет объяснена в последующих разделах.
Структуры
Структуры построены на основе синтаксиса карты путём передачи имени структуры между % и {. Например, %User{...}.
Выражения
Переменные
Переменные в Elixir должны начинаться с нижнего подчёркивания или буквы Unicode, которая не является заглавной или прописной. Переменная может продолжаться с помощью последовательности букв Unicode, цифр и подчеркиваний. Переменные могут заканчиваться на ? или !. Чтобы узнать больше о всех разрешенных символах Unicode в переменных, см. документ Синтаксис Unicode.
Рекомендации по именованию в Elixir рекомендуют использовать переменные в формате snake_case.
Неквалифицированные вызовы (локальные вызовы)
Неквалифицированные вызовы, такие как add(1, 2), должны начинаться с символов и затем следовать тем же правилам, что и переменные, которые необязательно следуют за скобками и аргументами.
Скобки необходимы для вызовов нулевой арности (т.е. вызовов без аргументов), чтобы избежать неоднозначности с переменными. Если скобки используются, они должны сразу следовать за именем функции без пробелов. Например, add (1, 2) — синтаксическая ошибка, так как (1, 2) обрабатывается как недопустимый блок, который пытаются передать как единственный аргумент функции add.
Рекомендации по именованию в Elixir рекомендуют использовать вызовы в формате snake_case.
Операторы
Как и многие языки программирования, Elixir также поддерживает операторы как неквалифицированные вызовы со своими правилами приоритета и ассоциативности. Конструкции, такие как =, when, & и @ просто обрабатываются как операторы. Полная справка представлена на странице Операторы.
Квалифицированные вызовы (удаленные вызовы)
Квалифицированные вызовы, такие как Math.add(1, 2), должны начинаться с символов и затем следовать тем же правилам, что и переменные, которые необязательно следуют за скобками и аргументами. Квалифицированные вызовы также поддерживают операторы, такие как Kernel.+(1, 2). Elixir также позволяет записывать имя функции между двойными или одинарными кавычками, позволяя использовать любые символы между кавычками, например, Math."++add++"(1, 2).
Аналогично неквалифицированным вызовам, скобки имеют различное значение для вызовов нулевой арности (т. е. вызовов без аргументов). Если используются скобки, например, mod.fun(), это означает вызов функции. Если скобки опущены, например, map.field, это означает доступ к полю карты.
Рекомендации по именованию в Elixir рекомендуют использовать вызовы в формате snake_case.
Псевдонимы
Псевдонимы — это конструкции, которые расширяются до атомов во время компиляции. Псевдоним String расширяется до атома :"Elixir.String". Псевдонимы должны начинаться с заглавной буквы ASCII, за которой могут следовать любые буквы, цифры или символы подчеркивания ASCII. Символы, не являющиеся ASCII, не поддерживаются в псевдонимах.
Несколько псевдонимов могут быть объединены с помощью ., например, MyApp.String, и это расширяется до атома :"Elixir.MyApp.String". Точка фактически является частью имени, но также может использоваться для композиции. Если вы определите alias MyApp.Example, as: Example в своём коде, то Example всегда будет расширяться до :"Elixir.MyApp.Example", а Example.String будет расширяться до :"Elixir.MyApp.Example.String".
Рекомендации по именованию в Elixir рекомендуют использовать псевдонимы в формате CamelCase.
Атрибуты модуля
Атрибуты модуля — это хранилище, специфичное для модуля, и записываются как композиция унарного оператора @ с переменными и локальными вызовами. Например, для записи в атрибут модуля с именем foo используйте @foo "value", а для чтения из него используйте @foo. Поскольку атрибуты модулей записываются с помощью существующих конструкций, они следуют тем же правилам, что и операторы, переменные и локальные вызовы, описанные выше.
Блоки
Блоки представляют собой несколько выражений 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
Сигилы
Сигилы начинаются с ~ и следуют за одной строчной буквой или одной или несколькими заглавными буквами, немедленно за которыми следует одна из следующих пар:
-
(и) -
{и} -
[и] -
<и> -
"и" -
'и' -
|и| -
/и/
После закрытия пары можно указать ноль или более символов ASCII и цифр в качестве модификатора. Сигилы выражаются как вызовы без квалификаторов, предваряемые 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]}]}
Квалифицированные кортежи
Квалифицированные кортежи (foo.{bar, baz}) представлены вызовом {:., [], [expr, :{}]}, где expr представляет левую часть точки, а аргументы — элементы внутри фигурных скобок. Это используется в Elixir для предоставления множественных псевдонимов:
quote do
Foo.{Bar, Baz}
end
#=> {{:., [], [{:__aliases__, [], [:Foo]}, :{}]}, [], [{:__aliases__, [], [:Bar]}, {:__aliases__, [], [:Baz]}]}
Необязательный синтаксис
Все вышеперечисленные конструкции являются частью синтаксиса Elixir и имеют собственное представление в составе AST Elixir. В этом разделе рассматриваются оставшиеся конструкции, которые являются альтернативными представлениями вышеперечисленных конструкций. Другими словами, нижеприведённые конструкции могут быть представлены в Elixir более чем одним способом и сохранять эквивалентность AST. Мы называем это «Необязательный синтаксис».
Для краткого знакомства с необязательным синтаксисом Elixir, см. этот документ. Ниже приведён более полный справочник.
Целые числа в других системах счисления и коды Unicode
Elixir позволяет целым числам содержать _ для разделения цифр и предоставляет удобные средства для представления целых чисел в других системах счисления:
1_000_000 #=> 1000000 0xABCD #=> 43981 (Hexadecimal base) 0o01234567 #=> 342391 (Octal base) 0b10101010 #=> 170 (Binary base) ?é #=> 233 (Unicode code point)
Эти конструкции существуют только на уровне синтаксиса. Все вышеприведенные примеры представлены в виде соответствующих целых чисел в 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. Скобки необходимы при вызове анонимных функций, таких как f.(1, 2, 3).
На практике разработчики предпочитают добавлять скобки к большинству своих вызовов. Они опускаются в основном в конструкциях потока управления Elixir, таких как defmodule, if, case, и т. д., и в определённых DSL.
Ключевые слова
Ключевые слова в 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 статичны и включают:
aftercatchelserescue
Вы можете увидеть их в использовании в конструкциях, таких как receive, try, и других.
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.15.4/syntax-reference.html