Luaの関数に動作を追加する。

_G環境、つまりどこからでも参照することのできるLuaの関数に、動作を追加したり、別の動作に差し替えたりする命令を追加します。

trigger.lua

-- trigger.lua
-- luaの関数の前後に、イベントを設定する。
-- または、luaの関数を外見はそのままに中身をそっくり入れ替える。

do
	-- トリガー関数保存変数
	local before_list = {}
	local overwrite_list = {}
	local after_list = {}

	-- 上書きされた元の関数の保存変数
	local original = {}
	
	-- _Gの関数を退避させる。
	local stack = function(func_name)
		if type(_G[func_name])~="function" then
			NSOkBox(func_name.."は関数ではありません。", "overwrite")
			NSEnd()
		end
		original[func_name] = original[func_name] or _G[func_name]
	end
	
	-- _Gを上書きする。
	local override = function(func_name)
		if not(original[func_name]) then
			stack(func_name)
			_G[func_name] = function(...)
				local arg = {...}
				if before_list[func_name] then
					arg = {before_list[func_name](unpack(arg))}
				end
				local res = {}
				
				if overwrite_list[func_name] then
					res = {overwrite_list[func_name](unpack(arg))}
				else
					res = {original[func_name](unpack(arg))}
				end
				
				if after_list[func_name] then
					return after_list[func_name](arg, res)
				end
				return unpack(res)
			end
		end
	end
	
	trigger = {}
	trigger.before = setmetatable({}, {__newindex=function(t, k, v)
		override(k)
		before_list[k] = v
	end})
	trigger.overwrite = setmetatable({}, {__newindex=function(t, k, v)
		override(k)
		overwrite_list[k] = v
	end})
	trigger.after = setmetatable({}, {__newindex=function(t, k, v)
		override(k)
		after_list[k] = v
	end})
	
	-- オリジナルの関数を実行する。
	org = setmetatable({}, {__index=function(t, k)
		if original[k] then
			return original[k]
		else
			return _G[k]
		end
	end})
end

解説

triggerで、関数に動作を追加します。

local add_func = function(...) return ... end
trigger.(timing).(func_name) = add_func

timingの部分には、before, overwrite, afterのどれかが入ります。それ以外は無効です。
その後の、func_nameには、動作を追加したい/差し替えたい関数の名前が入ります。
最後にイコールで新しい関数を代入することで完成です。

beforeの例

NSExecに対し、実行前に与えられた文字列を表示する動作を追加します。

trigger.before.NSExec = function(str)
	NSOkBox(str, "NSExec")
	return str
end

これで、他の全てのNSExec実行前に、NSExecに与えられる予定の文字列がポップアップします。
beforeタイミングに与えられる関数には、オリジナルの関数に与えられる引数がそのまま与えられます。
そして、この関数は、全く同じか加工した後の引数を返すことが期待されています。
上記の例で言えば、最後のreturn strです。
これは同じ物を返してもいいですし、加工して違うものを返してもよいです。引数のvalidation、汚染チェックや修正が可能です。もちろん、例のように注意に使ったり、あるいはログ保存目的でも構いませんし、条件次第ではNSEnd()してもよいでしょう。デバッグのために、製品版では外したり無効化したりもできるでしょう。

overwirteの例

overwriteでは、オリジナルの関数は実行されずに、ここで設定した新しい関数が実行されます。
新しい関数に与えられる引数は、beforeがあればbeforeの関数が返したものになり、そうでなければユーザーが設定したものになります。

afterの例

afterも基本的にな同じですが、引数が特殊になります。
二つのテーブルが与えられ、前者はオリジナルの関数に与えられた引数の配列であり、後者はその返り値の配列になります。

-- 何もしない例
trigger.after.NSExec = function(arg, res)
	return unpack(res)
end

beforeと同様に、関数の結果を記録したりするのに使えます。

念のため

一つの命令につき、before, overwrite, afterの三つを追加できます。

オリジナルを実行したい時には

orgを使います。

-- トリガーのない生を実行する。
org.NSExec(str)