Creación de scripts

1 - Introducción a Lua

Lua es un típico lenguaje de programación procedimental, que ofrece ricas características para el desarrollo. Se trata de un lenguaje potente y sencillo con un enorme conjunto de construcciones significativas.

Al ser un lenguaje orientado a ampliar el software existente, Lua no utiliza el concepto de "programa principal ". Lua funciona dentro del entorno de otros programas, llamados hosts. En este caso GEDKeeper es el host. Un host ejecuta parte del código (siguiente: script) escrito en Lua. Debido a que el host puede extender el potencial de Lua, este último puede resolver una amplia gama de tareas.

Lua es un software de libre distribución, por lo que no ofrece garantías cuando se utiliza de acuerdo con la licencia de Lua. Puede descargar Lua 5.4 en el sitio web oficial de Lua: www.lua.org.

Como muchos otros manuales, éste tiene un estilo formal. Para obtener información técnica detallada sobre el uso del lenguaje debe consultar la documentación en el sitio oficial de Lua. Hay un buen libro sobre Lua, que puede serle útil: se trata de Programming in Lua, ya en su cuarta edicion, de Roberto Ierusalimschy.

2 – Descripción del lenguaje Lua

Este capítulo describe las construcciones lingüísticas, cómo utilizarlas y qué significan.

Se presentará una interpretación utilizando BNF extendido, donde {a} significa cero o más elementos `a`, y [a] significa un elemento opcional. Se utilza texto normal para los símbolos no terminales, mientras que las palabras clave se resaltan con texto en negrita: palabra clave; todos los demás símbolos terminales se encierran entre comillas simples: '='.

2.1 – Convenciones léxicas

Los nombres (identificadores) en Lua son cadenas de letras, dígitos y caracteres de subrayado. No pueden comenzar con un dígito. Esta es una regla habitual en la mayoría de los lenguajes de programación. Los identificadores se usan para nombrar variables y tablas con valores.

Las siguientes palabras clave están reservadas y no pueden utilizarse como identificadores:

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

Lua es un lenguaje que distingue entre mayúsculas y minúsculas: `and` es una palabra clave, mientras que `And` y `AND` son dos identificadores válidos diferentes. Existe una convención, que dice que los nombres que comienzan con subrayado y se escriben en mayúsculas (`_VERSION`, por ejemplo), se reservan para nombrar las variables globales internas de Lua.

Estos son otros símbolos permitidos:

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

Los literales de cadena deben ir entre comillas simples o dobles, y pueden contener las siguientes secuencias de escape al estilo del lenguaje de programación C:: '\a' ("campana"), '\b' ("retroceso"), '\f' ("salto de página"), '\n' ("nueva línea"), '\r' ("retorno de carro"), '\t' ("tabulador horizontal"), '\v' ("tabulador vertical"), '\"' ("dobles comillas"), '\'' ("comilla simple"). Cuando necesite añadir un salto de línea en una línea larga de código, también puede utilizar la barra invertida. Para añadir un carácter a una cadena literal mediante un código de caracteres, utilice la siguiente secuencia de escape: \ddd, donde ddd es una secuencia de tres dígitos como máximo. (Cabe señalar que cuando se necesita añadir un dígito después de un carácter escrito por su código, hay que utilizar exactamente tres dígitos en la secuencia de escape). Las cadenas de Lua pueden contener cualquier valor de 8 bytes, incluyendo el carácter nulo, que se escribe como '\0'.

Debes utilizar secuencias de escape cuando necesites añadir un carácter de comillas dobles, un carácter de nueva línea, una barra invertida o un carácter nulo a un literal de cadena. Cualquier otro símbolo puede escribirse tal cual.

Los números pueden escribirse con la parte decimal y el exponente opcionales. Si desea definir un número hexadecimal, utilice el prefijo '0x'. A continuación se muestra un ejemplo de literales numéricos válidos:

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

