glueeveryday Lua functions |
|
local glue = require'glue'
tables | |
glue.index(t) -> dt | switch keys with values |
glue.keys(t[, sorted | cmp]) -> dt | make a list of all the keys |
glue.update(dt,t1,...) -> dt | merge tables - overwrites keys |
glue.merge(dt,t1,...) -> dt | merge tables - no overwriting |
glue.sortedpairs(t[, cmp])-> iterator |
like pairs() but in key order |
lists | |
glue.extend(dt,t1,...) -> dt | extend a list |
glue.append(dt,v1,...) -> dt | append non-nil values to a list |
glue.shift(t,i,n) -> t | shift list elements |
strings | |
glue.gsplit(s,sep[, plain]) -> iterator |
split a string by a pattern |
glue.trim(s) -> s | remove padding |
glue.escape(s[,mode])-> s | escape magic pattern characters |
glue.tohex(s) -> s | string to hex |
glue.fromhex(s) -> s | hex to string |
iterators | |
glue.collect([i,]iterator)-> t | collect iterated values into a list |
closures | |
glue.pass(...) -> ... | does nothing, returns back all arguments |
metatables | |
glue.inherit(t,parent) -> t | set or clear inheritance |
glue.autotable([t]) -> t | autotable pattern |
i/o | |
glue.fileexists(file) -> true | false | check if a file exists and it's readable |
glue.readfile(file[,format]) -> s | nil, err | read the contents of a file into a string |
glue.writefile(file,s[,format]) | write a string to a file |
errors | |
glue.assert(v,[message[,args...]]) -> args | assert with error message formatting |
glue.unprotect(ok,result,...) -> result,... | nil,result,... | unprotect a protected call |
glue.pcall(f,...) -> true,... | false,traceback | pcall with traceback (not for Lua 5.1) |
glue.fpcall(f,...) -> result | nil,traceback | coding with finally and except |
glue.fcall(f,...) -> result | |
modules | |
glue.autoload(t, submodule) -> t | autoload table keys from submodules |
glue.autoload(t, key, module|loader) -> t | autoload table keys from submodules |
glue.bin | get the script's directory |
glue.luapath(path[, index[, ext]]) | insert a path in package.path |
glue.cpath(path[, index]) | insert a path in package.cpath |
malloc | |
glue.malloc([ctype, ]size) -> cdata | allocate an array using system's malloc |
glue.malloc(ctype) -> cdata | allocate a C type using system's malloc |
glue.free(cdata) | free malloc'ed memory |
glue.index(t) -> dt
Switch table keys with values.
Extract a rfc850 date from a string. Use lookup tables for weekdays and months.
local weekdays = glue.index{'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'}
local months = glue.index{'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'}
--weekday "," SP 2DIGIT "-" month "-" 2DIGIT SP 2DIGIT ":" 2DIGIT ":" 2DIGIT SP "GMT"
--eg. Sunday, 06-Nov-94 08:49:37 GMT
function rfc850date(s)
local w,d,mo,y,h,m,s = s:match'([A-Za-z]+), (%d+)%-([A-Za-z]+)%-(%d+) (%d+):(%d+):(%d+) GMT'
d,y,h,m,s = tonumber(d),tonumber(y),tonumber(h),tonumber(m),tonumber(s)
w = assert(weekdays[w])
mo = assert(months[mo])
if y then y = y + (y > 50 and 1900 or 2000) end
return {wday = w, day = d, year = y, month = mo, hour = h, min = m, sec = s}
end
for k,v in pairs(rfc850date'Sunday, 06-Nov-94 08:49:37 GMT') do
print(k,v)
end
Output
day 6
sec 37
wday 1
min 49
year 1994
month 11
hour 8
Copy-paste a bunch of defines from a C header file and create an inverse lookup table to find the name of a value at runtime.
--from ibase.h
info_end_codes = {
isc_info_end = 1, --normal ending
isc_info_truncated = 2, --receiving buffer too small
isc_info_error = 3, --error, check status vector
isc_info_data_not_ready = 4, --data not available for some reason
isc_info_svc_timeout = 64, --timeout expired
}
info_end_code_names = glue.index(info_end_codes)
print(info_end_code_names[64])
Output
isc_info_svc_timeout
glue.keys(t[, sorted | cmp]) -> dt
Make a list of all the keys of t
, optionally sorted.
An API expects a list of things but you have them as keys in a table because you are indexing something on them.
For instance, you have a table of the form {socket = thread}
but socket.select
wants a list of sockets.
See also: glue.sortedpairs.
glue.update(dt,t1,...) -> dt
Update a table with elements of other tables, overwriting any existing keys.
Create an options table by merging the options received as an argument (if any) over the default options.
function f(opts)
opts = glue.update({}, default_opts, opts)
end
Shallow table copy:
t = glue.update({}, t)
Static multiple inheritance:
C = glue.update({}, A, B) --#TODO: find real-world example of multiple inheritance
See also: glue.extend, glue.inherit.
glue.merge(dt,t1,...) -> dt
Update a table with elements of other tables skipping on any existing keys.
Normalize a data object with default values:
glue.merge(t, defaults)
See also: glue.update.
glue.sortedpairs(t[,cmp]) -> iterator<k,v>
Like pairs() but in key order.
The implementation creates a temporary table to sort the keys in.
See also: glue.keys.
glue.extend(dt,t1,...) -> dt
Extend the list with the elements of other lists.
#dt
.Accumulating values from multiple list sources.
See also: glue.append, glue.update.
glue.append(dt,v1,...) -> dt
Append non-nil arguments to a list.
Appending an object to a flattened list of lists (eg. appending a path element to a 2d path).
See also: glue.extend, glue.update.
glue.shift(t,i,n) -> t
Shift all the list elements starting at index i
, n
positions to the left or further to the right.
For a positive n
, shift the elements further to the right, effectively creating room for n
new elements at index i
. When n
is 1, the effect is the same as for table.insert(t, i, t[i])
. The old values at index i
to i+n-1
are preserved, so #t
still works after the shifting.
For a negative n
, shift the elements to the left, effectively removing the n
elements at index i
. When n
is -1, the effect is the same as for table.remove(t, i)
.
Removing a portion of a list or making room for more elements inside the list.
See also: glue.extend.
glue.gsplit(s,sep[,plain]) -> iterator<e[,captures...]>
Split a string by a separator pattern (or plain string) and iterate over the elements.
glue.gsplit(',', ',')
produces 2 empty stringsfor s in glue.gsplit('Spam eggs spam spam and ham', '%s*spam%s*') do
print('"'..s..'"')
end
> "Spam eggs"
> ""
> "and ham"
gmatch
and gsub
sep
doesn't have very readable semanticsglue.trim(s) -> s
Remove whitespace (defined as Lua pattern %s
) from the beginning and end of a string.
glue.escape(s[,mode]) -> pat
Escape magic characters of the string s
so that it can be used as a pattern to string matching functions.
mode
can have the value "*i"
(for case insensitive), in which case each alphabetical character in s
will also be escaped as [aA]
so that it matches both its lowercase and uppercase variants.%z
pattern.Test the performance of the case-insensitive hack to see if it's feasible.
glue.tohex(s[,upper]) -> s
glue.tohex(n[,upper]) -> s
Convert a binary string or a Lua number to its hex representation.
upper
is anything non-false, like say, the string "upper"See also: glue.fromhex.
glue.fromhex(s) -> s
Convert a hex string to its binary representation.
See also: glue.tohex.
glue.collect([i, ]iterator) -> t
Iterate an iterator and collect its i'th return value of every step into a list.
Implementation of keys()
and values()
in terms of collect()
keys = function(t) return glue.collect(pairs(t)) end
values = function(t) return glue.collect(2,pairs(t)) end
Collecting string matches:
s = 'a,b,c,'
t = glue.collect(s:gmatch'(.-),')
for i=1,#t do print(t[i]) end
> a
> b
> c
Alt. name: ipack
- like pack but for iterators; collect is better at suggesting a process done in steps.
glue.pass(...) -> ...
The identity function. Does nothing, returns back all arguments.
Default value for optional callback arguments:
function urlopen(url, callback, errback)
callback = callback or glue.pass
errback = errback or glue.pass
...
callback()
end
glue.inherit(t, parent) -> t
glue.inherit(t, nil) -> t
Set a table to inherit attributes from a parent table, or clear inheritance.
If the table has no metatable (and inheritance has to be set, not cleared) make it one.
Logging mixin:
AbstractLogger = glue.inherit({}, function(t,k) error('abstract '..k) end)
NullLogger = glue.inherit({log = function() end}, AbstractLogger)
PrintLogger = glue.inherit({log = function(self,...) print(...) end}, AbstractLogger)
HttpRequest = glue.inherit({
perform = function(self, url)
self:log('Requesting', url, '...')
...
end
}, NullLogger)
LoggedRequest = glue.inherit({log = PrintLogger.log}, HttpRequest)
LoggedRequest:perform'http://lua.org/'
> Requesting http://lua.org/ ...
Defining a module in Lua 5.2
_ENV = glue.inherit({},_G)
...
Hints:
t = setmetatable({},{__index=parent})
is not much longer and it's idiomatic, but doesn't shout inheritance at you (you have to process the indirection, like with functional idioms) and you can't use it to change the parent (a minor quibble nevertheless).
Overriding of methods needs an easy way to access the "parent" or to invoke a method on the parent. A top-level class could provide this simply by defining function Object:parent() return getmetatable(self).__index end
.
glue.autotable([t]) -> t
Set a table to create/return missing keys as autotables.
In the example below, t.a
, t.a.b
, t.a.b.c
are created automatically as autotables.
local t = autotable()
t.a.b.c.d = 'hello'
glue.fileexists(file) -> true | false
Checks whether a file exists and it's available for reading.
See also: glue.readfile.
glue.readfile(file[,format]) -> s | nil, err
Read the contents of a file into a string.
format
can be t
in which case the file will be read in text mode (default is binary mode).See also: glue.writefile, glue.fileexists.
glue.writefile(file,s[,format])
Write the contents of a string to a file.
format
can be t
in which case the file will be written in text mode (default is binary mode).See also: glue.readfile.
glue.assert(v[, message[, format_args...]])
Like assert
but supports formatting of the error message using string.format.
This is better than assert(string.format(message, format_args...))
because it avoids creating the message string when the assertion is true.
glue.assert(depth <= maxdepth, 'maximum depth %d exceeded', maxdepth)
glue.unprotect(ok,result,...) -> result,... | nil,result,...
In Lua, API functions conventionally signal errors by returning nil and an error message instead of raising exceptions. In the implementation however, using assert() and error() is preferred to coding explicit conditional flows to cover exceptional cases. Use this function to convert error-raising functions to nice nil,error-returning functions:
function my_API_function()
return glue.unprotect(pcall(function()
...
assert(...)
...
error(...)
...
return result_value
end))
end
glue.pcall(f,...) -> true,... | false,error..'\n'..traceback
With Lua's pcall() you lose the stack trace, and with usual uses of pcall() you don't want that. This variant appends the traceback to the error message.
NOTE: Lua 5.2 and LuaJIT only.
glue.fpcall(f,...) -> result | nil,error..'\n'..traceback
glue.fcall(f,...) -> result
These constructs bring the ubiquitous try/finally/except idiom to Lua. The first variant returns nil,error when errors occur while the second re-raises the error.
local result = glue.fpcall(function(finally, except, ...)
local temporary_resource = acquire_resource()
finally(function() temporary_resource:free() end)
...
local final_resource = acquire_resource()
except(function() final_resource:free() end)
... code that might break ...
return final_resource
end, ...)
glue.autoload(t, submodules) -> t
glue.autoload(t, key, module|loader) -> t
Assign a metatable to t
such that when a missing key is accessed, the module said to contain that key is require'd automatically.
The submodules
argument is a table of form {key = module_name | load_function}
specifying the corresponding Lua module (or load function) that make each key available to t
. The alternative syntax allows specifying the key - submodule associations one by one.
Module autoloading allows you to split the implementation of a module in many submodules containing optional, self-contained functionality, without having to make this visible in the user API. This effectively separates how you split your APIs from how you split the implementation, allowing you to change the way the implementation is split at a later time while keeping the API intact.
main module (foo.lua):
local function bar() --function implemented in the main module
...
end
--create and return the module table
return glue.autoload({
...
bar = bar,
}, {
baz = 'foo_extra', --autoloaded function, implemented in module foo_extra
})
submodule (foo_extra.lua):
local foo = require'foo'
function foo.baz(...)
...
end
in usage:
local foo = require'foo'
foo.baz(...) -- foo_extra was now loaded automatically
glue.bin
Get the script's directory, based on lua-find-bin by David Manura. This allows finding files in the script's directory regardless of the directory that Lua is started in.
local foobar = glue.readfile(glue.bin .. '/' .. file_near_this_script)
This only works if glue itself can already be found and required (chicken/egg catch22 and the rest). The path is relative to the current directory, it stops working if the current directory is changed. The assumption is that if you can do chdir() then you can also do getfullpath() or at least getcurrentdir(), and thus correct glue.bin
in time with:
glue.bin = getfullpath(glue.bin)
or
glue.bin = getcurrentdir() .. '/' .. glue.bin
glue.luapath(path[, index[, ext]])
Insert a Lua search pattern in package.path
such that require
will be able to load Lua modules from that path. The optional index
arg specifies the insert position (default is 1, that is, before all existing paths; can be negative, to start counting from the end; can be the string 'after', which is the same as 0). The optional ext
arg specifies the file extension to use (default is "lua").
glue.cpath(path[, index])
Insert a Lua search pattern in package.cpath
such that require
will be able to load Lua/C modules from that path. The index
arg has the same meaning as with glue.luapath
.
glue.luapath(glue.bin)
glue.cpath(glue.bin)
require'foo' --looking for `foo` in the same directory as the running script first
glue.malloc([ctype, ]size) -> cdata
Allocate a ctype[size]
array with system's malloc. Useful for allocating larger chunks of memory without hitting the default allocator's 2 GB limit.
ctype(&)[size]
so ffi.sizeof(cdata) returns the correct size.ctype
defaults to char
.glue.free()
.REMEMBER! Just like with ffi.new
, casting the result cdata further will get you weak references to the allocated memory. To transfer ownership of the memory, use ffi.gc(original, nil); ffi.gc(pointer, glue.free)
.
NOTE: LuaJIT only.
CAVEAT: For primitive types, you must specify a size, or glue.free() will not work!
glue.malloc(ctype) -> cdata
Allocate a ctype
with system's malloc. The result has the type ctype&
.
glue.free(cdata)
Free malloc'ed memory.
local data = glue.malloc(100)
assert(ffi.sizeof(data) == 100)
glue.free(data)
local data = glue.malloc('int', 100)
assert(ffi.sizeof(data) == 100 * ffi.sizeof'int')
glue.free(data)
local data = glue.malloc('struct S')
assert(ffi.typeof(data) ==
assert(ffi.sizeof(data) == ffi.sizeof'struct S')
glue.free(data)
String functions are also in the glue.string
table. You can extend the Lua string
namespace:
glue.update(string, glue.string)
so you can use them as string methods:
s = s:trim()
for syntax highlighting
glue.index, glue.keys, glue.update, glue.merge, glue.extend, glue.append, glue.shift, glue.gsplit, glue.trim, glue.escape, glue.collect, glue.pass, glue.inherit, glue.fileexists, glue.readfile, glue.writefile, glue.assert, glue.unprotect, glue.pcall, glue.fpcall, glue.fcall, glue.autoload, glue.bin, glue.luapath, glue.cpath