Перейти на главную страничку сайта (список статей, файлы для скачивания)

ФОРУМ (здесь можно обсудить эту статью, а также любые проблемы программирования на различных макроязыках и в скриптовых средах)

Скриптовый язык Lua

Оглавление:
Основные свойства и возможности
Hello, World!
Особенности языка
Lua как встроенный язык текстового редактора SciTE
Пакет Lua for Windows

Основные свойства и возможности

Lua — популярный мощный скриптовый язык программирования. Lua в переводе с португальского означает "луна", и это слово не является аббревиатурой или акронимом. Lua разработан подразделением Tecgraf Католического университета Рио-де-Жанейро в Бразилии (Computer Graphics Technology Group of Pontifical Catholic University of Rio de Janeiro), история языка ведёт отсчёт с 1993 года. Lua распространяется свободно, с открытыми исходными текстами на языке Си. Основные особенности языка:

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

Чтобы установить Lua на Windows, просто распакуйте бинарный дистрибутив. Можете ассоциировать расширение .lua с интерпретатором lua5.1.exe, а также прописать путь к интерпретатору в PATH.

Основные возможности языка:

Некоторые полезные ресурсы:

Hello, World!

Создайте текстовый файл test.lua в кодировке cp866 следующего содержания:

print 'Привет, Lua!'
print("Привет, Lua!") -- комментарий
--[[ многострочный
комментарий]]

Запустите его на исполнение командой наподобие следующей:

lua5.1.exe test.lua

Вы можете скомпилировать скрипт в байт-код командой наподобие следующей:

luac5.1.exe test.lua

В результате вы получите файл luac.out (рядом с компилятором), который вы можете запустить на исполнение командой наподобие следующей, точно так же, как обычный скрипт:

lua5.1.exe luac.out

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

Программа lua5.1.exe при запуске без параметров является интерактивным интерпретатором. Запустите её и введите код наподобие следующего:

dofile("C:\\Temp\\test.lua")

Приведённый код запустит на исполнение указанный файл скрипта. Для выхода наберите код: os.exit().

Пример вычисления факториала (рекурсия):

function f(n)
    if n == 0 then
        return 1
    else 
        return n * f(n - 1)
    end
end
print(f(170))

Консольная игра "угадай число":

function prompt_read(prompt)
    print(prompt)
    return tonumber(io.read())
end

math.randomseed(os.time())
math.random()
num = math.random(1, 100)
tries = 0
print('Угадайте число от 0 до 100.')
repeat
    guess = prompt_read("Попытка: ")
    if (guess > 100) or (guess < 0) then
        print("Неверно!")
    elseif guess > num then
        print("Нет, нужно меньше!")
    elseif guess < num then
        print("Нет, нужно больше!")
    else
        print("Вы угадали!")
    end
    tries = tries + 1
until num == guess
print(string.format("Вам потребовалось %i попыток!", tries))

Особенности языка

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

Lua — регистрочувствительный (регистрозависимый) язык.

Появление локальной переменной осуществляется с помощью ключевого слова local. Все остальные переменные — глобальные. Область видимости локальной переменной распространяется на всю порцию (chunk) кода, в котором эта переменная была объявлена. Такой порцией является, например, файл скрипта. Эквивалентом порции кода является блок. В общем случае блок специфицируется с помощью двух ключевых слов следующим образом:

do
-- тело блока
end

Арифметические операторы, операторы сравнения переменных и логические операторы (and, or, not) вполне интуитивны, за исключением нескольких моментов. Оператор сравнения "не равно" записывается так:

a = 1; b = 2
if a ~= b then
    print "не равны"
end

Конкатенация строк записывается так:

print('один ' .. 'два')

Обмен содержимым двух переменных может быть записан так:

a, b = b, a

Если нужно присвоить значение переменной только в том случае, если этой переменной нет (или она равна nil, что одно и то же), можно поступить следующим образом:

var = var or 0

Управляющие структуры Lua достаточно интуитивны. Цикл с условием:

while выражение_условия do тело_блока end

Цикл с постусловием:

repeat тело_блока until выражение_условия

Цикл for:

for переменная=начальное_значение, конечное_значение, шаг do тело_блока end

