summaryrefslogtreecommitdiff
path: root/compat53
diff options
context:
space:
mode:
Diffstat (limited to 'compat53')
-rw-r--r--compat53/init.lua373
-rw-r--r--compat53/module.lua827
2 files changed, 1200 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 :
diff --git a/compat53/module.lua b/compat53/module.lua
new file mode 100644
index 0000000..30be6b5
--- /dev/null
+++ b/compat53/module.lua
@@ -0,0 +1,827 @@
1local _G, _VERSION = _G, _VERSION
2local lua_version = _VERSION:sub(-3)
3
4
5local M = _G
6
7if lua_version < "5.3" then
8
9 -- cache globals in upvalues
10 local error, ipairs, pairs, pcall, require, select, setmetatable, type =
11 error, ipairs, pairs, pcall, require, select, setmetatable, type
12 local debug, io, math, package, string, table =
13 debug, io, math, package, string, table
14 local io_lines = io.lines
15 local io_read = io.read
16 local unpack = lua_version == "5.1" and unpack or table.unpack
17
18 -- create module table
19 M = {}
20 local M_meta = {
21 __index = _G,
22 -- __newindex is set at the end
23 }
24 setmetatable(M, M_meta)
25
26 -- create subtables
27 M.io = setmetatable({}, { __index = io })
28 M.math = setmetatable({}, { __index = math })
29 M.string = setmetatable({}, { __index = string })
30 M.table = setmetatable({}, { __index = table })
31 M.utf8 = {}
32
33
34 -- select the most powerful getmetatable function available
35 local gmt = type(debug) == "table" and debug.getmetatable or
36 getmetatable or function() return false end
37
38 -- type checking functions
39 local checkinteger -- forward declararation
40
41 local function argcheck(cond, i, f, extra)
42 if not cond then
43 error("bad argument #"..i.." to '"..f.."' ("..extra..")", 0)
44 end
45 end
46
47
48 -- load utf8 library
49 local utf8_ok, utf8lib = pcall(require, "compat53.utf8")
50 if utf8_ok then
51 if lua_version == "5.1" then
52 utf8lib.charpattern = "[%z\1-\127\194-\244][\128-\191]*"
53 end
54 for k,v in pairs(utf8lib) do
55 M.utf8[k] = v
56 end
57 package.loaded["utf8"] = M.utf8
58 end
59
60
61 -- load table library
62 local table_ok, tablib = pcall(require, "compat53.table")
63 if table_ok then
64 for k,v in pairs(tablib) do
65 M.table[k] = v
66 end
67 end
68
69
70 -- load string packing functions
71 local str_ok, strlib = pcall(require, "compat53.string")
72 if str_ok then
73 for k,v in pairs(strlib) do
74 M.string[k] = v
75 end
76 end
77
78
79 -- try Roberto's struct module for string packing/unpacking if
80 -- compat53.string is unavailable
81 if not str_ok then
82 local struct_ok, struct = pcall(require, "struct")
83 if struct_ok then
84 M.string.pack = struct.pack
85 M.string.packsize = struct.size
86 M.string.unpack = struct.unpack
87 end
88 end
89
90
91 -- update math library
92 do
93 local maxint, minint = 1
94
95 while maxint+1 > maxint and 2*maxint > maxint do
96 maxint = maxint * 2
97 end
98 if 2*maxint <= maxint then
99 maxint = 2*maxint-1
100 minint = -maxint-1
101 else
102 maxint = maxint
103 minint = -maxint
104 end
105 M.math.maxinteger = maxint
106 M.math.mininteger = minint
107
108 function M.math.tointeger(n)
109 if type(n) == "number" and n <= maxint and n >= minint and n % 1 == 0 then
110 return n
111 end
112 return nil
113 end
114
115 function M.math.type(n)
116 if type(n) == "number" then
117 if n <= maxint and n >= minint and n % 1 == 0 then
118 return "integer"
119 else
120 return "float"
121 end
122 else
123 return nil
124 end
125 end
126
127 function checkinteger(x, i, f)
128 local t = type(x)
129 if t ~= "number" then
130 error("bad argument #"..i.." to '"..f..
131 "' (number expected, got "..t..")", 0)
132 elseif x > maxint or x < minint or x % 1 ~= 0 then
133 error("bad argument #"..i.." to '"..f..
134 "' (number has no integer representation)", 0)
135 else
136 return x
137 end
138 end
139
140 function M.math.ult(m, n)
141 m = checkinteger(m, "1", "math.ult")
142 n = checkinteger(n, "2", "math.ult")
143 if m >= 0 and n < 0 then
144 return true
145 elseif m < 0 and n >= 0 then
146 return false
147 else
148 return m < n
149 end
150 end
151 end
152
153
154 -- assert should allow non-string error objects
155 function M.assert(cond, ...)
156 if cond then
157 return cond, ...
158 elseif select('#', ...) > 0 then
159 error((...), 0)
160 else
161 error("assertion failed!", 0)
162 end
163 end
164
165
166 -- ipairs should respect __index metamethod
167 do
168 local function ipairs_iterator(st, var)
169 var = var + 1
170 local val = st[var]
171 if val ~= nil then
172 return var, st[var]
173 end
174 end
175 function M.ipairs(t)
176 if gmt(t) ~= nil then -- t has metatable
177 return ipairs_iterator, t, 0
178 else
179 return ipairs(t)
180 end
181 end
182 end
183
184
185 -- make '*' optional for io.read and io.lines
186 do
187 local function addasterisk(fmt)
188 if type(fmt) == "string" and fmt:sub(1, 1) ~= "*" then
189 return "*"..fmt
190 else
191 return fmt
192 end
193 end
194
195 function M.io.read(...)
196 local n = select('#', ...)
197 for i = 1, n do
198 local a = select(i, ...)
199 local b = addasterisk(a)
200 -- as an optimization we only allocate a table for the
201 -- modified format arguments when we have a '*' somewhere.
202 if a ~= b then
203 local args = { ... }
204 args[i] = b
205 for j = i+1, n do
206 args[j] = addasterisk(args[j])
207 end
208 return io_read(unpack(args, 1, n))
209 end
210 end
211 return io_read(...)
212 end
213
214 -- PUC-Rio Lua 5.1 uses a different implementation for io.lines!
215 function M.io.lines(...)
216 local n = select('#', ...)
217 for i = 2, n do
218 local a = select(i, ...)
219 local b = addasterisk(a)
220 -- as an optimization we only allocate a table for the
221 -- modified format arguments when we have a '*' somewhere.
222 if a ~= b then
223 local args = { ... }
224 args[i] = b
225 for j = i+1, n do
226 args[j] = addasterisk(args[j])
227 end
228 return io_lines(unpack(args, 1, n))
229 end
230 end
231 return io_lines(...)
232 end
233 end
234
235
236 -- update table library (if C module not available)
237 if not table_ok then
238 local table_concat = table.concat
239 local table_insert = table.insert
240 local table_remove = table.remove
241 local table_sort = table.sort
242
243 function M.table.concat(list, sep, i, j)
244 local mt = gmt(list)
245 if type(mt) == "table" and type(mt.__len) == "function" then
246 local src = list
247 list, i, j = {}, i or 1, j or mt.__len(src)
248 for k = i, j do
249 list[k] = src[k]
250 end
251 end
252 return table_concat(list, sep, i, j)
253 end
254
255 function M.table.insert(list, ...)
256 local mt = gmt(list)
257 local has_mt = type(mt) == "table"
258 local has_len = has_mt and type(mt.__len) == "function"
259 if has_mt and (has_len or mt.__index or mt.__newindex) then
260 local e = (has_len and mt.__len(list) or #list)+1
261 local nargs, pos, value = select('#', ...), ...
262 if nargs == 1 then
263 pos, value = e, pos
264 elseif nargs == 2 then
265 pos = checkinteger(pos, "2", "table.insert")
266 argcheck(1 <= pos and pos <= e, "2", "table.insert",
267 "position out of bounds" )
268 else
269 error("wrong number of arguments to 'insert'", 0)
270 end
271 for i = e-1, pos, -1 do
272 list[i+1] = list[i]
273 end
274 list[pos] = value
275 else
276 return table_insert(list, ...)
277 end
278 end
279
280 function M.table.move(a1, f, e, t, a2)
281 a2 = a2 or a1
282 f = checkinteger(f, "2", "table.move")
283 argcheck(f > 0, "2", "table.move",
284 "initial position must be positive")
285 e = checkinteger(e, "3", "table.move")
286 t = checkinteger(t, "4", "table.move")
287 if e >= f then
288 local m, n, d = 0, e-f, 1
289 if t > f then m, n, d = n, m, -1 end
290 for i = m, n, d do
291 a2[t+i] = a1[f+i]
292 end
293 end
294 return a2
295 end
296
297 function M.table.remove(list, pos)
298 local mt = gmt(list)
299 local has_mt = type(mt) == "table"
300 local has_len = has_mt and type(mt.__len) == "function"
301 if has_mt and (has_len or mt.__index or mt.__newindex) then
302 local e = (has_len and mt.__len(list) or #list)
303 pos = pos ~= nil and checkinteger(pos, "2", "table.remove") or e
304 if pos ~= e then
305 argcheck(1 <= pos and pos <= e+1, "2", "table.remove",
306 "position out of bounds" )
307 end
308 local result = list[pos]
309 while pos < e do
310 list[pos] = list[pos+1]
311 pos = pos + 1
312 end
313 list[pos] = nil
314 return result
315 else
316 return table_remove(list, pos)
317 end
318 end
319
320 do
321 local function pivot(list, cmp, a, b)
322 local m = b - a
323 if m > 2 then
324 local c = a + (m-m%2)/2
325 local x, y, z = list[a], list[b], list[c]
326 if not cmp(x, y) then
327 x, y, a, b = y, x, b, a
328 end
329 if not cmp(y, z) then
330 y, b = z, c
331 end
332 if not cmp(x, y) then
333 y, b = x, a
334 end
335 return b, y
336 else
337 return b, list[b]
338 end
339 end
340
341 local function lt_cmp(a, b)
342 return a < b
343 end
344
345 local function qsort(list, cmp, b, e)
346 if b < e then
347 local i, j, k, val = b, e, pivot(list, cmp, b, e)
348 while i < j do
349 while i < j and cmp(list[i], val) do
350 i = i + 1
351 end
352 while i < j and not cmp(list[j], val) do
353 j = j - 1
354 end
355 if i < j then
356 list[i], list[j] = list[j], list[i]
357 if i == k then k = j end -- update pivot position
358 i, j = i+1, j-1
359 end
360 end
361 if i ~= k and not cmp(list[i], val) then
362 list[i], list[k] = val, list[i]
363 k = i -- update pivot position
364 end
365 qsort(list, cmp, b, i == k and i-1 or i)
366 return qsort(list, cmp, i+1, e)
367 end
368 end
369
370 function M.table.sort(list, cmp)
371 local mt = gmt(list)
372 local has_mt = type(mt) == "table"
373 local has_len = has_mt and type(mt.__len) == "function"
374 if has_len then
375 cmp = cmp or lt_cmp
376 local len = mt.__len(list)
377 return qsort(list, cmp, 1, len)
378 else
379 return table_sort(list, cmp)
380 end
381 end
382 end
383
384 local function unpack_helper(list, i, j, ...)
385 if j < i then
386 return ...
387 else
388 return unpack_helper(list, i, j-1, list[j], ...)
389 end
390 end
391 function M.table.unpack(list, i, j)
392 local mt = gmt(list)
393 local has_mt = type(mt) == "table"
394 local has_len = has_mt and type(mt.__len) == "function"
395 if has_mt and (has_len or mt.__index) then
396 i, j = i or 1, j or (has_len and mt.__len(list)) or #list
397 return unpack_helper(list, i, j)
398 else
399 return unpack(list, i, j)
400 end
401 end
402 end -- update table library
403
404
405 -- bring Lua 5.1 (and LuaJIT) up to speed with Lua 5.2
406 if lua_version == "5.1" then
407 -- detect LuaJIT (including LUAJIT_ENABLE_LUA52COMPAT compilation flag)
408 local is_luajit = (string.dump(function() end) or ""):sub(1, 3) == "\027LJ"
409 local is_luajit52 = is_luajit and
410 #setmetatable({}, { __len = function() return 1 end }) == 1
411
412 -- cache globals in upvalues
413 local load, loadfile, loadstring, setfenv, xpcall =
414 load, loadfile, loadstring, setfenv, xpcall
415 local coroutine, os = coroutine, os
416 local coroutine_create = coroutine.create
417 local coroutine_resume = coroutine.resume
418 local coroutine_running = coroutine.running
419 local coroutine_status = coroutine.status
420 local coroutine_yield = coroutine.yield
421 local io_input = io.input
422 local io_open = io.open
423 local io_output = io.output
424 local io_write = io.write
425 local math_log = math.log
426 local os_execute = os.execute
427 local string_find = string.find
428 local string_format = string.format
429 local string_gmatch = string.gmatch
430 local string_gsub = string.gsub
431 local string_match = string.match
432 local string_rep = string.rep
433 local table_concat = table.concat
434
435 -- create subtables
436 M.coroutine = setmetatable({}, { __index = coroutine })
437 M.os = setmetatable({}, { __index = os })
438 M.package = setmetatable({}, { __index = package })
439
440 -- handle debug functions
441 if type(debug) == "table" then
442 local debug_setfenv = debug.setfenv
443 local debug_getfenv = debug.getfenv
444 local debug_setmetatable = debug.setmetatable
445
446 M.debug = setmetatable({}, { __index = debug })
447
448 if not is_luajit52 then
449 function M.debug.setuservalue(obj, value)
450 if type(obj) ~= "userdata" then
451 error("bad argument #1 to 'setuservalue' (userdata expected, got "..
452 type(obj)..")", 2)
453 end
454 if value == nil then value = _G end
455 if type(value) ~= "table" then
456 error("bad argument #2 to 'setuservalue' (table expected, got "..
457 type(value)..")", 2)
458 end
459 return debug_setfenv(obj, value)
460 end
461
462 function M.debug.getuservalue(obj)
463 if type(obj) ~= "userdata" then
464 return nil
465 else
466 local v = debug_getfenv(obj)
467 if v == _G or v == package then
468 return nil
469 end
470 return v
471 end
472 end
473
474 function M.debug.setmetatable(value, tab)
475 debug_setmetatable(value, tab)
476 return value
477 end
478 end -- not luajit with compat52 enabled
479 end -- debug table available
480
481
482 if not is_luajit52 then
483 function M.pairs(t)
484 local mt = gmt(t)
485 if type(mt) == "table" and type(mt.__pairs) == "function" then
486 return mt.__pairs(t)
487 else
488 return pairs(t)
489 end
490 end
491 end
492
493
494 if not is_luajit then
495 local function check_mode(mode, prefix)
496 local has = { text = false, binary = false }
497 for i = 1,#mode do
498 local c = mode:sub(i, i)
499 if c == "t" then has.text = true end
500 if c == "b" then has.binary = true end
501 end
502 local t = prefix:sub(1, 1) == "\27" and "binary" or "text"
503 if not has[t] then
504 return "attempt to load a "..t.." chunk (mode is '"..mode.."')"
505 end
506 end
507
508 function M.load(ld, source, mode, env)
509 mode = mode or "bt"
510 local chunk, msg
511 if type( ld ) == "string" then
512 if mode ~= "bt" then
513 local merr = check_mode(mode, ld)
514 if merr then return nil, merr end
515 end
516 chunk, msg = loadstring(ld, source)
517 else
518 local ld_type = type(ld)
519 if ld_type ~= "function" then
520 error("bad argument #1 to 'load' (function expected, got "..
521 ld_type..")", 2)
522 end
523 if mode ~= "bt" then
524 local checked, merr = false, nil
525 local function checked_ld()
526 if checked then
527 return ld()
528 else
529 checked = true
530 local v = ld()
531 merr = check_mode(mode, v or "")
532 if merr then return nil end
533 return v
534 end
535 end
536 chunk, msg = load(checked_ld, source)
537 if merr then return nil, merr end
538 else
539 chunk, msg = load(ld, source)
540 end
541 end
542 if not chunk then
543 return chunk, msg
544 end
545 if env ~= nil then
546 setfenv(chunk, env)
547 end
548 return chunk
549 end
550
551 M.loadstring = M.load
552
553 function M.loadfile(file, mode, env)
554 mode = mode or "bt"
555 if mode ~= "bt" then
556 local f = io_open(file, "rb")
557 if f then
558 local prefix = f:read(1)
559 f:close()
560 if prefix then
561 local merr = check_mode(mode, prefix)
562 if merr then return nil, merr end
563 end
564 end
565 end
566 local chunk, msg = loadfile(file)
567 if not chunk then
568 return chunk, msg
569 end
570 if env ~= nil then
571 setfenv(chunk, env)
572 end
573 return chunk
574 end
575 end -- not luajit
576
577
578 if not is_luajit52 then
579 function M.rawlen(v)
580 local t = type(v)
581 if t ~= "string" and t ~= "table" then
582 error("bad argument #1 to 'rawlen' (table or string expected)", 2)
583 end
584 return #v
585 end
586 end
587
588
589 if not is_luajit then
590 function M.xpcall(f, msgh, ...)
591 local args, n = { ... }, select('#', ...)
592 return xpcall(function() return f(unpack(args, 1, n)) end, msgh)
593 end
594 end
595
596
597 if not is_luajit52 then
598 function M.os.execute(cmd)
599 local code = os_execute(cmd)
600 -- Lua 5.1 does not report exit by signal.
601 if code == 0 then
602 return true, "exit", code
603 else
604 if package.config:sub(1, 1) == '/' then
605 code = code/256 -- only correct on Linux!
606 end
607 return nil, "exit", code
608 end
609 end
610 end
611
612
613 if not table_ok and not is_luajit52 then
614 M.table.pack = function(...)
615 return { n = select('#', ...), ... }
616 end
617 end
618
619
620 local main_coroutine = coroutine_create(function() end)
621
622 function M.coroutine.create(func)
623 local success, result = pcall(coroutine_create, func)
624 if not success then
625 if type(func) ~= "function" then
626 error("bad argument #1 (function expected)", 0)
627 end
628 result = coroutine_create(function(...) return func(...) end)
629 end
630 return result
631 end
632
633 if not is_luajit52 then
634 function M.coroutine.running()
635 local co = coroutine_running()
636 if co then
637 return co, false
638 else
639 return main_coroutine, true
640 end
641 end
642 end
643
644 function M.coroutine.yield(...)
645 local co, flag = coroutine_running()
646 if co and not flag then
647 return coroutine_yield(...)
648 else
649 error("attempt to yield from outside a coroutine", 0)
650 end
651 end
652
653 if not is_luajit then
654 function M.coroutine.resume(co, ...)
655 if co == main_coroutine then
656 return false, "cannot resume non-suspended coroutine"
657 else
658 return coroutine_resume(co, ...)
659 end
660 end
661
662 function M.coroutine.status(co)
663 local notmain = coroutine_running()
664 if co == main_coroutine then
665 return notmain and "normal" or "running"
666 else
667 return coroutine_status(co)
668 end
669 end
670 end -- not luajit
671
672
673 if not is_luajit then
674 M.math.log = function(x, base)
675 if base ~= nil then
676 return math_log(x)/math_log(base)
677 else
678 return math_log(x)
679 end
680 end
681 end
682
683
684 if not is_luajit then
685 function M.package.searchpath(name, path, sep, rep)
686 sep = (sep or "."):gsub("(%p)", "%%%1")
687 rep = (rep or package.config:sub(1, 1)):gsub("(%%)", "%%%1")
688 local pname = name:gsub(sep, rep):gsub("(%%)", "%%%1")
689 local msg = {}
690 for subpath in path:gmatch("[^;]+") do
691 local fpath = subpath:gsub("%?", pname)
692 local f = io_open(fpath, "r")
693 if f then
694 f:close()
695 return fpath
696 end
697 msg[#msg+1] = "\n\tno file '" .. fpath .. "'"
698 end
699 return nil, table_concat(msg)
700 end
701 end
702
703
704 local function fix_pattern(pattern)
705 return (string_gsub(pattern, "%z", "%%z"))
706 end
707
708 function M.string.find(s, pattern, ...)
709 return string_find(s, fix_pattern(pattern), ...)
710 end
711
712 function M.string.gmatch(s, pattern)
713 return string_gmatch(s, fix_pattern(pattern))
714 end
715
716 function M.string.gsub(s, pattern, ...)
717 return string_gsub(s, fix_pattern(pattern), ...)
718 end
719
720 function M.string.match(s, pattern, ...)
721 return string_match(s, fix_pattern(pattern), ...)
722 end
723
724 if not is_luajit then
725 function M.string.rep(s, n, sep)
726 if sep ~= nil and sep ~= "" and n >= 2 then
727 return s .. string_rep(sep..s, n-1)
728 else
729 return string_rep(s, n)
730 end
731 end
732 end
733
734 if not is_luajit then
735 do
736 local addqt = {
737 ["\n"] = "\\\n",
738 ["\\"] = "\\\\",
739 ["\""] = "\\\""
740 }
741
742 local function addquoted(c, d)
743 return (addqt[c] or string_format(d~="" and "\\%03d" or "\\%d", c:byte()))..d
744 end
745
746 function M.string.format(fmt, ...)
747 local args, n = { ... }, select('#', ...)
748 local i = 0
749 local function adjust_fmt(lead, mods, kind)
750 if #lead % 2 == 0 then
751 i = i + 1
752 if kind == "s" then
753 args[i] = _G.tostring(args[i])
754 elseif kind == "q" then
755 args[i] = '"'..string_gsub(args[i], "([%z%c\\\"\n])(%d?)", addquoted)..'"'
756 return lead.."%"..mods.."s"
757 end
758 end
759 end
760 fmt = string_gsub(fmt, "(%%*)%%([%d%.%-%+%# ]*)(%a)", adjust_fmt)
761 return string_format(fmt, unpack(args, 1, n))
762 end
763 end
764 end
765
766
767 function M.io.write(...)
768 local res, msg, errno = io_write(...)
769 if res then
770 return io_output()
771 else
772 return nil, msg, errno
773 end
774 end
775
776 if not is_luajit then
777 local function helper(st, var_1, ...)
778 if var_1 == nil then
779 if st.doclose then st.f:close() end
780 if (...) ~= nil then
781 error((...), 2)
782 end
783 end
784 return var_1, ...
785 end
786
787 local function lines_iterator(st)
788 return helper(st, st.f:read(unpack(st, 1, st.n)))
789 end
790
791 function M.io.lines(fname, ...)
792 local doclose, file, msg
793 if fname ~= nil then
794 doclose, file, msg = true, io_open(fname, "r")
795 if not file then error(msg, 2) end
796 else
797 doclose, file = false, io_input()
798 end
799 local st = { f=file, doclose=doclose, n=select('#', ...), ... }
800 for i = 1, st.n do
801 local t = type(st[i])
802 if t == "string" then
803 local fmt = st[i]:match("^%*?([aln])")
804 if not fmt then
805 error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
806 end
807 st[i] = "*"..fmt
808 elseif t ~= "number" then
809 error("bad argument #"..(i+1).." to 'for iterator' (invalid format)", 2)
810 end
811 end
812 return lines_iterator, st
813 end
814 end -- not luajit
815
816 end -- lua 5.1
817
818 -- further write should be forwarded to _G
819 M_meta.__newindex = _G
820
821end -- lua < 5.3
822
823
824-- return module table
825return M
826
827-- vi: set expandtab softtabstop=3 shiftwidth=3 :