Un guión doble ('--'), en cualquier lugar fuera de una cadena literal, inicia un comentario. Cuando un comentario comienza con un guión doble y no va seguido de un corchete largo de apertura [, se trata de un comentario corto. Continúa hasta el final de la línea. En caso contrario, se trata de un comentario largo. Continúa hasta un corchete largo de cierre. El comentario largo se utiliza frecuentemente para desactivar temporalmente una parte del código.

2.2 – Tipos y valores

Lua es un lenguaje con definición dinámica de tipos. Una variable en Lua puede almacenar valores de cualquier tipo. No hay forma de declarar un tipo definido por el usuario. Un valor en Lua puede ser almacenado como una variable, pasado como argumento de una función y ser un valor de retorno de una función.

Hay ocho tipos principales en Lua: nil (indefinido), boolean, number, string, function, userdata (datos definidos por el usuario), thread and table. nil es del tipo nil [vacio]. El propósito principal de nil es diferenciarse de otros valores y designar la falta de un valor válido. Los valores false y true son del tipo boolean. Los valores nil y false son considerados valores falsos, cualquier otro valor es verdadero. number es un tipo de valor de coma flotante de doble precisión. Array de caracteres tiene tipo string. Las cadenas en Lua pueden contener cualquier carácter de 8 bytes, incluyendo el carácter nulo '\0' (ver §2.1).

Lua puede utilizar una función implementada como código Lua, o una función suministrada por el host (ver §2.5.8).

Una instancia userdata (datos definidos por el usuario)) almacena cualquier dato propiedad del host. El valor de dicha instancia es una referencia a un bloque de memoria. Sólo se permiten operadores de asignación e igualdad para variables de tipo `userdata`. Sin embargo, utilizando )metatablas), el desarrollador puede definir operaciones con variables de este tipo (véase §2.8). El código Lua no puede crear o cambiar variables de tipo `userdata`, sólo el host puede hacerlo. Esto garantiza la integridad de los datos, propiedad del host.

El tipo table define matrices asociativas. Como índices de dichas matrices pueden utilizarse tanto números como otros valores, excepto nil. La tabla puede contener valores de varios tipos simultáneamente (excepto nil). Las tablas son la única forma de estructurar los datos en Lua; pueden usarse como arrays simples, mapas de caracteres, conjuntos, estructuras, árboles, etc. Para implementar un diccionario, Lua utiliza claves como índices de una tabla. La expresión `a.nombre` es equivalente a la expresión `a["nombre"]`. En Lua existen varias formas de crear tablas (véase §2.5.7).

Los índices y valores de las tablas pueden ser de cualquier tipo excepto nil. Y como las funciones también son un tipo de Lua, las tablas pueden contener funciones. Por tanto, las tablas pueden almacenar métodos (véase §2.5.9).

Una variable de los siguientes tipos: table, function, thread y userdata, nunca almacena un valor propio. Dicha variable almacena referencia al objeto respectivo. La asignación (directa o pasando argumentos a una función o devolviendo el resultado de una función) procesa referencias y nunca crea copias de objetos.

La función type devuelve una cadena que contiene el tipo de valor.

2.2.1 – Conversión de tipos

Lua convierte automáticamente los tipos de cadena y numéricos sobre la marcha. Cuando se realiza una operación aritmética con un operando de cadena, éste se convierte al número respectivo utilizando tipos de conversión generales. Cuando se aplica un número donde se espera una cadena, ésta se convierte al tipo cadena en un formato arbitrario apropiado. Por lo tanto, cuando necesite obtener una representación de cadena específica de un número, debe utilizar la función format de la librería de cadenas de Lua (véase string.format).

2.3 - Variables

Las variables almacenan valores mientras se ejecuta un programa. Existen tres tipos de variables en Lua: variables globales, variables locales y campos de tablas.

Un identificador individual declara una variable global o local (o parámetro de función, que es un caso particular de variable local):

var ::= Name

, donde Nombre es un identificador definido de acuerdo con §2.1.

Si una variable no se ha definido explícitamente como local, se considera una variable global (ver §2.4.7). Una variable local existe en un contexto léxico: cualquier función de este contexto puede acceder a la variable local. (ver §2.6).

Todas las variables se inician, por defecto, con el valor nil.

Para acceder a un elemento de una tabla por su índice, se utilizan corchetes:

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

2.4 – Operadores

Lua soporta el conjunto de operadores estándar, casi el mismo que lenguajes como Pascal y C. Lua dispone de operadores de asignación, control de flujo de ejecución, llamada a funciones y definición de variables.

2.4.1 – Chunk

Chunk es una unidad de ejecución en Lua. Un chunk es una secuencia de cualquier operador de Lua. Los operadores en un chunk están delimitados por punto y coma:

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

Lua no define "ningún operador", por lo tanto la expresión `;;` no está permitida.

Desde la perspectiva de Lua un chunk es una función sin nombre con un conjunto arbitrario de parámetros (véase §2.5.9). Un chunk puede definir variables locales y puede devolver valores.

2.4.2 – Bloques

Block es una lista de operadores; un bloque es sintácticamente equivalente a un chunk:

block ::= chunk

