Skip to content

Instantly share code, notes, and snippets.

@marcoonroad
Last active October 17, 2016 04:40
Show Gist options
  • Save marcoonroad/ac3d7f6c7bf4141e2bbaf26e3c54d8b7 to your computer and use it in GitHub Desktop.
Save marcoonroad/ac3d7f6c7bf4141e2bbaf26e3c54d8b7 to your computer and use it in GitHub Desktop.
local prototype = { }
local reason = { }
local metatable = { }
prototype.erase = '__please_erase_this_property_in_lookup_rules__'
prototype.protected = true
reason.protected = "Protected against delegated mutation [%s <- %s] by [protected = true]!"
reason.missing = "No such property/value for selector [%s]!"
reason.mutation = "Unable to perform property addition/mutation [%s <- %s]!"
reason.extend = "Receiver object is protected by [protected = true] against [extend] invocation!"
reason.erase = "The identifier [erase] is reserved!"
local function lookup (self, selector)
if rawequal (selector, 'erase') then return prototype.erase end
-- avoids infinite recursion --
local parent = rawget (self, 'parent')
while not rawequal (parent, nil) do
local value = rawget (parent, selector)
if rawequal (value, nil) then
parent = rawget (parent, 'parent')
elseif rawequal (value, prototype.erase) then
break
else
return value
end
end
parent = rawget (self, 'parent')
if rawequal (parent, nil) or rawequal (parent, prototype.erase) then
return prototype.missing (self, selector)
else
-- late-bounded error handling --
return self: missing (selector)
end
end
local function detection (self, selector, value)
if rawequal (selector, 'erase') then
error (reason.erase)
end
return rawset (self, selector, value)
end
local function split (self, selector, value)
if self.parent.protected then
selector = tostring (selector)
value = tostring (value)
error (reason.protected: format (selector, value))
elseif rawequal (selector, 'erase') then
error (reason.erase)
else
self.parent[ selector ] = value
end
end
local function immutable (_, selector, value)
selector = tostring (selector)
value = tostring (value)
error (reason.mutation: format (selector, value))
end
-- short wrappers for invocations --
local function same (self, that)
return self: same (that)
end
local function pretty (self)
return self: pretty ( )
end
metatable.cloning = {
__metatable = '<cloning-metatable>',
__index = lookup,
__newindex = detection,
__eq = same,
__tostring = pretty,
}
metatable.extending = {
__metatable = '<extending-metatable>',
__index = lookup,
__newindex = split,
__eq = same,
__tostring = pretty,
}
metatable.immutability = {
__metatable = '<immutability-metatable>',
__index = lookup,
__newindex = immutable,
__eq = same,
__tostring = pretty,
}
-- value sharing delegation --
function prototype: clone (structure)
if not rawequal (structure.erase, nil) then
error (reason.erase)
end
local object = { }
for selector, value in pairs (structure) do
object[ selector ] = value
end
object.parent = self
return setmetatable (object, metatable.cloning)
end
-- property sharing delegation --
function prototype: extend (structure)
if self.protected then error (reason.extend) end
if not rawequal (structure.erase, nil) then
error (reason.erase)
end
local object = { }
for selector, value in pairs (structure) do
object[ selector ] = value
end
object.parent = self
return setmetatable (object, metatable.extending)
end
-- safe-aliasing --
function prototype: immutable ( )
local structure = { }
structure.parent = self
return setmetatable (structure, metatable.immutability)
end
-- pretty-printing --
function prototype: pretty ( )
return '<object>'
end
-- equality --
function prototype: same (that)
return rawequal (self, that)
end
-- error handling --
function prototype: missing (selector)
selector = tostring (selector)
error (reason.missing: format (selector))
end
setmetatable (prototype, metatable.cloning)
return prototype: immutable ( )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment