# Оглавление

- 1 - Введение в Lua
- 2 – Описание языка Lua
- 3 - Использование Lua в программе GEDKeeper

# 1 - Введение в Lua

Lua является типичным процедурным языком программирования, предоставляющим широкие возможности для разработки. Это мощный и простой язык, обладающий всеми необходимыми выразительными средствами.

Будучи языком для создания расширений, Lua не имеет понятия основной программы, он работает в среде исполнения других программ, сокращенно называемых хост-программами. В данном случае хост-программой является программа GEDKeeper. Хост-программа позволяет запускать части кода (далее скрипты), написанные на Lua. Благодаря возможности расширения функциями хост-программы, Lua может применяться для решения широкого круга задач.

Lua является свободно распространяемым программным средством, поэтому предоставляется без каких-либо гарантий в соответствии с лицензией. Версия lua 5.1, которая описана в данном Руководстве, доступна на официальном сайте Lua www.lua.org.

Как и многие подобные Руководства, этот документ написан в формальном стиле. Для получения более подробной информации об особенностях применения языка рекомендуем обратиться к технической документации, доступной на официальном сайте Lua. Хорошим подспорьем в работе может оказаться книга Роберта Иерусалимского (Roberto Ierusalimschy) «Программирование на Lua», второе издание (Programming in Lua (Second Edition)).

# 2 – Описание языка Lua

В этой главе описываются элементы языка, способы их комбинирования и значение языковых конструкций.