Un bloque puede definirse explícitamente y, por tanto, definir el operador compuesto:

stat ::= do block end

Los operadores compuestos permiten limitar el ámbito de las variables locales. Los operadores compuestos también se utilizan en bucles y operadores condicionales (ver §2.4.4).

2.4.3 – Asignación

Lua soporta la asignación paralela. En el caso general, el operador de asignación es la lista de variables, el símbolo `=` y la lista de expresiones. Las entradas de la lista están delimitadas por comas:

stat ::= varlist1 '=' explist1

varlist1 ::= var {',' var}

explist1 ::= exp {',' exp}

Las expresiones se tratan en §2.5.

Antes de ser asignada, la lista de variables se ajusta con la lista de expresiones por su longitud. Si la lista de la derecha es más larga que la de la izquierda, los elementos sobrantes simplemente se ignoran. Si la lista de la derecha es más corta, los elementos que faltan se definen como nulos (nil). Si la lista de operadores termina con una llamada a una función, antes del ajuste, todos los valores de retorno se añaden a la lista de la derecha (excepto en los casos en que la llamada a la función esté entre paréntesis; ver §2.5).

Todas las expresiones se evalúan antes de la asignación. En el siguiente código:

i = 3

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

a `a[3]` se le asigna 20, porque `i` en la expresión `a[i]` tiene el mismo valor, que en la expresión `i + 1`, cuando está siendo evaluada. Del mismo modo, la siguiente línea

x, y = y, x

simplemente intercambia los valores de dos variables (el método "clásico" requiere una variable temporal).

2.4.4 – Estructuras de Control

Los operadores if, while y repeat tienen un significado y una sintaxis familiar :

stat ::= while exp do block end

stat ::= repeat block until exp

stat ::= if exp then block {elseif exp then block} [else block] end

Existen dos variantes del bucle for en Lua (ver §2.4.5).

Una expresión booleana en estructuras de control puede tener cualquier valor. false y nil son valores falsos. Todo lo demás es verdadero (¡incluidos 0 y una cadena vacía!).

El bucle repeat-until termina en la condición seguida de la palabra clave until. Por lo tanto, la condición del bucle puede utilizar variables locales del ámbito del bucle.

El operador return devuelve el valor de una función o chunk. La sintaxis del operador return permite devolver múltiples valores de una función o chunk:

stat ::= return [explist1]

El operador break finaliza un bucle while, repeat o for:

stat ::= break

break sale de un bucle, en cuyo ámbito se escribe `break`; los bucles exteriores continúan la ejecución.

return (o break) debe ser el último operador de un bloque (de lo contrario, se ignorarán todos los operadores siguientes). Si necesita return o break dentro de un bloque, debe utilizar un operador compuesto, como `do return end` o `do break end`.

2.4.5 – Operador for

El operador for tiene una notación sencilla y otra ampliada.

En su notación más simple, for ejecuta código mientras su variable de bucle, que se modifica mediante una progresión aritmética, no alcanza un límite definido.

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

El bucle ejecuta repetidamente block para la variable de bucle name, que inicialmente tiene el valor exp1, incrementa name en exp3 en cada iteración, mientras que exp2 se evalúa a valor verdadero.

Así, el siguiente código

for v = e1, e2, e3 do block end

es equivalente a

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

Cabe señalar que:

La notación extendida del operador for utiliza funciones iteradoras. En cada iteración del bucle se llama al iterador para obtener un nuevo valor para la variable del bucle. El bucle termina cuando su iterador devuelve nil. Sintaxis de la notación extendida del operador for:

stat ::= for namelist in explist1 do block end

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

El siguiente código

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

puede ser escrito como

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

Conviene reseñar que:

2.4.6 – Llamada a función

Para crear un efecto lateral, puede ser útil llamar a funciones, utilizadas como operadores:

stat ::= functioncall

En este caso se ignoran todos los valores de retorno. Las llamadas a funciones se tratan en §2.5.8.

2.4.7 – Declaraciones locales

Las variables locales se declaran en cualquier lugar dentro de un bloque. La declaración puede incluir la inicialización:

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

La inicialización tiene todas las propiedades de la operación de asignación (incluido el paralelismo), §2.4.3. Por defecto, cualquier variable se inicializa con nil.

Un chunk es un bloque (ver §2.4.1), por lo que se puede declarar una variable local fuera de cualquier bloque explícito. El ámbito de dicha variable local son los límites del chunk.

Las reglas de visibilidad de las variables locales se tratan en §2.6.

2.5 - Expresiones

Las siguientes construcciones son expresiones 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 ')'