Другой вариант цикла for, для обхода итерируемых объектов:

for переменная_цикла, доп_переменная in итеративное_выражение do тело_блока end

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

function makeaddfunc(x)
    return function(y) -- анонимная функция
        return x + y
    end
end
plusThree = makeaddfunc(3)
print(plusThree(5)) -- выведет 8

Поскольку Lua — язык с динамической типизацией, тип данных связывается не с переменной, а с её значением в данный момент. Любой переменной может быть присвоено значение любого типа, вне зависимости от того значения, которое она содержала раньше. До первого присваивания переменная содержит значение nil (если к ней обратиться). В Lua восемь основных типов данных: nil (неопределенный), boolean (логический), number (числовой), string (строковый), function (функция), userdata (пользовательские данные), thread (поток) и table (таблица).

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

Lua приводит типы автоматически, но не совсем так, как это делает большинство других языков:

print(type('1' + '2')) -- результат - число
print(type(1 .. 2)) -- результат - строка

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

Способы задания строк:

print(type("String double-quotes styled"))
print(type('String single-quotes styled'))
print(type([[ Универсальная строка.
Здесь возможны любые символы, в т.ч. непечатаемые. ]]))

Последовательности символов типа "[[" называются "длинными скобками" (long brackets). В "универсальных" (или "длинных") строках действительно возможны любые символы, вплоть до символа NULL (конец строки). Например, если вы вставите в строку символ BEL, то при печати в консоли вы услышите звук системного динамика. В принципе, ничто не помешает иметь в программе строку в несколько десятков мегабайт. Непосредственной поддержки Unicode-строк в Lua нет. Однако, ничто не помешает поместить в строку, например, последовательность символов в кодировке UTF-8.

Способы задания чисел:

print(type(12))
print(type(123.45))
print(type(1.23e-20))

Все числа в Lua являются числами с плавающей точкой двойной точности. Несмотря на это, Lua остаётся очень эффективным в плане производительности.

"Пустой" тип данных:

print(type(Var))
Var = 0
print(type(Var))
Var = nil
print(type(Var))

Булев тип данных:

a = 0; b = 1
print(type(a > b))
a = true
print(type(a))

При проверках только nil и false являются ложными значениями, всё остальное Lua считает истиной.

Так называемые таблицы (динамические ассоциативные массивы), на которых фактически основана вся синтаксическая мощь языка:

tbl = {}
print(type(tbl))
tbl[0] = {} -- вложенная таблица
tbl["str"] = 2
tbl[1.23] = nil -- это удаление элемента (если он есть), а не создание
for i,j in pairs(tbl) do -- обход таблицы
    print(i,j)
end
print(tbl[2]) -- нет такого элемента, это nil
tbl2 = tbl -- копирования не происходит!
print(tbl2, tbl) -- выдаст один и тот же адрес, это один объект

Ещё один небольшой пример, дающий представление о синтаксисе работы с таблицами:

tbl = {"one", "two", [234]="yes", word="script"}
print(tbl[1]) -- "one" 
print(tbl[234]) -- "yes"
print(tbl["word"]) -- "script"
print(tbl.word) -- "script"
tbl.newfield = 'ещё значение'
print(tbl.newfield) -- "ещё значение"

Несмотря на такие синтаксические "вольности", таблицы реализованы в плане производительности очень эффективно.

Оператор : (двоеточие) позволяет Lua автоматически передать "self" параметр в функцию, реализуя инкапсуляцию:

t = {}
function t:sayhello()
    print ("Привет, " .. self.Name)
end

t.Name = "Василий"
t:sayhello() -- вызов "метода"
t.sayhello(t) -- равнозначно предыдущему
t['sayhello'](t) -- равнозначно предыдущему

Далее кратко упомянем об остальных типах данных, чтобы получить представление о них.

Тип userdata служит для хранения произвольных данных (структур Си) в переменных Lua. Фактически значения этого типа соответствуют просто блокам памяти и не имеют никаких предопределённых операций, кроме сравнения и присваивания. Значения этого типа не могут быть непосредственно созданы в программе на Lua, а должны создаваться только через Си API (т.е. в вызывающей скрипт программе). Тип thread соответствует потоку выполнения. Тип function соответствует функциям. Функции в Lua могут быть записаны в переменные, переданы как параметры в другие функции и возвращены как результат выполнения функций.

