Просмотр исходного кода

New Lua class system. No longer supports super(), use Superclass.method(self, args) convention instead. __index__ and __set_callback renamed to __getvar and __setvar respectively. Full inheritance now supported with getter and setter acessors propagating properly through superclasses. Static methods now accessed as ClassName.staticMethod()

Ivan Safrin 13 лет назад
Родитель
Сommit
355ae20d37

+ 58 - 410
Bindings/Contents/LUA/API/class.lua

@@ -1,412 +1,60 @@
-local u = {}
-
-function u.wrongarg(n,expected,got)
-  return 'arg '..n..' expected to be '..expected..' (got '..tostring(got)..')'
-end
-
-local wrongarg = u.wrongarg
-
-function u.isname(name)
-  return type(name)=='string' and string.find(name,'^[_%a][_%w]*$')
-end
-
-local isname = u.isname
-
-function u.assert(value,errmsg,...)
-  if value then
-    return value
-  else
-    if type(errmsg)=='nil' then
-      error('assertion failed!',2)
-    elseif type(errmsg)=='string' then
-      error(errmsg,2)
-    else
-      error(errmsg(unpack(arg)),2)
-    end
-  end
-end
-
-local assert = u.assert
-
-function u.fwrongarg(...)
-  return function()
-    return wrongarg(unpack(arg))
-  end
-end
-
-local fwrongarg = u.fwrongarg
-
-
-
-
-local INFO = '__info'
-
-
-function u.isobject(o)
-  return type(o)=='table' and rawget(o,INFO)
-end
-
-local isobject = u.isobject
-
-
-local METAMETHODS = {
-  '__tostring',
-  '__add',
-  '__sub',
-  '__mul',
-  '__div',
-  '__pow',
-  '__lt',
-  '__le',
-  '__eq',
-  '__call',
-  '__unm',
-  '__concat',
-  '__newindex',
-}
-
-
-local metatable = {}
-
-for _, name in ipairs(METAMETHODS) do
-  local name = name
-  metatable[name] = function(...)
-    local a, b = unpack(arg)
-    local f
-    if isobject(a) then
-      f = a[name]
-    end
-    if not f and isobject(b) then
-      f = b[name]
-    end
-    if not f then
-    local name = name..'__'
-    if isobject(a) then
-      f = a[name]
-    end
-    if not f and isobject(b) then
-      f = b[name]
-    end
-    end
-    assert(f, function()
-                 local class = rawget(a,INFO).__class
-                 local cname = rawget(class,INFO).__name
-                 return 'meta-method not found: '..cname..':'..name
-               end)
-    return f(unpack(arg))
-  end
-end
-
-
-local
-function table2object(t)
-  assert(type(t)=='table', fwrongarg(1,'table',t))
-  local info = {}
-  rawset(t,INFO,info)
-  setmetatable(t,metatable)
-
-  local p = newproxy(true)
-  local mp = getmetatable(p)
-  function mp:__gc()
-    if rawget(t,INFO) == info then
-      t:finalize()
-    end
-  end
-  rawget(t,INFO).__proxy = p
-
-  return t
-end
-
-local
-function object2table(o)
-  assert(isobject(o), fwrongarg(1,'object',o))
-  setmetatable(o,nil)
-  rawset(o,INFO,nil)
-  return o
-end
-
-local
-function givename(o,name)
-  assert(isobject(o), fwrongarg(1,'object',o))
-  assert(isname(name), fwrongarg(2,'name',name))
-  rawget(o,INFO).__name = name
-  getfenv(2)[name] = o
-end
-
-local
-function setclass(o,class)
-  assert(isobject(o), fwrongarg(1,'object',o))
-  assert(isobject(class), fwrongarg(2,'object',class))
-  rawget(o,INFO).__class = class
-end
-
-local
-function setsuper(class,superclass)
-  assert(isobject(class), fwrongarg(1,'object',class))
-  assert(isobject(superclass), fwrongarg(2,'object',superclass))
-  rawget(class,INFO).__super = superclass
-end
-
-local
-function object2class(o,name)
-  assert(isobject(o), fwrongarg(1,'object',o))
-  assert(isname(name), fwrongarg(2,'name',name))
-  givename(o,name)
-  rawget(o,INFO).__methods = {}
-  rawget(o,INFO).__isclass = true
-  rawget(o,INFO).__cmethods = {}
-end
-
-local
-function findmethod(class,name,iscmethod)
-  local storage = iscmethod and '__cmethods' or '__methods'
-  while class do
-    local info = rawget(class,INFO)
-    value = info[storage][name]
-    if value ~= nil then
-      return value
-    end
-    class = info.__super
-  end
-end
-
-
-
-function metatable:__index(name)
-  local value
-
-  if rawget(self,INFO).__isclass then
-    value = findmethod(self,name,true)
-    if value ~= nil then
-      return value
-    end
-  end
-
-  local class = rawget(self,INFO).__class
-  value = findmethod(class,name)
-  if value ~= nil then
-    return value
-  end
-
-  if name ~= '__index' and name ~= '__index__' then
-    local index = self.__index or self.__index__ 
-    if index then
-      value = index(self,name)
-      if value ~= nil then
-        return value
-      end
-    end
-  end
-end
-
-
-local _Object = table2object{}
-object2class(_Object,"Object")
-
-
-
-local _Class = table2object{}
-object2class(_Class,"Class")
-
-
-setclass(Object,Class)
-setclass(Class,Class)
-setsuper(Class,Object)
-
-
-local
-function makesupermethod(self,name,iscmethod)
-  return function(...)
-    local method
-    local classinfo
-    if iscmethod then
-      classinfo = rawget(self,INFO)
-    else
-      local class = rawget(self,INFO).__class
-      classinfo = rawget(class,INFO)
-    end
-    local super = classinfo.__super
-    if super then
-      method = findmethod(super,name,iscmethod)
-    end
-    assert(method, "no super method for "..classinfo.__name..":"..name)
-    return method(self,unpack(arg))
-  end
-end
-
-local methodsmeta = {}
-
-function methodsmeta:__call(object,...)
-  local env = getfenv(self.__f)
-  local metafenv = {
-    __newindex = env,
-    __index = env,
-  }
-  local fenv = {
-    super = makesupermethod(object,self.__name,self.__iscmethod),
-  }
-  setmetatable(fenv,metafenv)
-  setfenv(self.__f,fenv)
-  local result = {self.__f(object,unpack(arg))}
-  setfenv(self.__f,env)
-  return unpack(result)
-end
-
-local
-function storemethod(storage,name,iscmethod,method)
-  if type(method) == 'function' then
-    local t = {
-      __name = name,
-      __f = method,
-      __iscmethod = iscmethod,
-    }
-    setmetatable(t,methodsmeta)
-    storage[name] = t
-  else
-    storage[name] = method
-  end
-end
-
-rawget(Class,INFO).__methods.__newindex =
-  function(self,name,method)	
-    storemethod(rawget(self,INFO).__methods,name,false,method)
-  end
-
-
-function Class:__call__(...)
-  local instance = self:new(unpack(arg))
-  instance:initialize(unpack(arg))
-  instance.__cbody = nil
-  local constructor = instance[rawget(self,INFO).__name]
-  if constructor ~= nil then
-  	constructor(instance, unpack(arg))
-  	constructor = nil
-  end
-  return instance
-end
-
-function Class:initialize(name,superclass)
-  assert(isname(name), fwrongarg(1,'name',name))
-  object2class(self,name)
-  superclass = superclass or Object
-  assert(isobject(superclass), fwrongarg(2,'object',superclass))
-  setsuper(self,superclass or Object)
-end
-
-function Class:name()
-  return rawget(self,INFO).__name
-end
-
-function Class:super()
-  return rawget(self,INFO).__super
-end
-
-function Class:classtable()
-  local t = {}
-  local mt = {}
-  function mt.__newindex(_,name,method)
-    storemethod(rawget(self,INFO).__cmethods,name,true,method)
-  end
-  setmetatable(t,mt)
-  return t
-end
-
-function Class:__tostring__()
-  return self:name()
-end
-
-function Class:derives(class)
-  local superclass = self:super()
-  if superclass then
-    return superclass == class or superclass:derives(class)
-  end
-end
-
-function Class:adopt(t,initialize,...)
-  assert(type(t)=='table', wrongarg(1,'table',t))
-  local o = table2object(t)
-  setclass(o,self)
-  if initialize then
-    o:initialize(unpack(arg))
-  end
-  return o
-end
-
-
-local Objectclass = Object:classtable()
-
-function Objectclass:new()
-  local o = table2object{}
-  setclass(o,self)
-  return o
-end
-
-
-function Object:initialize()
-end
-
-function Object:finalize()
-end
-
-function Object:class()
-  return rawget(self,INFO).__class
-end
-
-function Object:__eq__(other)
-  return rawequal(self,other) or (self.__ptr and other.__ptr and self.__ptr == other.__ptr)
-end
-
-function Object:__newindex__(name,value)
-  if self.__set_callback(self,name,value) == true then
-  	return
-  end
-  rawset(self,name,value)
-end
-
-function Object:instanceof(class)
-  return self:class() == class
-end
-
-function Object:__set_callback(name,value)
-	return false
-end
-
-function Object:inherits(class)
-  local _class = self:class()
-  return _class == class or _class:derives(class)
-end
-
-function Object:__tostring__()
-  return 'instance of '..self:class():name()
-end
-
-function Object:__concat__(other)
-  if isobject(self) then
-    self = tostring(self)
-  elseif isobject(other) then
-    other = tostring(other)
-  end
-  return self..other
-end
-
-function Object:totable(finalize)
-  if finalize then
-    self:finalize()
-  end
-  setmetatable(self,nil)
-  local info = rawget(self,INFO)
-  rawset(self,INFO,nil)
-  return self, info
-end
-
-
 function class(name)