Los números y las cadenas se tratan en §2.1; las variables -- en §2.1; la declaración de funciones -- en §2.5.9; la llamada a funciones -- en §2.5.8; el constructor de tablas -- en §2.5.7. Los argumentos implícitos, que se definen como `...`, sólo pueden utilizarse con la función correctamente declarada; ver §2.5.9.

Las operaciones binarias (binop) incluyen las operaciones aritméticas (ver §2.5.1, de comparación (ver §2.5.2), booleanas (ver §2.5.3) y de concatenación (ver §2.5.4). Las operaciones unarias incluyen el menos unario (ver §2.5.1), la negación not(ver §2.5.3) y la consulta de longitud # (ver §2.5.1).

El resultado de la llamada a una función y los parámetros implícitos pueden contener varios valores. Si se utilizan como operadores (§2.4.6) (sólo funciones), se ignora cualquier valor de retorno. Si es el último o el único elemento de la lista de expresiones, no se realiza ninguna corrección (si la llamada no está encerrada entre paréntesis). En todos los demás casos, Lua convierte la lista de retorno en un único elemento desechando todos los valores excepto el primero.

He aquí algunos ejemplos:

f()                -- el resultado de la función se ignora

g(f(), x)          -- sólo cuenta el primer valor de la lista - es el resultado de `f()`.

g(x, f())          -- g toma el valor de x y todos los valores de f()

a,b,c = f(), x     -- obtiene el primer elemento del resultado de la llamada a `f()` (`c` obtiene `nil`)

a,b = ...          -- `a` obtiene el primer argumento de `...`, `b` - el segundo (tanto `a` como `b` pueden obtener `nil`,
                   -- cuando la lista implícita de parámetros está vacía)

a,b,c = x, f()     -- dos resultados de f()

a,b,c = f()        -- tres resultados de f()

return f()         -- devuelve todos los valores de f()

return ...         -- devuelve todos los argumentos implícitos

return x,y,f()     -- devuelve `x`, `y` y todos los resultados de f()

{f()}              -- crear una lista con los resultados de f()

{...}              -- crear una lista con todos los parámetros implícitos

{f(), nil}         -- resultado único de f()

Una expresión encerrada entre paréntesis siempre devuelve un único valor. Así, `(f(x,y,z))` siempre da un único valor, incluso si `f` devuelve varios valores. El valor de `(f(x,y,z))` es el primer valor que devuelve `f`, o `nil` cuando `f` no devuelve ningún valor.

2.5.1 – Operaciones aritméticas

Lua soporta aritmética general: binaria, + (suma), - (resta), * (multiplicación), / (división), % (resto en la división) y ^ (potenciación); unario - (inversión del signo de un número). Cuando los operandos son números o cadenas (que son convertibles a números §2.2.1), todas las operaciones funcionan sin problemas. Funciones de potencia para cualquier exponente de potencia. Así, x^(-0.5) calcula el valor de la raíz cuadrada de `x`.

2.5.2 – Operaciones de comparación

Las siguientes son las operaciones de comparación de Lua:

==    ~=    <     >     <=    >=

Las operaciones de comparación siempre devuelven false o true.

El operador de igualdad (`==`) compara primero los tipos de los operandos. Cuando los tipos son diferentes, el operador devuelve false. En caso contrario, el operador compara los valores de los operandos. Los números y las cadenas se comparan de la forma habitual. Los objetos (tablas, tipos de datos definidos usados, hilos y funciones) se comparan por referencia: dos objetos son iguales cuando son el mismo objeto. Un objeto recién creado (tabla, tipo de datos definido, subproceso o función) no puede ser igual a un objeto ya existente.

Las reglas de conversión de §2.2.1 no afectan a los operadores de igualdad. Por ejemplo, `"0"==0` devuelve false, mientras que `t[0]` y `t["0"]` son dos registros de tabla diferentes.

El operador `~=` es el opuesto al operador de igualdad `==`.

Los operadores menor o mayor funcionan de la siguiente manera. Los números se comparan como de costumbre. Las cadenas se comparan en orden lexicográfico. Todos los demás casos provocarán la llamada del metamétodo (no se trata en este manual).

2.5.3 – Operaciones lógicas

Lua soporta las siguientes operaciones lógicas: and, or y not. Al igual que las estructuras de control §2.4.4), las operaciones lógicas consideran false y nil como falso y todo lo demás como verdadero.