Lua позволяет перегружать операции для таблиц и userdata с помощью метатаблиц. Метатаблица представляет из себя обычную таблицу, в качестве ключей которой используются строки (события), а в качестве значений — методы (обработчики). C метатаблицей может быть связана еще одна метатаблица (ключ "__metatable"), и, если событие не определено в первой метатаблице, оно ищется во второй, и т.д. Все глобальные переменные являются полями в специальных таблицах Lua, называемых таблицами окружения (environment tables).

Lua как встроенный язык текстового редактора SciTE

Как пример рассмотрим использование Lua в текстовом редакторе SciTE.

SciTE — мощный кросс-платформeнный (Win32, Linux) популярный текстовый редактор с открытым исходным кодом. Русскую сборку SciTE со множеством полезных дополнений и подробной русской документацией вы можете взять здесь (редактор не требует инсталляции в обычном понимании этого слова; дистрибутив является просто самораспаковывающимся архивом). SciTE Lua Scripting Extension в качестве языка сценариев использует Lua 5.1.

Изложенное ниже ни в коем случае не является руководством по настройке SciTE с помощью Lua; это лишь маленький пример, показывающий, как может выглядеть использование Lua как встроенного языка. Для получения информации обращайтесь к документации в поставке SciTE, а также изучайте файлы конфигурации и уже существующие в поставке скрипты. Примеры скриптов Lua для SciTE и документацию по этому вопросу вы можете найти здесь и здесь, а также в вышеупомянутой русской сборке SciTE (там же есть и русская документация). С помощью написания сценариев на Lua вы можете настроить SciTE под ваши потребности, добавляя новое поведение и функциональность.

Вы можете объявить новые команды, которые доступны через меню Tools и клавиатуру. Например, в файл настроек SciTEGlobal.properties в каталоге установки SciTE можно поместить следующие команды:

# имя команды №200 в меню "Tools":
command.name.200.*=Запустить мой скрипт
# подсистема Lua, не запрашивать сохранение текущего файла перед запуском:
command.mode.200.*=subsystem:lua,savebefore:no
# выполнить скрипт Lua из файла:
command.200.*=dofile $(SciteDefaultHome)\tools\myScript.lua

В результате в меню "Tools" появится команда "Запустить мой скрипт", при выборе которой исполнится скрипт \tools\myScript.lua, если он существует по такому пути в папке установки SciTE. Чтобы проверить работу скрипта, сохраните в нём текст наподобие следующего:

print('запущен')

Теперь в любой момент при вызове меню "Запустить мой скрипт" вы будете получать сообщение в нижней панели редактора.

Вы можете обрабатывать события, объявленные в интерфейсе расширений SciTE (SciTE Extension Interface), просто объявляя функции с именами этих событий: OnOpen, OnClose, OnSwitchFile и т.д. Для некоторых событий SciTE передаёт один или несколько аргументов в функцию обработки события. Обработчики событий возвращают булево значение (true показывает, что событие полностью обработано и другие обработчики не будут вызываться; в большинстве случаев обработчик должен вернуть false).

В файле \tools\SciTEStartup.lua в каталоге установки SciTE с помощью функции dofile грузятся скрипты, обрабатывающие события редактора (этот файл стартует при загрузке SciTE). Вы можете загрузить и свой скрипт, добавив в этот файл команду наподобие следующей:

dofile (props["SciteDefaultHome"].."\\tools\\myScript.lua")

Чтобы проверить работу скрипта \tools\myScript.lua, сохраните в нём текст наподобие следующего:

function OnChar(code)
    print('Вы ввели: ' .. code)
    return false
end

Теперь, после перезапуска SciTE, будет обрабатываться событие OnChar, в результате чего вы будете получать сообщение в нижней панели редактора при вводе каждого символа.

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

Пакет Lua for Windows

Пакет Lua для Windows (LfW) содержит всё, что необходимо для написания, выполнения и отладки самостоятельных сценариев Lua на Windows. В поставку включены многочисленные библиотеки и примеры, готовые к использованию, это пакет с "батарейками в комплекте". LfW поддерживает Windows 2000 и более новые версии Windows. Инсталлятор пакета версии 5.1.4.23 от 06.02.2009 имеет размер порядка 16 Мб.

