Этот раздел описывает лексику, синтаксис и семантику Lua. 4.1 Лексические соглашения в языке
Идентификатором в Lua может быть любая строка символов, цифр и символов подчеркивания, не начинающаяся цифрой. Это совпадает с определением идентификаторов в большинстве языков, за исключением того, что определение символа зависит от текущего региона. Любой символ, который считается алфавитным в текущем языке, может использоваться в идентификаторе. То есть, алфавитные символы берутся из настроек текущей системной локали. Замечу, что для лучшей переносимости программ и их применения в разных регионах лучше все же ограничиться латинским алфавитом, цифрами и символом подчеркивания: они применимы везде. Следующие слова зарезервированы, и не могут использоваться как идентификаторы:
and break do else elseif return
end for function if in then
local nil not or repeat until while
Lua представляет собой язык, чувствительный к регистру символов: and является зарезервированным словом, но And и AND (если региональный язык разрешает) не одно и то же. Значит, приведенные варианты уже можно использовать как имена переменных. Кроме того, идентификаторы, начинающиеся с символа подчеркивания, сопровождаемого прописными буквами (типа _INPUT) зарезервированы для внутренних переменных. Их не стоит применять в своих программах.
Следующие строки обозначают другие лексемы (tokens):
~= <= >= < > == = + - *
( ) { } [ ] ; , . .. ... /
Литеральные строки могут быть разграничены одиночными или двойными кавычками, и могут содержать C-подобные управляющие последовательности: \a (bell), \b (backspace), \f (form feed), \n (newline), \r (carriage return), \t (horizontal tab), \v (vertical tab), \\ (backslash), \" (double quote), \' (single quote), и \newline (то есть, наклонная черта влево, сопровождаемая реальным newline, который приводит к переводу строки). Символ в строке может также быть определен числовым значением, через управляющую последовательность \ddd, где ddd последовательность до трех десятичных цифр. Строки в Lua могут содержать любое 8-разрядное значение, включая вложенные нули, которые могут быть определены как \000.
Литеральные строки могут также быть разграничены парами [[ ... ]]. Литералы в этой форме в скобках могут занимать по несколько строк, содержать вложенные пары скобок [[ ... ]] и не интерпретировать управляющие последовательности. Эта форма особенно удобна для записи строк, которые содержат части программы или другие цитируемые строки. Как пример, в системе использующей ASCII-кодировку, следующие три литерала эквивалентны:
1) "alo\n123\""
2) '\97lo\10\04923"'
3) [[alo
123"]]
Комментарии начинаются с двойного тире (--) и выполняются до конца строки. Кроме того, первая строка составной части всегда пропущена, если начинается с символа #. Это средство позволяет использование Lua как интерпретатора скриптов в Unix-системах.
Числовые константы могут быть написаны с факультативной целой частью и тоже факультативным дробной частями. Допустимо применение экспоненциальной формы запитси. Примеры имеющих силу числовых констант:
3 3.0 3.1416 314.16e-2 0.31416E1
4.2 Приведение
Lua обеспечивает некоторые автоматические преобразования между значениями во время выполнения. Любая арифметическая операция, примененная к строке, пробует преобразовывать эту строку в соответствующее число, следуя обычным правилам. Наоборот, всякий раз, когда используется число, а ожидается строка, это число будет преобразовано в строку в приемлемом формате. Формат выбран так, чтобы преобразование из числа в строку было таким, чтобы обратное преобразование из строки в число было точным. Таким образом, преобразование не обязательно генерирует хороший текст для некоторых чисел. Для полного управления тем, как числа будут преобразованы в строки, используйте функцию format.
4.3 Корректировка
Функции в Lua могут возвращать много значений. Потому, что не имеется никаких объявлений типа когда функция вызвана, система не знает, сколько значений вернется, или сколько параметры требуется. Следовательно, иногда список значений должен быть откорректирован во время выполнения к данной длине. Если имеется большее количество значений, чем необходимы, то лишние значения отбрасываются. Если имеется меньшее количество значений, чем необходимы, то список расширен добавлением потребного количества nil. Эта корректировка происходит в многократных назначениях .
4.4 Инструкции
Lua поддерживает почти стандартный набор инструкций, подобных таким же наборам на Pascal или C. Стандартные команды включают присваивание, контроль выполнения и вызовы процедур. Нестандартные команды включают конструкторы таблицы и объявления локальных переменных.
4.4.1 Блоки
Блоком является список инструкций. Синтаксически блок равен составной части (chunk):
block ::= chunk
Блок может быть явно разграничен:
stat ::= do block end
Явные блоки полезны, чтобы управлять областью видимости (контекстом) локальных переменных. Явные блоки также иногда используются, чтобы добавить возврат или разрывать инструкцию в середине другого блока.
4.4.2 Присваивания
Lua поддерживает такую удобную вещь, как многократные присваивания. Следовательно, синтаксис определяет список переменных с левой стороны и список выражений с правой сторона. Элементы в обоих списках отделяются запятыми:
stat ::= varlist1 `=' explist1
varlist1 ::= var {`,' var}
Эта инструкция сначала оценивает все значения справа и возможные индексы слева, а затем делает примваивание. Так, код:
i = 3
i, a[i] = 4, 20
установит a[3] в 20, но не воздействует на a[4] потому, что i в a[i] оценен прежде, чем ему было присвоено значение 4. Многократное присваивание может использоваться, чтобы поменять местами два значения, например:
x, y = y, x
Два списка в многократном присваивании могут иметь различные длины. Перед собственно присваиванием, список значений будет откорректирован к длине списка имеющихся переменных.
Одиночное имя может обозначать глобальную переменную, локальную переменную или формальный параметр:
var ::= name
Квадратные скобки используются, чтобы индексировать таблицу:
var ::= varorfunc `[' exp1 `]'
varorfunc ::= var | functioncall
varorfunc должен иметь в качестве результата значение из таблицы, где поле, индексированное значением выражения exp1, получает назначенное ему значение.
Синтаксис var.NAME представляет собой только синтаксический аналог для выражения var["NAME"]:
var ::= varorfunc `.' name
Значение присваиваний, оценок глобальных переменных и индексированных переменных может быть изменено методами тэгов. Фактически, назначение x=val, где x представляет собой глобальную переменную, является эквивалентным обращению setglobal("x",val), а присваивание t[i]=val эквивалентно settable_event(t,i,val). (setglobal находится в базисной библиотеке, settable_event используется только для объяснительных целей).
4.4.3 Структуры управления
Структуры управления if, while и repeat имеют обычное значение и знакомый синтаксис:
stat ::= while exp1 do block end
stat ::= repeat block until exp1
stat ::= if exp1 then block {elseif exp1 then block} [else block] end
Выражение exp1 условия структуры управления может возвращать любое значение. Все значения, отличные от nil, рассматриваются как истина, только nil считается ложью.
Инструкция return используется, чтобы возвратить значения из функции или из chunk. Поскольку функции или составные части могут возвращать больше, чем одно значение, синтаксис для инструкции return:
stat ::= return [explist1]
Инструкция break может использоваться, чтобы завершить выполнение цикла, переходя к следующей инструкции сразу после цикла:
stat ::= break
break заканчивает самый внутренний вложенный цикл (while, repeat или for).
По синтаксическим причинам инструкции return и break могут быть написаны только как последние инструкции блока. Если действительно необходимо вставить их в середину, надо применить явный внутренний блок, например, do return end потому, что теперь return в самом деле последняя инструкция во внутреннем блоке.
4.4.4 Инструкция For
Инструкция for имеет две формы, по одной для чисел и таблиц. Числовая версия цикла for имеет следующий синтаксис:
stat ::= for name `=' exp1 `,' exp1 [`,' exp1] do block end
Инструкция for, подобная:
for var = e1 ,e2, e3 do block end
является заменителем кода:
do
local var, _limit, _step = tonumber(e1), tonumber(e2), tonumber(e3)
if not (var and _limit and _step) then error() end
while (_step>0 and var<=_limit) or
(_step<=0 and var>=_limit) do
block
var = var+_step
end
end
Обратите внимание на следующее:
sep=0pt
_limit и _step являются невидимыми переменными. Имена здесь даны только для объяснительных целей.
Поведение неопределено, если Вы меняете значение var внутри блока.
Если третье выражение (step) отсутствует, то используется step 1.
Значения limit и step оценены только однажды, перед стартом цикла.
Переменная var локальна для инструкции: Вы не можете использовать ее значение после окончания работы for.
Вы можете использовать break, чтобы выйти из for. Если Вы нуждаетесь в значении индекса, присвойте его другой переменной перед выходом.
Таблица для инструкции for пересекает все пары (index,value) данной таблицы. Это имеет следующий синтаксис:
stat ::= for name `,' name in exp1 do block end
Инструкция for, подобная:
for index, value in exp do block end
равносильна такому коду:
do
local _t = exp
local index, value = next(t, nil)
while index do
block
index, value = next(t, index)
end
end
Обратите внимание на следующее:
sep=0pt
_t является невидимомй переменномй. Имя здесь дано только для объяснительных целей.
Поведение неопределено, если Вы меняете значение index внутри блока.
Поведение неопределено, если Вы меняете таблицу _t при работе цикла.
Переменнае index и var локальны для инструкции: Вы не можете использовать их значения после окончания работы for.
Вы можете использовать break, чтобы выйти из for. Если Вы нуждаетесь в значениях index или value, присвойте их другим переменным перед выходом.
Порядок, в котором элементы таблицы будут пересечены, неопределен, даже для числовых индексов. Если Вы хотите пересекать индексы в числовом порядке, используйте числовой вариант for.
4.4.5 Обращения к функции как инструкции
Из-за возможных побочных эффектов, обращения к функции могут быть выполнены как инструкции:
stat ::= functioncall
В этом случае все возвращенные значения утрачены.
4.4.6 Локальные объявления
Локальные переменные могут быть объявлены где-нибудь внутри блока. Объявление может включать начальное присваивание:
stat ::= local declist [init]
declist ::= name {`,' name}
init ::= `=' explist1
Если представлено начальное назначение, то оно имеет ту же самую семантику многократного назначения. Иначе все переменные инициализированы nil.
Сhunk также блок, так что локальные переменные могут быть объявлены снаружи любого явного блока.
Область действия (контекст) локальных переменных начинается после объявления и продолжается до конца блока. Таким образом, код local print=print создает локальную переменную, названную print, чье начальное значение будет взято из глобальной переменной с тем же самым именем.
4.5 Выражения
4.5.1 Базисные выражения
Базисные выражения в Lua такие:
exp ::= `(' exp `)'
exp ::= nil
exp ::= number
exp ::= literal
exp ::= var
exp ::= upvalue
exp ::= function
exp ::= functioncall
exp ::= tableconstructor
Доступ к глобальной переменной x эквивалентен обращению getglobal("x"), а доступ к индексированной переменной t[i] эквивалентен обращению к gettable_event(t,i).
Нетерминальный exp1 используется, чтобы указать, что значения, возвращенные выражением должны быть откорректированы к одному значению:
exp1 ::= exp
4.5.2 Арифметические операторы
Lua поддерживает комплект обычных арифметических операторов: двоичный + (сложение), - (вычитание), * (умножение), / (деление), ^ (возведение в степень), а также унарный - (обращение знака числа). Если операнды числа или строки, которые могут быть преобразованы в числа, то все операции за исключением возведения в степень имеют обычное значение. Иначе будет вызван соответствующий метод тэга. Возведение в степень всегда вызывает метод тэга. Стандартная математическая библиотека переопределяет этот метод для чисел, давая ожидаемое значение.
4.5.3 Реляционные операторы
Реляционные операторы в Lua:
== ~= < > <= >=
Эти операторы возвращают nil как ложь, или любое другое значение (но не nil) в качестве истины.
Равенство (==) сначала сравнивает тэги операндов. Если они различны, то результатом будет nil. Иначе сравниваются их значения. Числа и строки сравниваются обычным способом. Таблицы, userdata, и функции сравниваются как ссылки, то есть две таблицы рассматриваются равными только, если они реально та же самая таблица. Оператор ~= прямо противоположен оператору равенства (==).
Правила преобразования НЕ применяются к сравнениям равенства. Таким образом, "0"==0 вернет false, а t[0] и t["0"] обозначают различные записи в таблице.
Операторы порядка работают следующим образом. Если оба параметра числа, то они сравниваются также. Иначе, если оба параметра строки, то их значения сравниваются, используя лексикографический порядок. Во всех остальных ситуациях будет вызван метод lt тэга.
4.5.4 Логические операторы
Логические операторы в Lua:
and or not
Подобно структурам управления, все логические операторы рассматривают nil как false (ложь), а все остальное как истину (true).
Оператор конъюнкции and вернет nil, если первый параметр nil, иначе это возвращает второй параметр. Оператор дизъюнкции or вернет первый параметр, если он отличается от nil, в противном случае это возвращает второй параметр. Операторы and и or используют краткое вычисление, то есть второй операнд оценен только в случае необходимости. Имеются две полезных идиомы в Lua, которые используют логические операторы. Первая идиома:
x = x or v
Которая является эквивалентной:
if x == nil then x = v end
Эта идиома устанавливает x к значению по умолчанию v, когда x не установлен.
Вторая идиома такая:
x = a and b or c
Которая должна читаться как x=(a and b) or c. Эта идиома эквивалентна:
if a then x = b else x = c end
При условии, что b не nil.
4.5.5 Объединения
Оператор объединения строк в Lua обозначен двумя точками (`..'). Если оба операнда строки или числа, они будут преобразованы в строки.. Иначе будет вызван метод concat тэга.
4.5.6 Старшинство
Порядок старшинства в Lua следует из таблицы ниже. Операторы в ней перечислены в порядке от низкого к более высокому приоритету:
and or
< > <= >= ~= ==
..
+ -
* /
not - (unary)
^
Все двоичные операторы ассоциативны слева, кроме возведения в степень, который является ассоциативным справа. Прекомпилятор может перестраивать порядок оценки ассоциативных операторов (типа .. или +), пока эти оптимизация не изменяют нормальные результаты. Однако, эти оптимизация могут изменять некоторые результаты, если Вы определяете не ассоциативные методы тэгов для этих операторов.
4.5.7 Конструкторы таблиц
Конструкторы таблиц представляют собой выражения, которые создают таблицы: каждый раз конструктор оценен, и новая таблица создана. Конструкторы могут использоваться, чтобы создать пустые таблицы или создать таблицу и инициализировать некоторые из полей (необязательно все). Общий синтаксис для конструкторов:
tableconstructor ::= `{' fieldlist `}'
fieldlist ::= lfieldlist|ffieldlist|lfieldlist `;'
ffieldlist|ffieldlist `;' lfieldlist
lfieldlist ::= [lfieldlist1]
ffieldlist ::= [ffieldlist1]
Форма lfieldlist1 используется, чтобы инициализировать списки:
lfieldlist1 ::= exp {`,' exp} [`,']
Выражения в списке назначены последовательным числовым индексам, начиная с 1 (но не с 0!). Например, код:
a = {"v1", "v2", 34}
является эквивалентным коду:
do
local temp = {}
temp[1] = "v1"
temp[2] = "v2"
temp[3] = 34
a = temp
end
Форма ffieldlist1 инициализирует другие поля в таблице:
ffieldlist1 ::= ffield {`,' ffield} [`,']
ffield ::= `[' exp `]' `=' exp | name `=' exp
Например такая запись:
a = {[f(k)] = g(y), x = 1, y = 3, [0] = b+c}
эквивалентна такому коду:
do
local temp = {}
temp[f(k)] = g(y)
temp.x = 1 -- or temp["x"] = 1
temp.y = 3 -- or temp["y"] = 3
temp[0] = b+c
a = temp
end
Выражения, подобные {x=1, y=4} фактически синтаксический аналог для выражения вида {["x"]=1, ["y"]=4}.
Обе формы могут иметь факультативную конечную запятую и могут использоваться в том же самом конструкторе, разделенные точкой с запятой. Например, все формы ниже правильны:
x = {;}
x = {"a", "b",}
x = {type="list"; "a", "b"}
x = {f(0), f(1), f(2),; n=3,}
4.5.8 Вызовы функций
Вызовы функций в Lua имеют синтаксис:
functioncall ::= varorfunc args
Сначала вычисляется varorfunc. Если значение имеет тип function, то эта функция будет вызвана с данными параметрами. Иначе вызывается метод function тэга, имея первым параметром значение varorfunc с перечисленными затем первоначальными параметрами обращения.
Форма
functioncall ::= varorfunc `:' name args
Может использоваться, чтобы вызвать methods. Обращение v:name(...) синтаксически аналогично v.name(v, ...), за исключением того, что v будет оценен только однажды. Параметры имеют следующий синтаксис:
args ::= `(' [explist1] `)'
args ::= tableconstructor
args ::= literal
explist1 ::= {exp1 `,'} exp
Все выражения параметра оценены перед обращением. Обращение в форме f{...} синтаксически аналогично f({...}), то есть список параметров представляет собой одиночную новую таблицу. Обращение в форме f'...' (f"..." или f[[...]]) синтаксически аналогично f('...'), то есть список параметров представляет собой одиночную строку литералов.
Потому, что функция может возвращать любое число результатов, число результатов должно быть откорректировано прежде, чем они используются. Если функция вызвана как инструкция, то список возврата откорректирован к 0, таким образом отбрасывая все возвращенные значения. Если функция вызвана в месте, которое нуждается в одиночном значении (синтаксически обозначенном нетерминальным exp1), то список возврата откорректирован к 1, таким образом отбрасывая все возвращенные значения, но не первый. Если функция вызвана в месте, которое может использовать много значений (синтаксически обозначено нетерминальным exp), то никакая корректировка не будет сделана. Единственные места, которые могут обрабатывать много значений, это последние (или единственные) выражения в присваивании, в списке параметров или в инструкции return. Имеются примеры:
f() -- 0 результатов
g(f(), x) -- f() 1 результат
g(x, f()) -- g получает x и все значения, возвращенные f()
a,b,c = f(), x -- f() скорректирован к 1 результату (и c получает nil)
a,b,c = x, f() -- f() 2 результата
a,b,c = f() -- f() 3 результата
return f() -- возвращает все значения, возвращенные f()
return x,y,f() -- вернет a, b и все, что вернет f()
4.5.9 Определение функций
Синтаксис для определения функций такой:
function ::= function `(' [parlist1] `)' block end
stat ::= function funcname `(' [parlist1] `)' block end
funcname ::= name | name `.' name | name `:' name
Инструкция
function f () ... end
является только синтаксическим аналогом для
f = function () ... end
а инструкция
function v.f () ... end
является синтаксическим аналогом для
v.f = function () ... end
Функциональное определение представляет собой выполнимое выражение, чье значение имеет тип function. Когда Lua прекомпилирует chunk, все функциональные тела также прекомпилируются. Затем, всякий раз, когда Lua выполняет функциональное определение верхние переменные (upvalues) фиксируются, и функция выполняется. Этот функциональный образец (или замкнутое выражение) представляет собой конечное значение выражения. Различные образцы той же самой функции могут иметь различные верхние переменные.
Параметры действуют как локальные переменные, инициализированные со значениями параметра:
parlist1 ::= `...'
parlist1 ::= name {`,' name} [`,' `...']
Когда функция вызвана, список параметров будет откорректирован к длине списка параметров, если функция не vararg-функция, которая обозначена тремя точками (`...') в конце списка параметра. Функция vararg не корректирует список параметров, вместо этого она собирает все лишние параметры в неявный параметр, названный arg. Значением arg является таблицы из n полей, чьим значением является число параметров дополнительного пространства и сами эти параметры, перечисленные в полях 1, 2, ..., n.
Как пример, рассмотрите следующие определения:
function f(a, b) end
function g(a, b, ...) end
function r() return 1,2,3 end
Имеем следующее отображение параметров:
ВЫЗОВ ПАРАМЕТРЫ
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, arg={n=0}
g(3, 4) a=3, b=4, arg={n=0}
g(3, 4, 5, 8) a=3, b=4, arg={5, 8; n=2}
g(5, r()) a=5, b=1, arg={2, 3; n=2}
Результаты возвращены, используя инструкцию return. Если управление достигает конца функции без того, чтобы столкнуться с инструкцией return, то функция будет завершена без результатов.
Синтаксис
funcname ::= name `:' name
используется для определения методов, то есть функции, которые имеют неявный дополнительный параметр self .
Инструкция
function v:f (...) ... end
является только синтаксическим аналогом для
v.f = function (self, ...) ... end
Обратите внимание, что функция получает дополнительный формальный параметр self.
4.6 Зона видимости и Upvalues
Функциональное тело может обратиться к собственным локальным переменным (которые включают и параметры), а также к глобальным переменным, пока они не затенены локальными переменными с тем же самым именем. Функция не может обращаться к локальной переменной из функции включения, так как такие переменные больше не могут существовать, когда функция вызвана. Однако, функция может обращаться к значению локальной переменной из функции включения, используя upvalues, чей синтаксис:
upvalue ::= `%' name
upvalue подобен переменному выражению, но его значение закрепляется, когда функция, в которой он появляется запускается. Имя, используемое в upvalue, может быть именем любой переменной, видимой в том месте, где функция определена, то есть пригодны глобальные переменные и локальные переменные. Обратите внимание, что, когда upvalue таблица, только ссылка на эту таблицу (которая и является значением upvalue) закрепляется, а содержание самой таблицы может быть изменено по желанию. Использование значений таблицы как upvalues представляет собой методику для наличия перезаписываемого но частного состояния, приложенного к функциям.
Имеются некоторые примеры:
a,b,c = 1,2,3 -- глобальные переменные
local d
function f (x)
local b = {} -- x и b локальны для f, b затеняет глобальную b
local g = function (a)
local y -- a и y локальны для g
p = a -- OK, доступ к локальной a
p = c -- OK, доступ к глобальной c
p = b -- ERROR: невозможен доступ к переменной вне зоны видимости
p = %b -- OK, доступ к замороженной b (локальная f)
%b = 3 -- ERROR: нельзя менять upvalue
%b.x = 3 -- OK, изменение содержимого таблицы
p = %c -- OK, доступ к замороженному значению глобальной c
p = %y -- ERROR: `y' невидима, где `g' определена
p = %d -- ERROR: `d' невидима, где `g' определена
end -- g
end -- f
4.7 Обработка ошибок
Поскольку Lua язык расширений, все действия Lua начинаются из C-кода в ведущей программе, вызывающей функцию из Lua-библиотеки. Всякий раз, когда ошибка происходит в течение Lua-трансляции или выполнения, вызывается функция _ERRORMESSAGE и затем соответствующая функция из библиотеки (lua_dofile, lua_dostring, lua_dobuffer или lua_call) завершена, возвращая условие ошибки.
Ошибки распределения памяти представляют собой исключительную ситуацию из предыдущего правила. Когда происходит сбой распределения памяти, Lua не может выполнить функцию _ERRORMESSAGE. Так что, для этого вида ошибки, Lua не вызывает функцию _ERRORMESSAGE. Вместо этого соответствующая функция из библиотеки немедленно завершится со специальным кодом ошибки (LUA_ERRMEM). Это и другие коды ошибки определено в заголовочном файле lua.h, подробности в разделе 5.8.
Единственный параметр _ERRORMESSAGE: строка, описывающая ошибку. Заданное по умолчанию определение для этого: обращение к функции _ALERT, которая печатает сообщение на stderr. Стандартная библиотека ввода-вывода переопределяет _ERRORMESSAGE и использует средства отладки, чтобы печатать некоторую дополнительную информацию, типа расположения обращений в стеке.
Lua-код может явно генерировать ошибку, вызывая функцию error. Lua-код может перехватить ошибку, используя обращение к функции call.
4.8 Методы тэгов
Lua обеспечивает мощный механизм, чтобы расширить семантику, названный методами тэгов. Это определенная программистом функция, которая вызвана в специфических точках в течение выполнения программы Lua, позволяя программисту изменить стандартное поведение Lua в этих точках. Каждая из этих точек названа событием (event).
Метод тэга для некоего специфического события выбран согласно тэгу значения. Функция settagmethod изменяет метод тэга, связанный с данной парой (tag, event). Первый параметр представляет собой тэг, второй строку (имя события), а третий параметр (функция) задает новый метод или nil, чтобы восстановить заданное по умолчанию поведение для пары. Функция settagmethod возвращает предыдущий метод тэга для этой пары. Функция-компаньон gettagmethod получает тэг и имя события и возвращает текущий метод, связанный с парой.
Методы тэгов вызваны при соответствующих событиях, которые идентифицированы данными именами. Семантика методов лучше объяснена функцией Lua, описывающей поведение интерпретатора в каждом событии. Эта функция не только показывает, когда метод вызван, но также параметры, результаты и заданное по умолчанию поведение. Код, показанный здесь, только иллюстративен: реальное поведение сложно закодировано в интерпретаторе и намного более эффективно, чем это моделирование.
``add'':
Вызван, когда операция + применяется к не числовым операндам.
Функция getbinmethod ниже определяет, как Lua выбирает метод для двоичной операции. Сначала Lua пробует первый операнд. Если тэг не определяет метод для операции, то Lua пробует второй операнд. Если это также терпит неудачу, то Lua получает метод из тэга 0.
function getbinmethod (op1, op2, event)
return gettagmethod(tag(op1), event) or
gettagmethod(tag(op2), event) or gettagmethod(0, event)
end
При использовании этой функции, метод события ``add'' такой:
function add_event (op1, op2)
local o1, o2 = tonumber(op1), tonumber(op2)
if o1 and o2 then -- both operands are numeric
return o1+o2 -- '+' here is the primitive 'add'
else -- at least one of the operands is not numeric
local tm = getbinmethod(op1, op2, "add")
if tm then
-- call the method with both operands and an extra
-- argument with the event name
return tm(op1, op2, "add")
else -- no tag method available: default behavior
error("unexpected type at arithmetic operation")
end
end
end
``sub'':
Вызван, когда операция - применяется к не числовым операндам. Поведение подобно событию ``add''.
``mul'':
Вызван, когда операция * применяется к не числовым операндам. Поведение подобно событию ``add''.
``div'':
Вызван, когда операция / применяется к не числовым операндам. Поведение подобно событию ``add''.
``pow'':
Вызван, когда операция ^ (возведение в степень) применяется к числовым операндам.
function pow_event (op1, op2)
local tm = getbinmethod(op1, op2, "pow")
if tm then
-- call the method with both operands and an extra
-- argument with the event name
return tm(op1, op2, "pow")
else -- no tag method available: default behavior
error("unexpected type at arithmetic operation")
end
end
``unm'':
Вызван, когда одноместная операция - применяется к не числовому операнду.
function unm_event (op)
local o = tonumber(op)
if o then -- operand is numeric
return -o -- '-' here is the primitive 'unm'
else -- the operand is not numeric.
-- Try to get a tag method from the operand;
-- if it does not have one, try a "global" one (tag 0)
local tm = gettagmethod(tag(op), "unm") or
gettagmethod(0, "unm")
if tm then
-- call the method with the operand, nil, and an extra
-- argument with the event name
return tm(op, nil, "unm")
else -- no tag method available: default behavior
error("unexpected type at arithmetic operation")
end
end
end
``lt'':
Вызван, когда операция порядка применяется к не числовому или не строчному операнду. Это соответствует оператору <.
function lt_event (op1, op2)
if type(op1) == "number" and type(op2) == "number" then
return op1 < op2 -- numeric comparison
elseif type(op1) == "string" and type(op2) == "string" then
return op1 < op2 -- lexicographic comparison
else local tm = getbinmethod(op1, op2, "lt")
if tm then return tm(op1, op2, "lt")
else error("unexpected type at comparison");
end
end
end
Другие операторы порядка используют этот метод согласно обычным эквивалентностям:
a>b <=> b a<=b <=> not (b a>=b <=> not (a ``concat'':
Вызван, когда конкатенация применяется к не строчным операндам.
function concat_event (op1, op2)
if (type(op1) == "string" or type(op1) == "number") and
(type(op2) == "string" or type(op2) == "number") then
return op1..op2 -- primitive string concatenation
else local tm = getbinmethod(op1, op2, "concat")
if tm then return tm(op1, op2, "concat")
else error("unexpected type for concatenation")
end
end
end
``index'':
Вызван, когда Lua пробует найти значение индекса, не представленного в таблице.
``getglobal'':
Вызван всякий раз, когда Lua нуждается в значении глобальной переменной. Этот метод может быть установлен только для nil и для тэгов, порожденных вызовом newtag. Обратите внимание, что тэг представляет собой текущее значение глобальной переменной.
function getglobal (varname)
-- access the table of globals
local value = rawget(globals(), varname)
local tm = gettagmethod(tag(value), "getglobal")
if not tm then return value
else return tm(varname, value)
end
end
Функция getglobal определена в базисной библиотеке.
``setglobal'':
Вызван всякий раз, когда Lua присваивает значение глобальной переменной. Этот метод не может быть установлен для чисел, строк, таблиц и userdata с заданным по умолчанию тэгом.
function setglobal (varname, newvalue)
local oldvalue = rawget(globals(), varname)
local tm = gettagmethod(tag(oldvalue), "setglobal")
if not tm then rawset(globals(), varname, newvalue)
else tm(varname, oldvalue, newvalue)
end
end
Функция setglobal определена в базисной библиотеке.
``gettable'':
Вызван всякий раз, когда Lua обращается к индексированной переменной. Этот метод не может быть установлен для таблиц с заданным по умолчанию тэгом.
function gettable_event (table, index)
local tm = gettagmethod(tag(table), "gettable")
if tm then return tm(table, index)
elseif type(table) ~= "table" then
error("indexed expression not a table");
else local v = rawget(table, index)
tm = gettagmethod(tag(table), "index")
if v == nil and tm then return tm(table, index)
else return v
end
end
end
``settable'':
Вызван, когда Lua присваивает значение индексированной переменной. Этот метод не может быть установлен для таблиц с заданным по умолчанию тэгом.
function settable_event (table, index, value)
local tm = gettagmethod(tag(table), "settable")
if tm then tm(table, index, value)
elseif type(table) ~= "table"
then error("indexed expression not a table")
else rawset(table, index, value)
end
end
``function'':
Вызван, когда Lua пробует вызывать не функциональное значение.
function function_event (func, ...)
if type(func) == "function" then return call(func, arg)
else local tm = gettagmethod(tag(func), "function")
if tm then for i=arg.n,1,-1 do arg[i+1] = arg[i]
end
arg.n = arg.n+1
arg[1] = func
return call(tm, arg)
else error("call expression not a function")
end
end
end
``gc'':
Вызван, когда Lua начинает уборку мусора в userdata. Этот метод может быть установлен только из C, и не может быть установлен для userdata с заданным по умолчанию тэгом. Для каждого объекта userdata, который будет собран, Lua делает эквивалент следующей функции в цикле уборки мусора:
function gc_event (obj)
local tm = gettagmethod(tag(obj), "gc")
if tm then tm(obj)
end
end
В цикле уборки мусора методы тэгов для userdata вызываются в порядке, обратном созданию тэгов, то есть первые методы, которые будут вызваны, связаны с последним тэгом, созданным в программе. Кроме того, в конце цикла Lua делает эквивалент обращения gc_event(nil).