La operación de negación not siempre devuelve false o true. Operación de conjunción and devuelve su primer operando si valor del primer operando es false o nil; en caso contrario y devuelve el segundo operando. La operación de disyunción or devuelve el primer operando cuando el valor del primer operando no es igual a false ni a nil; en caso contrario or devuelve el segundo argumento. Ambos operadores evalúan el segundo operando sólo si es necesario.

Ejemplos:

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

(En este manual --> muestra el resultado de la evaluación)

2.5.4 - Concatenación

El operador de concatenación de cadenas en Lua son dos puntos: `..`. Cuando ambos operandos son números o cadenas, se convertirán en cadenas según las reglas descritas en §2.2.1. En caso contrario se llama al metamétodo `.` (no se trata en este manual).

2.5.5 – Obteniendo la longitud

El operador unario # obtiene la longitud. Esta operación devuelve el número de bytes de una cadena (en el sentido habitual, es la longitud de una cadena en la que cada carácter ocupa un byte).

Cualquier índice integral `n` es la longitud de la tabla `t`, cuando t[n] no es nil pero t[n+1] es nil. Además, `#t = 0` si t[1] es nil. Para matrices de 1 a n, que no contengan nil, la longitud es `n`, es decir, el índice del último valor. Cuando una matriz tiene "huecos" (valores nulos entre valores no nulos), `#t` es el índice de un elemento, seguido del valor nulo (por tanto, cualquier valor nulo es el final de la matriz propiamente dicho).

2.5.6 – Precedencia de los operadores

La siguiente tabla muestra la precedencia de los operadores en Lua. La potenciación tiene la mayor prioridad y luego en orden descendente:

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

Para cambiar el orden de evaluación de una expresión se utilizan paréntesis. La concatenación ('..') y la potenciación ('^') son operadores asociativos a la derecha. Todos los demás operadores binarios son asociativos a la izquierda.

2.5.7 – Constructor de tablas

El constructor de tabla es una expresión. Cualquier constructor de tabla en código provoca la creación de una nueva tabla. Los constructores pueden crear tablas vacías o inicializadas parcial o totalmente. Esta es la sintaxis del constructor de tabla:

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

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

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

fieldsep ::= ',' | ';'

Cada campo, escrito como `[exp1] = exp2`, añade el valor `exp2` con la clave `exp1` a la tabla. El campo `nombre = exp` es equivalente al campo `[["nombre["] = exp`. El campo `exp` es equivalente a [i] = exp, donde `i` es un contador entero autoincrementado con semilla igual a 1. Los campos definidos en otros formatos no afectan a este contador. Por ejemplo, la siguiente definición:

    a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 }

es equivalente a

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

Si el último campo de la lista de definiciones se declaró como `exp`, y `exp` es una llamada a función o una lista de parámetros indefinidos, todos los valores devueltos por esta expresión se añaden secuencialmente a esta lista ((§2.5.8)). Para evitar esto, encierre la llamada a función (o lista de parámetros indefinidos) entre paréntesis (§2.5).

La lista de campos puede terminar con un delimitador. Esto hace que el código generado sea más legible.

2.5.8 – Llamada a funciones

La llamada a una función en Lua tiene la siguiente sintaxis:

functioncall ::= prefixexp args

La expresión prefija y los argumentos se evalúan primero en una llamada a función. Cuando la expresión prefijada es de tipo función, se llama a la función con los argumentos suministrados. De lo contrario, se llama a metamehod (no se trata en este manual).

La siguiente notación

functioncall ::= prefixexp ':' Name args

puede utilizarse para llamar a "métodos" `v: name` expresión (args) es una sintaxis análoga a `v.name` (`v,args`), pero `v` se evalúa sólo una vez.

He aquí la definición de los argumentos:

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

args ::= tableconstructor

args ::= String

Todas las expresiones se evalúan antes de realizar la llamada. Una llamada realizada mediante la expresión `f{campos}` es un análogo de la expresión `f ({campos})`; por lo tanto, la lista de argumentos es una nueva tabla per se. Una llamada realizada por `f'cadena'` (o `f "cadena"`, o `f[[cadena]]`) es un análogo de `f('cadena')`; pero en este caso un único literal de cadena es una lista de argumentos

La sintaxis poco precisa de Lua tiene una excepción: no se puede añadir un salto de línea justo antes de `(` cuando se llama a una función. Esta excepción evita la ambigüedad en Lua. Si escribieras

a = f

(g).x(a)

Lua procesa esto como al expresión `a = f(g).x(a)`. Si todavía necesita dos expresiones, debe añadir un punto y coma entre las expresiones. Si realmente necesita llamar a la función `f`, debe eliminar el salto de línea antes de `(g)`.