-  assert(isname(name), fwrongarg(1,'name',name))
-  local _class = Class(name)
-  return function(superclass)
-    assert(isobject(superclass), fwrongarg(1,'object',superclass))
-    setsuper(_class,superclass)
-  end
+	local cls = {}
+	cls.__classname = name
+	
+	cls.__tostring = function(c)
+		return "Class of type "..c.__classname
+	end
+
+	cls.__newindex = function(t,k,v)
+		retVal = false
+		if t["__setvar"] ~= nil then
+			retVal = t["__setvar"](t,k,v)
+		end
+		if retVal == false then
+			rawset(t,k,v)
+		end
+	end
+
+	cls.__index = function(t,k)
+		local prototype = rawget(t,"__prototype")
+		if prototype ~= nil then
+			ret = rawget(prototype,k)
+			if ret ~= nil then return ret end
+		end
+
+		if k ~= "__index" and k ~= "__getvar" then
+			if t["__getvar"] ~= nil then
+				local ret = t["__getvar"](t,k)
+				if ret ~= nil then return ret end
+			end
+			return rawget(t,k)
+		end
+	end
+
+	_G[name] = setmetatable(cls, {
+		 __call = function (c, ...)
+		local instance = setmetatable({}, cls)
+		instance.__prototype = cls
+		if cls[name] then
+			cls[name](instance, ...)
+		end	
+		return instance
+	end})
+	
+	return function(superclass)
+		if type(superclass) == 'table' then
+			cls[superclass.__classname] = {}
+			setmetatable(cls[superclass.__classname], superclass)
+			for i,v in pairs(superclass) do
+				if cls[i] == nil then
+					cls[i] = v
+				else
+					cls[superclass.__classname][i] = v
+				end
+			end
+			cls.__baseclass = superclass
+		end
+		
+	end
 end