В пакет включены:

Дополнительные библиотеки пакета предоставляют возможности:

Скрипт "Hello, World!" будет выглядеть точно так же, как в одноимённом разделе выше, с той разницей, что для его запуска следует использовать команду наподобие такой:

lua.exe test.lua

Интерпретатор wlua.exe предназначен для запуска скриптов с графическим интерфейсом, без отображения окна командного интерпретатора. Такие скрипты могут иметь расширение .wlua, которое после установки пакета уже ассоциировано с wlua.exe.

Простейший пример работы с COM:

require "luacom"
sh = luacom.CreateObject("WScript.Shell")
res = sh:popup("Текст сообщения", 0, "Текст заголовка", 4+16)
print(res)

Пример работы с ADO, вывод всех констант (перечислений) ADO:

require "luacom"
conn = luacom.CreateObject("ADODB.Connection")
typeinfo = luacom.GetTypeInfo(conn)
typelib = typeinfo:GetTypeLib()
enums = typelib:ExportEnumerations()
for key, val in pairs(enums) do
    print(key)
    print("============================")
    if(type(val)=="table") then
        for key, val in pairs(val) do
            print(tostring(key) .. " = " .. tostring(val))
        end
    end
    print()
end

Простейшие примеры работы с ADSI и WMI:

require "luacom"

root = luacom.GetObject("ADs:")
print(root:Class())
for index, item in luacomE.pairs(root) do
    print(index .. ") " .. item:Name())
end

oWMIService = luacom.GetObject("winmgmts:{impersonationLevel=Impersonate}!\\\\.\\root\\cimv2")
oDrives = oWMIService:ExecQuery("select Name,DriveType from Win32_LogicalDisk")
for index, item in luacomE.pairs(oDrives) do
    print(item:Name())
end

Простейшие примеры работы с Win32 API:

require "alien"

f = alien.Kernel32.Beep
f:types{ret = 'long', abi = 'stdcall', 'long', 'long' }
print(f(500, 100))
print(f(550, 100))
print(f(600, 300))

f = alien.Kernel32.ExpandEnvironmentStringsA
f:types{ret = "long", abi = 'stdcall', "string", "pointer", "long" }
local buffer = alien.buffer(512)
f("%USERPROFILE%", buffer, 512)
print("%USERPROFILE% - " .. tostring(buffer))

f = alien.User32.MessageBoxA
f:types{ret = 'long', abi = 'stdcall', 'long', 'string', 'string', 'long' }
print(f(0, "Привет, MessageBoxA (stdcall)!", "Заголовок", 64))

Скрипт выше подаст несколько звуковых сигналов системным динамиком, выведет на консоль значение переменной среды %USERPROFILE%, а затем отобразит окно с приветствием.

Простейшие способы использования классов .NET:

require 'luanet'
luanet.load_assembly "System"
Console = luanet.import_type "System.Console"
Math = luanet.import_type "System.Math"
Console.WriteLine("sqrt(2) равен {0}", Math.Sqrt(2))

require 'CLRPackage'
import "System"
import "System.IO"
Console.WriteLine("Текущий каталог: {0}", Directory.GetCurrentDirectory())

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

Ещё один пример с использованием классов .NET:

require 'CLRPackage'
import "System"
import "System.Windows.Forms"

f = Form()
f.Text = 'Привет, Lua!'
b = Button()
b.Dock = DockStyle.Fill
b.Text = DateTime.Now:ToString()
b.Click:Add(function()
    b.Text = DateTime.Now:ToString()
end)
f.Controls:Add(b)
f:ShowDialog()

Пример выше создаст окно с единственной кнопкой, на которой отображается текущее время. Нажатие на кнопку будет обновлять время.

Скриншоты примеров работы с графической библиотекой wxWidgets (сами примеры вы найдёте в поставке):





Людоговский Александр, 10.02.2009г.

Перейти на главную страничку сайта (список статей, файлы для скачивания)

© 2007 http://www.script-coding.com При любом использовании материалов сайта обязательна ссылка на него как на источник информации, а также сохранение целостности и авторства материалов.