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