-
-classu = u

+ 3 - 1
Bindings/Contents/LUA/API/defaults.lua

@@ -2,7 +2,9 @@
 for k,v in pairs(math) do _G[k]=v end for k,v in pairs(table) do _G[k]=v end
 _G["count"]=_G["getn"]
 
-_G["print"] = _G["debugPrint"]
+_G["print"] = function(msg)
+	_G["debugPrint"](tostring(msg))
+end
 
 __core__services__instance = Polycore.CoreServices_getInstance()
 

+ 27 - 14
Bindings/Scripts/create_lua_library/create_lua_library.py

@@ -18,12 +18,12 @@ def mkdir_p(path): # Same effect as mkdir -p, create dir and all necessary paren
 # Note we expect className to be a valid string
 def template_returnPtrLookup(prefix, className, ptr):
 	out = ""
-	out += "%sif __ptr_lookup[%s][%s] ~= nil then\n" % (prefix, className, ptr)
-	out += "%s\treturn __ptr_lookup[%s][%s]\n" % (prefix, className, ptr)
+	out += "%sif _G[\"__ptr_lookup\"][%s][%s] ~= nil then\n" % (prefix, className, ptr)
+	out += "%s\treturn _G[\"__ptr_lookup\"][%s][%s]\n" % (prefix, className, ptr)
 	out += "%selse\n" % (prefix)