Una llamada final es cuando se llama a una función con `return function call`. Lua soporta la auto-llamada final (o llamada final recursiva): en este caso el llamante usa la pila del llamador. Por tanto, el número de llamadas finales recursivas no está limitado. Cabe destacar que una llamada final elimina la información de depuración sobre el invocador. La sintaxis de la llamada final sólo permite llamar a una única función, mencionada después del operador return. Así, return da el mismo resultado que la función. No hay llamada final válida en el siguiente ejemplo:

return (f(x))        -- result list get truncated

return 2 * f(x)      -- el resultado se duplica

return x, f(x)       -- se devuelven varios valores

f(x); return         -- el resultado de la llamada se ignora

return x or f(x)     -- result list get truncated

2.5.9 – Declaración de funciones

Esta es la sintaxis de declaración de funciones:

function ::= function funcbody

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

O, en una versión más simplificada:

stat ::= function funcname funcbody

stat ::= local function Name funcbody

funcname ::= Name {'.' Name} [':' Name]

La expresión

function f () body end

es evaluada como

f = function () body end

La expresión

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

es evaluada como

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

La expresión

local function f () body end

es evaluada com

local f; f = function () body end

pero no como

local f = function () body end

La diferencia aparecerá cuando el cuerpo de la función utilice el mismo nombre de función, por ejemplo, en una llamada de recursión.

La declaración de funciones es una expresión evaluada, su tipo es el tipo de la función. Cuando Lua precompila un chunk, todas las funciones en el chunk son también precompiladas. Así, cuando Lua procesa una declaración de función, la función ya está instanciada (o cerrada). Esta instancia (o cierre) es el valor final de la expresión "declaración de función". Diferentes instancias de la misma función pueden referirse a diferentes variables locales externas y tener diferentes tablas de entorno.

Los argumentos de las funciones son variables locales propiamente dichas; se inicializan con los valores de los argumentos:

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

Cuando se llama a una función, la longitud de la lista de argumentos pasada se ajusta según la declaración, si la función no es una función con lista de argumentos variable. No se realiza ningún ajuste para las funciones con lista de argumentos variable; todos los argumentos se pasan como resultado múltiple de la función. Cuando una expresión indefinida se utiliza dentro de otra expresión o dentro de una lista de expresiones, su lista de valores se trunca en una entrada. Cuando dicha expresión es la última de la lista de expresiones, no se realiza el truncamiento (pero sólo si la llamada no está encerrada entre paréntesis).

Examinemos las siguientes declaraciones:

function f(a, b) end

function g(a, b, ...) end

function r() return 1,2,3 end

Este es un ejemplo de cómo se asignan los argumentos a los parámetros de una función:

CALL            PARAMETERS

f(3)             a=3, b=nil

f(3, 4)          a=3, b=4

f(3, 4, 5)       a=3, b=4

f(r(), 10)       a=1, b=10

f(r())           a=1, b=2


g(3)             a=3, b=nil, ... -->  (nada)

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

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

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

Una función devuelve su resultado con el operador return (ver §2.4.4). Si se omite el operador return, y el flujo de ejecución llega al final de la función, ésta se completa pero no devuelve ningún valor.

La sintaxis con dos puntos (`:`) se utiliza cuando es necesario definir métodos. Tales funciones reciben `self` como primer argumento implícito. Así, la expresión:

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

es similar a:

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

2.6 – Ámbito

Lua es un lenguaje con delimitación léxica de ámbitos. El ámbito de una variable comienza después de la declaración de la variable y existe hasta el final del bloque donde la variable fue declarada. Examinemos el siguiente ejemplo:

x = 10                  -- una variable global

do                      -- inicio de un bloque
    local x = x         -- declaración de una variable local

    print(x)            --> 10

    x = x+1

    do                  -- inicio de un bloque cerrado
        local x = x+1   -- otra variable local 'x'

        print(x)        --> 12
    end

    print(x)            --> 11
end

print(x)                --> 10  (variable global)

Cabe destacar que en la declaración `local x = x`, la variable local se declara en ámbito ajeno, por lo que se asigna valor a la variable externa.

Según las reglas de delimitación léxica de los ámbitos, las variables locales son accesibles para las funciones declaradas en los ámbitos de las variables. Cuando una función utiliza una variable de este tipo, la variable se denomina variable local externa (relativa a una función declarada en el ámbito de la variable).

Cada declaración local crea una nueva variable local. Por ejemplo:

a = {}

local x = 20

