Справочник по синтаксису
Синтаксис 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 соответственно.
Строки
Однострочные строки в 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 для формального описания.
Рекомендации по именованию в Elixir рекомендуют использовать переменные в формате snake_case.
Неквалифицированные вызовы (локальные вызовы)
Неквалифицированные вызовы, такие как add(1, 2), должны начинаться с подчеркивания или буквы Unicode, которая не находится в верхнем или прописном регистре. Вызов может продолжаться, используя последовательность букв Unicode, цифр и подчеркиваний. Вызовы могут заканчиваться ? или !. См. Синтаксис Unicode для формального описания.
Скобки для неквалифицированных вызовов необязательны, за исключением вызовов нулевой арности, которые в противном случае были бы неоднозначны с переменными. Если скобки используются, они должны следовать за именем функции без пробелов. Например, add (1, 2) — синтаксическая ошибка, так как (1, 2) обрабатывается как недопустимый блок, который пытаются передать в качестве единственного аргумента функции add.
Рекомендации по именованию в Elixir рекомендуют использовать вызовы в формате snake_case.
Операторы
Как и многие языки программирования, Elixir также поддерживает операторы как неквалифицированные вызовы с их правилами приоритета и ассоциативности. Конструкции, такие как =, when, & и @ просто обрабатываются как операторы. См. страницу «Операторы» для получения полной справки.
Квалифицированные вызовы (удаленные вызовы)
Квалифицированные вызовы, такие как Math.add(1, 2), должны начинаться с подчеркивания или буквы Unicode, которая не находится в верхнем или прописном регистре. Вызов может продолжаться, используя последовательность букв Unicode, цифр и подчеркиваний. Вызовы могут заканчиваться ? или !. См. Синтаксис Unicode для формального описания.
Рекомендации по именованию в 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
Сигилы
Сигилы начинаются с ~ и следуют за буквой и одной из следующих пар:
-
(и) -
{и} -
[и] -
<и> -
"и" -
'и' -
|и| -
/и/
После закрытия пары можно указать ноль или более символов 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.
Целые числа в других системах счисления и кодовые точки 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, и других.
Резюме
Этот документ предоставляет справочник по синтаксису 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.13.4/syntax-reference.html