Я люблю Lua. I love Lua.

Как добавить в Notepad++ парсер для Function List

Posted in Uncategorized by ilovelua on 25 декабря, 2018

Для начала нужно установить Notepad++ БЕЗ инсталлятора (из архива).
Если Notepad++ поставить при помощи инсталлятора, то все, что описано ниже, работать НЕ будет.

В файле functionList.xml в папке  Notepad++  в секцию <associationMap> добавить строку

<association id= «lua_function» langID=»23″ />

ВАЖНО: если изменить строку «lua_function» на что-либо другое (например, на «lua_parse»), то работать НЕ будет.

В конец секции <parsers> добавить парсер, взятый отсюда (первая выдача в гугле по запросу «notepad++ lua function list»):

https://stackoverflow.com/questions/19246077/how-to-add-lua-functions-to-the-notepad-functionlist-xml

<!-- Basic lua parser for functionList.xml in Notepad++ 6.5.3 -->
<!-- See http://notepad-plus-plus.org/features/function-list.html -->
<parser id="lua_function" displayName="Lua" commentExpr="--.*?$">
    <!-- Basic lua table view, nested lua table not supported -->
    <classRange
        mainExpr="[.\w]+[\s]*=[\s]*\{"
        openSymbole="\{"
        closeSymbole="\}"
        displayMode="node">
        <className>
            <nameExpr expr="[.\w]+"/>
        </className>
        <function
            mainExpr="[.\w]+[\s]*=[\s]*['&quot;]?[\w]+['&quot;]?">
            <functionName>
                <funcNameExpr expr=".*"/>
            </functionName>
        </function>
    </classRange>
    <!-- Basic lua functions support -->
    <function 
        mainExpr="(function[\s]+[.\w]+(:[\w]+)?)|([.\w]+[\s]*=[\s]*function)"
        displayMode="$className->$functionName">
        <functionName>
            <nameExpr expr="((?<=function)[\s]+[.:\w]+)|(([.\w]+)(?=([\s]*=[\s]*function)))"/>
        </functionName>
        <className>
            <nameExpr expr="[.\w]+(?=:)"/>
        </className>
    </function>
</parser>

 

Профит!

 

 

 

 

Заполнение таблиц данными

Posted in Uncategorized by ilovelua on 20 января, 2016

Нередко встает задача заполнить таблицу данными.
Допустим, нам нужно создать список параметров.
Каждый параметр — это таблица вида:

{
name = 'Name',
value = '',
type = 'string',
}

Соответственно, список параметров будет выглядеть как-то так:

{
	{
		name = 'Name',
		value = '',
		type = 'string',
	},
	{
		name = 'Order',
		value = 1,
		type = 'number',
	},
	{
		name = 'File',
		value = 'default.lua',
		type = 'string',
	},
}

Если параметров много, то для удобства заполнения я создаю функцию:

local function createParameter(name, value, type)
	return {
		name	= name,
		value	= value,
		type	= type,
	}
end

Список параметров начинает выглядеть так:

{
    createParameter('Name',     '',             'string'),
    createParameter('Order',    1,              'number'),
    createParameter('File',     'default.lua',  'string'),
}

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

local function Property(name)
    return {
        name    = name,
        
        Value = function(self, value)
            self.value = value
            
            return self
        end,
        
        Type = function(self, type)
            self.type = type
            
            return self
        end,
    }
end

Теперь заполнение таблицы параметров выглядит так:

{
    Property('Name')    :Value('')              :Type('string'),
    Property('Order')   :Value(1)               :Type('number'),
    Property('File')    :Value('default.lua')   :Type('string'),
}

Теперь каждый параметр подписан, а если есть опциональные поля, то их можно пропустить, а не писать такое:

createParameter('Name', '', nil, nil, nil, 'string'),

 

PS
Не помню, сам я это придумал, или где-то украл 🙂

Tagged with: , ,

Lua 5.3 Руководство

Posted in Uncategorized by ilovelua on 13 ноября, 2015

Добавил руководство по Lua 5.3, переведенное Николаем Ведерниковым.
К сожалению, слетела оригинальная верстка. Кому надо, оригиналы лежат тут:

CHM

HTML

Tagged with: ,

Книжка по Lua на русском языке

Posted in Uncategorized by ilovelua on 20 мая, 2014

Вышла книга Роберту Иерузалимски: Программирование на языке Lua

Подробнее: http://www.labirint.ru/books/433606/

Подробнее: http://www.ozon.ru/context/detail/id/26893654/

Подробнее: http://www.combook.ru/product/10780471/

Судя по описанию, это перевод третьего издания «Programming in Lua».

Избавление от циклических зависимостей модулей

Posted in Uncategorized by ilovelua on 14 марта, 2014

Итак, у вас есть два модуля, каждый из которых использует другой.

ModuleA.lua:

local ModuleB	= require('ModuleB')

return {
	fun1 = function() print('~~~ModuleA fun1') end,
	fun2 = function() print('~~~ModuleA calls ModuleB.fun1()', ModuleB().fun1()) end,
}

ModuleB.lua:

local ModuleA	= require('ModuleA')

return {
	fun1 = function() print('~~~ModuleB fun1') end,
	fun2 = function() print('~~~ModuleB calls ModuleA.fun1()', ModuleA().fun1()) end,
}

В главной программе вы наивно пытаетесь их включить:

local ModuleA = require('ModuleA')
local ModuleB = require('ModuleB')

и получаете ошибку:

can't load module_test.lua: [string "ModuleB.lua"]:1: loop or previous error loading module 'ModuleA'

Как быть? Шаблон Медиатор приходит на помощь!

ModuleMediator.lua:

return {
	getModuleA = function() return require('ModuleA') end,
	getModuleB = function() return require('ModuleB') end,
}

Исправляем файлы модулей.
ModuleA.lua:

local ModuleMediator	= require('ModuleMediator')

return {
	fun1 = function() print('~~~ModuleA fun1') end,
	fun2 = function() print('~~~ModuleA calls ModuleB.fun1()', ModuleMediator.getModuleB().fun1()) end,
}

ModuleB.lua:

local ModuleMediator	= require('ModuleMediator')

return {
	fun1 = function() print('~~~ModuleB fun1') end,
	fun2 = function() print('~~~ModuleB calls ModuleA.fun1()', ModuleMediator.getModuleA().fun1()) end,
}

В главной программе все осталось по старому:

local ModuleA = require('ModuleA')
local ModuleB = require('ModuleB')

ModuleA.fun1()
ModuleA.fun2()

ModuleB.fun1()
ModuleB.fun2()

Запускаем:

~~~ModuleA fun1
~~~ModuleB fun1
~~~ModuleA calls ModuleB.fun1()
~~~ModuleB fun1
~~~ModuleA fun1
~~~ModuleB calls ModuleA.fun1()

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

Перехватчик для события удаления стейта.

Posted in Uncategorized by ilovelua on 11 февраля, 2014

Если Lua-библиотека, написанная на С, взаимодействует с несколькими Lua стейтами, то может потребоваться узнать о том, что какой-то из Lua стейтов удаляется. Сделать это можно так: при регистрации библиотеки в Lua стейте создаем безымянную userdata переменную, и у нее устанавливаем метатаблицу с методом __gc. Поскольку при удалении стейта у всех объектов вызываются финализаторы, то будет вызвана и эта функция безымянного объекта.

int onDestroyLuaState(lua_State* L)
{
	// делаем что-то полезное
	return 0;
}

const luaL_Reg libname[] = {
	// common functions
	{"Func1", func1},
	{"Func2", func2},
        ...
	{0, 0},
};

extern "C" __declspec(dllexport) int luaopen_libname(lua_State* L)
{
	lua_newuserdata(L, 0); // dummy for GC
	lua_createtable(L, 0, 1); // metatable
	lua_pushcfunction(L, onDestroyLuaState);
	lua_setfield(L, -2, "__gc");
	lua_setmetatable(L, -2);

	luaL_register(L, luaModuleName, libname);

	return 1;
}
Tagged with: ,

Новые и старые модули

Posted in Uncategorized by ilovelua on 23 января, 2014

Это перевод вот этой статьи.

Создание и использование модулей

Есть два способа создания модулей — старый(и не рекомендуемый) для 5.0 и ранних версий 5.1, и новый для 5.1 и 5.2. Кроме нового способа мы также рассмотрим старый, поскольку вам могут встретиться программы, в которых он используется.

Создадим файл примера с следующим содержанием:

local mymodule = {}

function mymodule.foo()
    print("Hello World!")
end

return mymodule

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

> mymodule = require "mymodule"
> mymodule.foo()
Hello World!

В обычном файле скрипта рекомендуется сделать переменную mymodule локальной:

local mymodule = require "mymodule"
mymodule.foo()

Если мы сделаем переменную mymodule локальной в интерактивном интерпретаторе, то на следующей строке она выйдет из области видимости и мы не сможем ей воспользоваться. Поэтому мы должны сделать переменную mymodule глобальной.

Поскольку вы можете подключать один и тот же модуль в различных файлах, Lua кеширует модули в таблице package.loaded. Чтобы показать, как работает кеширование, изменим, например, функцию foo в mymodule.lua чтобы она печатала "Hello Module!". Если мы продолжим сессию в интерпретаторе, то произойдет следующее:

