Files
aux-addon-ClassicAPI/libs/module.lua
T
Manuel Simon Hirsig e3565015ad refactoring
2016-08-26 16:39:57 +02:00

147 lines
5.0 KiB
Lua

if module then return end
local type, setmetatable, setfenv, unpack, next, pcall, _G = type, setmetatable, setfenv, unpack, next, pcall, getfenv(0)
local start_declaration, declaration, env_mt, interface_mt
local INDEX, NEWINDEX, CALL = 1, 2, 3
local function error(msg, ...) return _G.error(format(msg or '', unpack(arg))..'\n'..debugstack(), 0) end
local function import_error() error 'Invalid imports.' end
local function declaration_error() error 'Invalid declaration.' end
local function collision_error(key) error('"%s" already exists.', key) end
local function nop() end
local function id(_) return _ end
local function const(_) return function() return _ end end
--local function vararg_id(...) return unpack(arg) end
--local function vararg_const(...) return function() return unpack(arg) end end
local _state, _public = {}, {[nop]=true}
do
local function declare(self, public, type, name, value)
if _G.type(value) ~= 'function' and (not type or declaration_error()) then value, type = const(value), INDEX end
if self[type][name] then declaration_error() end
self[type][name], _public[value] = value, public
if self[CALL][name] and self[INDEX][name] then declaration_error() end
end
declaration = nop
do
local function extract(v)
local call, get, set
call, get, set, v.call, v.get, v.set = v.call, v.get, v.set, nil, nil, nil
if next(v) then error() end
return call, get, set
end
local PUBLIC = {public=true, private=false}
local PREFIX_TYPE = {method=CALL, getter=INDEX, setter=NEWINDEX}
local SUFFIX_TYPE = {call=CALL, get=INDEX, set=NEWINDEX}
local state, public, type, name
local function intercept(self, event, key, value)
if self ~= state then declaration_error() end
if event == INDEX and (not name or declaration_error()) then
if PUBLIC[key] then
public = (public ~= nil and declaration_error()) or PUBLIC[key]
elseif PREFIX_TYPE[key] then
type = (type and declaration_error()) or PREFIX_TYPE[key]
elseif not type or declaration_error() then
name = key
end
return true
elseif event == NEWINDEX then
if name then
type = SUFFIX_TYPE[key] or declaration_error()
key = name
end
declare(self, public, type, key, value)
declaration = nop
return true
elseif event == CALL then
if name then
local success, call, get, set = pcall(extract, value)
if not success then declaration_error() end
if call then declare(self, public, type, name, call) end
if get then declare(self, public, type, name, get) end
if set then declare(self, public, type, name, set) end
else
self.access, self.type = public, type
end
declaration = nop
return true
end
end
function start_declaration(self, public, type)
if declaration ~= nop then declaration_error() end
declaration, state, public, type, name = intercept, self, public, type, nil
end
end
env_mt = {__metatable=false}
function env_mt:__index(key) local state=_state[self]
if declaration(state, INDEX, key) then return self end
if state[INDEX][key] then
return state[INDEX][key]()
elseif state[CALL][key] then
return state[CALL][key]
else
return _G[key]
end
end
function env_mt:__newindex(key, value) local state=_state[self]
if declaration(state, NEWINDEX, key, value) then return end
if state[NEWINDEX][key] then state[NEWINDEX][key](value) end
declare(state, state.modifiers, key, value) -- TODO
end
function env_mt:__call(key, value)
declaration(_state[self], CALL, key, value)
end
end
interface_mt = {__metatable=false}
function interface_mt:__index(key) local state=_state[self]
local call = state[CALL][key]
if call and _public[call] then
return call
else
local index = state[INDEX][key]
return (_public[index] and index or nop)()
end
end
function interface_mt:__newindex(key, value) local state=_state[self]
local f = state[NEWINDEX][key] or state[CALL][key] or nop
if _public[f] then f(value) end
end
--function interface_mt:__call(key, ...) local state=_state[self]
-- -- TODO new instance
--end
function module(...)
local env, interface = setmetatable({}, env_mt), setmetatable({}, interface_mt)
local state; state = {
[CALL]={error=error, nop=nop, id=id, const=const},
[INDEX]={
_G = const(_G),
M = const(env),
I = const(interface),
private = function() start_declaration(state, false); return env end,
public = function() start_declaration(state, true); return env end,
accessor = function() start_declaration(state, nil, INDEX); return env end,
mutator = function() start_declaration(state, nil, NEWINDEX); return env end,
},
[NEWINDEX]={},
public=false,
}
for i=1,arg.n do
local module = state[arg[i] or import_error()] or import_error()
local call, index, newindex = state[CALL], state[INDEX], state[NEWINDEX]
for _, type in {CALL, INDEX, NEWINDEX} do
for k, f in module[type] do
if _public[f] and (not (call[k] or index[k] or newindex[k]) or import_error()) then
state[type] = f
end
end
end
end
_state[env], _state[interface] = state, state
setfenv(2, env)
end