Lua側からluasub定義や引数の取得を容易にするスクリプト

の三つを踏まえた上で、luasubを簡単にできるようにしてみた。

使い方

下記スクリプトを実行する。前提三つの作成物を踏まえた上で、さらに下記の物が生成される。

luasub
NScripter側のluasub宣言と、関数の登録、二重登録チェックを一度にやってしまう関数。
NSPop
引数取得の何か。キーを与えることで様々な動作をする。

変更削除しないこと。

定義

たとえば、getspsizeをnsluaで再実装するためには、下記のようにする。

-- luasub.命令名(関数実体, 引数定義)
luasub.getspsize(NSSpGetInfo, NSPop.iIII)

引数定義については、NSPopを使う。
NSPopの後にドット、それからi, I, s, S, D, Lの6種類のみで構成された文字列をつけると、それは特定のパターンで引数を取得する関数になる。
文字の意味は下記の通り。

i
数値
s
文字列
D
ID
L
ラベル
I
数値変数の変数番号
S
文字列変数の変数番号

これらを隙間なくつなげることで、引数定義になる。
上記の例であれば、最初に数値を一つ。それから数値変数を扱う関数が三つになる。
luasubで定義された命令が、NScripter側から実行されると、まず引数が取得される。取得された引数のうち、IとSを除いた物が関数実体に渡される。(上記の場合は、先頭の数値のみ)
関数実体は何らかの返り値を返すことが期待される。
関数実体からの返り値は、IとSで定義されたNScripter側の変数に順番を合わせて記録される。
こうすることで、nslua内で使うことを想定に作られた通常の関数を、そのまま関数実体として扱うことができる。
※可変引数を実装したい場合は、引数定義に固定部分までを記述して、関数実体内に地道にNSPop系を使って実装してください。

-- lspとlsphを再実装してみる。
function lsp_main(visible, spnum, tag, x, y)
	local alpha = 255
	if NSCheckComma() then NSPopComma(); alpha = NSPopInt() end
	NSSpLoad(spnum, tag)
	NSSpMove(spnum, x, y, alpha)
	NSSpVisible(spnum, visible)
end
function lsp(...) return lsp_main(true, ...) end
function lsph(...) return lsp_main(false, ...) end
luasub.lsp(lsp, NSPop.isii) -- 登録
luasub.lsph(lsph, NSPop.isii) -- 登録

スクリプト(前提三つもふくんでいます)

-- NSExec/NSSetIntValue/NSSetStrValueのログを取るスクリプト
do
	local fh = io.open("nsexec.log", "w")
	fh:setvbuf("full")
	local original1 = _G.NSExec
	_G.NSExec = function(str)
		fh:write(str); fh:flush()
		original1(str)
		fh:write("\n"); fh:flush()
	end
	local original2 = _G.NSSetIntValue
	_G.NSSetIntValue = function(num, v)
		fh:write("%"..tostring(num).."="..tostring(v).."\n")
		original2(num, v)
		fh:flush()
	end
	local original3 = _G.NSSetStrValue
	_G.NSSetStrValue = function(num, v)
		fh:write("$"..tostring(num).."="..v.."\n")
		original3(num, v)
		fh:flush()
	end