> mymodule = require "mymodule"
> mymodule.foo()
Hello World!

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

> package.loaded.mymodule = nil
> mymodule = require "mymodule"
> mymodule.foo()
Hello Module!

Еще одна приятная вещь — это возможность именовать модули произвольным образом, поскольку модули это обычные таблицы, сохраненные в переменной. Если название mymodule.lua покажется нам слишком длинным, мы можем его сократить:

> m = require "mymodule"
> m.foo()
Hello Module!

Другие способы создания модулей

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

Создать таблицу в начале скрипта и добавлять функции в нее:

local mymodule = {}

local function private()
    print("in private function")
end

function mymodule.foo()
    print("Hello World!")
end

function mymodule.bar()
    private()
    mymodule.foo() -- need to prefix function call with module
end

return mymodule

Сделать все функции локальными и добавить их в таблицу в конце скрипта:

local function private()
    print("in private function")
end

local function foo()
    print("Hello World!")
end

local function bar()
    private()
    foo() -- do not prefix function call with module
end

return {
  foo = foo,
  bar = bar,
}

Комбинация двух предыдущих способов:

local mymodule = {}

local function private()
    print("in private function")
end

local function foo()
    print("Hello World!")
end
mymodule.foo = foo

local function bar()
    private()
    foo()
end
mymodule.bar = bar

return mymodule

Вы даже можете изменить окружение блока кода(the chunk’s environment), чтобы сохранить в нем только необходимые глобальные переменные:

local print = print -- the new env will prevent you from seeing global variables

local M = {}
if setfenv then
	setfenv(1, M) -- for 5.1
else
	_ENV = M -- for 5.2
end

local function private()
    print("in private function")
end

function foo()
    print("Hello World!")
end

function bar()
    private()
    foo()
end

return M

Или, если вы не хотите сохранять глобальные переменные:

local M = {}
do
	local globaltbl = _G
	local newenv = setmetatable({}, {
		__index = function (t, k)
			local v = t[k]
			if v == nil then return globaltbl[k] end
			return v
		end,
		__newindex = M,
	})
	if setfenv then
		setfenv(1, newenv) -- for 5.1
	else
		_ENV = newenv -- for 5.2
	end
end

local function private()
    print("in private function")
end

function foo()
    print("Hello World!")
end

function bar()
    private()
    foo()
end

return M

Обратите внимание, что в этом случае доступ к глобальным переменным и переменным модуля может стать медленнее, поскольку используется функция __index. Пустая «прокси» таблица используется вместо таблицы _G(глобальное пространство имен) для того, чтобы не было возможности из модуля обращаться к глобальным переменным и не происходило следующее:
> require "mymodule"
> mymodule.foo()
Hello World!
> mymodule.print("example") -- unwanted __index metamethod
example

Старый способ создания модулей

В Lua 5.0 and 5.1 есть функция module, которая используется таким образом:
mymodule.lua:

module("mymodule", package.seeall)

function foo() -- create it as if it's a global function
    print("Hello World!")
end

И модуль может использоваться так:

> require "mymodule"
> mymodule.foo()
Hello World!

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

Из-за этого вы внутри модуля не видите глобальных переменных(например, print). Одно из решений — сохранить все необходимые глобальные переменные в локальных перед вызовом функции module, но это может быть утомительно, и решением было добавить второй параметр в функцию module, который должен быть функцией, в которую таблица модуля передается как параметр. package.seeall устанавливает для модуля метатаблицу с функцией __index, которая указывает на глобальное пространство имен, и благодаря этому модуль может обращаться к глобальным переменным. Проблема этого решения в том, что пользователь модуля может обращаться к глобальным переменным через модуль:

> require "mymodule"
> mymodule.foo()
Hello World!
> mymodule.print("example")
example

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

Таблица package

Как было сказано ранее, Lua использует библиотеку package для управления модулями.

package.path (для модулей написанных на Lua) и package.cpath (для модулей написанных на C) это места, где Lua ищет модули.

> =package.path
./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua;/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/init.lua
> =package.cpath
./?.so;/usr/local/lib/lua/5.1/?.so;/usr/lib/x86_64-linux-gnu/lua/5.1/?.so;/usr/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so

package.loaded это таблица, где уже загруженные модули хранятся по именам. Как обсуждалось ранее, это работает как кеш, чтобы модули не загружались многократно, функция require сначала ищет модуль в этой таблице, и, если ничего не найдено, пытается его загрузить.

package.preload это таблица функций, связанных с именами модулей. Прежде чем искать в файловой системе, require проверяет, не содержит ли package.preload нужный ключ. Если функция найдена, то результат ее вызова и будет значением, которое возвратит require.

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

Загрузка модулей

