language-lua2-0.1.0.5: test/samples/2.lua
-- The constructor makes a tuple definition, which has its own intern table
-- tuples may have an optional binding to alias a key to a given tuple index
--[[
Tuples are immutable lists that can be used as table keys because they have
value semantics, that is, the tuple constructor returns the same identity for
the exact same list of identities
tuples are composed of only immutable values
TODO allow tuples to contain other tuples?
]]
local tuple = {}
local NAN = setmetatable({}, {__tostring = function() return "NAN" end})
local NIL = setmetatable({}, {__tostring = function() return "NIL" end})
local allowed_primitive_types = {
number = true,
string = true,
["nil"] = true,
}
local function value_to_key(value)
if value == nil then
return NIL
end
-- We rely on the property that NaN is the only value that doesn't equal itself
if value ~= value then
return NAN
end
return value
end
-- use a weak table?
local function create_index()
return {}
end
local function insert_tuple(tuple_def, index, raw_tuple)
local tuple = setmetatable({}, tuple_def.tuple_mt)
tuple_def.tuples[index] = tuple
tuple_def.raw_tuples[tuple] = raw_tuple
return tuple
end
local function get_tuple(values, index)
return values[index]
end
local function find_tuple_n(values, cur_index, count, value, ...)
if count == 0 then
return get_tuple(values, cur_index)
end
local key = value_to_key(value)
cur_index = cur_index[key]
if not cur_index then
return nil
end
return find_tuple_n(values, cur_index, count - 1, ...)
end
local function find_tuple(tuple_def, ...)
local cur_index = tuple_def.index
local values = tuple_def.tuples
local count = select('#', ...)
assert(count <= tuple_def.size)
return find_tuple_n(values, cur_index, count, ...)
end
local function new_tuple_n(tuple_def, cur_index, raw_tuple, count, value, ...)
if count == 0 then
return insert_tuple(tuple_def, cur_index, raw_tuple)
end
local key = value_to_key(value)
local next_index = cur_index[key]
if not next_index then
next_index = create_index()
cur_index[key] = next_index
end
local ndx = #raw_tuple + 1
local value_type = type(value)
assertf(allowed_primitive_types[value_type] or tuple.is_a(value),
"invalid tuple value %s (at position %d)", tostring(value), ndx)
raw_tuple[ndx] = value
return new_tuple_n(tuple_def, next_index, raw_tuple, count - 1, ...)
end
local function new_tuple(tuple_def, ...)
local cur_index = tuple_def.index
assert(select('#', ...) == tuple_def.size)
local raw_tuple = {}
return new_tuple_n(tuple_def, cur_index, raw_tuple, tuple_def.size, ...)
end
local function tuple_to_string(tuple_def, tuple)
local tuple_strs = {}
for i=1,#tuple_def.raw_tuples[tuple] do
tuple_strs[i] = tostring(tuple_def.raw_tuples[tuple][i])
end
-- TODO memoize this value in the tuple?
local tuple_content = table.concat(tuple_strs, ",")
return ("%s<%s>"):format(tuple_def.data_type, tuple_content)
end
function tuple.is_a(inst)
local mt = getmetatable(inst)
return mt and mt.__tuple
end
--- define a tuple
-- @tparam number size number of elements in the tuple
-- @tparam table binding list of keys to use as an alias for the tuple index
function tuple.define(data_type, size, binding)
local wrapper = {}
binding = binding or {}
assert(#binding == 0 or #binding == size)
for tuple_ndx, alias_key in ipairs(binding) do
assert(type(alias_key) == "string")
wrapper[alias_key] = tuple_ndx
end
local tuple_def = {
data_type = data_type,
size = size,
raw_tuples = {}, -- tuple lookup to raw tuple
tuples = {}, -- element N (last X) lookup to tuple
index = {}, -- element X lookup to element X + 1
lookup_alias = wrapper,
}
-- use the same metatable for all tuples
tuple_def.tuple_mt = {
__index = function(t, k)
local raw = tuple_def.raw_tuples[t]
return raw[k] or raw[tuple_def.lookup_alias[k]]
end,
__data_type = tuple_def.data_type,
__tuple = true,
__newindex = false,
__tostring = function(t)
return tuple_to_string(tuple_def, t)
end
}
return tuple_def
end
--- create an instance of `tuple_def` populated from the vararg
function tuple.new(tuple_def, ...)
return find_tuple(tuple_def, ...) or new_tuple(tuple_def, ...)
end
return tuple