Исходный код Справочник синтаксиса
Синтаксис 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. Символы Unicode не поддерживаются в псевдонимах.
Несколько псевдонимов могут быть объединены с помощью ., например 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). Дерево абстрактного синтаксиса 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]
Помните, что, поскольку списки и кортежи из двух элементов являются литералами, по определению ключевые слова также являются литералами (на самом деле, единственная причина, по которой кортежи из двух элементов являются литералами, — это поддержка ключевых слов как литералов).
Чтобы быть допустимым синтаксисом ключевого слова, : не может предшествовать пробелам (foo : 1 недопустимо) и должен следовать за пробелами (foo:1 недопустимо).
Ключевые слова как последние аргументы
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-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.17.2/syntax-reference.html