Posted in Uncategorized by ilovelua on 13 сентября, 2013

Если вы решили часть модулей вынести в отдельную папку, например:

./mylib/
./mylib/Module1.lua
./mylib/Module2.lua
./mylib/Module3.lua

то обращаться к ним из других модулей можно так:

local module1 = require('mylib.Module1')

Только нужно помнить, что в файле с модулем нужно также указывать относительный путь.
Например, в файле ./mylib/Module1.lua нужно написать:

module('mylib.Module1')

 

Вот здесь http://lua-users.org/wiki/ModulesTutorial написано, как нужно оформлять модули «по-новому».
Мы в своем проекте пока используем функцию module(). Возможно, вскоре мы от нее откажемся.

Tagged with: , ,

Итераторы

Posted in Uncategorized by ilovelua on 29 августа, 2013

После того, как написал несколько раз такой код:

function(command, ...)
	...

	local combos = command.combos

	if combos then
		local deviceCombos = combos[deviceName]

		if deviceCombos then
			for i, combo in ipairs(deviceCombos) do
				-- что-то сделать с combo
				...
			end
		end
	end

	...
end

мне стало понятно, что нужно придумать что-нибудь поизящнее, чем эти двойные проверки. И тут я вспомнил про итераторы! Programming in Lua говорит нам, что итератором может быть функция, которая при каждом вызове возвращает следующий элемент коллекции. Поскольку deviceCombos это массив, а мне при переборе индекс элемента не нужен, то задача упрощается:

function commandCombos(command, deviceName)
	local pos = 0
	local combos
	local deviceCombos

	if command then
		combos = command.combos

		if combos then
			deviceCombos = combos[deviceName]
		end
	end

	return function()
		if deviceCombos then
			pos = pos + 1
			return deviceCombos[pos]
		end
	end
end

Обратите внимание, что переменная pos — это upvalue.
Теперь проверим получившуюся функцию:

local commandNil = nil
local commandNoCombos = {}
local commandEmptyCombos = {
	combos = {},
}
local command = {
	combos = {
		mouse = {
			{key = 'MOUSE1', action = 1},
			{key = 'MOUSE2', action = 2},
		},
		keyboard  = {
			{key = 'A', action = 11},
			{key = 'B', action = 22},
		},
	}
}

for combo in commandCombos(commandNil) do
	assert(false)
end

for combo in commandCombos(commandNoCombos) do
	assert(false)
end

for combo in commandCombos(commandNoCombos) do
	assert(false)
end

for combo in commandCombos(commandEmptyCombos) do
	assert(false)
end

for combo in commandCombos(command, 'mouse') do
	print('~~~mouse', combo.key, combo.action)
end

for combo in commandCombos(command, 'keyboard') do
	print('~~~keyboard', combo.key, combo.action)
end

На выходе ожидаемо получаем:

~~~mouse MOUSE1 1
~~~mouse MOUSE2 2
~~~keyboard A 11
~~~keyboard B 22

Скорость цикла for

Posted in Uncategorized by ilovelua on 8 августа, 2013

В чужом коде увидел такое:

for i = 1, #data.values do
	local value = data.values[i]
	...
end

И тут же осудил внутри себя этого нерадивого разработчика «Экий ты, брат, ленивец! Нет бы написать «правильно»!»:

for i, value in ipairs(data.values) do
	...
end

И тут меня сомнение взяло: второй вариант мне лично кажется красивее и правильнее, но быстр ли он? И быстро был написан тест:

local data = {
	values = {}
}

for i = 1, 10000 do
	table.insert(data.values, i)
end

local function simpleForTest()
	local j
	local t = os.clock()

	for k = 1, 100 do
		for i = 1, #data.values do
			j = data.values[i]
		end
	end

	print('~~~#', os.clock() - t)
end

local function ipairsTest()
	local j
	local t = os.clock()

	for k = 1, 100 do
		for i, val in ipairs(data.values) do
			j = val
		end
	end

	print('~~~ipairs', os.clock() - t)
end

for i = 1, 10 do
	simpleForTest()
	ipairsTest()
end

А результаты он давал такие:
~~~# 0.063
~~~ipairs 0.121
~~~# 0.063
~~~ipairs 0.122
~~~# 0.063
~~~ipairs 0.122
~~~# 0.062
~~~ipairs 0.121
~~~# 0.063
~~~ipairs 0.122
~~~# 0.063
~~~ipairs 0.122
~~~# 0.063
~~~ipairs 0.122
~~~# 0.062
~~~ipairs 0.122
~~~# 0.063
~~~ipairs 0.122
~~~# 0.063
~~~ipairs 0.122

То есть «некрасивый» код работает в два раза быстрее «красивого». Вот так.