Язык программирования LUA
Lua — это бесплатное программное обеспечение, распространяемое в исходном коде. Его можно использовать для любых целей, включая коммерческие, совершенно бесплатно.
Все версии доступны для загрузки. Эта документация пишется для текущей версии — Lua 5.4, а текущий релиз — Lua 5.4.8.
Lua
Lua — это бесплатное программное обеспечение, распространяемое в исходном коде. Его можно использовать для любых целей, включая коммерческие, совершенно бесплатно.
Все версии доступны для загрузки. Текущая версия — Lua 5.4, а текущий релиз — Lua 5.4.8.
Инструменты
Основной репозиторий модулей Lua — это LuaRocks. Также смотрите Awesome Lua. Предварительно скомпилированные библиотеки и исполняемые файлы Lua доступны на LuaBinaries. Вики lua-users содержит множество пользовательских дополнений для Lua.
Сборка
Lua реализован на чистом ANSI C и компилируется без изменений на всех платформах, где есть компилятор ANSI C. Lua также компилируется без проблем как C++.
Lua очень легко собрать и установить. В пакете есть подробные инструкции, но вот простой пример терминальной сессии, которая загружает текущий релиз Lua и собирает его на распространенных платформах:
curl -L -R -O https://www.lua.org/ftp/lua-5.4.8.tar.gz
tar zxf lua-5.4.8.tar.gz
cd lua-5.4.8
make all test
Введение
Lua — это мощный, эффективный, легковесный и встраиваемый язык сценариев. Он поддерживает процедурное программирование, объектно-ориентированное программирование, функциональное программирование, программирование на основе данных и описание данных.
Lua сочетает в себе простую процедурную синтаксис с мощными конструкциями описания данных, основанными на ассоциативных массивах и расширяемой семантике. Lua является динамически типизированным языком, работает путем интерпретации байт-кода с помощью виртуальной машины на основе регистров и имеет автоматическое управление памятью с помощью генерационного сборщика мусора, что делает его идеальным для конфигурации, сценариев и быстрого прототипирования.
Lua реализован как библиотека, написанная на чистом C, общепринятом подмножестве стандартного C и C++. Распределение Lua включает в себя хост-программу под названием lua, которая использует библиотеку Lua для предоставления полного, автономного интерпретатора Lua, как для интерактивного, так и для пакетного использования. Lua предназначен как мощный, легковесный, встраиваемый язык сценариев для любой программы, которая в этом нуждается, а также как мощный, но легковесный и эффективный автономный язык.
Как язык расширения, Lua не имеет понятия “главной” программы: он работает встраиваемым в хост-клиент, называемым программой встраивания или просто хостом. (Часто этот хост — это автономная программа lua.) Хост-программа может вызывать функции для выполнения фрагмента кода Lua, может записывать и считывать переменные Lua, а также может регистрировать функции C, которые будут вызываться кодом Lua. С помощью функций C Lua может быть расширен для работы с широким спектром различных областей, создавая таким образом настраиваемые языки программирования, которые разделяют синтаксическую основу.
Lua является бесплатным программным обеспечением и предоставляется, как обычно, без каких-либо гарантий, как указано в его лицензии. Реализация, описанная в этом руководстве, доступна на официальном сайте Lua, www.lua.org.
Как и любой другой справочный мануал, этот документ в некоторых местах может быть сухим. Для обсуждения решений, стоящих за дизайном Lua, смотрите технические статьи, доступные на сайте Lua. Для подробного введения в программирование на Lua смотрите книгу Роберто, “Программирование на Lua”.
1 - Базовые концепции языка LUA
Этот раздел описывает основные концепции языка.
2.1 – Значения и типы
Lua — это динамически типизированный язык. Это означает, что переменные не имеют типов; только значения имеют типы. В языке отсутствуют определения типов. Все значения имеют свой собственный тип.
Все значения в Lua являются значениями первого класса. Это означает, что все значения могут храниться в переменных, передаваться в качестве аргументов другим функциям и возвращаться в качестве результатов.
В Lua есть восемь основных типов: nil, boolean, number, string, function, userdata, thread и table.
- Тип
nil
имеет одно единственное значение — nil
, основное свойство которого заключается в том, что оно отличается от любого другого значения; оно часто представляет отсутствие полезного значения.
- Тип
boolean
имеет два значения: false
и true
. Оба nil и false делают условие ложным; их вместе называют ложными значениями. Любое другое значение делает условие истинным. Несмотря на свое название, false часто используется как альтернатива nil, с ключевым отличием в том, что false ведет себя как обычное значение в таблице, в то время как nil в таблице представляет отсутствующий ключ.
- Тип
number
представляет как целые числа, так и вещественные (числа с плавающей запятой), используя два подтипа: integer
и float
. Стандартный Lua использует 64-битные целые числа и числа с двойной точностью (64 бита), но вы также можете скомпилировать Lua так, чтобы он использовал 32-битные целые числа и/или числа с одинарной точностью (32 бита). Опция с 32 битами как для целых чисел, так и для чисел с плавающей запятой особенно привлекательна для малых машин и встроенных систем. (Смотрите макрос LUA_32BITS в файле luaconf.h.)
Если не указано иное, любое переполнение при манипуляциях с целыми значениями оборачивается, согласно обычным правилам арифметики с дополнительным кодом. (Другими словами, фактический результат — это уникальное представимое целое число, которое равно математическому результату по модулю 2n, где n — это количество битов целого типа.)
Lua имеет явные правила о том, когда используется каждый подтип, но также автоматически выполняет преобразования между ними по мере необходимости (см. §3.4.3). Поэтому программист может в основном игнорировать разницу между целыми числами и числами с плавающей запятой или полностью контролировать представление каждого числа.
- Тип
string
представляет собой неизменяемые последовательности байтов. Lua является 8-битным чистым языком: строки могут содержать любое 8-битное значение, включая встроенные нули (’\0’). Lua также не зависит от кодировки; он не делает предположений о содержимом строки. Длина любой строки в Lua должна помещаться в целое число Lua.
Lua может вызывать (и манипулировать) функциями, написанными как на Lua, так и на C (см. §3.4.10). Обе они представлены типом function
.
-
Тип userdata предоставляется для хранения произвольных данных C в переменных Lua. Значение userdata
представляет собой блок необработанной памяти. Существует два вида userdata
: полное userdata
, которое является объектом с блоком памяти, управляемым Lua, и легкое userdata
, которое просто представляет собой значение указателя C. У userdata
нет предопределенных операций в Lua, кроме присваивания и теста идентичности. С помощью метатаблиц программист может определить операции для значений полного userdata
(см. §2.4). Значения userdata
не могут быть созданы или изменены в Lua, только через C API. Это гарантирует целостность данных, принадлежащих хост-программе и библиотекам C.
-
Тип thread
представляет собой независимые потоки выполнения и используется для реализации корутин (см. §2.6). Потоки Lua не связаны с потоками операционной системы. Lua поддерживает корутины на всех системах, даже на тех, которые не поддерживают потоки нативно.
-
Тип table
реализует ассоциативные массивы, то есть массивы, которые могут иметь в качестве индексов не только числа, но и любое значение Lua, кроме nil и NaN. (Not a Number — это специальное значение с плавающей запятой, используемое стандартом IEEE 754 для представления неопределенных числовых результатов, таких как 0/0.) Таблицы могут быть гетерогенными; то есть они могут содержать значения всех типов (кроме nil). Любой ключ, связанный со значением nil, не считается частью таблицы. Напротив, любой ключ, который не является частью таблицы, имеет связанное значение nil.
Таблицы являются единственным механизмом структурирования данных в Lua; их можно использовать для представления обычных массивов, списков, символических таблиц, множеств, записей, графов, деревьев и т. д. Для представления записей Lua использует имя поля в качестве индекса. Язык поддерживает это представление, предоставляя a.name как синтаксический сахар для a[“name”]. В Lua есть несколько удобных способов создания таблиц (см. §3.4.9).
Как и индексы, значения полей таблицы могут быть любого типа. В частности, поскольку функции являются значениями первого класса, поля таблицы могут содержать функции. Таким образом, таблицы также могут содержать методы (см. §3.4.11).
Индексирование таблиц следует определению сырого равенства (raw equality) в языке. Выражения a[i]
и a[j]
обозначают один и тот же элемент таблицы тогда и только тогда, когда i
и j
сыро равны (то есть равны без учёта метаметодов).
В частности:
- Числа с плавающей точкой, имеющие целочисленные значения, считаются равными соответствующим целым числам (например,
1.0 == 1
).
- Чтобы избежать неоднозначностей, любое число с плавающей точкой, используемое в качестве ключа и равное целому числу, преобразуется в это целое число.
Пример:
Если написать a[2.0] = true
, то фактически в таблицу будет вставлен ключ 2
(целое число), а не 2.0
.
Ключевые моменты:
- Сырое равенство (
raw equality
) — сравнение без метаметодов.
- Целые и дробные числа —
1.0
и 1
считаются одинаковыми ключами.
- Автоматическое преобразование —
a[2.0]
превращается в a[2]
.
Это важно учитывать при работе с таблицами, чтобы избежать неожиданного поведения.
Таблицы, функции, потоки и значения (полного) userdata являются объектами: переменные на самом деле не содержат эти значения, а только ссылки на них. Присваивание, передача параметров и возвращение функций всегда манипулируют ссылками на такие значения; эти операции не подразумевают никакого рода копирования.
Функция библиотеки type возвращает строку, описывающую тип данного значения (см. type).
2.2 – Окружения и глобальное окружение
Как мы обсудим далее в §3.2 и §3.3.3, любое обращение к свободному имени (то есть имени, не привязанному к какому-либо объявлению) var синтаксически переводится в _ENV.var. Более того, каждый блок компилируется в области внешней локальной переменной с именем _ENV (см. §3.3.2), так что само _ENV никогда не является свободным именем в блоке.
Несмотря на существование этой внешней переменной _ENV и перевод свободных имен, _ENV является совершенно обычным именем. В частности, вы можете определять новые переменные и параметры с этим именем. Каждое обращение к свободному имени использует _ENV, который виден в данной точке программы, следуя обычным правилам видимости Lua (см. §3.5).
Любая таблица, используемая в качестве значения _ENV, называется окружением.
Lua хранит особое окружение, называемое глобальным окружением. Это значение хранится по специальному индексу в C-регистре (см. §4.3). В Lua глобальная переменная _G инициализируется этим же значением. (_G никогда не используется внутренне, поэтому изменение его значения повлияет только на ваш собственный код.)
Когда Lua загружает блок, значение по умолчанию для его переменной _ENV — это глобальное окружение (см. load). Таким образом, по умолчанию свободные имена в коде Lua ссылаются на записи в глобальном окружении и, следовательно, также называются глобальными переменными. Более того, все стандартные библиотеки загружаются в глобальное окружение, и некоторые функции там работают с этим окружением. Вы можете использовать load (или loadfile), чтобы загрузить блок с другим окружением. (В C вам нужно загрузить блок, а затем изменить значение его первого upvalue; см. lua_setupvalue.)
2.3 – Обработка ошибок
Несколько операций в Lua могут вызывать ошибку. Ошибка прерывает нормальный поток выполнения программы, который может продолжиться, поймав ошибку.
Lua-код может явно вызвать ошибку, вызвав функцию error. (Эта функция никогда не возвращает значение.)
Чтобы поймать ошибки в Lua, вы можете выполнить защищенный вызов, используя pcall (или xpcall). Функция pcall вызывает заданную функцию в защищенном режиме. Любая ошибка во время выполнения функции останавливает ее выполнение, и управление немедленно возвращается в pcall, который возвращает код состояния.
Поскольку Lua является встроенным языком расширения, код Lua начинает выполняться по вызову из C-кода в хост-программе. (Когда вы используете Lua в автономном режиме, приложение lua является хост-программой.) Обычно этот вызов защищен; поэтому, когда происходит незащищенная ошибка во время компиляции или выполнения блока Lua, управление возвращается в хост, который может предпринять соответствующие меры, такие как вывод сообщения об ошибке.
Каждый раз, когда возникает ошибка, объект ошибки передается с информацией об ошибке. Сам Lua генерирует только ошибки, объект ошибки которых является строкой, но программы могут генерировать ошибки с любым значением в качестве объекта ошибки. Обработка таких объектов ошибок зависит от программы Lua или ее хоста. По историческим причинам объект ошибки часто называется сообщением об ошибке, даже если он не обязательно должен быть строкой.
Когда вы используете xpcall (или lua_pcall в C), вы можете передать обработчик сообщений, который будет вызван в случае ошибок. Эта функция вызывается с оригинальным объектом ошибки и возвращает новый объект ошибки. Она вызывается до того, как ошибка развернет стек, чтобы собрать больше информации об ошибке, например, проверяя стек и создавая трассировку стека. Этот обработчик сообщений также защищен защищенным вызовом; поэтому ошибка внутри обработчика сообщений снова вызовет обработчик сообщений. Если этот цикл продолжается слишком долго, Lua прерывает его и возвращает соответствующее сообщение. Обработчик сообщений вызывается только для обычных ошибок времени выполнения. Он не вызывается для ошибок выделения памяти и для ошибок при выполнении финализаторов или других обработчиков сообщений.
Lua также предлагает систему предупреждений (см. warn). В отличие от ошибок, предупреждения никоим образом не мешают выполнению программы. Обычно они просто генерируют сообщение для пользователя, хотя это поведение можно адаптировать из C (см. lua_setwarnf).
2.4 – Метатаблицы и метаметоды
Каждое значение в Lua может иметь метатаблицу. Эта метатаблица — это обычная таблица Lua, которая определяет поведение оригинального значения при определенных событиях. Вы можете изменить несколько аспектов поведения значения, установив конкретные поля в его метатаблице. Например, когда нечисловое значение является операндом сложения, Lua проверяет наличие функции в поле __add
метатаблицы значения. Если она находит такую функцию, Lua вызывает ее для выполнения сложения.
Ключом для каждого события в метатаблице является строка с именем события, предшествующая двумя подчеркиваниями; соответствующее значение называется метазначением. Для большинства событий метазначение должно быть функцией, которая затем называется метаметодом. В предыдущем примере ключом является строка “__add
”, а метаметодом — функция, выполняющая сложение. Если не указано иное, метаметодом может быть любое вызываемое значение, которое является либо функцией, либо значением с метаметодом __call
.
Вы можете запросить метатаблицу любого значения, используя функцию getmetatable
. Lua запрашивает метаметоды в метатаблицах, используя сырой доступ (см. rawget).
Вы можете заменить метатаблицу таблиц, используя функцию setmetatable
. Вы не можете изменить метатаблицу других типов из кода Lua, кроме как с помощью библиотеки debug
(§6.10).
Таблицы и полное userdata
имеют индивидуальные метатаблицы, хотя несколько таблиц и userdata
могут делить свои метатаблицы. Значения всех других типов делят одну единственную метатаблицу на тип; то есть, существует одна единственная метатаблица для всех чисел, одна для всех строк и т. д. По умолчанию значение не имеет метатаблицы, но библиотека строк устанавливает метатаблицу для типа строк (см. §6.4).
Подробный список операций, контролируемых метатаблицами, приведен ниже. Каждое событие идентифицируется своим соответствующим ключом. По соглашению, все ключи метатаблиц, используемые Lua, состоят из двух подчеркиваний, за которыми следуют строчные латинские буквы.
-
__add
: операция сложения (+). Если любой операнд для сложения не является числом, Lua попытается вызвать метаметод. Он начинает с проверки первого операнда (даже если это число); если этот операнд не определяет метаметод для __add
, то Lua проверит второй операнд. Если Lua находит метаметод, он вызывает этот метаметод с двумя операндами в качестве аргументов, и результат вызова (приведенный к одному значению) является результатом операции. В противном случае, если метаметод не найден, Lua вызывает ошибку.
-
__sub
: операция вычитания (-). Поведение аналогично операции сложения.
-
__mul
: операция умножения (*). Поведение аналогично операции сложения.
-
__div
: операция деления (/). Поведение аналогично операции сложения.
-
__mod
: операция взятия остатка (%) . Поведение аналогично операции сложения.
-
__pow
: операция возведения в степень (^). Поведение аналогично операции сложения.
-
__unm
: операция отрицания (унарный -). Поведение аналогично операции сложения.
-
__idiv
: операция целочисленного деления (//). Поведение аналогично операции сложения.
-
__band
: побитовая операция И (&). Поведение аналогично операции сложения, за исключением того, что Lua попытается вызвать метаметод, если любой операнд не является ни целым числом, ни числом с плавающей запятой, приводимым к целому (см. §3.4.3).
-
__bor
: побитовая операция ИЛИ (|). Поведение аналогично побитовой операции И.
-
__bxor
: побитовая операция исключающего ИЛИ (двойной ~). Поведение аналогично побитовой операции И.
-
__bnot
: побитовая операция НЕ (унарный ~). Поведение аналогично побитовой операции И.
-
__shl
: побитовый сдвиг влево («). Поведение аналогично побитовой операции И.
-
__shr
: побитовый сдвиг вправо (»). Поведение аналогично побитовой операции И.
-
__concat
: операция конкатенации (..). Поведение аналогично операции сложения, за исключением того, что Lua попытается вызвать метаметод, если любой операнд не является ни строкой, ни числом (которое всегда приводимо к строке).
-
__len
: операция длины (#). Если объект не является строкой, Lua попытается вызвать его метаметод. Если метаметод существует, Lua вызывает его с объектом в качестве аргумента, и результат вызова (всегда приведенный к одному значению) является результатом операции. Если метаметода нет, но объект является таблицей, то Lua использует операцию длины таблицы (см. §3.4.7). В противном случае Lua вызывает ошибку.
-
__eq
: операция равенства (==). Поведение аналогично операции сложения, за исключением того, что Lua попытается вызвать метаметод только тогда, когда сравниваемые значения являются либо обеими таблицами, либо обоими полными userdata и они не равны по примитивному сравнению. Результат вызова всегда преобразуется в логическое значение.
-
__lt
: операция “меньше чем” (<). Поведение аналогично операции сложения, за исключением того, что Lua попытается вызвать метаметод только тогда, когда сравниваемые значения не являются ни обоими числами, ни обеими строками. Более того, результат вызова всегда преобразуется в логическое значение.
-
__le
: операция “меньше или равно” (<=). Поведение аналогично операции “меньше чем”.
-
__index
: операция доступа по индексу table[key]. Это событие происходит, когда table не является таблицей или когда ключ отсутствует в таблице. Метазначение ищется в метатаблице таблицы. Метазначение для этого события может быть либо функцией, либо таблицей, либо любым значением с метазначением __index
. Если это функция, она вызывается с таблицей и ключом в качестве аргументов, и результат вызова (приведенный к одному значению) является результатом операции. В противном случае окончательный результат — это результат индексации этого метазначения с ключом. Эта индексация является обычной, а не сырой, и, следовательно, может вызвать другой метаметод __index
.
-
__newindex
: операция присваивания по индексу table[key] = value. Как и событие индексации, это событие происходит, когда table не является таблицей или когда ключ отсутствует в таблице. Метазначение ищется в метатаблице таблицы. Как и в случае с индексацией, метазначение для этого события может быть либо функцией, либо таблицей, либо любым значением с метазначением __newindex
. Если это функция, она вызывается с таблицей, ключом и значением в качестве аргументов. В противном случае Lua повторяет присваивание индексации для этого метазначения с тем же ключом и значением. Это присваивание является обычным, а не сырым, и, следовательно, может вызвать другой метаметод __newindex
.
Каждый раз, когда вызывается метазначение __newindex
, Lua не выполняет примитивное присваивание. Если это необходимо, сам метаметод может вызвать rawset
для выполнения присваивания.
__call
: операция вызова func(args)
. Это событие происходит, когда Lua пытается вызвать значение, не являющееся функцией (то есть func не является функцией). Метаметод ищется в func. Если он присутствует, метаметод вызывается с func в качестве первого аргумента, за которым следуют аргументы оригинального вызова (args). Все результаты вызова являются результатами операции. Это единственный метаметод, который позволяет возвращать несколько результатов.