Справочник по синтаксису
Синтаксис 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.
Атомы
Неопределенные атомы начинаются с двоеточия (:), за которым сразу же должен следовать подчеркивание или буква Юникода. Атом может продолжать использовать последовательность букв, цифр, подчеркиваний и @. Атомы могут заканчиваться на ! или ?. См. Синтаксис Юникода для формального описания. Действительными неопределенными атомами являются: :ok, :ISO8601, и :integer?.
Если за двоеточием сразу следует пара двойных или одинарных кавычек, окружающих имя атома, то атом считается определенным. В отличие от неопределенного атома, этот может состоять из любого символа Юникода (не только букв), например, :'🌢 Elixir', :"++olá++", и :"123".
Определенные и неопределенные атомы с одинаковым именем считаются эквивалентными, поэтому :atom, :"atom", и :'atom' представляют один и тот же атом. Единственная проблема заключается в том, что компилятор будет предупреждать, когда в атомах используются кавычки, которые не должны использоваться.
Все операторы в Elixir также являются допустимыми атомами. Примеры допустимых операторов - :foo, :FOO, :foo_42, :foo@bar, и :++. Недопустимые примеры - :@foo (@ не допускается в начале), :123 (цифры не допускаются в начале) и :(*) (не является допустимым оператором).
true, false, и nil - зарезервированные слова, которые представляются атомами :true, :false и :nil соответственно.
Строки
Строки в Elixir на одной строке записываются в двойных кавычках, например, "foo". Любая двойная кавычка внутри строки должна быть экранирована с помощью \. Строки поддерживают символы Юникода и хранятся в виде бинарных данных 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'. Любая одинарная кавычка внутри строки должна быть экранирована с помощью \. Списки символов состоят из целых неотрицательных чисел, где каждое целое число представляет код Юникода.
Многострочные списки символов записываются с тремя одинарными кавычками ('''), аналогично многострочным строкам.
Списки символов всегда представляются самими собой в AST.
Для более подробной информации, пожалуйста, прочитайте раздел «Списки символов» в модуле List.
Списки, кортежи и бинарные данные
Структуры данных, такие как списки, кортежи и бинарные данные, отмечаются соответственно разделителями [...], {...}, и <<...>>. Каждый элемент разделяется запятой. Разрешена и конечная запятая, например, в [1, 2, 3,].
Карты и списки ключевых слов
Карты используют обозначение %{...} и каждое значение ключа указывается парами, помеченными =>, например %{"hello" => 1, 2 => "world"}.
Как списки ключевых слов (список кортежей из двух элементов, где первый элемент - атом), так и карты с атомными ключами поддерживают запись в виде ключевых слов, где символ двоеточия : перемещается в конец атома. %{hello: "world"} эквивалентно %{:hello => "world"}, а [foo: :bar] эквивалентно [{:foo, :bar}]. Это обозначение является синтаксическим сахаром, который генерирует то же самое представление AST. Оно будет объяснено в следующих разделах.
Структуры
Структуры создаются на основе синтаксиса карты путем передачи имени структуры между % и {. Например, %User{...}.
Выражения
Переменные
Переменные в Elixir должны начинаться с подчеркивания или буквы Юникода, которая не находится в верхнем регистре или в регистре заглавной буквы. Переменная может продолжать использовать последовательность букв, цифр и подчеркиваний Юникода. Переменные могут заканчиваться на ? или !. См. Синтаксис Юникода для формального описания.
Рекомендации по именованию в Elixir рекомендуют использовать переменные в формате snake_case.
Неквалифицированные вызовы (локальные вызовы)
Неквалифицированные вызовы, такие как add(1, 2), должны начинаться с подчеркивания или буквы Юникода, не находящейся в верхнем регистре или в регистре заглавной буквы. Вызов может продолжить использование последовательности букв, цифр и подчеркиваний. Вызовы могут заканчиваться на ? или !. См. Синтаксис Юникода для формального описания.
Скобки для неквалифицированных вызовов необязательны, за исключением вызовов нулевой арности, которые в противном случае были бы неоднозначны с переменными. Если скобки используются, они должны следовать сразу за именем функции без пробелов. Например, add (1, 2) - это синтаксическая ошибка, так как (1, 2) обрабатывается как недопустимый блок, который пытается быть передан в качестве единственного аргумента функции add.
Рекомендации по именованию в Elixir рекомендуют использовать вызовы в формате snake_case.
Операторы
Как и во многих языках программирования, Elixir также поддерживает операторы как неквалифицированные вызовы со своими правилами приоритета и ассоциативности. Конструкции, такие как =, when, & и @ просто рассматриваются как операторы. См. страницу «Операторы» для полного справочника.
Квалифицированные вызовы (удаленные вызовы)
Квалифицированные вызовы, такие как Math.add(1, 2), должны начинаться с подчеркивания или буквы Юникода, которая не находится в верхнем регистре или в регистре заглавной буквы. Вызов может продолжить использование последовательности букв, цифр и подчеркиваний. Вызовы могут заканчиваться на ? или !. См. Синтаксис Юникода для формального описания.
Рекомендации по именованию в Elixir рекомендуют использовать вызовы в формате snake_case.
Для квалифицированных вызовов Elixir также допускает запись имени функции в двойных или одинарных кавычках, что позволяет выполнять вызовы, такие как Math."++add++"(1, 2). Операторы могут использоваться в качестве квалифицированных вызовов без кавычек, например, Kernel.+(1, 2).
Скобки для квалифицированных вызовов необязательны. Если скобки используются, они должны следовать непосредственно за именем функции без пробелов.
Псевдонимы
Псевдонимы - это конструкции, которые расширяются до атомов во время компиляции. Псевдоним String расширяется до атома :"Elixir.String". Псевдонимы должны начинаться с символа ASCII в верхнем регистре, за которым могут следовать любая буква ASCII, цифра или символ подчеркивания. Символы, не являющиеся 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
Сигилы
Сигилы начинаются с ~ и следуют за буквой, а за ней - одной из следующих пар:
-
(и) -
{и} -
[и] -
<и> -
"и" -
'и' -
|и| -
/и/
После закрытия пары можно указать ноль или более символов 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]}]} Синтаксический сахар
Все перечисленные выше конструкции являются частью синтаксиса 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 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), которые эквивалентны 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.9.4/syntax-reference.html