fixed issues with metamethods. All tests passing

This commit is contained in:
Enrique García Cota 2011-09-07 21:27:15 +02:00
parent fad5027340
commit 0ba7409a88
3 changed files with 12 additions and 233 deletions

View File

@ -1,218 +0,0 @@
-----------------------------------------------------------------------------------------------------------------------
-- inspect.lua - v1.1.1 (2011-01)
-- Enrique García Cota - enrique.garcia.cota [AT] gmail [DOT] com
-- human-readable representations of tables.
-- inspired by http://lua-users.org/wiki/TableSerialization
-----------------------------------------------------------------------------------------------------------------------
-- Apostrophizes the string if it has quotes, but not aphostrophes
-- Otherwise, it returns a regular quoted string
local function smartQuote(str)
if string.match( string.gsub(str,"[^'\"]",""), '^"+$' ) then
return "'" .. str .. "'"
end
return string.format("%q", str )
end
local controlCharsTranslation = {
["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v", ["\\"] = "\\\\"
}
local function unescapeChar(c) return controlCharsTranslation[c] end
local function unescape(str)
local result, _ = string.gsub( str, "(%c)", unescapeChar )
return result
end
local function isIdentifier(str)
return string.match( str, "^[_%a][_%a%d]*$" )
end
local function isArrayKey(k, length)
return type(k)=='number' and 1 <= k and k <= length
end
local function isDictionaryKey(k, length)
return not isArrayKey(k, length)
end
local sortOrdersByType = {
['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
['function'] = 5, ['userdata'] = 6, ['thread'] = 7
}
local function sortKeys(a,b)
local ta, tb = type(a), type(b)
if ta ~= tb then return sortOrdersByType[ta] < sortOrdersByType[tb] end
if ta == 'string' or ta == 'number' then return a < b end
return false
end
local function getDictionaryKeys(t)
local length = #t
local keys = {}
for k,_ in pairs(t) do
if isDictionaryKey(k, length) then table.insert(keys,k) end
end
table.sort(keys, sortKeys)
return keys
end
local function getToStringResultSafely(t, mt)
local __tostring = type(mt) == 'table' and mt.__tostring
local string, status
if type(__tostring) == 'function' then
status, string = pcall(__tostring, t)
string = status and string or 'error: ' .. tostring(string)
end
return string
end
local Inspector = {}
function Inspector:new(v, depth)
local inspector = {
buffer = {},
depth = depth,
level = 0,
counters = {
['function'] = 0,
['userdata'] = 0,
['thread'] = 0,
['table'] = 0
},
pools = {
['function'] = setmetatable({}, {__mode = "kv"}),
['userdata'] = setmetatable({}, {__mode = "kv"}),
['thread'] = setmetatable({}, {__mode = "kv"}),
['table'] = setmetatable({}, {__mode = "kv"})
}
}
setmetatable( inspector, {
__index = Inspector,
__tostring = function(instance) return table.concat(instance.buffer) end
} )
return inspector:putValue(v)
end
function Inspector:puts(...)
local args = {...}
for i=1, #args do
table.insert(self.buffer, tostring(args[i]))
end
return self
end
function Inspector:tabify()
self:puts("\n", string.rep(" ", self.level))
return self
end
function Inspector:up()
self.level = self.level - 1
end
function Inspector:down()
self.level = self.level + 1
end
function Inspector:putComma(comma)
if comma then self:puts(',') end
return true
end
function Inspector:putTable(t)
if self:alreadySeen(t) then
self:puts('<table ', self:getOrCreateCounter(t), '>')
elseif self.level >= self.depth then
self:puts('{...}')
else
self:puts('<',self:getOrCreateCounter(t),'>{')
self:down()
local length = #t
local mt = getmetatable(t)
local string = getToStringResultSafely(t, mt)
if type(string) == 'string' and #string > 0 then
self:puts(' -- ', unescape(string))
if length >= 1 then self:tabify() end -- tabify the array values
end
local comma = false
for i=1, length do
comma = self:putComma(comma)
self:puts(' '):putValue(t[i])
end
local dictKeys = getDictionaryKeys(t)
for _,k in ipairs(dictKeys) do
comma = self:putComma(comma)
self:tabify():putKey(k):puts(' = '):putValue(t[k])
end
if mt then
comma = self:putComma(comma)
self:tabify():puts('<metatable> = '):putValue(mt)
end
self:up()
if #dictKeys > 0 or mt then -- dictionary table. Justify closing }
self:tabify()
elseif length > 0 then -- array tables have one extra space before closing }
self:puts(' ')
end
self:puts('}')
end
return self
end
function Inspector:alreadySeen(v)
local tv = type(v)
return self.pools[tv][v] ~= nil
end
function Inspector:getOrCreateCounter(v)
local tv = type(v)
local current = self.pools[tv][v]
if not current then
current = self.counters[tv] + 1
self.counters[tv] = current
self.pools[tv][v] = current
end
return current
end
function Inspector:putValue(v)
local tv = type(v)
if tv == 'string' then
self:puts(smartQuote(unescape(v)))
elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then
self:puts(tostring(v))
elseif tv == 'table' then
self:putTable(v)
else
self:puts('<',tv,' ',self:getOrCreateCounter(v),'>')
end
return self
end
function Inspector:putKey(k)
if type(k) == "string" and isIdentifier(k) then
return self:puts(k)
end
return self:puts( "[" ):putValue(k):puts("]")
end
local function inspect(t, depth)
depth = depth or 4
return tostring(Inspector:new(t, depth))
end
return inspect

View File

@ -4,7 +4,7 @@
-- Based on YaciCode, from Julien Patte and LuaObject, from Sebastien Rocca-Serra
-----------------------------------------------------------------------------------------------------------------------
local _classes = setmetatable({}, {__mode = "k"})
local _classes = setmetatable({}, { __mode = "k" })
local function _setClassDictionariesMetatables(klass)
local dict = klass.__instanceDict
@ -13,7 +13,6 @@ local function _setClassDictionariesMetatables(klass)
local super = klass.super
if super then
local superStatic = super.static
setmetatable(dict, klass.__inherited)
setmetatable(klass.static, { __index = function(_,k) return dict[k] or superStatic[k] end })
else
setmetatable(klass.static, { __index = function(_,k) return dict[k] end })
@ -23,7 +22,8 @@ end
local function _addInstanceMethodToClass(klass, name, method)
klass.__instanceDict[name] = method
for subclass, _ in pairs(klass.subclasses) do
subclass.__inherited[name] = method
local existing = subclass.__instanceDict[name]
subclass.__instanceDict[name] = existing or method
end
end
@ -38,15 +38,15 @@ end
local function _copyInheritedMethods(klass, super)
for name, method in pairs(super.__instanceDict) do
if klass.metamethods[method] then klass.__instanceDict[name] = method
else klass.__inherited[name] = method end
local existing = klass.__instanceDict[name]
klass.__instanceDict[name] = existing or method
end
end
local function _createClass(name, super)
local klass = {
name = name, super = super, static = {}, subclasses = setmetatable({}, {__mode = "k"}),
__mixins = {}, __instanceDict={}, __inherited ={}
name = name, super = super, static = {}, subclasses = setmetatable({}, { __mode = "k" }),
__mixins = {}, __instanceDict = {}
}
_setClassDictionariesMetatables(klass)

View File

@ -1,7 +1,5 @@
require 'middleclass'
local inspect = require 'inspect'
context('Metamethods', function()
context('Custom Metamethods', function()
@ -10,6 +8,7 @@ context('Metamethods', function()
-- I'll be using 'a' instead of 'self' on this example since it is shorter
local Vector= class('Vector')
function Vector.initialize(a,x,y,z) a.x, a.y, a.z = x,y,z end
function Vector.__tostring(a) return a.class.name .. '[' .. a.x .. ',' .. a.y .. ',' .. a.z .. ']' end
function Vector.__eq(a,b) return a.x==b.x and a.y==b.y and a.z==b.z end
function Vector.__lt(a,b) return a() < b() end
@ -50,13 +49,12 @@ context('Metamethods', function()
assert_equal(values[1], values[2])
end)
end
context('Inherited Metamethods', function()
local Vector2= class('Vector2', Vector)
function Vector2:initialize(x,y,z) Vector.initialize(self,x,y,z) end
local c = Vector2:new(1,2,3)
print(inspect(c))
local d = Vector2:new(2,4,6)
for metamethod,values in pairs({
__tostring = { tostring(c), "Vector2[1,2,3]" },
@ -77,7 +75,7 @@ context('Metamethods', function()
end)
end
end)
end)
context('Default Metamethods', function()
@ -99,8 +97,7 @@ context('Metamethods', function()
end)
context('An instance', function()
test('has a tostring metamethod, returning a different result from Object.__tostring', function()
assert_not_equal(Peter.__tostring, Object.__tostring)
test('has a tostring metamethod', function()
assert_equal(tostring(peter), 'instance of class Peter')
end)
end)