3 – Язык Lua: Лексика и синтаксис
Categories:
Оглавление
3 – Язык
В этом разделе описываются лексика, синтаксис и семантика Lua. Другими словами, здесь описывается, какие лексемы допустимы, как их можно комбинировать и что означают их комбинации.
Языковые конструкции будут объясняться с использованием обычной расширенной формы Бэкуса-Наура (РБНФ), в которой {a} означает 0 или более a, а [a] означает необязательное a. Нетерминалы показаны как нетерминал, ключевые слова показаны как kword, а другие терминальные символы показаны как ‘=’. Полный синтаксис Lua можно найти в §9 в конце этого руководства.
3.1 – Лексические соглашения
Lua — это язык со свободной формой записи. Он игнорирует пробелы и комментарии между лексическими элементами (токенами), за исключением случаев, когда они выступают в роли разделителей между двумя токенами. В исходном коде Lua распознает как пробелы стандартные пробельные символы ASCII: пробел, перевод страницы, перевод строки, возврат каретки, горизонтальную табуляцию и вертикальную табуляцию.
Имена (также называемые идентификаторами) в Lua могут быть любой строкой, состоящей из латинских букв, арабско-индийских цифр и символов подчеркивания, не начинающейся с цифры и не являющейся зарезервированным словом. Идентификаторы используются для именования переменных, полей таблиц и меток.
Следующие ключевые слова зарезервированы и не могут использоваться в качестве имен:
and break do else elseif end
false for function global goto if
in local nil not or repeat
return then true until while
Lua — регистрозависимый язык: and — зарезервированное слово, но And и AND — два разных допустимых имени. В соответствии с соглашением, в программах следует избегать создания имен, начинающихся с подчеркивания, за которым следует одна или несколько заглавных букв (например, _VERSION).
Следующие строки обозначают другие токены:
+ - * / % ^ #
& ~ | << >> //
== ~= <= >= < > =
( ) { } [ ] ::
; : , . .. ...
Короткая литеральная строка может быть ограничена парными одинарными или двойными кавычками и может содержать следующие C-подобные escape-последовательности: \a (звонок), \b (забой), \f (перевод страницы), \n (перевод строки), \r (возврат каретки), \t (горизонтальная табуляция), \v (вертикальная табуляция), \\ (обратная косая черта), \" (кавычка [двойная]) и \' (апостроф [одинарная кавычка]). Обратная косая черта, за которой следует разрыв строки, приводит к появлению символа новой строки в строке. Escape-последовательность \z пропускает следующий за ней диапазон пробельных символов, включая разрывы строк; это особенно полезно для разбиения и отступа длинной литеральной строки на несколько строк без добавления символов новой строки и пробелов в содержимое строки. Короткая литеральная строка не может содержать неэкранированные разрывы строк или escape-последовательности, не образующие допустимую управляющую последовательность.
Мы можем указать любой байт в короткой литеральной строке, включая встроенные нули, по его числовому значению. Это можно сделать с помощью escape-последовательности \xXX, где XX — это последовательность ровно из двух шестнадцатеричных цифр, или с помощью escape-последовательности \ddd, где ddd — последовательность до трех десятичных цифр. (Обратите внимание, что если за десятичной escape-последовательностью должна следовать цифра, она должна быть выражена ровно тремя цифрами).
Кодировка UTF-8 символа Unicode может быть вставлена в литеральную строку с помощью escape-последовательности \u{XXX} (с обязательными фигурными скобками), где XXX — это последовательность из одной или более шестнадцатеричных цифр, представляющих кодовую точку символа. Эта кодовая точка может быть любым значением меньше 231. (Здесь Lua использует оригинальную спецификацию UTF-8, которая не ограничена допустимыми кодовыми точками Unicode).
Литеральные строки также могут быть определены с использованием длинного формата, заключенного в длинные скобки. Мы определяем открывающую длинную скобку уровня n как открывающую квадратную скобку, за которой следует n знаков равенства, а затем еще одна открывающая квадратная скобка. Так, открывающая длинная скобка уровня 0 записывается как [[, уровня 1 — как [=[, и так далее. Закрывающая длинная скобка определяется аналогично; например, закрывающая длинная скобка уровня 4 записывается как ]====]. Длинный литерал начинается с открывающей длинной скобки любого уровня и заканчивается на первой закрывающей длинной скобке того же уровня. Он может содержать любой текст, кроме закрывающей скобки того же уровня. Литералы в этой скобочной форме могут занимать несколько строк, не интерпретируют никакие escape-последовательности и игнорируют длинные скобки любых других уровней. Любой вид последовательности конца строки (возврат каретки, перевод строки, возврат каретки с последующим переводом строки или перевод строки с последующим возвратом каретки) преобразуется в простой перевод строки. Если за открывающей длинной скобкой сразу следует перевод строки, этот перевод строки не включается в строку.
В качестве примера, в системе, использующей ASCII (где ‘a’ кодируется как 97, перевод строки как 10, а ‘1’ как 49), следующие пять литеральных строк обозначают одну и ту же строку:
a = 'alo\n123"'
a = "alo\n123\""
a = '\97lo\10\04923"'
a = [[alo
123"]]
a = [==[
alo
123"]==]
Любой байт в литеральной строке, на который явно не повлияли предыдущие правила, представляет сам себя. Однако Lua открывает файлы для разбора в текстовом режиме, и файловые функции системы могут иметь проблемы с некоторыми управляющими символами. Поэтому безопаснее представлять двоичные данные в виде закавыченного литерала с явными escape-последовательностями для нетекстовых символов.
Числовая константа (или число) может быть записана с необязательной дробной частью и необязательным десятичным порядком, обозначаемым буквой ’e’ или ‘E’. Lua также принимает шестнадцатеричные константы, начинающиеся с 0x или 0X. Шестнадцатеричные константы также могут иметь необязательную дробную часть плюс необязательный двоичный порядок, обозначаемый буквой ‘p’ или ‘P’ и записываемый в десятичном виде. (Например, 0x1.fp10 обозначает 1984, то есть 0x1f / 16, умноженное на 210).
Числовая константа с десятичной точкой или порядком обозначает число с плавающей запятой (float); в противном случае, если ее значение помещается в целое число или она является шестнадцатеричной константой, она обозначает целое число (integer); в противном случае (то есть десятичное целое число, вызывающее переполнение) она обозначает float. Шестнадцатеричные числа без точки и порядка всегда обозначают целое значение; если значение переполняется, оно “сворачивается” (wrap around), чтобы соответствовать допустимому целому числу.
Примеры допустимых целочисленных констант:
3 345 0xff 0xBEBADA
Примеры допустимых констант с плавающей запятой:
3.0 3.1416 314.16e-2 0.31416E1 34e1
0x0.1E 0xA23p-4 0X1.921FB54442D18P+1
Комментарий начинается с двойного дефиса (--) в любом месте вне строки. Если текст сразу после -- не является открывающей длинной скобкой, комментарий является коротким комментарием, который длится до конца строки. В противном случае это длинный комментарий, который длится до соответствующей закрывающей длинной скобки.
3.2 – Переменные
Переменные — это места, хранящие значения. В Lua существует три вида переменных: глобальные переменные, локальные переменные и поля таблиц.
Одиночное имя может обозначать глобальную переменную или локальную переменную (или формальный параметр функции, который является особым видом локальной переменной) (см. §2.2):
var ::= Name
Name обозначает идентификаторы (см. §3.1).
Поскольку переменные имеют лексическую область видимости, локальные переменные могут свободно использоваться функциями, определенными внутри их области видимости (см. §2.2).
До первого присваивания переменной ее значение равно nil.
Квадратные скобки используются для индексации таблицы:
var ::= prefixexp ‘[’ exp ‘]’
Значение доступов к полям таблицы может быть изменено через метатаблицы (см. §2.4).
Синтаксис var.Name — это просто синтаксический сахар для var["Name"]:
var ::= prefixexp ‘.’ Name
Доступ к глобальной переменной x эквивалентен _ENV.x.
3.3 – Инструкции (Statements)
Lua поддерживает почти общепринятый набор инструкций, похожий на те, что есть в других обычных языках. Этот набор включает блоки, присваивания, управляющие структуры, вызовы функций и объявления переменных.
3.3.1 – Блоки
Блок — это список инструкций, которые выполняются последовательно:
block ::= {stat}
В Lua есть пустые инструкции, которые позволяют разделять инструкции точкой с запятой, начинать блок с точки с запятой или писать две точки с запятой подряд:
stat ::= ‘;’
Как вызовы функций, так и присваивания могут начинаться с открывающей скобки. Эта возможность приводит к неоднозначности в грамматике Lua. Рассмотрим следующий фрагмент:
a = b + c
(print or io.write)('done')
Грамматика может интерпретировать этот фрагмент двумя способами:
a = b + c(print or io.write)('done')
a = b + c; (print or io.write)('done')
Текущий парсер всегда интерпретирует такие конструкции первым способом, рассматривая открывающую скобку как начало аргументов вызова. Чтобы избежать этой неоднозначности, рекомендуется всегда предварять точкой с запятой инструкции, начинающиеся со скобки:
;(print or io.write)('done')
Блок может быть явно выделен для создания одиночной инструкции:
stat ::= do block end
Явные блоки полезны для управления областью видимости объявлений переменных. Явные блоки также иногда используются для добавления инструкции return в середине другого блока (см. §3.3.4).
3.3.2 – Чанки
Единица компиляции Lua называется чанком (chunk). Синтаксически чанк — это просто блок:
chunk ::= block
Lua обрабатывает чанк как тело анонимной функции с переменным числом аргументов (см. §3.4.11). Таким образом, чанки могут определять локальные переменные, получать аргументы и возвращать значения. Более того, такая анонимная функция компилируется как находящаяся в области видимости внешней локальной переменной с именем _ENV (см. §2.2). Результирующая функция всегда имеет _ENV в качестве своей единственной внешней переменной, даже если она не использует эту переменную.
Чанк может храниться в файле или в строке внутри программы-хоста. Чтобы выполнить чанк, Lua сначала загружает его, предварительно компилируя код чанка в инструкции для виртуальной машины, а затем Lua выполняет скомпилированный код с помощью интерпретатора виртуальной машины.
Чанки также могут быть предварительно скомпилированы в двоичную форму; подробности см. в программе luac и функции string.dump. Программы в исходной и скомпилированной формах взаимозаменяемы; Lua автоматически определяет тип файла и действует соответственно (см. load). Имейте в виду, что, в отличие от исходного кода, злонамеренно созданные двоичные чанки могут вызвать крах интерпретатора.
3.3.3 – Присваивание
Lua допускает множественное присваивание. Поэтому синтаксис присваивания определяет список переменных слева и список выражений справа. Элементы в обоих списках разделяются запятыми:
stat ::= varlist ‘=’ explist
varlist ::= var {‘,’ var}
explist ::= exp {‘,’ exp}
Выражения обсуждаются в §3.4.
Перед присваиванием список значений выравнивается по длине списка переменных (см. §3.4.12).
Если переменная одновременно присваивается и читается внутри множественного присваивания, Lua гарантирует, что все чтения получат значение переменной до присваивания. Таким образом, код
i = 3
i, a[i] = i+1, 20
устанавливает a[3] в 20, не затрагивая a[4], потому что i в a[i] вычисляется (в 3) до того, как ей присваивается 4. Аналогично, строка
x, y = y, x
обменивает значения x и y, а
x, y, z = y, z, x
циклически переставляет значения x, y и z.
Обратите внимание, что эта гарантия распространяется только на доступы, синтаксически находящиеся внутри инструкции присваивания. Если функция или метаметод, вызванные во время присваивания, изменяют значение переменной, Lua не дает никаких гарантий относительно порядка этого доступа.
Присваивание глобальному имени x = val эквивалентно присваиванию _ENV.x = val (см. §2.2).
Значение присваиваний полям таблиц и глобальным переменным (которые на самом деле тоже являются полями таблиц) может быть изменено через метатаблицы (см. §2.4).
3.3.4 – Управляющие структуры
Управляющие структуры if, while и repeat имеют обычный смысл и знакомый синтаксис:
stat ::= while exp do block end
stat ::= repeat block until exp
stat ::= if exp then block {elseif exp then block} [else block] end
В Lua также есть инструкция for в двух вариантах (см. §3.3.5).
Выражение-условие управляющей структуры может возвращать любое значение. И false, и nil проверяются как ложь. Все значения, отличные от nil и false, проверяются как истина. В частности, число 0 и пустая строка также проверяются как истина.
В цикле repeat–until внутренний блок не заканчивается на ключевом слове until, а только после условия. Таким образом, условие может ссылаться на локальные переменные, объявленные внутри блока цикла.
Инструкция goto передает управление программой на метку. По синтаксическим причинам метки в Lua также считаются инструкциями:
stat ::= goto Name
stat ::= label
label ::= ‘::’ Name ‘::’
Метка видна во всем блоке, где она определена, за исключением вложенных функций. goto может переходить на любую видимую метку, если при этом не происходит входа в область видимости объявления переменной. Метка не должна быть объявлена там, где видна предыдущая метка с тем же именем, даже если эта другая метка была объявлена в охватывающем блоке.
Инструкция break завершает выполнение цикла while, repeat или for, переходя к следующей инструкции после цикла:
stat ::= break
break завершает самый внутренний объемлющий цикл.
Инструкция return используется для возврата значений из функции или чанка (который обрабатывается как анонимная функция). Функции могут возвращать более одного значения, поэтому синтаксис инструкции return таков:
stat ::= return [explist] [‘;’]
Инструкция return может быть записана только как последняя инструкция блока. Если необходимо выполнить возврат в середине блока, можно использовать явный внутренний блок, как в идиоме do return end, потому что теперь return является последней инструкцией в своем (внутреннем) блоке.
3.3.5 – Инструкция For
Инструкция for имеет две формы: числовую и обобщенную.
Числовой цикл for
Числовой цикл for повторяет блок кода, пока управляющая переменная проходит через арифметическую прогрессию. Он имеет следующий синтаксис:
stat ::= for Name ‘=’ exp ‘,’ exp [‘,’ exp] do block end
Заданный идентификатор (Name) определяет управляющую переменную, которая является новой переменной только для чтения (const), локальной для тела цикла (block).
Цикл начинается с однократного вычисления трех управляющих выражений. Их значения называются соответственно начальным значением, пределом и шагом. Если шаг отсутствует, по умолчанию он равен 1.
Если и начальное значение, и шаг являются целыми числами, цикл выполняется с целыми числами; обратите внимание, что предел может не быть целым числом. В противном случае три значения преобразуются в числа с плавающей запятой, и цикл выполняется с плавающей запятой. В этом случае следует опасаться точности чисел с плавающей запятой.
После этой инициализации тело цикла повторяется, при этом значение управляющей переменной проходит через арифметическую прогрессию, начиная с начального значения, с разностью, заданной шагом. Отрицательный шаг создает убывающую последовательность; шаг, равный нулю, вызывает ошибку. Цикл продолжается, пока значение меньше или равно пределу (больше или равно для отрицательного шага). Если начальное значение уже больше предела (или меньше, если шаг отрицательный), тело не выполняется.
Для целочисленных циклов управляющая переменная никогда не “сворачивается” (wrap around); вместо этого цикл заканчивается в случае переполнения.
Обобщенный цикл for
Обобщенная инструкция for работает с функциями, называемыми итераторами. На каждой итерации функция-итератор вызывается для получения нового значения, останавливаясь, когда это новое значение равно nil. Обобщенный цикл for имеет следующий синтаксис:
stat ::= for namelist in explist do block end
namelist ::= Name {‘,’ Name}
Инструкция for, такая как
for var_1, ···, var_n in explist do body end
работает следующим образом.
Имена var_i объявляют переменные цикла, локальные для тела цикла. Первая из этих переменных является управляющей переменной, которая является переменной только для чтения (const).
Цикл начинается с вычисления explist для получения четырех значений: функции-итератора, состояния, начального значения для управляющей переменной и закрывающего значения.
Затем, на каждой итерации, Lua вызывает функцию-итератор с двумя аргументами: состоянием и управляющей переменной. Результаты этого вызова затем присваиваются переменным цикла в соответствии с правилами множественного присваивания (см. §3.3.3). Если управляющая переменная становится nil, цикл завершается. В противном случае выполняется тело, и цикл переходит к следующей итерации.
Закрывающее значение ведет себя как закрываемая переменная (to-be-closed variable) (см. §3.3.8), которая может использоваться для освобождения ресурсов при завершении цикла. В остальном оно не влияет на цикл.
3.3.6 – Вызовы функций как инструкции
Для обеспечения возможных побочных эффектов вызовы функций могут выполняться как инструкции:
stat ::= functioncall
В этом случае все возвращаемые значения отбрасываются. Вызовы функций объясняются в §3.4.10.
3.3.7 – Объявления переменных
Локальные и глобальные переменные могут быть объявлены в любом месте внутри блока. Объявление может включать инициализацию:
stat ::= local attnamelist [‘=’ explist]
stat ::= global attnamelist [‘=’ explist]
Если инициализация отсутствует, локальные переменные инициализируются значением nil; глобальные переменные остаются без изменений. В противном случае инициализация подвергается тому же выравниванию, что и множественное присваивание (см. §3.3.3). Более того, для глобальных переменных инициализация вызовет ошибку времени выполнения, если переменная уже определена, то есть имеет значение, отличное от nil.
Список имен может иметь префиксный атрибут (имя в угловых скобках), и каждое имя переменной может иметь постфиксный атрибут:
attnamelist ::= [attrib] Name [attrib] {‘,’ Name [attrib]}
attrib ::= ‘<’ Name ‘>’
Префиксный атрибут применяется ко всем именам в списке; постфиксный атрибут применяется к конкретному имени. Существует два возможных атрибута: const, который объявляет константу или переменную только для чтения, то есть переменную, которая не может использоваться в левой части присваивания, и close, который объявляет закрываемую переменную (to-be-closed variable) (см. §3.3.8). Только локальные переменные могут иметь атрибут close. Список переменных может содержать не более одной закрываемой переменной.
Lua предлагает также коллективное объявление для глобальных переменных:
stat ::= global [attrib] ‘*’
Эта специальная форма неявно объявляет как глобальные все имена, которые ранее не были объявлены явно. В частности, global<const> * неявно объявляет как глобальные только для чтения все имена, не объявленные ранее явно; см. следующий пример:
global X
global<const> *
print(math.pi) -- Ok, 'print' и 'math' только для чтения
X = 1 -- Ok, объявлена как для чтения-записи
Y = 1 -- Ошибка, Y только для чтения
Как отмечено в §2.2, все чанки начинаются с неявного объявления global *, но это вводное объявление становится недействительным внутри области видимости любого другого объявления global. Следовательно, программа, которая не использует объявления global или начинается с global *, имеет свободный доступ на чтение и запись к любой глобальной переменной; программа, которая начинается с global<const> *, имеет свободный доступ только для чтения к любой глобальной переменной; а программа, которая начинается с любого другого объявления global (например, global none), может ссылаться только на объявленные переменные.
Обратите внимание, что для глобальных переменных эффект любого объявления является чисто синтаксическим (за исключением необязательного присваивания):
global X <const>, _G
X = 1 -- ОШИБКА
_ENV.X = 1 -- Ok
_G.print(X) -- Ok
foo() -- 'foo' может свободно изменять любую глобальную переменную
Чанк также является блоком (см. §3.3.2), поэтому переменные могут быть объявлены в чанке вне любого явного блока.
Правила видимости для объявлений переменных объясняются в §2.2.
3.3.8 – Закрываемые переменные (To-be-closed Variables)
Закрываемая переменная ведет себя как константная локальная переменная, за исключением того, что ее значение закрывается всякий раз, когда переменная выходит из области видимости, включая нормальное завершение блока, выход из блока по break/goto/return или выход из-за ошибки.
Здесь закрыть значение означает вызвать его метаметод __close. При вызове метаметода само значение передается в качестве первого аргумента. Если произошла ошибка, объект ошибки, вызвавший выход, передается в качестве второго аргумента; в противном случае второй аргумент отсутствует.
Значение, присваиваемое закрываемой переменной, должно иметь метаметод __close или быть ложным значением. (nil и false игнорируются как закрываемые значения).
Если несколько закрываемых переменных выходят из области видимости при одном и том же событии, они закрываются в порядке, обратном порядку их объявления.
Если во время выполнения метода закрытия возникает ошибка, эта ошибка обрабатывается как ошибка в обычном коде, где была определена переменная. После ошибки остальные ожидающие методы закрытия все равно будут вызваны.
Если сопрограмма (coroutine) выполняет yield и никогда не возобновляется, некоторые переменные могут никогда не выйти из области видимости и, следовательно, никогда не будут закрыты. (Эти переменные — те, которые созданы внутри сопрограммы и находятся в области видимости в точке, где сопрограмма выполнила yield). Аналогично, если сопрограмма завершается с ошибкой, она не разворачивает свой стек, поэтому не закрывает никакие переменные. В обоих случаях вы можете либо использовать финализаторы, либо вызвать coroutine.close для закрытия переменных. Однако если сопрограмма была создана через coroutine.wrap, то соответствующая функция закроет сопрограмму в случае ошибок.
3.4 – Выражения
Базовыми выражениями в Lua являются следующие:
exp ::= prefixexp
exp ::= nil | false | true
exp ::= Numeral
exp ::= LiteralString
exp ::= functiondef
exp ::= tableconstructor
exp ::= ‘...’
exp ::= exp binop exp
exp ::= unop exp
prefixexp ::= var | functioncall | ‘(’ exp ‘)’
Числа и литеральные строки объясняются в §3.1; переменные — в §3.2; определения функций — в §3.4.11; вызовы функций — в §3.4.10; конструкторы таблиц — в §3.4.9. Выражения vararg, обозначаемые тремя точками ('...'), могут использоваться только непосредственно внутри вариадической функции; они объясняются в §3.4.11.
Бинарные операторы включают арифметические операторы (см. §3.4.1), побитовые операторы (см. §3.4.2), операторы отношения (см. §3.4.4), логические операторы (см. §3.4.5) и оператор конкатенации (см. §3.4.6). Унарные операторы включают унарный минус (см. §3.4.1), унарное побитовое НЕ (см. §3.4.2), унарное логическое not (см. §3.4.5) и унарный оператор длины (см. §3.4.7).
3.4.1 – Арифметические операторы
Lua поддерживает следующие арифметические операторы:
+: сложение-: вычитание*: умножение/: деление с плавающей запятой (float division)//: деление нацело вниз (floor division)%: остаток от деления (modulo)^: возведение в степень-: унарный минус
За исключением возведения в степень и деления с плавающей запятой, арифметические операторы работают следующим образом: Если оба операнда являются целыми числами, операция выполняется над целыми числами, и результат является целым числом. В противном случае, если оба операнда — числа, они преобразуются в числа с плавающей запятой, операция выполняется в соответствии с машинными правилами арифметики с плавающей запятой (обычно стандарт IEEE 754), и результатом является число с плавающей запятой. (Библиотека string приводит строки к числам в арифметических операциях; подробности см. в §3.4.3).
Возведение в степень и деление с плавающей запятой (/) всегда преобразуют свои операнды в числа с плавающей запятой, и результатом всегда является число с плавающей запятой. Возведение в степень использует функцию pow стандарта ISO C, поэтому оно работает и для нецелочисленных показателей.
Деление нацело вниз (//) — это деление, которое округляет частное в сторону минус бесконечности, в результате чего получается пол (floor) от деления операндов.
Остаток от деления (%) определяется как остаток от деления, которое округляет частное в сторону минус бесконечности (деление нацело вниз).
В случае переполнения в целочисленной арифметике все операции “сворачиваются” (wrap around).
3.4.2 – Побитовые операторы
Lua поддерживает следующие побитовые операторы:
&: побитовое И (AND)|: побитовое ИЛИ (OR)~: побитовое исключающее ИЛИ (XOR)>>: сдвиг вправо<<: сдвиг влево~: унарное побитовое НЕ (NOT)
Все побитовые операции преобразуют свои операнды в целые числа (см. §3.4.3), работают со всеми битами этих целых чисел и дают в результате целое число.
Как правый, так и левый сдвиги заполняют освобождающиеся биты нулями. Отрицательные сдвиги смещают в противоположном направлении; сдвиги с абсолютными значениями, равными или превышающими количество бит в целом числе, дают в результате ноль (поскольку все биты сдвигаются за пределы).
3.4.3 – Приведения и преобразования
Lua предоставляет некоторые автоматические преобразования между типами и представлениями во время выполнения. Побитовые операторы всегда преобразуют операнды с плавающей запятой в целые числа. Возведение в степень и деление с плавающей запятой всегда преобразуют целочисленные операнды в числа с плавающей запятой. Все другие арифметические операции, применяемые к смешанным числам (целым и с плавающей запятой), преобразуют целочисленный операнд в число с плавающей запятой. C API также преобразует целые числа в числа с плавающей запятой и наоборот по мере необходимости. Более того, конкатенация строк принимает числа в качестве аргументов, помимо строк.
При преобразовании из целого числа в число с плавающей запятой, если целое значение имеет точное представление в виде числа с плавающей запятой, то оно и является результатом. В противном случае преобразование дает ближайшее большее или ближайшее меньшее представимое значение. Такой вид преобразования никогда не завершается неудачей.
Преобразование из числа с плавающей запятой в целое проверяет, имеет ли число с плавающей запятой точное представление в виде целого числа (то есть число с плавающей запятой имеет целое значение и находится в диапазоне целочисленного представления). Если да, то это представление является результатом. В противном случае преобразование завершается неудачей.
В нескольких местах Lua приводит строки к числам, когда это необходимо. В частности, библиотека string устанавливает метаметоды, которые пытаются привести строки к числам во всех арифметических операциях. Если преобразование не удается, библиотека вызывает метаметод другого операнда (если он присутствует) или вызывает ошибку. Обратите внимание, что побитовые операторы не выполняют этого приведения.
Всегда рекомендуется не полагаться на неявные приведения строк к числам, поскольку они применяются не всегда; в частности, "1"==1 ложно, а "1"<1 вызывает ошибку (см. §3.4.4). Эти приведения существуют в основном для совместимости и могут быть удалены в будущих версиях языка.
Строка преобразуется в целое число или число с плавающей запятой в соответствии со своим синтаксисом и правилами лексера Lua. Строка также может иметь начальные и конечные пробелы и знак. Все преобразования строк в числа принимают как точку, так и текущий локальный разделитель целой и дробной части в качестве символа десятичной точки. (Однако лексер Lua принимает только точку). Если строка не является допустимым числом, преобразование завершается неудачей. При необходимости результат этого первого шага затем преобразуется в конкретный числовой подтип в соответствии с предыдущими правилами для преобразований между числами с плавающей запятой и целыми числами.
Преобразование чисел в строки использует неуказанный человекочитаемый формат. Для преобразования чисел в строки каким-либо определенным способом используйте функцию string.format.
3.4.4 – Операторы отношения
Lua поддерживает следующие операторы отношения:
==: равенство~=: неравенство<: меньше>: больше<=: меньше или равно>=: больше или равно
Эти операторы всегда дают результат false или true.
Равенство (==) сначала сравнивает типы своих операндов. Если типы различны, то результатом является false. В противном случае сравниваются значения операндов. Строки равны, если они имеют одинаковое байтовое содержимое. Числа равны, если они обозначают одно и то же математическое значение.
Таблицы, пользовательские данные (userdata) и потоки (threads) сравниваются по ссылке: два объекта считаются равными, только если это один и тот же объект. Каждый раз, когда вы создаете новый объект (таблицу, userdata или поток), этот новый объект отличается от любого ранее существовавшего объекта. Функция всегда равна самой себе. Функции с любым обнаруживаемым различием (разное поведение, разное определение) всегда различны. Функции, созданные в разное время, но без обнаруживаемых различий, могут быть классифицированы как равные или нет (в зависимости от деталей внутреннего кэширования).
Вы можете изменить способ сравнения таблиц и userdata в Lua, используя метаметод __eq (см. §2.4).
Сравнения на равенство не преобразуют строки в числа или наоборот. Таким образом, "0"==0 дает false, а t[0] и t["0"] обозначают разные записи в таблице.
Оператор ~= является точным отрицанием равенства (==).
Операторы порядка работают следующим образом. Если оба аргумента — числа, то они сравниваются в соответствии с их математическими значениями, независимо от их подтипов. В противном случае, если оба аргумента — строки, то их значения сравниваются в соответствии с текущей локалью. В противном случае Lua пытается вызвать метаметод __lt или __le (см. §2.4). Сравнение a > b транслируется в b < a, а a >= b транслируется в b <= a.
В соответствии со стандартом IEEE 754, специальное значение NaN не считается ни меньше, ни равным, ни больше какого-либо значения, включая само себя.
3.4.5 – Логические операторы
Логическими операторами в Lua являются and, or и not. Как и управляющие структуры (см. §3.3.4), все логические операторы считают false и nil ложью, а все остальное — истиной.
Оператор отрицания not всегда возвращает false или true. Оператор конъюнкции and возвращает свой первый аргумент, если это значение ложно (false или nil); в противном случае and возвращает свой второй аргумент. Оператор дизъюнкции or возвращает свой первый аргумент, если это значение отличается от nil и false; в противном случае or возвращает свой второй аргумент. И and, и or используют сокращенное вычисление (short-circuit evaluation); то есть второй операнд вычисляется, только если это необходимо. Вот несколько примеров:
10 or 20 --> 10
10 or error() --> 10
nil or "a" --> "a"
nil and 10 --> nil
false and error() --> false
false and nil --> false
false or nil --> nil
10 and 20 --> 20
3.4.6 – Конкатенация
Оператор конкатенации строк в Lua обозначается двумя точками ('..'). Если оба операнда являются строками или числами, то числа преобразуются в строки в неуказанном формате (см. §3.4.3). В противном случае вызывается метаметод __concat (см. §2.4).
3.4.7 – Оператор длины
Оператор длины обозначается унарным префиксным оператором #.
Длина строки — это количество ее байтов. (Это обычный смысл длины строки, когда каждый символ занимает один байт).
Оператор длины, примененный к таблице, возвращает границу (border) в этой таблице. Граница в таблице t — это любое неотрицательное целое число, которое удовлетворяет следующему условию:
(border == 0 or t[border] ~= nil) and
(t[border + 1] == nil or border == math.maxinteger)
Другими словами, граница — это любой положительный целочисленный индекс, присутствующий в таблице, за которым следует отсутствующий индекс, плюс два предельных случая: ноль, когда индекс 1 отсутствует; и максимальное значение для целого числа, когда этот индекс присутствует. Обратите внимание, что ключи, не являющиеся положительными целыми числами, не влияют на границы.
Таблица ровно с одной границей называется последовательностью (sequence). Например, таблица {10,20,30,40,50} является последовательностью, так как у нее только одна граница (5). Таблица {10,20,30,nil,50} имеет две границы (3 и 5) и, следовательно, не является последовательностью. (nil по индексу 4 называется дырой). Таблица {nil,20,30,nil,nil,60,nil} имеет три границы (0, 3 и 6), поэтому она тоже не является последовательностью. Таблица {} является последовательностью с границей 0.
Когда t — последовательность, #t возвращает ее единственную границу, что соответствует интуитивному понятию длины последовательности. Когда t не является последовательностью, #t может вернуть любую из ее границ. (Конкретная граница зависит от деталей внутреннего представления таблицы, которые, в свою очередь, могут зависеть от того, как таблица была заполнена, и от адресов памяти ее нечисловых ключей).
Вычисление длины таблицы имеет гарантированное наихудшее время O(log n), где n — наибольший целочисленный ключ в таблице.
Программа может изменить поведение оператора длины для любого значения, кроме строк, через метаметод __len (см. §2.4).
3.4.8 – Приоритет операторов
Приоритет операторов в Lua соответствует таблице ниже, от низшего к высшему:
or
and
< > <= >= ~= ==
|
~
&
<< >>
..
+ -
* / // %
унарные операторы (not # - ~)
^
Как обычно, вы можете использовать скобки для изменения приоритетов в выражении. Операторы конкатенации ('..') и возведения в степень ('^') правоассоциативны. Все остальные бинарные операторы левоассоциативны.
3.4.9 – Конструкторы таблиц
Конструкторы таблиц — это выражения, которые создают таблицы. Каждый раз, когда конструктор вычисляется, создается новая таблица. Конструктор можно использовать для создания пустой таблицы или для создания таблицы и инициализации некоторых ее полей. Общий синтаксис конструкторов таков:
tableconstructor ::= ‘{’ [fieldlist] ‘}’
fieldlist ::= field {fieldsep field} [fieldsep]
field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp
fieldsep ::= ‘,’ | ‘;’
Каждое поле вида [exp1] = exp2 добавляет в новую таблицу запись с ключом exp1 и значением exp2. Поле вида name = exp эквивалентно ["name"] = exp. Поля вида exp эквивалентны [i] = exp, где i — последовательные целые числа, начиная с 1; поля в других форматах не влияют на этот подсчет. Например,
a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 }
эквивалентно
do
local t = {}
t[f(1)] = g
t[1] = "x" -- 1-е exp
t[2] = "y" -- 2-е exp
t.x = 1 -- t["x"] = 1
t[3] = f(x) -- 3-е exp
t[30] = 23
t[4] = 45 -- 4-е exp
a = t
end
Порядок присваиваний в конструкторе не определен. (Этот порядок имел бы значение только при наличии повторяющихся ключей).
Если последнее поле в списке имеет вид exp и выражение является выражением с множественными результатами (multires expression), то все значения, возвращаемые этим выражением, последовательно входят в список (см. §3.4.12).
Список полей может иметь необязательный завершающий разделитель для удобства машинно-генерируемого кода.
3.4.10 – Вызовы функций
Вызов функции в Lua имеет следующий синтаксис:
functioncall ::= prefixexp args
При вызове функции сначала вычисляются prefixexp и args. Если значение prefixexp имеет тип function, то эта функция вызывается с заданными аргументами. В противном случае, если присутствует метаметод __call у prefixexp, он вызывается: его первым аргументом является значение prefixexp, за которым следуют исходные аргументы вызова (см. §2.4).
Форма
functioncall ::= prefixexp ‘:’ Name args
может использоваться для эмуляции методов. Вызов v:name(args) является синтаксическим сахаром для v.name(v, args), за исключением того, что v вычисляется только один раз.
Аргументы имеют следующий синтаксис:
args ::= ‘(’ [explist] ‘)’
args ::= tableconstructor
args ::= LiteralString
Все выражения-аргументы вычисляются перед вызовом. Вызов формы f{fields} является синтаксическим сахаром для f({fields}); то есть список аргументов — это единственная новая таблица. Вызов формы f'string' (или f"string", или f[[string]]) является синтаксическим сахаром для f('string'); то есть список аргументов — это единственная литеральная строка.
Вызов формы return functioncall, не находящийся в области видимости закрываемой переменной, называется хвостовым вызовом (tail call). Lua реализует правильные хвостовые вызовы (proper tail calls или proper tail recursion): при хвостовом вызове вызываемая функция повторно использует стековый фрейм вызывающей функции. Следовательно, нет ограничения на количество вложенных хвостовых вызовов, которые может выполнить программа. Однако хвостовой вызов стирает любую отладочную информацию о вызывающей функции. Обратите внимание, что хвостовой вызов происходит только при определенном синтаксисе, когда return имеет один-единственный вызов функции в качестве аргумента, и это происходит вне области видимости любой закрываемой переменной. Этот синтаксис заставляет вызывающую функцию возвращать в точности то, что возвращает вызываемая функция, без каких-либо промежуточных действий. Таким образом, ни один из следующих примеров не является хвостовым вызовом:
return (f(x)) -- результаты выравниваются до 1
return 2 * f(x) -- результат умножается на 2
return x, f(x) -- дополнительные результаты
f(x); return -- результаты отбрасываются
return x or f(x) -- результаты выравниваются до 1
3.4.11 – Определения функций
Синтаксис определения функции следующий:
functiondef ::= function funcbody
funcbody ::= ‘(’ [parlist] ‘)’ block end
Следующий синтаксический сахар упрощает определения функций:
stat ::= function funcname funcbody
stat ::= local function Name funcbody
stat ::= global function Name funcbody
funcname ::= Name {‘.’ Name} [‘:’ Name]
Инструкция
function f () body end
транслируется в
f = function () body end
Инструкция
function t.a.b.c.f () body end
транслируется в
t.a.b.c.f = function () body end
Инструкция
local function f () body end
транслируется в
local f; f = function () body end
а не в
local f = function () body end
(Это имеет значение только тогда, когда тело функции содержит рекурсивные ссылки на f). Аналогично, инструкция
global function f () body end
транслируется в
global f; global f = function () body end
Второе global делает присваивание инициализацией, которая вызовет ошибку, если эта глобальная переменная уже определена.
Синтаксис с двоеточием используется для эмуляции методов, добавляя неявный дополнительный параметр self в функцию. Таким образом, инструкция
function t.a.b.c:f (params) body end
является синтаксическим сахаром для
t.a.b.c.f = function (self, params) body end
Определение функции — это выполняемое выражение, значением которого является значение типа function. Когда Lua предкомпилирует чанк, все тела его функций также предкомпилируются, но они еще не создаются. Затем, когда Lua выполняет определение функции, функция инстанцируется (или замыкается). Этот экземпляр функции, или замыкание (closure), является окончательным значением выражения.
Результаты возвращаются с помощью инструкции return (см. §3.3.4). Если управление достигает конца функции без встречи инструкции return, то функция возвращается без результатов.
Существует системно-зависимое ограничение на количество значений, которые может вернуть функция. Гарантируется, что этот предел составляет не менее 1000.
Параметры
Параметры действуют как локальные переменные, которые инициализируются значениями аргументов:
parlist ::= namelist [‘,’ varargparam] | varargparam
varargparam ::= ‘...’ [Name]
Когда функция Lua вызывается, она выравнивает свой список аргументов по длине списка параметров (см. §3.4.12), если только функция не является вариадической, что указывается тремя точками ('...') в конце ее списка параметров. Вариадическая функция не выравнивает свой список аргументов; вместо этого она собирает все дополнительные аргументы и предоставляет их функции через vararg-таблицу. В этой таблице значения с индексами 1, 2 и т. д. являются дополнительными аргументами, а значение с индексом "n" — это количество дополнительных аргументов.
В качестве примера рассмотрим следующие определения:
function f(a, b) end
function g(a, b, ...) end
function r() return 1,2,3 end
Тогда мы имеем следующее отображение аргументов в параметры и в vararg-таблицу:
CALL PARAMETERS
f(3) a=3, b=nil
f(3, 4) a=3, b=4
f(3, 4, 5) a=3, b=4
f(r(), 10) a=1, b=10
f(r()) a=1, b=2
g(3) a=3, b=nil, va. table -> {n = 0}
g(3, 4) a=3, b=4, va. table -> {n = 0}
g(3, 4, 5, 8) a=3, b=4, va. table -> {5, 8, n = 2}
g(5, r()) a=5, b=1, va. table -> {2, 3, n = 2}
Vararg-таблица в вариадической функции может иметь необязательное имя, указанное после трех точек. Если оно присутствует, это имя обозначает локальную переменную только для чтения, которая ссылается на vararg-таблицу. Если у vararg-таблицы нет имени, доступ к ней можно получить только через vararg-выражение.
Vararg-выражение также записывается как три точки, и его значением является список значений в vararg-таблице от 1 до целочисленного значения по индексу "n". (Следовательно, если код не изменяет vararg-таблицу, этот список соответствует дополнительным аргументам в вызове функции). Этот список ведет себя как результаты функции с множественными результатами (см. §3.4.12).
В качестве оптимизации, если vararg-таблица удовлетворяет некоторым условиям, код не создает фактическую таблицу, а вместо этого транслирует индексные выражения и vararg-выражения в доступы к внутренним данным vararg. Условия следующие: если у vararg-таблицы есть имя, это имя не является upvalue во вложенной функции, и оно используется только как базовая таблица в синтаксических конструкциях t[exp] или t.id. Обратите внимание, что анонимная vararg-таблица всегда удовлетворяет этим условиям.
3.4.12 – Списки выражений, множественные результаты и выравнивание
Как вызовы функций, так и vararg-выражения могут давать множественные значения. Эти выражения называются выражениями с множественными результатами (multires expressions).
Когда выражение с множественными результатами используется как последний элемент списка выражений, все результаты выражения добавляются к списку значений, создаваемому списком выражений. Обратите внимание, что одиночное выражение в месте, где ожидается список выражений, является последним выражением в этом (одноэлементном) списке.
Вот места, где Lua ожидает список выражений:
- Инструкция
return, напримерreturn e1,e2,e3(см. §3.3.4). - Конструктор таблицы, например
{e1,e2,e3}(см. §3.4.9). - Аргументы вызова функции, например
foo(e1,e2,e3)(см. §3.4.10). - Множественное присваивание, например
a,b,c = e1,e2,e3(см. §3.3.3). - Объявление
localилиglobal, которое аналогично множественному присваиванию. - Начальные значения в обобщенном цикле
for, напримерfor k in e1,e2,e3 do ... end(см. §3.3.5).
В последних четырех случаях список значений из списка выражений должен быть выровнен до определенной длины: количество параметров при вызове невариадической функции (см. §3.4.11), количество переменных в множественном присваивании или объявлении и ровно четыре значения для обобщенного цикла for. Выравнивание следует этим правилам: если значений больше, чем нужно, лишние значения отбрасываются; если значений меньше, чем нужно, список дополняется значениями nil. Когда список выражений заканчивается выражением с множественными результатами, все результаты этого выражения входят в список значений до выравнивания.
Когда выражение с множественными результатами используется в списке выражений, не будучи последним элементом, или в месте, где синтаксис ожидает одиночное выражение, Lua выравнивает список результатов этого выражения до одного элемента. Как частный случай, синтаксис ожидает одиночное выражение внутри выражения в скобках; следовательно, добавление скобок вокруг выражения с множественными результатами заставляет его выдавать ровно один результат.
Нам редко нужно использовать vararg-выражение в месте, где синтаксис ожидает одиночное выражение. (Обычно проще добавить обычный параметр перед вариадической частью и использовать этот параметр). Когда такая необходимость возникает, мы рекомендуем присвоить vararg-выражение одиночной переменной и использовать эту переменную вместо него.
Вот несколько примеров использования выражений с множественными результатами. Во всех случаях, когда конструкции нужен “n-й результат”, а такого результата нет, используется nil.
print(x, f()) -- печатает x и все результаты из f().
print(x, (f())) -- печатает x и первый результат из f().
print(f(), x) -- печатает первый результат из f() и x.
print(1 + f()) -- печатает 1, сложенную с первым результатом из f().
local x = ... -- x получает первый vararg-аргумент.
x,y = ... -- x получает первый vararg-аргумент,
-- y получает второй vararg-аргумент.
x,y,z = w, f() -- x получает w, y получает первый результат из f(),
-- z получает второй результат из f().
x,y,z = f() -- x получает первый результат из f(),
-- y получает второй результат из f(),
-- z получает третий результат из f().
x,y,z = f(), g() -- x получает первый результат из f(),
-- y получает первый результат из g(),
-- z получает второй результат из g().
x,y,z = (f()) -- x получает первый результат из f(), y и z получают nil.
return f() -- возвращает все результаты из f().
return x, ... -- возвращает x и все полученные vararg-аргументы.
return x,y,f() -- возвращает x, y и все результаты из f().
{f()} -- создает список со всеми результатами из f().
{...} -- создает список со всеми vararg-аргументами.
{f(), 5} -- создает список с первым результатом из f() и 5.