for i=1,10 do

    local y = 0

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

end

El bucle crea diez instancias de la función, que utilizan diferentes variables `y` y la misma variable `x`.

2.7 – Tratamiento de errores

Lua es un lenguaje de extensión, por lo tanto, Lua comienza a funcionar cuando un código anfitrión ha llamado a la función de la biblioteca de Lua lua_pcall. Si se produce un error mientras el código Lua está siendo compilado o ejecutado, la aplicación anfitriona obtiene el control de la ejecución y realiza la gestión del error (muestra un mensaje de error, por ejemplo)

El programa Lua puede generar un error explícitamente, llamando a la función error. Si desea manejar errores en el propio código Lua, utilice la función pcall.

2.8 – Recolector de basura

Lua gestiona los recursos de memoria automáticamente. Esto significa que no tienes que preocuparte de la asignación de memoria cuando creas un objeto, y de la liberación de memoria una vez que un objeto se vuelve innecesario. Lua ejecuta un recolector de basura cada cierto tiempo, que elimina en segundo plano los objetos obsoletos (es decir, los objetos que ya no son accesibles desde el código Lua). El recolector de basura procesa todos los objetos de Lua: tablas, instancias de datos de usuario, funciones, hilos y cadenas.

3 - Uso de Lua en GEDKeeper

3.1 - Convención de nombres

Tipos de datos:
`void` es un argumento o resultado de función vacío;
`int` es un argumento entero o el resultado de una función;
`string` es un argumento de cadena o el resultado de una función;
`boolean` es un argumento booleano o el resultado de una función.

Atención: la primera entrada de cualquier lista de base de datos tiene índice cero. Por lo tanto, para enumerar todos los registros de la base de datos, por ejemplo, debe utilizar el siguiente código:


for i = 0, get_records_count() - 1 do -- i.e. número de elementos "-1"
    ...
end

3.2 - Estructura de datos

Cualquier estructura de base de datos se maneja mediante punteros. Un puntero es una variable especial, que almacena (se refiere a) alguna ubicación de memoria donde se encuentra una estructura.

Existen los siguientes tipos de punteros a estructuras:

3.3 - API

