Repository navigation
Expand file tree
/
Copy pathreadonly.lua
More file actions
112 lines (64 loc) · 3.48 KB
/
Copy pathreadonly.lua
File metadata and controls
112 lines (64 loc) · 3.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
---@module readonly
-- @author Christopher VanZomeren
-- @copyright (c) 2014 Christopher VanZomeren
assert(..., 'Do not use as main file; use require from different file')
local _id = select(2, ...) or ...
local assert = assert
local error = error
local pairs, ipairs = pairs, ipairs
local select = select
local setmetatable = setmetatable
local stderr = io.stderr
local tostring = tostring
local type = type
local require = require 'relative_require' (...)
local weak = require('.weak')
local _ENV = {}
if setfenv then setfenv(1, _ENV) end
local function warn(msg) stderr:write(_id, ': WARNING: ', msg, '\n') end
local target_of = weak 'k'
local readonly_mt = {__metatable = 'readonly'}
local is_usable_type = {boolean = true, string = true, number = true, ['nil'] = true, table = true}
local is_immutable_type = {boolean = true, string = true, number = true, ['nil'] = true}
local function isreadonly(t)
local type_t = type(t)
assert(is_usable_type[type_t])
return is_immutable_type[type_t] or target_of[t] ~= nil
end
local function readonly(_, t)
if isreadonly(t) then return t
else
local readonly_t = setmetatable({}, readonly_mt)
target_of[readonly_t] = t
return readonly_t
end
end
function readonly_mt.__call(t) error('Attempt to call '..tostring(target_of[t])..' through readonly interface.', 2) end
function readonly_mt.__index(t, k) return target_of[t][k] end
function readonly_mt.__newindex(t) error('Attempt to modify '..tostring(target_of[t])..' through readonly interface.', 2) end
function readonly_mt.__pairs(t)
local iter = pairs(target_of[t])
return function(t, ...) return iter(target_of[t] or t, ...) end
end
function readonly_mt.__ipairs(t)
local iter = ipairs(target_of[t])
return function(t, ...) return iter(target_of[t] or t, ...) end
end
function readonly_mt.__len(t) return readonly(nil, #(target_of[t])) end
function readonly_mt.__unm(t) return readonly(nil, -(target_of[t])) end
function readonly_mt.__tostring(t) return tostring(target_of[t]) end
function readonly_mt.__add(t1, t2) return readonly(nil, (target_of[t1] or t1) + (target_of[t2] or t2)) end
function readonly_mt.__sub(t1, t2) return readonly(nil, (target_of[t1] or t1) - (target_of[t2] or t2)) end
function readonly_mt.__mul(t1, t2) return readonly(nil, (target_of[t1] or t1) * (target_of[t2] or t2)) end
function readonly_mt.__div(t1, t2) return readonly(nil, (target_of[t1] or t1) / (target_of[t2] or t2)) end
function readonly_mt.__mod(t1, t2) return readonly(nil, (target_of[t1] or t1) % (target_of[t2] or t2)) end
function readonly_mt.__pow(t1, t2) return readonly(nil, (target_of[t1] or t1) ^ (target_of[t2] or t2)) end
function readonly_mt.__concat(t1, t2) return readonly(nil, (target_of[t1] or t1) .. (target_of[t2] or t2)) end
function readonly_mt.__eq(t1, t2) return (target_of[t1] or t1) == (target_of[t2] or t2) end
function readonly_mt.__lt(t1, t2) return (target_of[t1] or t1) < (target_of[t2] or t2) end
function readonly_mt.__le(t1, t2) return (target_of[t1] or t1) <= (target_of[t2] or t2) end
local _test = readonly(_,{1})
if not pairs(_test)(_test) then warn('pairs does not respect metamethods; iteration of readonly objects will not work') end
if not ipairs(_test)(_test, 0) then warn('ipairs does not respect metamethods; iteration of readonly objects will not work') end
if #_test ~= 1 then warn('\'#\' operator does not respect metamethods; use on readonly objects will not work') end
return setmetatable({isreadonly = isreadonly}, {__call = readonly, __metatable = 'callable'})