Справочник по синтаксису
Синтаксис 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.
Списки, кортежи и двоичные данные
Структуры данных, такие как списки, кортежи и двоичные данные, помечаются соответственно разделителями [...], {...}, и <<...>>. Каждый элемент разделяется запятой. Допускается также наличие запятой в конце, как в [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.
Целые числа в других системах счисления и коды Юникода
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.8.2/syntax-reference.html