void print(string text)
Muestra la cadena de caracteres 'text'.
void progress_init(int length, string title)
Muestra la ventana con un estado de progreso, donde `length` es el número de etapas de progreso, `title` es una cabecera.
void progress_done()
Oculta la ventana con un estado de progreso.
void progress_step()
Aumenta en uno el número de etapas de progreso completadas.
int strpos(string substr, string str)
Busca la primera aparición de `substr` en `str` y devuelve su índice.
void update_view()
Actualiza todas las listas (esto es necesario después de la actualización masiva).
string select_file()
Muestra el diálogo "Abrir archivo" y devuelve el nombre del archivo.
int get_records_count()
Devuelve el número de registros de la base de datos.
record_ptr get_record(int index)
Obtiene un registro de la base de datos por su índice. Atención: los registros de todo tipo se localizan aleatoriamente en la base de datos. Se almacenan en la secuencia en la que se añadieron a la base de datos. Por lo tanto, debe comprobar siempre el tipo de registro.
int get_record_type(record_ptr)
Obtiene el tipo del registro especificado. Los posibles valores de retorno son:
string get_record_type_name(int type)
Convierte el tipo de registro de vista numérica a vista de cadena.
string get_record_xref(record_ptr)
Devuelve el identificador del registro, utilizado para enlaces de referencias cruzadas entre registros de la base de datos.
string get_record_uid(record_ptr)
Devuleve el identificador único global del registro especificado.
delete_record(record)
Elimina el registro especificado de la base de datos.
boolean record_is_filtered(record_ptr)
Devuelve `true` cuando el registro especificado fue filtrado y es visible en la vista actual.
string get_individual_name(record)
Devuelve el nombre completo de la persona especificada.
int get_individual_associations_count(individual_ptr)
Devuelve el número de asociaciones de la persona especificada.
ptr get_individual_association(individual_ptr, int index)
Devuelve la asociación, para la persona indicada, utilizando el índice dado.
delete_individual_association(individual_ptr, index)
Elimina la asociación especificada de la persona indicada.
int get_individual_events_count(individual_ptr)
Devuelve el número de hechos de la persona especificada.
event_ptr get_individual_event(individual_ptr, int index)
Devuelve el hecho, para la persona indicada, utilizando el índice especificado.
delete_individual_event(individual_ptr, int index)
Elimina el hecho especificado de la persona indicada.
string get_event_value(event_ptr)
Convierte el evento especificado en una cadena de texto.
string get_event_place(event_ptr)
Devuelve la ubicación del evento especificado.
string get_event_date(event_ptr)
Devuelve la fecha del evento especificado en forma de cadena.
string get_event_name(event_ptr)
Devuelve nombre - identificador del tipo de evento especificado (atención: se trata de un identificador interno de evento, ver Identificadores de tipos de evento ).
individual_ptr create_individual(string name, string patronymic, string family, string sex)
Crea un nuevo registro de persona donde: `nombre` es el nombre del individuo, `patronímico` es el nombre patronímico del individuo, `familia` es el apellido del individuo, `sexo` es el sexo del individuo (valores posibles: "N" -- no definido, "M" -- masculino, "F" -- femenino, "U" -- desconocido).
family_ptr create_family()
Crea un nuevo registro de familia.
bind_family_spouse(family_ptr family, individual_ptr spouse)
Adjunta `esposo` marido a `family`. Atención: el registro `spouse` debe tener el sexo definido, ya que se utiliza en la detección automática de un rol en la familia.
bool csv_load(string filename, bool first_line_is_schema)
Carga la tabla CSV del archivo `filename`. `first_line_is_schema` define si la primera fila del archivo es una fila de encabezado.
bool csv_create(string fileName, int columnsCount, int rowsCount)
Crea una nueva tabla CSV con el nombre de archivo y el número de columnas y filas dados (campos obligatorios).
csv_close()
Cierra la tabla CSV abierta anteriormente (para lectura o escritura).
int csv_get_cols()
Devuelve el número de columnas de la tabla CSV.
int csv_get_rows()
Devuelve el número de filas de la tabla CSV.
string csv_get_cell(col, row)
Obtiene el valor de la celda especificada en la tabla CSV (las filas y columnas se numeran a partir de cero).
void csv_write_cell(string content)
Escribe el valor de una celda de una tabla CSV (el inicio de una nueva línea se realiza automáticamente en función del número de columnas especificado).
note_ptr create_note()
Crea un nuevo registro de nota.
bind_record_note(record_ptr, note_ptr)
Adjunta la nota al registro.
add_note_text(note_ptr, string text)
Añade a la nota especificada el nuevo texto `text`..
record_ptr select_record(int record_type)
Muestra el diálogo de selección de registros.
bind_record_source(record_ptr, source_ptr, string page, int quality)
Adjunta la fuente especificada al registro especificado y establece la `página` y la `calidad` de la fuente (0..3).
string define_sex(string name, string patronymic)
Devuelve el identificador de sexo evaluado a partir de `name` y `patronymic`.
set_event_place(event_ptr, string place)
Establece la ubicación en el evento especificado.
source_record create_source(string name)
Crea una nueva fuente con el nombre especificado.
source_record find_source(string name)
Busca en la lista una fuente con el nombre especificado.
event_ptr create_event(record_ptr, string sign)
Crea un nuevo hecho en el registro individual o familiar, donde `sign` es el tipo del hecho, definido como cadena de texto.
set_event_date(event_ptr, string date)
Establece la fecha `date` para el evento especificado.
bind_family_child(family_ptr, individual_ptr child)
Añade el hijo especificado a la familia dada.
association_ptr add_individual_association(individual_ptr, string relation, individual_ptr rel_individual)
Añade a `individual_record` una nueva asociación de enlace a `rel_individual`. Por ejemplo, se puede utilizar cuando se crea un vínculo con los padrinos.
string define_patronymic(string father_name, string child_sex, bool confirm)
Define el nombre patronímico de un niño que es `child_sex` y tiene padre con `father_name`, usando el diccionario interno. El parámetro `confirm` define si se debe mostrar una confirmación al usuario en casos discutibles.
family_record get_individual_parents_family(individual_ptr)
Devuelve la familia de los padres de la persona especificada.
int get_individual_spouses_count(individual_ptr)
Devuelve el número de cónyuges de la persona indicada.
family_ptr get_individual_spouse_family(individual_ptr, int index)
Devuelve la familia de la persona especificada y su mujer o su marido, `index` es un número de matrimonio.
individual_ptr get_family_husband(family_ptr)
Devuelve el marido en la familia especificada.
individual_ptr get_family_wife(family_ptr)
Devuelve la esposa en la familia especificada.
int get_family_childs_count(family_ptr)
Devuelve el número de hijos de la familia especificada.
individual_ptr get_family_child(family_ptr, int index)
Devuelve la persona hija de la familia especificada, referenciada por el número de hijo.