end
-- NScripter側のデフォルト命令をLua側から簡単に実行させる。
do
	_G.Cd = setmetatable({}, {__index=function(t, k)
		local func = function() return k:lower() end
		rawset(t, k, func)
		return rawget(t, k)
	end})
	_G.Lb = setmetatable({}, {__index=function(t, k)
		local func = function() return "*"..k:lower() end
		rawset(t, k, func)
		return rawget(t, k)
	end})
	local shi = {}
	local shs = {}
	local res = {}
	_G.iRet = function(v)
		local num = #shi
		shi[num+1] = NSGetIntValue(num)
		if v then
			NSSetIntValue(num, v)
		else
			res[1+#res] = function() return NSGetIntValue(num) end
		end
		return "%"..tostring(num)
	end
	_G.sRet = function(v)
		local num = #shs
		shs[num+1] = NSGetStrValue(num)
		if v then
			NSSetStrValue(num, v)
		else
			res[1+#res] = function() return NSGetStrValue(num) end
		end
		return "$"..tostring(num)
	end
	local comeback = function()
		local ret = {}
		for i, v in ipairs(res) do ret[1+#ret] = v() end
		res = {}
		for i, v in ipairs(shi) do NSSetIntValue(i-1, v) end
		shi = {}
		for i, v in ipairs(shs) do NSSetStrValue(i-1, v) end
		shs = {}
		return ret
	end
	_G.Ns = setmetatable({}, {__index=function(t, k)
		local func = function(...)
			local str = "_"..k
			local tp
			for i, v in ipairs({...}) do
				if i == 1 then str = str .. " " else str = str .. "," end
				tp = type(v)
				    if tp == "number"   then str = str .. tostring(math.floor(v))
				elseif tp == "string"   then str = str .. sRet(v)
				elseif tp == "function" then str = str .. v()
				end
			end
			NSExec(str)
			return unpack(comeback())
		end
		rawset(t, k, func)
		return rawget(t, k)
	end})
end
-- numaliasをLua側から簡単に実行できるようにする。
-- NScripter側の数値を簡単に取得、変更できるようにする。
do
	local alias_count = 100
	_G.next_numalias = function(num)
		if num then alias_count = num end
		return alias_count
	end
	local ob1 = {}
	setmetatable(ob1, {__index=function(t, k)
		local shelter = NSGetIntValue(0)
		NSExec("mov %0,"..k)
		rawset(t, k, NSGetIntValue(0))
		NSSetIntValue(0, shelter)
		return rawget(t, k)
	end})
	_G.Nm = ob1

	local ei = setmetatable({}, {__index=function(t, k)
		rawset(t, k, function(v)
			if v then NSSetIntValue(k, v) end
			return NSGetIntValue(k)
		end)
		return rawget(t, k)
	end})
	local obi = setmetatable({}, {__index=function(t, k)
		if type(k) == "string" then
			rawset(t, k, ei[ob1[k]])
		else
			rawset(t, k, ei[k])
		end
		return rawget(t, k)
	end})
	_G.iNm = obi

	local es = setmetatable({}, {__index=function(t, k)
		rawset(t, k, function(v)
			if v then NSSetStrValue(k, v) end
			return NSGetStrValue(k)
		end)
		return rawget(t, k)
	end})
	local obs = setmetatable({}, {__index=function(t, k)
		if type(k) == "string" then
			rawset(t, k, es[ob1[k]])
		else
			rawset(t, k, es[k])
		end
		return rawget(t, k)
	end})
	_G.sNm = obs

	local ob2 = {}
	setmetatable(ob2, {__index=function(t, numalias) return function(num)
		local temp = rawget(ob1, numalias)
		if type(temp) == "nil" then
			if num then
				rawset(ob1, numalias, num)
			else
				rawset(ob1, numalias, alias_count)
				alias_count = alias_count + 1
			end
			NSExec("numalias "..numalias..","..tostring(rawget(ob1, numalias)))
		else
			NSOkBox(numalias.." は既に定義されています。", "numalias")
			NSEnd()
		end
		return rawget(ob1, numalias)
	end end})
	_G.numalias = ob2
end
-- luasub定義・二重登録チェック・引数の簡単な取得をやる(肝)
do
	_G.NSPop = setmetatable({}, {__index=function(t, k)
		local arg = {}
		for i, v in ipairs({k:byte(1, k:len())}) do
			arg[i] = string.char(v)
		end
		local res = {}
		rawset(t, k, function()
			for i, tp in ipairs(arg) do
				    if tp == "i" then res[1+#res] = NSPopInt()
				elseif tp == "s" then res[1+#res] = NSPopStr()
				elseif tp == "I" then res[1+#res] = iNm[NSPopIntRef()]
				elseif tp == "S" then res[1+#res] = sNm[NSPopStrRef()]
				elseif tp == "D" then res[1+#res] = NSPopID()
				elseif tp == "L" then res[1+#res] = NSPopLabel()
				end
				if i == #arg then break end
				if NSCheckComma() then NSPopComma() else break end
			end
			return res
		end)
		return rawget(t, k)
	end})
	_G.luasub = setmetatable({}, {__index=function(t, k)
		local pn = "NSCOM_"..k
		if _G[pn] then
			NSOkBox("命令 "..k.." は既に定義されています。", "luasub")
			NSEnd()
		end
		Ns.luasub(Cd[k])
		return function(func, pop)
			if pop then
				_G[pn] = function()
					local arg = {}
					local res = {}
					for i, v in ipairs(pop()) do
						if type(v) == "function" then
							res[1+#res] = v
						else
							arg[1+#arg] = v
						end
					end
					for i, v in ipairs({func(unpack(arg))}) do
						if res[i] then res[i](v) end
					end
				end
			else
				_G[pn] = func
			end
		end
	end})
end