-	out += "%s\t__ptr_lookup[%s][%s] = _G[%s](\"__skip_ptr__\")\n" % (prefix, className, ptr, className)
-	out += "%s\t__ptr_lookup[%s][%s].__ptr = %s\n" % (prefix, className, ptr, ptr)
-	out += "%s\treturn __ptr_lookup[%s][%s]\n" % (prefix, className, ptr)
+	out += "%s\t_G[\"__ptr_lookup\"][%s][%s] = _G[%s](\"__skip_ptr__\")\n" % (prefix, className, ptr, className)
+	out += "%s\t_G[\"__ptr_lookup\"][%s][%s].__ptr = %s\n" % (prefix, className, ptr, ptr)
+	out += "%s\treturn _G[\"__ptr_lookup\"][%s][%s]\n" % (prefix, className, ptr)
 	out += "%send\n" % (prefix)
 	return out
 	
@@ -146,6 +146,7 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api
 
 				luaClassBindingOut = "" # Def: The local lua file to generate for this class.
 				inherits = False
+				parentClass = ""
 				if len(c["inherits"]) > 0: # Does this class have parents?
 					if c["inherits"][0]["class"] not in ignore_classes:
 
@@ -158,6 +159,7 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api
 							luaClassBindingOut += "require \"Polycode/ParticleEmitter\"\n\n"
 
 						luaClassBindingOut += "class \"%s\" (%s)\n\n" % (ckey, c["inherits"][0]["class"])
+						parentClass = c["inherits"][0]["class"]
 						inherits = True
 				if inherits == False: # Class does not have parents
 					luaClassBindingOut += "class \"%s\"\n\n" % ckey
@@ -193,7 +195,7 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api
 				# TODO: Remove or generalize ParticleEmitter special casing. These lines are marked with #SPEC
 
 				if len(classProperties) > 0: # If there are properties, add index lookup to the metatable
-					luaClassBindingOut += "function %s:__index__(name)\n" % ckey
+					luaClassBindingOut += "function %s:__getvar(name)\n" % ckey
 					# Iterate over property structures, creating if/else clauses for each.
 					# TODO: Could a table be more appropriate for 
 					for pp in classProperties:
@@ -217,7 +219,7 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api
 							
 						# If type is a class
 						else:
-							luaClassBindingOut += "\t\tretVal = %s.%s_get_%s(self.__ptr)\n" % (libName, ckey, pp["name"])
+							luaClassBindingOut += "\t\tlocal retVal = %s.%s_get_%s(self.__ptr)\n" % (libName, ckey, pp["name"])
 							luaClassBindingOut += template_returnPtrLookup("\t\t", template_quote(pp["type"]), "retVal")
 
 						# Generate C++ side of binding:
@@ -250,6 +252,10 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api
 						pidx = pidx + 1
 
 					luaClassBindingOut += "\tend\n"
