summaryrefslogtreecommitdiff
path: root/compat53/init.lua
diff options
context:
space:
mode:
Diffstat (limited to 'compat53/init.lua')
-rw-r--r--compat53/init.lua373
1 files changed, 373 insertions, 0 deletions
diff --git a/compat53/init.lua b/compat53/init.lua
new file mode 100644
index 0000000..a7f0c80
--- /dev/null
+++ b/compat53/init.lua
@@ -0,0 +1,373 @@
1local lua_version = _VERSION:sub(-3)
2
3
4if lua_version < "5.3" then
5
6 local _G, pairs, require, select, type =
7 _G, pairs, require, select, type
8 local debug, io = debug, io
9 local unpack = lua_version == "5.1" and unpack or table.unpack
10
11 local M = require("compat53.module")
12
13 -- select the most powerful getmetatable function available
14 local gmt = type(debug) == "table" and debug.getmetatable or
15 getmetatable or function() return false end
16 -- metatable for file objects from Lua's standard io library
17 local file_meta = gmt(io.stdout)
18
19
20 -- make '*' optional for file:read and file:lines
21 if type(file_meta) == "table" and type(file_meta.__index) == "table" then
22
23 local function addasterisk(fmt)
24 if type(fmt) == "string" and fmt:sub(1, 1) ~= "*" then
25 return "*"..fmt
26 else
27 return fmt
28 end
29 end
30
31 local file_lines = file_meta.__index.lines
32 file_meta.__index.lines = function(self, ...)
33 local n = select('#', ...)
34 for i = 1, n do
35 local a = select(i, ...)
36 local b = addasterisk(a)
37 -- as an optimization we only allocate a table for the
38 -- modified format arguments when we have a '*' somewhere
39 if a ~= b then
40 local args = { ... }
41 args[i] = b
42 for j = i+1, n do
43 args[j] = addasterisk(args[j])
44 end
45 return file_lines(self, unpack(args, 1, n))
46 end
47 end
48 return file_lines(self, ...)
49 end
50
51 local file_read = file_meta.__index.read
52 file_meta.__index.read = function(self, ...)
53 local n = select('#', ...)
54 for i = 1, n do
55 local a = select(i, ...)
56 local b = addasterisk(a)
57 -- as an optimization we only allocate a table for the
58 -- modified format arguments when we have a '*' somewhere
59 if a ~= b then
60 local args = { ... }
61 args[i] = b
62 for j = i+1, n do
63 args[j] = addasterisk(args[j])
64 end
65 return file_read(self, unpack(args, 1, n))
66 end
67 end
68 return file_read(self, ...)
69 end
70
71 end -- got a valid metatable for file objects
72
73
74 -- changes for Lua 5.1 only
75 if lua_version == "5.1" then
76
77 -- cache globals
78 local error, pcall, rawset, setmetatable, tostring, xpcall =
79 error, pcall, rawset, setmetatable, tostring, xpcall
80 local coroutine, package, string = coroutine, package, string
81 local coroutine_resume = coroutine.resume
82 local coroutine_running = coroutine.running
83 local coroutine_status = coroutine.status
84 local coroutine_yield = coroutine.yield
85 local io_type = io.type
86
87
88 -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag)
89 local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ"
90 local is_luajit52 = is_luajit and
91 #setmetatable({}, { __len = function() return 1 end }) == 1
92
93
94 -- make package.searchers available as an alias for package.loaders
95 local p_index = { searchers = package.loaders }
96 setmetatable(package, {
97 __index = p_index,
98 __newindex = function(p, k, v)
99 if k == "searchers" then
100 rawset(p, "loaders", v)
101 p_index.searchers = v
102 else
103 rawset(p, k, v)
104 end
105 end
106 })
107
108
109 if type(file_meta) == "table" and type(file_meta.__index) == "table" then
110 if not is_luajit then
111 local function helper(_, var_1, ...)
112 if var_1 == nil then
113 if (...) ~= nil then
114 error((...), 2)
115 end
116 end
117 return var_1, ...
118 end
119
120 local function lines_iterator(st)
121 return helper(st, st.f:read(unpack(st, 1, st.n)))
122 end
123
124 local file_write = file_meta.__index.write
125 file_meta.__index.write = function(self, ...)
126 local res, msg, errno = file_write(self, ...)
127 if res then
128 return self
129 else
130 return nil, msg, errno
131 end
132 end
133
134 file_meta.__index.lines = function(self, ...)
135 if io_type(self) == "closed file" then
136 error("attempt to use a closed file", 2)
137 end
138 local st = { f=self, n=select('#', ...), ... }
139 for i = 1, st.n do
140 local t = type(st[i])
141 if t == "string" then
142 local fmt = st[i]:match("^*?([aln])")
143 if not fmt then
144 error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
145 end
146 st[i] = "*"..fmt
147 elseif t ~= "number" then
148 error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
149 end
150 end
151 return lines_iterator, st
152 end
153 end -- not luajit
154 end -- file_meta valid
155
156
157 -- the (x)pcall implementations start a new coroutine internally
158 -- to allow yielding even in Lua 5.1. to allow for accurate
159 -- stack traces we keep track of the nested coroutine activations
160 -- in the weak tables below:
161 local weak_meta = { __mode = "kv" }
162 -- maps the internal pcall coroutines to the user coroutine that
163 -- *should* be running if pcall didn't use coroutines internally
164 local pcall_mainOf = setmetatable({}, weak_meta)
165 -- table that maps each running coroutine started by pcall to
166 -- the coroutine that resumed it (user coroutine *or* pcall
167 -- coroutine!)
168 local pcall_previous = setmetatable({}, weak_meta)
169 -- reverse of `pcall_mainOf`. maps a user coroutine to the
170 -- currently active pcall coroutine started within it
171 local pcall_callOf = setmetatable({}, weak_meta)
172 -- similar to `pcall_mainOf` but is used only while executing
173 -- the error handler of xpcall (thus no nesting is necessary!)
174 local xpcall_running = setmetatable({}, weak_meta)
175
176 -- handle debug functions
177 if type(debug) == "table" then
178 local debug_getinfo = debug.getinfo
179 local debug_traceback = debug.traceback
180
181 if not is_luajit then
182 local function calculate_trace_level(co, level)
183 if level ~= nil then
184 for out = 1, 1/0 do
185 local info = (co==nil) and debug_getinfo(out, "") or debug_getinfo(co, out, "")
186 if info == nil then
187 local max = out-1
188 if level <= max then
189 return level
190 end
191 return nil, level-max
192 end
193 end
194 end
195 return 1
196 end
197
198 local stack_pattern = "\nstack traceback:"
199 local stack_replace = ""
200 function debug.traceback(co, msg, level)
201 local lvl
202 local nilmsg
203 if type(co) ~= "thread" then
204 co, msg, level = coroutine_running(), co, msg
205 end
206 if msg == nil then
207 msg = ""
208 nilmsg = true
209 elseif type(msg) ~= "string" then
210 return msg
211 end
212 if co == nil then
213 msg = debug_traceback(msg, level or 1)
214 else
215 local xpco = xpcall_running[co]
216 if xpco ~= nil then
217 lvl, level = calculate_trace_level(xpco, level)
218 if lvl then
219 msg = debug_traceback(xpco, msg, lvl)
220 else
221 msg = msg..stack_pattern
222 end
223 lvl, level = calculate_trace_level(co, level)
224 if lvl then
225 local trace = debug_traceback(co, "", lvl)
226 msg = msg..trace:gsub(stack_pattern, stack_replace)
227 end
228 else
229 co = pcall_callOf[co] or co
230 lvl, level = calculate_trace_level(co, level)
231 if lvl then
232 msg = debug_traceback(co, msg, lvl)
233 else
234 msg = msg..stack_pattern
235 end
236 end
237 co = pcall_previous[co]
238 while co ~= nil do
239 lvl, level = calculate_trace_level(co, level)
240 if lvl then
241 local trace = debug_traceback(co, "", lvl)
242 msg = msg..trace:gsub(stack_pattern, stack_replace)
243 end
244 co = pcall_previous[co]
245 end
246 end
247 if nilmsg then
248 msg = msg:gsub("^\n", "")
249 end
250 msg = msg:gsub("\n\t%(tail call%): %?", "\000")
251 msg = msg:gsub("\n\t%.%.%.\n", "\001\n")
252 msg = msg:gsub("\n\t%.%.%.$", "\001")
253 msg = msg:gsub("(%z+)\001(%z+)", function(some, other)
254 return "\n\t(..."..#some+#other.."+ tail call(s)...)"
255 end)
256 msg = msg:gsub("\001(%z+)", function(zeros)
257 return "\n\t(..."..#zeros.."+ tail call(s)...)"
258 end)
259 msg = msg:gsub("(%z+)\001", function(zeros)
260 return "\n\t(..."..#zeros.."+ tail call(s)...)"
261 end)
262 msg = msg:gsub("%z+", function(zeros)
263 return "\n\t(..."..#zeros.." tail call(s)...)"
264 end)
265 msg = msg:gsub("\001", function()
266 return "\n\t..."
267 end)
268 return msg
269 end
270 end -- is not luajit
271 end -- debug table available
272
273
274 if not is_luajit52 then
275 local coroutine_running52 = M.coroutine.running
276 function M.coroutine.running()
277 local co, ismain = coroutine_running52()
278 if ismain then
279 return co, true
280 else
281 return pcall_mainOf[co] or co, false
282 end
283 end
284 end
285
286 if not is_luajit then
287 local function pcall_results(current, call, success, ...)
288 if coroutine_status(call) == "suspended" then
289 return pcall_results(current, call, coroutine_resume(call, coroutine_yield(...)))
290 end
291 if pcall_previous then
292 pcall_previous[call] = nil
293 local main = pcall_mainOf[call]
294 if main == current then current = nil end
295 pcall_callOf[main] = current
296 end
297 pcall_mainOf[call] = nil
298 return success, ...
299 end
300
301 local function pcall_exec(current, call, ...)
302 local main = pcall_mainOf[current] or current
303 pcall_mainOf[call] = main
304 if pcall_previous then
305 pcall_previous[call] = current
306 pcall_callOf[main] = call
307 end
308 return pcall_results(current, call, coroutine_resume(call, ...))
309 end
310
311 local coroutine_create52 = M.coroutine.create
312
313 local function pcall_coroutine(func)
314 if type(func) ~= "function" then
315 local callable = func
316 func = function (...) return callable(...) end
317 end
318 return coroutine_create52(func)
319 end
320
321 function M.pcall(func, ...)
322 local current = coroutine_running()
323 if not current then return pcall(func, ...) end
324 return pcall_exec(current, pcall_coroutine(func), ...)
325 end
326
327 local function xpcall_catch(current, call, msgh, success, ...)
328 if not success then
329 xpcall_running[current] = call
330 local ok, result = pcall(msgh, ...)
331 xpcall_running[current] = nil
332 if not ok then
333 return false, "error in error handling ("..tostring(result)..")"
334 end
335 return false, result
336 end
337 return true, ...
338 end
339
340 function M.xpcall(f, msgh, ...)
341 local current = coroutine_running()
342 if not current then
343 local args, n = { ... }, select('#', ...)
344 return xpcall(function() return f(unpack(args, 1, n)) end, msgh)
345 end
346 local call = pcall_coroutine(f)
347 return xpcall_catch(current, call, msgh, pcall_exec(current, call, ...))
348 end
349 end -- not luajit
350
351 end -- lua 5.1
352
353
354 -- handle exporting to global scope
355 local function extend_table(from, to)
356 if from ~= to then
357 for k,v in pairs(from) do
358 if type(v) == "table" and
359 type(to[k]) == "table" and
360 v ~= to[k] then
361 extend_table(v, to[k])
362 else
363 to[k] = v
364 end
365 end
366 end
367 end
368
369 extend_table(M, _G)
370
371end -- lua < 5.3
372
373-- vi: set expandtab softtabstop=3 shiftwidth=3 :