Конструкции языка будут вводится с использованием расширенной BNF, где запись {a} означает 0 или более элементов a, а запись [a] означает его необязательное вхождение. Нетерминальные символы отображаются обычным шрифтом, ключевые слова выделяются жирным шрифтом kword, все остальные терминальные символы заключаются в апострофы `=?.

## 2.1 – Лексические соглашения

Именами (идентификаторами) в Lua могут быть любые строки из букв, цифр и символа подчеркивания, не начинающиеся с цифры. Это правило типично для большинства языков программирования. Идентификаторы используются для именования переменных и таблиц значений.

Следующие ключевые слова зарезервированы и не могут быть использованы в именах:

    and      break    do	 else       elseif
    end      false    for    function   if
    in       local    nill   not        or
    repeat   return   then   true       until   while

Lua является языком, чувствительным к регистру символов: and – ключевое слово, тогда как And и AND – два разных допустимых идентификатора. По соглашению, имена, начинающиеся с символа подчеркивания и записанные в верхнем регистре (например, _VERSION), зарезервированы для использования в качестве внутренних глобальных переменных, используемых Lua.

В следующих строках показаны другие допустимые символы:

    +   *   /   %   ^   /   #
    ==   ~=   <=   >=   <   >   =
    (   )   {   }   [   ]
    ;   :   ,   .    ..   ...

Литеральные строки должны быть заключены в одинарные или двойные кавычки и могут содержать следующие С-подобные escape-последовательности: '\a' («звонок»), '\b' («забой»), '\f' («перевод страницы»), '\n' («перевод на новую строку»), '\r' («возврат каретки»), '\t' («горизонтальная табуляция»), '\v' («вертикальная табуляция»), '\\\"' («двойная кавычка»), and'\'' (апостроф [«одинарная кавычка»]). Кроме того, обратный слеш ставится перед концом строки в редакторе, когда для удобства набора длинные непрерывные строки записываются в несколько строк. Символ в строке также может быть представлен своим кодом с помощью escape-последовательности \ddd, где ddd- последовательность из не более чем трех цифр. (Заметим, что если после символа, записанного с помощью своего кода, должна идти цифра, то код символа в escape-последовательности должен содержать ровно три цифры). Строки в Lua могут содержать любые 8-битные значения, включая ноль, который записывается как '\0'.

Чтобы поместить одни двойные кавычки, символы новой строки, обратный слэш, или нулевой символ в строку, ограниченную двойными кавычками вы должны использовать escape-последовательности. Любой другой символ может быть напрямую включен в строку.

Числовая константа может быть записана с опциональной десятичной частью и опциональной десятичной экспонентой. Lua также принимает целые шестнадцатеричные константы, с префиксом 0x. Примеры правильных числовых констант

         3   3.0   3.1416   314.16e-2   0.31416E1   0xff   0x56

Комментарий начинается с двойного дефиса (--) везде за пределами строки. Если текст непосредственно после -- не содержит длинную открывающую скобку [, комментарий является коротким, который продолжается до конца строки. Иначе, это длинный комментарий, который продолжается пока не встретится закрывающая длинная скобка ]. Длинный комментарий часто встречается для временной деактивации кода.

## 2.2 – Значения и типы

Lua представляет собой язык с динамическим определением типов данных. Переменная языка может содержать значения любого типа. Возможности определения пользовательских типов данных отсутствуют. Все значения в Lua могут храниться в переменных, использоваться в качестве аргументов при вызове функций и возвращаться в виде результата их выполнения.

В Lua восемь основных типов: nil (неопределенный), boolean (логический), number (числовой), string (строковый), function (функция), userdata (пользовательские данные), thread (поток), и table (таблица). Nil - это тип значения nil [пустое значение], главное свойство которого – отличаться от всех остальных значений и обозначать отсутствие пригодного значения. К типу Boolean относятся значения false (ложь) и true (истина). Значения nil и false считаются ложными, любое другое значение считается истинным. К типу Number относятся вещественные числа (двойной точности с плавающей запятой). Тип String обозначает массивы символов. Строки Lua могут содержать любые 8 битные символы, включая ноль ('\0') (см. §2.1).

В Lua можно использовать функции, написанные на Lua или предоставляемые хост-программой (см. §2.5.8).

Тип userdata (пользовательские данные) позволяет хранить любые данных из хост-программы в переменных Lua. Значение этого типа является ссылкой на блок физической памяти и не имеет предопределенных операций в Lua, за исключением присваивания и проверки на равенство. Однако, используя метатаблицы, программист может определить операции над значениями этого типа (см. §2.8). Значения типа userdata не могут быть созданы или изменены непосредственно в Lua, это возможно только с помощью хост-программе. Такой подход гарантирует целостность данных, принадлежащих ведущей программе.

Тип table (таблица) определяет ассоциативные массивы. Такие массивы могут индексироваться не только числами, но и любыми значениями (за исключением nil). Таблица может содержать значения сразу нескольких типов (кроме nil). Таблицы представляют собой единственный механизм структурирования данных в Lua; они могут использоваться как простые массивы, таблицы символов, множества, поля записей, деревья и так далее. Для представления словарей Lua использует имя поля в качестве индекса таблицы. Представление в виде a.name считается тождественным представлению a["name"]. В Lua есть несколько способов создания таблиц (см. §2.5.7).

Индексы и значения полей таблицы могут быть любого типа (кроме nil). В частности, так как функции являются значениями встроенного типа, поля таблицы могут содержать и функции. Таким образом, таблицы могут хранить методы methods (см. §2.5.9).

Переменные типа table, function, thread и userdata не содержат самих данных, в них хранятся только ссылки на соответствующий объект. Присваивание, передача параметров и возврат результата из функции оперируют только ссылками на значения, эти операции никогда не ведут к созданию копий.

Библиотечная функция type возвращает строку, описывающую тип данного значения.

### 2.2.1 – Приведение типов

Lua обеспечивает автоматическое преобразование между строковыми и числовыми значениями в процессе выполнения. Любая арифметическая операция, применяемая к строке, пытается преобразовать эту строку в соответствующее число по обычным правилам приведения. Когда же число используется там, где ожидается строка, это число преобразуется в строку в произвольном подходящем формате. Так что для получения какого-то конкретного представления числа в строке необходимо использовать функцию format из библиотеки работы со строками (см. string. format ).

## 2.3 - Переменные

Переменные используются для хранения значений в процессе выполнения программы. В Lua есть три вида переменных: глобальные, локальные и поля таблиц.

Отдельный идентификатор может обозначать глобальную или локальную переменную (либо формальный параметр функции, что является частным случаем локальной переменной) :

    var ::= Name

Где Name – идентификатор, определяемый в соответствии с §2.1.

Любая переменная считается глобальной, если она явно не объявлена как локальная (см. §2.4.7). Локальные переменные существуют в лексическом контексте: локальные переменные доступны функциям, определенным внутри этого контекста (см. §2.6).

До первого явного присвоения значением переменной является nil.

Квадратные скобки используются для доступа к элементу таблицы по индексу:

    var ::= prefixexp '[' exp ']'

Запись var.Name аналогична записи var["Name"]:

    var ::= prefixexp '.' Name

## 2.4 – Операторы

В Lua поддерживается стандартный набор операторов, почти как в Pascal или C. Он состоит из операторов присваивания, операторов управления потоком исполнения, вызова функций и описания переменных.

### 2.4.1 – Порции

Единица исполнения Lua называется chunk (порция). Порция – это любая последовательность операторов Lua. Операторы в порции могут разделяться точкой с запятой:

    chunk ::= {stat [';']}

Пустого оператора в языке нет, поэтому выражение ';;' не допустимо.

Lua воспринимает порцию как неименованную функцию с произвольным набором параметров (см. §2.5.9). Порция может определять локальные переменные и возвращать значения.

### 2.4.2 – Блоки

Блок это список операторов; синтаксически блок тождественно равен порции (chunk) :

    block ::= chunk

Блок операторов может быть явно ограничен, таким образом представляется составной оператор:

    stat ::= do block end

С помощью составных операторов можно ограничивать области видимости локальных переменных. Также составные операторы используются в циклах и условном операторе (см. §2.4.4).

### 2.4.3 – Присваивание

Lua поддерживает параллельное присваивание. В общем случае, оператор присваивания выглядит как список переменных, символ ‘=’ и список выражений. Элементы списков указываются через запятую:

    	stat ::= varlist1 '=' explist1

    	varlist1 ::= var {',' var}

    	explist1 ::= exp {',' exp}

Выражения (exp) рассмотрены в §2.5.

Перед выполнением присваивания список переменных согласовывается по длине со списком выражений. Если список справа длиннее, то его лишние элементы просто отбрасываются. Если короче, то недостающие позиции дополняются значениями nil. Если список операторов оканчивается вызовом функции, то перед согласованием все возвращаемые оттуда значения вставляются в правый список (за исключением случаев, когда вызов взят в скобки; смотрите §2.5).

Перед выполнением присваивания вычисляется значение всех выражений. Код

    	i = 3

    	i, a[i] = i+1, 20

означает, что переменной a[3] присваивается значение 20, потому что i в выражении a[i] имеет то же самое значение, что и в момент вычисления выражения i+1. Аналогично, строка

    	x, y = y, x

является простым способом обмена значениями двух переменных (при «традиционном» способе требуется дополнительная переменная).

### 2.4.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 в двух вариантах (см. §2.4.5).

Логическое выражение в управляющих конструкциях может возвращать любое значение. Значения false и nil считаются ложными. Все остальные значения считаются истинными (в том числе значение 0 и пустая строка!).

Цикл repeat–until оканчивается условием, идущим следом за until, поэтому в условии можно ссылаться на локальные переменные, описанные внутри цикла.

Выражение return используется для того, чтобы возвратить значения из функции или порции. Синтаксис оператора return позволяет функции или порции вернуть несколько значений:

    	stat ::= return [explist1]

Оператор break используется для досрочного выхода из циклов while, repeat и for:

    	stat ::= break

Break прерывает цикл, в теле которого встречается, внешние циклы продолжают выполнение.

Return (или break) должен быть последним оператором в блоке (иначе следующие за ним операторы никогда не выполнятся). Если действительно необходимо вставить return или break в середину блока, то следует применить составной оператор, например, do return end и do break end.

### 2.4.5 – Оператор For

Оператор for допускает простую и расширенную формы записи.

В простой форме for выполняет блок кода до тех пор, пока переменная цикла, изменяющаяся в арифметической прогрессии, не достигнет установленного порога.

    	stat ::= for Name ' = ' exp1 ',' exp2 [', ' exp3] do block end

block повторяется для переменной цикла name начиная со значения выражения exp1, до тех пор пока выполняется выражение exp2 с шагом выражения exp3.

Таким образом, запись

    	for v = 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

    			local v = var

    			block

    			var = var + step
    		end
        end

Обратите внимание, что:

- Все три логических выражения вычисляются только один раз перед началом цикла, причем полученные значения должны быть числами.
- var, limit, и step - неявные переменные, мы условно именовали их здесь для объяснения логики работы
- Если выражение step (шаг) отсутствует, то по умолчанию используется 1
- Для выхода из цикла for используйте break.
- Переменная v является локальной для цикла; вы не сможете использовать ее значение после выхода из цикла for. Если вам необходимо значение этой переменной, присвойте его другой переменной перед выходом из цикла.

Расширенная форма оператора for реализована с использованием функций итераторов. На каждом обороте для получения нового значения переменной цикла вызывается итератор. Цикл заканчивается, когда итератор вернет nil. Синтаксис расширенного оператора for:

    	stat ::= for namelist in explist1 do block end

    	namelist ::= Name {', '~ Name}

Запись

    	for var_1, ···, var_n in explist do block end

можно представить как

    	do
    		local f, s, var = explist

    		while true do
    			local var_1, ···, var_n = f(s, var)

    			var = var_1

    			if var == nil then break end

    			block
    		end
        end

Заметим, что

- explist вычисляется только однажды. Его результатом является функция-итератор, таблица состояний и начальное значение индекса
- f, s, и var неявные переменные, именованные здесь для примера
- Выйти из цикла можно с помощью оператора break.
- Переменная var_i является локальной; вы не сможете использовать ее значение после выхода из for. Если вам необходимо ее значение, заранее сохраните его в другой переменной.

### 2.4.6 – Вызов функции

Для создания побочных эффектов может быть полезен вызов функций, используемый в качестве оператора:

    	stat ::= functioncall

В этом случе все возвращаемые значения отбрасываются. Вызовы функций рассматриваются в §2.5.8.

### 2.4.7 – Локальные объявления

Локальные переменные могут быть объявлены где угодно внутри блока. Объявление может включать инициализацию:

    	stat ::= local namelist ['=' explist1]

Инициализация обладает всеми свойствами операции присваивания (в том числе параллельностью) (см. §2.4.3). По умолчанию все переменные инициализируются значением nil.

Порция является блоком (см. §2.4.1), поэтому локальные переменные могут быть объявлены вне любого явно заданного блока. Областью действия таких локальных переменных являются границы порции.

Правила видимости для локальных переменных рассмотрены в §2.6.

## 2.5 - Выражения

Выражениями в Lua являются следующие конструкции:

    	exp ::= prefixexp

    	exp ::= nil | false | true

    	exp ::= Number

    	exp ::= String

    	exp ::= function

    	exp ::= tableconstructor

    	exp ::= '...'

    	exp ::= exp binop exp

    	exp ::= unop exp

    	prefixexp ::= var | functioncall | '(' exp ')'

Числа и символьные строки рассмотрены в §2.1; переменные - в §2.3; описания функций - в §2.5.9; вызовы функций - в §2.5.8; конструкторы таблиц - в §2.5.7. Неявные аргументы, обозначаемые ‘...', могут использоваться только внутри соответственно заданной функции; смотрите §2.5.9.

К бинарным операциям (binop в формальном определении выражения) относятся арифметические (см. §2.5.1), операции сравнения (§2.5.2), булевские (§2.5.3) и операции конкатенации (смотреть §2.5.4). Унарными являются унарный минус (§2.5.1), отрицание not (§2.5.3) и операция получения длины # (§2.5.5).

Результат вызова функций и неявные параметры могут содержать несколько значений. Если при этом они используются в качестве оператора (§2.4.6) (только для функций), то все возвращаемые значения отбрасываются. Если это последний (или единственный) элемент в списке выражений, то никакая корректировка не проводится (если вызов не взят в скобки). В остальных случаях Lua приводит возвращаемый список к одному элементу, отбрасывая все значения кроме первого.

Далее несколько примеров:

    	f()                -- результат функции отбрасывается

    	g(f(), x)          -- берется первое значение из списка - результата вызова f()

    	g(x, f())          -- g получает x и все значения, полученные из f()

    	a,b,c = f(), x     -- берется первый элемент результата вызова f()(и c получает nil)

    	a,b = ...          -- a получает первый параметр из ..., b - второй (причем а и b могут получить nil, если в качестве неявных параметров ничего не передано)

    	a,b,c = x, f()     -- 2 результата из f()

    	a,b,c = f()        -- 3 результата из f()

    	return f()         -- возвращает все значения из f()

    	return ...         -- возвращает все полученные неявные аргументы

    	return x,y,f()     -- вернет a, b и все, что вернет f()

    	{f()}              -- создаст список со всем результатами вызова f()

    	{...}              -- создаст список со всеми неявными параметрами

    	{f(), nil}         -- 1 результат из f()

Выражение, заключенное в скобки, всегда возвращает только одно значение. Таким образом, (f(x,y,z)) всегда даст единственное значение, даже если f возвращает несколько. (Значение (f(x,y,z)) это первое значение, полученное из f, или nil, если f не возвращает значений.)

### 2.5.1 – Арифметические операции

Lua поддерживает обычные арифметические операции: двоичные + (сложение), - (вычитание), * (умножение), / (деление), % (остаток от деления), и ^ (возведение в степень); а также унарный минус - (изменение знака числа). Если операнды являются числами или строками (которые могут быть преобразованы в числа §2.2.1), то операции выполняются обычным образом. Возведение в степень работает для любого показателя степени. Например, x^(-0.5) подсчитывает величину, обратную квадратному корню из x.

### 2.5.2 – Операции сравнения

Операции сравнения в Lua:

    	==    ~=    <     >     <=    >=

Эти операции всегда возвращают false или true.

Сравнение на равенство (==) сначала сравнивает типы операндов. Если типы различны, то результатом будет false. Иначе сравниваются значения операндов. Числа и строки сравниваются обычным способом. Объекты (таблицы, пользовательские данные, потоки и функции) сравниваются по ссылке: два объекта считаются равными, только если они являются одним и тем же объектом. Создаваемый объект (таблица, пользовательские данные, поток или функция) не может быть равен ни одному из уже существующих.

Правила преобразования из §2.2.1 НЕ работают в сравнениях на равенство. Например, "0"==0 вернет false, а t[0] и t["0"] обозначают различные записи в таблице.

Оператор ~= прямо противоположен оператору равенства (==).

Операторы сравнения на больше-меньше работают следующим образом. Если оба параметра - числа, то они сравниваются как обычно. Если оба параметра строки, то их значения сравниваются в соответствии с лексикографическим порядком. Во всех остальных ситуациях будет вызван метаметод (в данном руководстве не рассматривается).

### 2.5.3 – Логические операции

В Lua это операции and (и), or (или), и not (не). Так же, как и в управляющих конструкциях (§2.4.4), все логические операции рассматривают false и nil как ложь, а все остальное как истину.

Операция отрицания not всегда возвращает false или true. Операция конъюнкции and возвращает свой первый параметр, если его значение false или nil; в противном случае and возвращает второй параметр. Оператор дизъюнкции or возвращает первый параметр, если его значение отлично от nil и false; в противном случае or возвращает второй параметр. Оба оператора вычисляют второй операнд только в случае необходимости.

Примеры:

        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

(В данном руководстве, --> указывает на результат выражения.)

### 2.5.4 - Конкатенация

Оператор конкатенации (соединения) строк в Lua обозначается двумя точками ('..'). Если оба операнда являются строками или числами, то они будут преобразованы в строки согласно правилам §2.2.1. Иначе будет вызван метаметод (в данном руководстве не рассматривается).

### 2.5.5 – Получение длины

Операция получения длины обозначается унарным #. В результате применения операции к строке возвращается количество байт (в обычном понимании это длина строки, в которой каждый символ занимает 1 байт).

Длиной таблицы t считается любой целый индекс n такой, что t[n] не равен nil, а t[n+1] равно nil. Кроме того, если t[ 1] равен nil, то #t = 0. Для регулярных массивов от 1 до n, не содержащих значений nil, длиной является n, то есть индекс последнего значения. Если в массиве присутствуют "дыры" (т.е., значения nil между ненулевыми значениями), то значением #t является индекс элемента, непосредственно предшествующего элементу nil (поэтому любое значение nil по сути означает конец массива).

### 2.5.6 – Приоритет операций

Приоритет операций Lua показан на таблице ниже. Самым высоким приоритетом обладает операция возведения в степень, далее по убыванию:

        or
        and
        <     >     <=    >=    ~=    ==
        ..
        +     -
        *     /     %
        not   #     - (unary)
        ^

Как обычно, для изменения порядка вычисления выражений вы можете использовать скобки. Конкатенация ('..') и возведение в степень ('^') - правоассоциативные операторы. Все остальные бинарные операторы левоассоциативные.

### 2.5.7 – Конструкторы таблиц

Конструкторы таблиц тоже относятся к выражениям. Обработка любого встречающегося в коде конструктора ведет к созданию новой таблицы. С помощью конструкторов можно создать как пустые, так и частично либо полностью заполненные таблицы. Полное описание синтаксиса конструкторов:

        tableconstructor::= '{' [fieldlist] '}'

        fieldlist::= field {fieldsep field} [fieldsep]

        field::= '[' exp ']' '=' exp | Name '=' exp | exp

        fieldsep::= ',' | ';'

Каждое поле вида [exp1] = exp2 добавляет в новую таблицу значение exp2 с ключом exp1. Поле вида 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"

    		t[2] = "y"

    		t.x = 1

    		t[3] = f(x)

    		t[30] = 23

    		t[4] = 45

    		a = t

    	end

Если последнее поле в списке задано в форме exp, и exp – это вызов функции или неопределенный список параметров, то все значения, возвращаемые этим выражением, последовательно включаются в этот список (§2.5.8). Чтобы этого избежать, необходимо заключить вызов функции (или список неопределенных параметров) в скобки (§2.5).

Список полей может оканчиваться разделителем, что улучшает читабельность машинно-генерируемого кода.

### 2.5.8 – Вызовы функций

Вызовы функций в Lua имеют следующий синтаксис:

    	functioncall ::= prefixexp args

В вызове функции сначала вычисляются префиксное выражение и аргументы. Если значение префиксного выражения имеет тип function, то эта функция будет вызвана с указанными аргументами. В противном случае вызывается метаметод (в данном руководстве не рассматривается).

Форма записи

    	functioncall ::= prefixexp ':' Name args

может использоваться для вызова "методов". Запись v:name (args) синтаксически аналогична записи v.name ( v,args), только v вычисляется один раз.

Аргументы описываются следующим образом:

    	args ::= '(' [explist1] ')'

        args ::= tableconstructor

        args ::= String

Все выражения вычисляются перед вызовом. Вызов в форме f{fields} синтаксически аналогичен f ({ fields}); то есть список аргументов является по сути новой таблицей. Вызов в форме f'string' (или f"string" или f[[string]]) синтаксически равен f('string' ); в данном случае список аргументов - единственная символьная строка.

Исключением в довольно свободном синтаксисе Lua является правило, по которому нельзя переходить на новую строку непосредственно перед символом '(' в вызове функции. Это ограничение позволяет избежать некоторой двусмысленности в языке. Если вы напишите

    	a = f

        (g).x(a)

Lua трактует эту запись как выражение a = f ( g).x(a). Поэтому, если вам нужно 2 выражения, вы должны добавить точку с запятой между ними. Если вы действительно хотите вызвать f, вы необходимо убрать переход на новую строку перед (g).

Вызов в форме return functioncall называется концевым вызовом. Lua также поддерживает концевой вызов «себя» (или рекурсивный концевой вызов): в этом случае вызванная функция использует стек вызывающей функции. Поэтому количество вложенных концевых вызовов может быть любым. Заметим только, что концевой вызов стирает отладочную информацию о вызывающей функции. Синтаксис концевого вызова допускает только единичный вызов функции после оператора return. Таким образом, return вернет в точности тот результат, что вернет вызов функции. Ни один из представленных ниже примеров не является допустимым концевым вызовом:

        return (f(x))        -- список-результат обрезается

        return 2 * f(x)      -- удвоение результата функции

        return x, f(x)       -- возвращается несколько значений

        f(x); return         -- результат вызова отбрасывается

        return x or f(x)     -- список-результат обрезается

### 2.5.9 – Объявление функций

Синтаксис объявления функций:

        function ::= function funcbody

        funcbody ::= '(' [parlist1] ')' block end

Или в упрощенном виде

        stat ::= function funcname funcbody

        stat ::= local 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

(Разница проявится в том случае, если в теле функции используется имя этой функции, например при рекурсивном вызове)

Объявление функции является выполняемым выражением, его результатом будет значение типа function. Когда Lua прекомпилирует порцию, тела всех упоминающихся в ней функций также прекомпилируются. Таким образом, всякий раз, когда Lua обрабатывает объявление функции, функция уже конкретизирована (или замкнута). Этот конкретный экземпляр функции (или замыкание) и является конечным значением выражения «объявление функции». Различные экземпляры одной и той же функции могу ссылаться на различные внешние локальные переменные и иметь различные таблицы окружения.

Параметры функции фактически являются локальными переменными, которые инициализированы входными значениями:

    	parlist1 ::= namelist [',' '...'] | '...'

В момент вызова функции длина списка передаваемых параметров приводится в соответствие спецификации, если это не функция с неопределенным количеством параметров. Для функций с неопределенным количеством параметров такая коррекция не проводится; все входные параметры попадают в функцию в виде неопределенного выражения, которое также обозначается с тремя точками. Значением этого выражения является список всех полученных входных параметров, как в случае множественного результата функции. Если неопределенное выражение используется внутри другого выражения или в середине списка выражений, то его значение-список урезается до одного элемента. Если это выражение стоит в конце списка выражений, урезания не происходит (если конечно вызов не заключен в круглые скобки).

Рассмотрим следующие объявления:

        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, ... -->  (ничто)

    	g(3, 4)          a=3, b=4,   ... -->  (ничто)

    	g(3, 4, 5, 8)    a=3, b=4,   ... -->  5  8

    	g(5, r())        a=5, b=1,   ... -->  2  3

Результаты возвращаются из функции оператором return (см. §2.4.4). Если управление достигает конца функции, а оператор return не встретился, то функция завершается и ничего не возвращает.

Синтаксис с двоеточием ‘:’ используется для определения методов. Эти функции неявно получают параметр self в качестве первого аргумента. Таким образом, выражение

    	function t.a.b.c:f (params) body end

аналогично

    	t.a.b.c.f = function (self, params) body end

## 2.6 – Области видимости

Lua язык с лексическим разграничением областей видимости. Область видимости переменной начинается первым выражением после ее объявления и действует до конца блока, в котором это объявление встречается. Рассмотрим следующий пример:

    	x = 10                	-- глобальная переменная variable

    	do                    	-- начало блока
    		local x = x         -- объявление локальной переменной

    		print(x)            --> 10

    		x = x+1

    		do                  -- начало вложенного блока
    			local x = x+1   -- другая локальная 'x'

    			print(x)        --> 12
    		end

    		print(x)            --> 11
    	end

    	print(x)              	--> 10  (глобальная переменная)

Отметим, что в объявлении local x = x локальная переменная объявляется еще не в области своей видимости, поэтому присваивается именно внешняя переменная.

В соответствии с правилами лексического разграничения областей видимости, локальные переменные доступны в функциях, определенных внутри их области видимости. Локальная переменная, используемая в таких функциях, называется внешней локальной переменной (по отношению к определенной внутри ее области видимости функции).

Обработка каждого объявления local ведет к созданию новой локальной переменной. Рассмотрим следующий пример:

    	a = {}

    	local x = 20

    	for i=1,10 do

    		local y = 0

    		a[i] = function () y=y+1; return x+y end

    	end

Цикл создает 10 экземпляров функции, в которых используются различные переменные y и один и тот же x.

## 2.7 – Обработка ошибок

Поскольку Lua является языком расширений, работа Lua начинается с момента вызова в хост-программе функции из Lua-библиотеки (lua_pcall). При возникновении ошибки в процессе компиляции или выполнения Lua управление возвращается в хост-программу, где и осуществляется ее обработка (например, вывод сообщения о ошибке).

Lua-код может явно генерировать ошибку, вызывая функцию error. Если вам нужно перехватывать ошибки в самом Lua, вы можете использовать функцию pcall.

## 2.8 – Сборщик мусора

Lua осуществляет автоматическое управление памятью. Это означает, что вам не нужно думать о выделении памяти при создании новых объектов и ее освобождении, когда объект становится ненужным. Lua время от времени автоматически запускает процедуру сборки мусора для удаления устаревших объектов (то есть объектов, которые более недоступны из Lua). Сборщик мусора обрабатывает все объекты Lua: таблицы, данные типа userdata, функции, потоки и строки.

# 3 - Использование Lua в программе GEDKeeper

## 3.1 - Соглашения

Типы данных:
void - пустой аргумент или результат функции;
int - целочисленный аргумент или результат функции;
string - строковый аргумент или результат функции;
boolean - логический аргумент или результат функции;

Внимание: Все списки базы данных нумеруются от нуля, поэтому для перебора всех записей базы данных следует использовать, к примеру, такую конструкцию:

    for i = 0, get_records_count() - 1 do -- т.е. число элементов "-1"
    	...
    end

## 3.2 - Структуры данных

Все действия над структурами данных базе выполняются посредством указателей (pointer) на эти структуры. Указатель - специальная переменная, указывающая программе на ту или иную структуру в памяти.

Указатели на структуры имеют следующие типы:

- pointer (простой указатель)
  - record_pointer (указатель на запись)
    - individual_pointer (указатель на запись персоны)
    - family_pointer (указатель на запись семьи)
    - note_pointer (указатель на запись заметки)
    - source_pointer (указатель на запись источника)
    - repository_pointer (указатель на запись архив)
    - multimedia_pointer (указатель на запись мультимедиа-объекта)
    - group_pointer (указатель на запись группы)
    - research_pointer (указатель на запись исследования)
    - task_pointer (указатель на запись задачи)
    - commumication_pointer (указатель на запись коммуникаций)
    - location_pointer (указатель на запись места)
- struct_pointer (указатель на структуру - вложенный объект записи)
  - association_pointer (указатель на ассоциацию)
  - event_pointer (указатель на событие/факт)

## 3.3 - Функции (API)

void print(string text)
  вывод строки text;
void progress_init(int length, string title)
  показывает окно прогрессии, где length - число этапов прогрессии, title - заголовок;
void progress_done()
  скрывает окно прогрессии;
void progress_step()
  увеличивает число выполненных этапов прогрессии на один;
int strpos(string substr, string str)
  возвращает индекс первого вхождения подстроки substr в строке str;
void update_view()
  полностью обновляет вид всех списков (бывает необходимо после массовой обработки данных);
string select_file()
  открывает диалог выбора файла и возвращает строку с именем файла
string select_new_file()
  открывает диалог выбора сохраняемого файла и возвращает строку с именем файла
int get_records_count()
  возвращает число записей в БД;
record_ptr get_record(int index)
  возвращает запись базы данных по заданному индексу. Внимание: записи различных типов располагаются в БД в произвольном порядке - так, как они вводились. Для обработки всех записей определенного типа, необходимо проверять их тип.
int get_record_type(record_ptr)
  возвращает код - числовую константу, определяющую тип записи; возможные варианты:
  - rtNone - тип не определяется либо неизвестен
  - rtIndividual - персональная запись
  - rtFamily - семья
  - rtNote - заметка
  - rtMultimedia - мультимедийная запись
  - rtSource - запись источника
  - rtRepository - архив или хранилище источников
  - rtGroup - группа
  - rtResearch - исследование
  - rtTask - задача
  - rtCommunication - коммуникации/переписка
  - rtLocation - место
  - rtSubmission - [зарезервировано (пока не используется)]
  - rtSubmitter - исследователь базы данных
string get_record_type_name(int type)
  возвращает строку с идентификатором типа записи по его коду.
string get_record_xref(record_ptr)
  возвращает строку - идентификатор записи, обеспечивающий перекрестные ссылки между записями в базе
string get_record_uid(record_ptr)
  возвращает глобальный уникальный идентификатор записи в базе данных
delete_record(record)
  удаляет запись из базы данных
boolean record_is_filtered(record_ptr)
  возвращает логический признак, что данная запись отфильтрована и содержится в текущей отображаемой выборке
string get_individual_name(record)
  возвращает полное имя персоны.
int get_individual_associations_count(individual_ptr)
  возвращает количество ассоциаций заданной персоны.
ptr get_individual_association(individual_ptr, int index)
  возвращает ассоциацию заданной персоны по индексу.
delete_individual_association(individual_ptr, index)
  удаляет ассоциацию персоны с заданным индексом.
int get_individual_events_count(individual_ptr)
  возвращает количество фактов персоны.
event_ptr get_individual_event(individual_ptr, int index)
  возвращает заданный индексом факт персоны.
delete_individual_event(individual_ptr, int index)
  удаляет заданный индексом факт персоны.
string get_event_value(event_ptr)
  возвращает строковое значение факта персоны.
string get_event_place(event_ptr)
  возвращает строку места факта персоны.
string get_event_date(event_ptr)
  возвращает строковое значение даты факта
string get_event_name(event_ptr)
  возвращает строку с именем-идентификатором типа факта (внимание - это внутренние идентификаторы фактов, см. Идентификаторы типа факта)
individual_ptr create_individual(string name, string patronymic, string family, string sex)
  создает новую персональную запись, где name-имя, patronymic-отчество, family-фамилия, sex-пол (варианты значений: "N"-не задан, "M"-мужской, "F"-женский, "U"-неопределенный)
family_ptr create_family()
  создает новую запись семьи
bind_family_spouse(family_ptr family, individual_ptr spouse)
  присоединяет супруга "spouse" к семье "family". Внимание: у записи супруга должен быть задан пол, т.к. по нему автоматически определяется роль в семье.
bool csv_load(string filename, bool first_line_is_schema)
  загрузить csv-таблицу, первый параметр определяет имя файла, второй - содержит ли первая строка файла заголовки колонок
bool csv_create(string fileName, int columnsCount, int rowsCount)
  создать новую csv-таблицу с заданными именем файла, количеством столбцов и строк (обязательно)
csv_close()
  закрыть csv-таблицу (чтение или запись)
int csv_get_cols()
  получить количество столбцов в csv-таблице
int csv_get_rows()
  получить количество строк в csv-таблице
string csv_get_cell(col, row)
  получить содержимое ячейки в csv-таблице (столбцы и строки нумеруются от нуля)
void csv_write_cell(string content)
  записать значение ячейки CSV-таблицы (переводы строк выполняются автоматически согласно заданному числу столбцов)
note_ptr create_note()
  создать новую запись заметки
bind_record_note(record_ptr, note_ptr)
  присоединить к записи заметку
add_note_text(note_ptr, string text)
  добавляет в запись заметки текстовую строку
record_ptr select_record(int record_type)
  вызывает диалог выбора записи из имеющихся
bind_record_source(record_ptr, source_ptr, string page, int quality)
  присоединяет к любой записи заданный источник, устанавливает страницу (page) и качество источника (quality, 0..3)
string define_sex(string name, string patronymic)
  возвращает идентификатор пола, определенный по имени и отчеству
set_event_place(event_ptr, string place)
  установить факту свойство места
source_record create_source(string name)
  создает новый источник с заданным названием
source_record find_source(string name)
  ищет в списке источник с требуемым названием
event_ptr create_event(record_ptr, string sign)
  создать новый факт в записях персон или семей, где sign - строковый тип факта
set_event_date(event_ptr, string date)
  установить факту дату
bind_family_child(family_ptr, individual_ptr child)
  присоединить к семье ребенка
association_ptr add_individual_association(individual_ptr, string relation, individual_ptr rel_individual)
  добавляет персоне individual_record, ссылку-ассоциацию на персону rel_individual. Используется, к примеру, когда нужно сделать ссылки на крестных.
string define_patronymic(string father_name, string child_sex, bool confirm)
  определяет при помощи встроенного словаря отчество ребенка с полом child_sex по имени отца father_name. Параметр confirm(true|false) определяет - спрашивать ли пользователя в сомнительных случаях.
family_record get_individual_parents_family(individual_ptr)
  возвращает семью родителей данной персоны
int get_individual_spouses_count(individual_ptr)
  возвращает число супругов данной персоны
family_ptr get_individual_spouse_family(individual_ptr, int index)
  возвращает семью данной персоны и его(её) супруги, index - определяет номер брака
individual_ptr get_family_husband(family_ptr)
  возвращает персону мужа данной семьи
individual_ptr get_family_wife(family_ptr)
  возвращает персону жены данной семьи
int get_family_childs_count(family_ptr)
  возвращает число детей в данной семье
individual_ptr get_family_child(family_ptr, int index)
  возвращает персону ребенка заданной семьи по его номеру
location_ptr create_location(string name, string date)
  создает новую запись локации
int get_location_usages(locationPtr)
  возвращает количество использований локации