+					if inherits:
+						luaClassBindingOut += "\tif %s[\"__getvar\"] ~= nil then\n" % (parentClass)
+						luaClassBindingOut += "\t\treturn %s.__getvar(self, name)\n" % (parentClass)
+						luaClassBindingOut += "\tend\n"
 					luaClassBindingOut += "end\n"
 
 				luaClassBindingOut += "\n\n"
@@ -257,7 +263,7 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api
 				# Iterate over properties again, creating setters
 				pidx = 0 # Def: Count of 
 				if len(classProperties) > 0: # If there are properties, add index setter to the metatable
-					luaClassBindingOut += "function %s:__set_callback(name,value)\n" % ckey
+					luaClassBindingOut += "function %s:__setvar(name,value)\n" % ckey
 					for pp in classProperties:
 						pp["type"] = typeFilter(pp["type"])
 						
@@ -295,7 +301,14 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api
 						# Notice: Setters for object types are not created.
 					if pidx != 0:
 						luaClassBindingOut += "\tend\n"
-					luaClassBindingOut += "\treturn false\n"
+					if inherits:
+						luaClassBindingOut += "\tif %s[\"__setvar\"] ~= nil then\n" % (parentClass)
+						luaClassBindingOut += "\t\treturn %s.__setvar(self, name, value)\n" % (parentClass)
+						luaClassBindingOut += "\telse\n"
+						luaClassBindingOut += "\t\treturn false\n"
+						luaClassBindingOut += "\tend\n"
+					else:
+						luaClassBindingOut += "\treturn false\n"
 					luaClassBindingOut += "end\n"
 
 				# Iterate over methods
@@ -501,7 +514,7 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api
 							luaClassBindingOut += "\t\tself.__ptr = %s.%s(self)\n" % (libName, ckey)
 						else:
 							luaClassBindingOut += "\t\tself.__ptr = %s.%s(unpack(arg))\n" % (libName, ckey)
-						luaClassBindingOut += "\t\t__ptr_lookup.%s[self.__ptr] = self\n" % (ckey)
+						luaClassBindingOut += "\t\t_G[\"__ptr_lookup\"].%s[self.__ptr] = self\n" % (ckey)
 						luaClassBindingOut += "\tend\n"
 						luaClassBindingOut += "end\n\n"
 					else: # Non-constructors.						
@@ -512,7 +525,7 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api
 							else:
 								luaClassBindingOut += "\tlocal retVal =  %s.%s_%s(self.__ptr)\n" % (libName, ckey, pm["name"])
 						else: # Static method
-							luaClassBindingOut += "function %s_%s(%s)\n" % (ckey, pm["name"], ", ".join(paramlist))
+							luaClassBindingOut += "function %s.%s(%s)\n" % (ckey, pm["name"], ", ".join(paramlist))
 							if len(lparamlist):
 								luaClassBindingOut += "\tlocal retVal = %s.%s_%s(%s)\n" % (libName, ckey, pm["name"], ", ".join(lparamlist))
 							else:
@@ -542,12 +555,12 @@ def createLUABindings(inputPath, prefix, mainInclude, libSmallName, libName, api
 
 				
 				luaClassBindingOut += "\n\n"
-				luaClassBindingOut += "if not __ptr_lookup then __ptr_lookup = {} end\n"
-				luaClassBindingOut += "__ptr_lookup.%s = {}\n\n" % (ckey)
+				luaClassBindingOut += "if not _G[\"__ptr_lookup\"] then _G[\"__ptr_lookup\"] = {} end\n"
+				luaClassBindingOut += "_G[\"__ptr_lookup\"].%s = {}\n\n" % (ckey)
 				
 				# Delete method (Lua side)
 				luaClassBindingOut += "function %s:__delete()\n" % (ckey)
-				luaClassBindingOut += "\t__ptr_lookup.%s[self.__ptr] = nil\n" % (ckey)
+				luaClassBindingOut += "\t_G[\"__ptr_lookup\"].%s[self.__ptr] = nil\n" % (ckey)
 				luaClassBindingOut += "\t%s.delete_%s(self.__ptr)\n" % (libName, ckey)
 				luaClassBindingOut += "end\n"
 				if ckey == "EventHandler": # See LuaEventHandler above