-- Package for object oriented framework
-- (c) 2006 Markus Nentwig
-- This file is in the public domain (may be used freely)
-- No warranty is given or implied.

local version="0v4"

-- global switch for built-in self tests, also used by other mnoo modules
--_SELFTEST=true

-- ########################################################################################
-- # Report API errors at the perspective at the caller (error messages that point into
-- # this file are confusing to the user).
-- # "usererror" is 2nd parameter to all our error()s
-- # Production code: Use 2
-- # Development / debugging of this file: Use 1
-- ########################################################################################
local usererror=2

-- ########################################################################################
-- # Most common error messages. Using functions for easier comparison in self test.
-- ########################################################################################
local eheader="mnoo:"
local eheader_object="mnoo object:"
local eheader_class="mnoo class:"

local function ERROR_classExpected()
   return eheader.."class expected!"   
end

local function ERROR_objectExpected()
   return eheader.."object expected!"
end

local function ERROR_redeclaration(key)
   return eheader.."redeclaration of '"..key.."'"
end

local function ERROR_class_undeclared_read(key)
   return eheader_class.."read attempt on undeclared field '"..key.."'"
end

local function ERROR_class_undeclared_write(key)
   return eheader_class.."write attempt on undeclared field '"..key.."'"
end

local function ERROR_object_undeclared_write(key)
   return eheader_object.."attempt to write undeclared field '"..key.."'"
end

local function ERROR_object_undeclared_read(key)
   return eheader_object.."attempt to read undeclared field '"..key.."'"
end

local function ERROR_object_undeclared_symbol(key)
   return eheader_object.."field '"..key.."' is not declared"
end

local function ERROR_object_invalid_write(key, symtype)
   return eheader_object.."attempt to write '"..key.."', which is of type '"..symtype.."'"
end

local function ERROR_object_invalid_read(key, symtype)
   return eheader_object.."attempt to read '"..key.."', which is of type '"..symtype.."'"
end

local function ERROR_class_invalid_write(key, symtype)
   return eheader_class.."attempt to write '"..key.."', which is of type '"..symtype.."'"
end

local function ERROR_class_invalid_read(key, symtype)
   return eheader_class.."attempt to read '"..key.."', which is of type '"..symtype.."'"
end

local function ERROR_object_noSerializationSupport()
   return eheader_object.."no serialization supported"
end

local function ERROR_object_serializeInvalid()
   return eheader_object.."permanent data contains type that can not be serialized (userdata, function, table as key)"
end

local function ERROR_object_serializeMeta()
   return eheader_object.."cannot serialize non-object table with associated metatable"   
end

local function ERROR_deserialize_unknown_class(ID)
   return eheader.."The serialized code refers to a class with the ID '"..tostring(ID).."', which is unknown (not yet declared?)"
end

local function ERROR_serialize_duplicate_class1(ID)
   return eheader.."The serialization ID '"..ID.."' has already been registered!"
end

local function ERROR_serialize_duplicate_class2(ID)
   return "Serialization was already enabled for this class!"
end

local function ERROR_object_private_method(key)
   return "Method "..key.." is private"
end

local function ERROR_private_must_be_objectmethod(key)
   return "only objectmethods may be private ('"..key.."')"
end

local function ERROR_sym_is_already_private(key)
   return "'"..key.."' is already private"
end

local function ERROR_private_env_read(sym)
   return "Attempt to read '"..sym.."' from protected private environment"
end

local function ERROR_private_env_write(sym)
   return "Attempt to write '"..sym.."' into protected private environment"
end

-- ########################################################################################
-- # Library body
-- ########################################################################################
local lib={
   storage_hash={} -- default constructors for each class are kept here, indexed by
   -- the ID set with "enable_serialize"
}

-- ########################################################################################
-- # Error message for looking up unknown fields from storage_hash (for deserialize)
-- ########################################################################################
local storage_index=
   function(storage, key) 
      local result=rawget(storage, key) 
      if result then 
	 return result 
      else 
	 error(ERROR_deserialize_unknown_class(key), 4) -- report error at caller's perspective
      end
   end
setmetatable(lib.storage_hash, {__index=storage_index})

-- ########################################################################################
-- # Purpose:
-- # checks, whether self is class
-- # returns true/false
-- # internal use and API
-- # DO NOT WRITE CODE THAT RELIES ON THIS FUNCTION (except for assertions).
-- # Reason: Slow
-- ########################################################################################
local isClass=
   function(self)
      -- may not use error()
      
      -- class is type table
      if type(self) ~= "table" then return false end

      -- class must have a metatable
      local meta=getmetatable(self) 
      if not meta then return false end
      
      -- class must have symbols_hash field
      local symbols_hash=rawget(meta, "symbols_hash") 
      if type(symbols_hash) ~= "table" then return false end

      -- class must have ancestors_ilist field
      local ancestors_ilist=rawget(meta, "ancestors_ilist") 
      if type(ancestors_ilist) ~= "table" then return false end

      return true      
   end

local object_getClass=
   function(self)
      local meta=getmetatable(self) assert(meta)
      local class=rawget(meta, "class") assert(class)
      return class
   end

local classIsA
classIsA=
   function(c1, c2)
      if c1 == c2 then return true end
      
      assert(isClass(c1)) -- !!
      assert(isClass(c2)) -- !!
      local ancestors_ilist=rawget(getmetatable(c1), "ancestors_ilist") assert(ancestors_ilist)
      for _, subc1 in ipairs(ancestors_ilist) do
	 if classIsA(subc1, c2) then return true end
      end
      return false
   end

-- ########################################################################################
-- # Purpose:
-- # checks, whether self is object
-- # returns true/false
-- # internal use and API
-- # DO NOT WRITE CODE THAT RELIES ON THIS FUNCTION (except for assertions).
-- # Reason: Slow
-- ########################################################################################
local isObject=
   function(self)
      -- may not use error()
      
      -- object is either table or userdata type
      if type(self) ~= "table" and type(self) ~= "userdata" then return false end

      -- object must have a metatable
      local meta=getmetatable(self) 
      if not meta then return false end
      
      -- metatable must have class entry
      local class=rawget(meta, "class") 
      if type(class) ~= "table" then return false end

      -- class entry must be valid class
      return isClass(class)
   end

-- ########################################################################################
-- # Purpose:
-- # checks, whether object has class c in its inheritage
-- # returns true/false, error if not object
-- ########################################################################################
local libAPI_classIsA=
   function(self, c)
      if not isClass(self) then error(ERROR_classExpected(), usererror) end
      if not isClass(c) then error(ERROR_classExpected(), usererror) end
      
      return classIsA(self, c)
   end

-- ########################################################################################
-- # Purpose:
-- # checks, whether object has class c in its inheritage
-- # returns true/false, error if not object
-- ########################################################################################
local libAPI_objectIsA=
   function(self, c)
      if not isObject(self) then error(ERROR_objectExpected(), usererror) end
      if not isClass(c) then error(ERROR_classExpected(), usererror) end

      local class=object_getClass(self)
      return classIsA(class, c)
   end

local libAPI_objectGetClass=
   function(self)
      if not isObject(self) then error(ERROR_objectExpected(), usererror) end      
      return object_getClass()
   end

local track_attribute
track_attribute= -- self is class
   function(self, attr, symname)
      local cmeta=getmetatable(self)
      local attr_hash=rawget(cmeta, attr)
      local result=rawget(attr_hash, symname)
      if result ~= nil then return result end
      
      -- handle all ancestors
      for _, parentclass in ipairs(rawget(cmeta, "ancestors_ilist")) do
	 result=track_attribute(parentclass, attr, symname)
	 if result ~= nil then return result end
      end
      return nil
   end

-- Sets perm_hash[field]=true for all permanent object
-- data entries of this class and all its ancestors
local sl_collect_permanent_fields -- declaration, because recursive
   sl_collect_permanent_fields=
   function(self, perm_hash)
      assert(isClass(self))
      
      -- all permanent fields of this class
      local cmeta=getmetatable(self)
      for symbol, symtype in pairs(rawget(cmeta, "symbols_hash")) do
	 if symtype=="objectdata" and perm_hash[key]==nil then
	    local a=track_attribute(self, "permanent", symbol)
	    if a ~= nil then
	       perm_hash[symbol]=a
	    end
	 end
      end
      
      -- handle all ancestors
      for _, parentclass in ipairs(rawget(cmeta, "ancestors_ilist")) do
	 sl_collect_permanent_fields(parentclass, perm_hash)
      end
   end

local sl_table, sl_any, sl_object -- predeclaration

-- serialize anything
-- (dispatcher to sl_... functions)
sl_any=
   function(arg)
      local t=type(arg)

      if t=="nil" then
	 return "nil"

      elseif t=="string" then
	 -- format string so that it can be safely read back by the lua interpreter
	 -- (lua reference manual, "string manipulation" section, "string.format" function)
	 return string.format('%q', arg)

      elseif t=="number" then
	 return arg

      elseif t=="table" then	 
	 if getmetatable(arg) then
	    if isObject(arg) then 
	       return sl_object(arg)
	    else
	       return nil, ERROR_object_serializeMeta()
	    end
	 else
	    return dumpTable(arg, indent)
	 end

      elseif t=="userdata" then
	 return nil, ERROR_object_serializeInvalid()

      elseif t=="function" then
	 return nil, ERROR_object_serializeInvalid()

      elseif t=="boolean" then
	 if arg == true then 
	    return "true" 
	 else 
	    return "false" 
	 end
      else 
	 assert(nil, "unknown type???")
      end
   end -- function

-- serialize table (not object)
-- returns lua code
-- or nil, error
sl_table=
   function(arg)
      local dump={}
      local k, v
      for k, v in pairs(arg) do
	 local sk, sv, err
	 sk, err=sl_any(k) 
	 if not sk then return nil, err end
	 local sv=sl_any(v) 
	 if not sv then return nil, err end
	 table.insert(dump, "["..sk.."]="..sv)
      end
      return "{"..table.concat(dump, ",").."}"
   end

-- serialize object
-- returns lua code
-- or nil, error
sl_object=
   function(self) 
      
      local dump={}
      local k, v

      -- ########################################################################################
      -- # Check, that this object supports serialization.
      -- ########################################################################################
      local ometa=getmetatable(self)
      local class=ometa.class
      local cmeta=getmetatable(class)
      local serializeID=rawget(cmeta, "serializeID") 
      if not serializeID then return nil, ERROR_object_noSerializationSupport() end
      
      -- ########################################################################################
      -- # Collect all permanent object data fields (recurse through class inheritage)
      -- ########################################################################################
      local perm_hash={}
      sl_collect_permanent_fields(class, perm_hash)
      
      for key, _ in pairs(perm_hash) do
	 -- ########################################################################################
	 -- # Look up from object
	 -- # permanent entries MUST be object data. They can only be in this object.
	 -- ########################################################################################
	 local val=rawget(self, key)
      
	 -- no need to write nil entries, undefined entries are nil anyway
	 if val ~= nil then
	    -- ########################################################################################
	    -- # Serialize key and value as list member ["key"]="value"
	    -- ########################################################################################
	    local skey, sval, err
	    skey, err=sl_any(key) if not skey then return nil, err end
	    sval, err=sl_any(val) if not sval then return nil, err end
	    
	    table.insert(dump, "["..skey.."]="..sval)
	 end
      end -- for key

      -- ########################################################################################
      -- # The table "storage" contains the default constructor function for this class.
      -- # Return code that calls it to reconstruct the object.
      -- ########################################################################################
      return "constructors."..serializeID.."{"..table.concat(dump, ",").."}"
   end

-- ########################################################################################
-- # Purpose:
-- # Serializes an object (creates a piece of lua code that reconstructs the permanent
-- # part of the object)
-- # returns: lua code (ASCII string)
-- ########################################################################################
local libAPI_serialize=
   function(self)
      if not isObject(self) then error(ERROR_objectExpected(), usererror) end

      local result, err=sl_any(self)
      if result then
	 return "return function(constructors) return "..result.." end"
      else
	 error(err)
      end
   end

-- ########################################################################################
-- # Purpose:
-- # Restores and returns a serialized object
-- # returns object (or error)
-- ########################################################################################
local libAPI_deserialize=
   function(ASCII)
      
      local eval, err

      -- parse ASCII to lua chunk
      eval, err=loadstring(ASCII) if err then return err end
      assert(type(eval)=="function")
      
      -- Evaluate lua chunk. Returns the function from "return function(storage)... end"
      eval=eval() assert(type(eval)=="function")
      
      -- Call the returned function.
      eval=eval(lib.storage_hash)
      return eval
   end

-- ########################################################################################
-- # Purpose:
-- # Searches the given class and its ancestors for the declaration of key.
-- # returns whoever declared it and its type ("class" or "object").
-- # internal use only
-- ########################################################################################
local track_declaration_class -- forward declaration, because recursive
track_declaration_class=   
   function(self, key)
      -- may not use error()
      assert(isClass(self))

      local meta=getmetatable(self)
      
      local symbols_hash=rawget(meta, "symbols_hash")
      local decltype=rawget(symbols_hash, key)
      if decltype then return self, decltype end
      
      local who
      for _, parentclass in ipairs(rawget(meta, "ancestors_ilist")) do
	 who, decltype=track_declaration_class(parentclass, key)
	 if who then return who, decltype end
      end -- for parent class
      return nil, nil
   end

-- ########################################################################################
-- # Purpose:
-- # Searches the given object's class and its ancestors for the declaration of key.
-- # returns whoever declared it and its type ("class" or "object").
-- ########################################################################################
local track_declaration_object=
   function(self, key)
      -- may not use error()
      assert(isObject(self))

      local ometa=getmetatable(self)		 		  
      local class=ometa.class
      return track_declaration_class(class, key)
   end

local internal_declare=
   function(self, symname, symtype) -- self is class
      -- ########################################################################################
      -- # Search, whether the entry is declared, where, and as what.
      -- ########################################################################################
      local who, prev_symtype=track_declaration_class(self, symname)
      if who then
	 
	 -- the only legal redeclaration is classdata (means that the derived class
	 -- will keep its own value, instead of sharing it with the parent class)
	 if who == self or prev_symtype ~= "classdata" or symtype ~= "classdata" then
	    error(ERROR_redeclaration(symname), usererror) 
	 end --t[4]
      end -- if already declared 

      local symbols_hash=rawget(getmetatable(self), "symbols_hash")
      rawset(symbols_hash, symname, symtype)
   end

-- ########################################################################################
-- # Purpose:
-- # declares a class function (overloading it will not affect the parent's class
-- # implementation of that function)
-- # Arguments: "symname", "symname", ...
-- ########################################################################################
local classAPI_declareClassfunction=
   function(self, ...)
      if not isClass(self) then error(ERROR_classExpected(), usererror) end
      
      for _, key in ipairs(arg) do
	 internal_declare(self, key, "classfunction")
      end
   end

-- ########################################################################################
-- # Purpose:
-- # declares a class data entry (example: object counter, log file etc). 
-- # All derived classes share the same entry!
-- # A derived class may redeclare the entry and use its own copy.
-- # Arguments: "symname", "symname", ...
-- ########################################################################################
local classAPI_declareClassdata=
   function(self, ...)
      if not isClass(self) then error(ERROR_classExpected(), usererror) end
      
      for _, key in ipairs(arg) do
	 internal_declare(self, key, "classdata")
      end
   end 

-- ########################################################################################
-- # Purpose:
-- # declares an object method (overloading it will not affect the parent's class
-- # implementation of that function)
-- # Arguments: "symname", "symname", ...
-- ########################################################################################
local classAPI_declareObjectmethod=
   function(self, ...)
      if not isClass(self) then error(ERROR_classExpected(), usererror) end
      
      for _, key in ipairs(arg) do
	 internal_declare(self, key, "objectmethod")
      end
   end 

-- ########################################################################################
-- # Purpose:
-- # declares an object data entry
-- # Arguments: "symname", "symname", ...
-- ########################################################################################
local classAPI_declareObjectdata=
   function(self, ...)
      if not isClass(self) then error(ERROR_classExpected(), usererror) end
      
      for _, key in ipairs(arg) do
	 internal_declare(self, key, "objectdata")
      end
   end 

-- ########################################################################################
-- # Purpose:
-- # declares an object data entry as permanent (for serialize/restore)
-- # Arguments: "symname", "symname", ...
-- ########################################################################################
local classAPI_declarePermanentObjectdata=
   function(self, ...)
      if not isClass(self) then error(ERROR_classExpected(), usererror) end
      
      local perm_hash=rawget(getmetatable(self), "permanent")
      for _, key in ipairs(arg) do
	 internal_declare(self, key, "objectdata")
	 rawset(perm_hash, key, true)
      end
   end 

-- ########################################################################################
-- # called by lua when writing a field that is nil into the object 
-- ########################################################################################
local function obj_newindex(self, key, val) 
   if not isObject(self) then error(ERROR_objectExpected(), usererror) end
   
   -- ########################################################################################
   -- # Search, whether the entry is declared, where, and as what.
   -- ########################################################################################
   local who, symtype=track_declaration_object(self, key)
   if not who then
      error(ERROR_object_undeclared_write(key), usererror)
   end
   
   if symtype=="objectmethod" then

      local meta=getmetatable(self) assert(meta)
      local class=rawget(meta, "class") assert(class)
      local private=track_attribute(class, "private", key)
      if private then
	 -- ########################################################################################
	 -- # Private object method! Is the caller allowed to call?
	 -- ########################################################################################
	 local caller_env=getfenv(2)
	 local friendstatus=track_attribute(class, "friends", caller_env)
	 if not friendstatus then 
	    error(ERROR_object_private_method(key), usererror)
	 end
      end
   end

   if symtype=="objectdata" then
      rawset(self, key, val) -- write into object (self)
   else
      error(ERROR_object_invalid_write(key, symtype), usererror)
   end
end

-- ########################################################################################
-- # called by lua when retrieving a nil field from the object 
-- ########################################################################################
local function obj_index(self, key) 
   assert(isObject(self))
   
   -- ########################################################################################
   -- # Search, whether the entry is declared, where, and as what.
   -- ########################################################################################
   local who, symtype=track_declaration_object(self, key)
   if not who then
      error(ERROR_object_undeclared_read(key), usererror)
   end

   if symtype=="objectdata" then
      return rawget(self, key) -- retrieve from object (self)
   elseif symtype=="objectmethod" then
      
      local class=rawget(getmetatable(self), "class")
      local private=track_attribute(class, "private", key)
      if private then
	 -- ########################################################################################
	 -- # Private object method! Is the caller allowed to call?
	 -- ########################################################################################
--	 print("class index "..key.." env "..tostring(getfenv(2)))
	 local caller_env=getfenv(2)
	 local friendstatus=track_attribute(class, "friends", caller_env)
	 if not friendstatus then 
	    error(ERROR_object_private_method(key), usererror)
	 end
      end
   
      return rawget(who, key) -- from whatever class defines it (if a class overloads it, it redefines it)
   else
      error(ERROR_object_invalid_read(key, symtype), usererror)
   end
end

-- ########################################################################################
-- # Purpose:
-- # Create or retrieve the metatable attached to an object of this class.
-- # It is common to all objects of a class, and generated when an object is
-- # instantiated the first time.
-- # Then the metatable gets cached in the class metatable (because otherwise we'd create
-- # a new metatable with the same content for every object - unnecessary memory use)
-- ########################################################################################
local function get_object_metatable(class)
   local cmeta=getmetatable(class) assert(cmeta)
   local objectmeta=rawget(cmeta, "objectmeta")
   if not objectmeta then
      assert(obj_index)
      objectmeta={
	 __newindex=obj_newindex,
	 __index=obj_index,
	 class=class
      }
      rawset(cmeta, "objectmeta", objectmeta)
   end
   return objectmeta
end

-- ########################################################################################
-- # Purpose:
-- # Returns the metatable for a given class. Attaching it to a table or userdata
-- # will turn it into an object.
-- # Executing "setmetatable" for userdata requires C level access (for security reasons),
-- # and can be done by the library that generates the userdata.
-- ########################################################################################
local classAPI_getObjectMetatable=
   function(class)
      if not isClass(class) then error(ERROR_classExpected(), usererror) end
      return get_object_metatable(class)
   end

-- ########################################################################################
-- # Purpose:
-- # Creates new object for a class (constructor)
-- # Arguments: class and table/userdata (optional)
-- # returns: object
-- ########################################################################################
local classAPI_new=
   function(actualclass, self)
      if not isClass(actualclass) then error(ERROR_classExpected(), usererror) end
      
      if not self then
	 self={} -- create object body 
      else
	 -- ########################################################################################
	 -- # Optional 2nd arg gives body of object
	 -- # Used by deserialize operation
	 -- ########################################################################################
	 local t=type(self)
	 if t ~= "table" then error("new: 2nd argument must be table", usererror) end
	 if getmetatable(self) then error("new: Optional 2nd argument must be table or userdata without metatable (i.e. NO object)", usererror) end
      end
      
      local ometa=get_object_metatable(actualclass)

      setmetatable(self, ometa)
      return self
   end-- t[90]

-- ########################################################################################
-- # Purpose:
-- # metamethod for looking up a nil value from a class
-- ########################################################################################
local function class_index(self, key) 
   assert(isClass(self))
   
   -- ########################################################################################
   -- # Search, whether the entry is declared, where, and as what.
   -- ########################################################################################
   local who, symtype=track_declaration_class(self, key)
   if not who then
      error(ERROR_class_undeclared_read(key), usererror)
   end

   if symtype=="classfunction" or symtype=="classdata" then
      return rawget(who, key)
   else
      error(ERROR_class_invalid_read(key, symtype), usererror)
   end
end

-- ########################################################################################
-- # Purpose:
-- # metamethod for assigning a non-existing value to a class
-- ########################################################################################
local function class_newindex(self, key, val) 
   assert(isClass(self))
   
   -- ########################################################################################
   -- # Search, whether the entry is declared, where, and as what.
   -- ########################################################################################
   local who, symtype=track_declaration_class(self, key)
   if not who then
      error(ERROR_class_undeclared_write(key), usererror) -- t[2]
   end
   
   if symtype == "classdata" then
      -- ########################################################################################
      -- # A derived class shares the data elements with its parent class (for example
      -- # "number of objects counter". Therefore, the write operation is redirected
      -- # to the parent class.
      -- ########################################################################################
      rawset(who, key, val)

   elseif symtype == "classfunction" or symtype == "objectmethod" then
      if who ~= self then
	 -- ########################################################################################
	 -- # Currently the classfunction is defined by a parent class.
	 -- # Classfunctions are overloaded. The function implementation of a parent class is
	 -- # not affected.
	 -- # Therefore define it in this class.
	 -- ########################################################################################
	 local symbols_hash=rawget(getmetatable(self), "symbols_hash")
	 rawset(symbols_hash, key, symtype) -- declare
      end
      rawset(self, key, val)
   else
      error(ERROR_class_invalid_write(key, symtype), usererror)
   end

   -- ########################################################################################
   -- # The environment that creates a class is automatically entitled to access
   -- # private methods
   -- ########################################################################################
   local friends_hash=rawget(getmetatable(self), "friends") assert(friends_hash)
   rawset(friends_hash, getfenv(2), true)
end

local classAPI_setPrivate=
   function(self, ...)
      if not isClass(self) then error(ERROR_classExpected(), usererror) end

      local cmeta=getmetatable(self)
      local attr_hash=rawget(cmeta, "private") assert(attr_hash)
      for _, sym in ipairs(arg) do
	 local who, symdef=track_declaration_class(self, sym)
	 if not who then error(ERROR_object_undeclared_symbol(sym), usererror) end
	 if symdef ~= "objectmethod" then error(ERROR_private_must_be_objectmethod(sym), usererror) end
	 if rawget(attr_hash, sym) then error(ERROR_sym_is_already_private(sym), usererror) end
	 rawset(attr_hash, sym, true)	 
      end
   end

-- ########################################################################################
-- # myclass:getObjectmethod("symbolname")
-- # Note: does not check privacy
-- ########################################################################################
local classAPI_getObjectmethod=
   function(self, symname)
      if not isClass(self) then error(ERROR_classExpected(), usererror) end

      -- ########################################################################################
      -- # Search, whether the entry is declared, where, and as what.
      -- ########################################################################################
      local who, symtype=track_declaration_class(self, symname)
      if not who then
	 error(ERROR_class_undeclared_read(symname), usererror)
      end
      
      if symtype == "objectmethod" then
	 return rawget(who, symname)
      else
	 error(ERROR_class_invalid_read(symname, symtype), usererror)
      end      
   end

-- ########################################################################################
-- # myclass:getClassfunction("symbolname")
-- # Note: does not check privacy
-- ########################################################################################
local classAPI_getClassfunction=
   function(self, symname)
      if not isClass(self) then error(ERROR_classExpected(), usererror) end

      -- ########################################################################################
      -- # Search, whether the entry is declared, where, and as what.
      -- ########################################################################################
      local who, symtype=track_declaration_class(self, symname)
      if not who then
	 error(ERROR_class_undeclared_read(symname), usererror)
      end
      
      if symtype == "classfunction" then
	 return rawget(who, symname)
      else
	 error(ERROR_class_invalid_read(symname, symtype), usererror)
      end      
   end

-- ########################################################################################
-- # All classes are derived from a common base class (that is otherwise invisible to
-- # the user). Create it.
-- ########################################################################################
local function build_common_baseclass()

   -- symbol declarations
   local common_baseclass_symbols_hash={
      declareClassfunction="classfunction",
      declareClassdata="classfunction",
      declareObjectmethod="classfunction",
      declareObjectdata="classfunction",
      new="classfunction",
      classIsA="classfunction",
      declarePermanentObjectdata="classfunction",
      setPrivate="classfunction",

      objectIsA="objectmethod",
      getClass="objectmethod",
      getObjectmethod="classfunction",
      getClassfunction="classfunction"
   }
   
   -- symbol definitions
   local common_baseclass={
      declareClassfunction=classAPI_declareClassfunction,
      declareClassdata=classAPI_declareClassdata, 
      declareObjectmethod=classAPI_declareObjectmethod, 
      declareObjectdata=classAPI_declareObjectdata,
      declarePermanentObjectdata=classAPI_declarePermanentObjectdata,
      new=classAPI_new,
      classIsA=libAPI_classIsA,
      objectIsA=libAPI_objectIsA, -- should be classAPI
      getClass=libAPI_objectGetClass, -- should be classAPI
      setPrivate=classAPI_setPrivate,
      getObjectmethod=classAPI_getObjectmethod, 
      getClassfunction=classAPI_getClassfunction
  }
   
   local common_baseclass_meta={
      ancestors_ilist={},
      symbols_hash=common_baseclass_symbols_hash,
      permanent={}, -- attribute
      private={}, -- attribute
      friends={},
      __newindex=class_newindex,
      __index=class_index 
   }
   
   -- check, that declared
   for key, value in pairs(common_baseclass) do
      assert(common_baseclass_symbols_hash[key], key)
   end

   -- check, that defined
   for key, value in pairs(common_baseclass_symbols_hash) do
      assert(common_baseclass[key], key)
   end

   -- exception: Defined but not implemented
   common_baseclass_symbols_hash.deserialize_callback="objectmethod"
   
   -- build class
   setmetatable(common_baseclass, common_baseclass_meta)
   return common_baseclass
end

local common_baseclass=build_common_baseclass()

-- ########################################################################################
-- # Purpose:
-- # Creates new class, either base class or derived from parent class(es)
-- # Arguments: none or parent class(es)
-- # returns: Class
-- ########################################################################################
local function libAPI_newclass(...)
   
   -- all data fields go into metatable! This prevents accidental access.
   local classmeta={
      
      symbols_hash={},      
      ancestors_ilist={}, -- list of all classes this class is derived from
      permanent={}, -- attribute
      private={}, -- attribute
      friends={},
      __newindex=class_newindex,
      __index=class_index
   }
   newcl={}

   -- ########################################################################################
   -- # Inheritance: link to parent class(es)
   -- ########################################################################################
   for _, parentclass in ipairs(arg) do
      if not isClass(parentclass) then error(ERROR_classExpected(), usererror) end
      table.insert(classmeta.ancestors_ilist, parentclass)	 
   end

   -- ########################################################################################
   -- # Any class not derived from given base class(es) is implicitly derived from
   -- # common_baseclass
   -- ########################################################################################
   if table.getn(classmeta.ancestors_ilist) < 1 then
      table.insert(classmeta.ancestors_ilist, common_baseclass)
   end
   
   setmetatable(newcl, classmeta)
   return newcl	 
end -- t[1]

-- ########################################################################################
-- # Purpose:
-- # Enables serialization support for the given class
-- # Important: when child classes are derived, this procedure must be repeated for each
-- # of them!
-- ########################################################################################
local function libAPI_enable_serialize(self, ID)
   if not isClass(self) then error(ERROR_classExpected(), usererror) end
   if type(ID) ~= "string" then error(eheader.."2nd arg (ID) must be string!", usererror) end

   local storage=lib.storage_hash assert(storage)
   
   local cmeta=getmetatable(self)
   local serializeID=rawget(cmeta, "serializeID") 
   if serializeID then error(ERROR_serialize_duplicate_class2(), usererror) end

   -- store the ID
   rawset(cmeta, "serializeID", ID)
    
   -- write the default constructor
   -- note: Storage has metatable that throws error for read access to nil fields. Cannot use storage[ID]!
   if rawget(storage, ID) then error(ERROR_serialize_duplicate_class1(ID), usererror) end
   rawset(storage, ID, 
	  function(data)
	     -- data is the table with contents. Class_new assigns only the metatable.
	     -- Note that it doesn't matter whether or not the class implementation overloads the "new" constructor, because it is not used at all.

	     -- ########################################################################################
	     -- # Reconstruct object (by adding the metatable to the given data structure, for which
	     -- # we take ownership)
	     -- ########################################################################################	     
	     local obj=classAPI_new(self, data)

	     -- ########################################################################################
	     -- # Call deserialize_callback object method, if available
	     -- ########################################################################################
	     local who, symdef=track_declaration_object(obj, "deserialize_callback")
	     assert(who) assert(symdef == "objectmethod") -- declared in baseclass as object method
	     
	     local cb=rawget(who, "deserialize_callback")
	     if cb then
		if type(cb) ~= "function" then
		   error(eheader_object.."deserialize_callback must be function!", usererror) 
		end
		cb(obj)
	     end
	     return obj
	  end
       )
end

local dump=
   function(self) 
      if isObject(self) then
	 print("dump object ("..tostring(self).."), defines")
	 local ometa=getmetatable(self)
	 local class=rawget(ometa, "class")
	 dump(class)
	 print("end object ("..tostring(self)..")")
      elseif isClass(self) then
	 print("dump class("..tostring(self).."), defines: ")
	 for a,b in rawget(getmetatable(self), "symbols_hash") do
	    print(a,b)
	 end
	 print("end class("..tostring(self)..")")
      else assert(nil) end
   end

local libAPI_createPrivateEnvironment=
   function(...)
      local newEnv={}
      
      -- copy standard symbols to new environment
      for _, key in ipairs({'string', 'xpcall', 'tostring', 'gcinfo', 'loadlib', 'os', 'unpack', 'require', 'getfenv', 'setmetatable', 'next', '_TRACEBACK', 'assert', 'tonumber', 'io', 'rawequal', 'collectgarbage', 'arg', 'getmetatable', '_LOADED', 'rawset', 'math', 'pcall', 'debug', '__pow', 'type', 'table', 'coroutine', 'print', '_G', 'newproxy', 'rawget', 'loadstring', '_VERSION', 'dofile', 'setfenv', 'pairs', 'ipairs', 'error', 'loadfile'}) do
	 newEnv[key]=_G[key]
      end

      -- copy user-requested symbols to new environment
      for _, key in ipairs(arg) do
	 newEnv[key]=_G[key]
      end
      
      -- Prevent access to 'global' variables (within the new environment) that aren't non-nil by now
      
      local env_meta={
	 __newindex = function(env, sym, val) error(ERROR_private_env_write(sym), usererror) end,
	 __index = function(env, sym, val) error(ERROR_private_env_read(sym), usererror) end
      }
   
      setmetatable(newEnv, env_meta)
      
      return newEnv
   end

-- ########################################################################################
-- # Obtain mnoo version
-- ########################################################################################
local libAPI_getVersion=function() return version end

-- ########################################################################################
-- # Write API functions into lib
-- ########################################################################################
lib.newclass=libAPI_newclass
lib.isClass=isClass
lib.isObject=isObject
lib.enable_serialize=libAPI_enable_serialize
lib.ASCII_dump=sl_any -- excluding header to restore objects (for debug purposes)
lib.serialize=libAPI_serialize -- including header to restore objects
lib.serialise=libAPI_serialize 
lib.deserialize=libAPI_deserialize
lib.deserialise=libAPI_deserialize
lib.createPrivateEnvironment=libAPI_createPrivateEnvironment
lib.objectIsA=libAPI_objectIsA
lib.classIsA=libAPI_classIsA
lib.getObjectMetatable=classAPI_getObjectMetatable
lib.getVersion=libAPI_getVersion

-- ########################################################################################
-- # Built-in self test (optional)
-- ########################################################################################
if _SELFTEST then
   local test=
      function(testfun, check, case)
	 local flag, result=pcall(testfun)
	 result=result or "OK"
	 assert(string.find(result, check, 0, true), "***"..result.."*** instead of ***"..check.."***"..(case or "") )
      end

   -- ########################################################################################
   -- # Normal operation:
   -- # - class creation
   -- ########################################################################################
   test( -- t[1]
	function()
	   local creature_cl=lib.newclass()  
	   creature_cl:declarePermanentObjectdata("name") -- object entry
	   creature_cl:declareObjectmethod("getName") -- class entry (permanent)
	   creature_cl.getName=
	      function(self) return self.name end
	   
	   local animal_cl=lib.newclass(creature_cl) -- simple inheritance
	   local dog_cl=lib.newclass(animal_cl)
	   
	   local object_in_space_cl=lib.newclass()
	   object_in_space_cl:declarePermanentObjectdata("posx", "posy", "posz")
	   object_in_space_cl:declareObjectmethod("moveTo")
	   object_in_space_cl.moveTo=function(self, x,y,z) self.posx=x self.posy=y self.posz=z end
	   
	   local critter_cl=lib.newclass(creature_cl, object_in_space_cl) -- multiple inheritance

	   local waldi=dog_cl:new()
	   local freddy=critter_cl:new()
	   freddy.name="Freddy" 
	   waldi.name="Waldi"

	   freddy:moveTo(1,2,3)
	   assert(freddy:getName()=="Freddy")
	   assert(waldi:getName()=="Waldi")

	   -- check objectIsA
	   assert(waldi:objectIsA(dog_cl))
	   assert(waldi:objectIsA(animal_cl))
	   assert(waldi:objectIsA(creature_cl))
	   assert(lib.isObject(waldi))

	   assert(not waldi:objectIsA(critter_cl))
	   assert(not waldi:objectIsA(object_in_space_cl))

	   assert(freddy:objectIsA(critter_cl))
	   assert(freddy:objectIsA(creature_cl))
	   assert(freddy:objectIsA(object_in_space_cl))

	   assert(not freddy:objectIsA(animal_cl))
	   assert(not freddy:objectIsA(dog_cl))
	   

	end, 
	"OK")

   -- ########################################################################################
   -- # Undeclared write access to class
   -- ########################################################################################
   test( -- t[2]
	function()local c=lib.newclass() c.x="1" end, 
	ERROR_class_undeclared_write("x"))

   -- ########################################################################################
   -- # Redeclaration in same class
   -- ########################################################################################
   for i1=1, 4 do
      for i2=1, 4 do
	 local sym="a"..i1.."/"..i2
	 
	 test( -- t[4]
	      function()
		 local c=lib.newclass() 
		 if tostring(i1)=="1" then
		    c:declareClassfunction(sym)
		 elseif tostring(i1)=="2" then
		    c:declareClassdata(sym)
		 elseif tostring(i1)=="3" then
		    c:declareObjectmethod(sym)
		 elseif tostring(i1)=="4" then
		    c:declareObjectdata(sym)
		 else 
		    assert(nil)
		 end

		 if tostring(i2)=="1" then
		    c:declareClassfunction(sym)
		 elseif tostring(i2)=="2" then
		    c:declareClassdata(sym)
		 elseif tostring(i2)=="3" then
		    c:declareObjectmethod(sym)
		 elseif tostring(i2)=="4" then
		    c:declareObjectdata(sym)
		 else 
		    assert(nil)
		 end
	      end, 
	      ERROR_redeclaration(sym)
	   )
      end -- for i2
   end -- for i1

   -- ########################################################################################
   -- # Redeclaration in child class
   -- ########################################################################################
   for i1=1, 4 do
      for i2=1, 4 do
	 local sym="a"..i1.."/"..i2
	 
	 -- redeclaring a class data element in a derived class is allowed!
	 local expectedError
	 if tostring(i1)=="2" and tostring(i2)=="2" then
	    expectedError="OK"
	 else
	    expectedError=ERROR_redeclaration(sym)
	 end
	 
	 test(
	      function()
		 local c=lib.newclass() 
		 local c2=lib.newclass(c)
		 if tostring(i1)=="1" then
		    c:declareClassfunction(sym)
		 elseif tostring(i1)=="2" then
		    c:declareClassdata(sym)
		 elseif tostring(i1)=="3" then
		    c:declareObjectmethod(sym)
		 elseif tostring(i1)=="4" then
		    c:declareObjectdata(sym)
		 else 
		    assert(nil)
		 end

		 if tostring(i2)=="1" then
		    c2:declareClassfunction(sym)
		 elseif tostring(i2)=="2" then
		    c2:declareClassdata(sym)
		 elseif tostring(i2)=="3" then
		    c2:declareObjectmethod(sym)
		 elseif tostring(i2)=="4" then
		    c2:declareObjectdata(sym)
		 else 
		    assert(nil)
		 end
	      end, 
	      expectedError
	   )
      end -- for i2
   end -- for i1

   -- ########################################################################################
   -- # Derived classes share classdata
   -- ########################################################################################
   test(
	function()
	   -- c0 is separate. 
	   local c0=lib.newclass() c0:declareClassdata("val")

	   -- c1..c6 share the same data element
	   local c1=lib.newclass() c1:declareClassdata("val")
	   local c2=lib.newclass(c1)
	   local c3=lib.newclass(c2)
	   local c4=lib.newclass(c3)
	   local c5=lib.newclass(c4)
	   local c6=lib.newclass(c5)

	   c0.val=1        c1.val=1
	   c0.val=c0.val*2 c2.val=c2.val*2
	   c0.val=c0.val*2 c3.val=c3.val*2
	   c0.val=c0.val*2 c4.val=c4.val*2
	   assert(tostring(c0.val)=="8") 
	   assert(tostring(c1.val)=="8")

	   local c7=lib.newclass(c6) c7:declareClassdata("val")
	   assert(c7.val == nil)
	   c7.val="99"
	   assert(tostring(c1.val)=="8")
	   assert(tostring(c7.val)=="99")	   
	end, 
	"OK")

   -- ########################################################################################
   -- # Try to assign object data to class (error)
   -- ########################################################################################
   test(
	function()
	   local c=lib.newclass() c:declareObjectdata("val")
	   c.val="1" end, 
	ERROR_class_invalid_write("val", "objectdata"))

   -- ########################################################################################
   -- # Assign value to object (errors)
   -- ########################################################################################
   for i=1, 4 do

      local expectedError
      if tostring(i)=="1" then
	 expectedError=ERROR_object_invalid_write("val", "classfunction")
      elseif tostring(i)=="2" then
	 expectedError=ERROR_object_invalid_write("val", "classdata")
      elseif tostring(i)=="3" then
	 expectedError=ERROR_object_invalid_write("val", "objectmethod")
      elseif tostring(i)=="4" then
	 expectedError="OK"
      else assert(nil) end
      
      test(
	   function()
	      local c=lib.newclass() 
	      if tostring(i)=="1" then
		 c:declareClassfunction("val")
	      elseif tostring(i)=="2" then
		 c:declareClassdata("val")
	      elseif tostring(i)=="3" then
		 c:declareObjectmethod("val")
	      elseif tostring(i)=="4" then
		 c:declareObjectdata("val")
	      else 
		 assert(nil)
	      end
	      local o=c:new()
	      o.val=1
	   end,
	   expectedError
	)
   end -- for i
      
   -- ########################################################################################
   -- # Read undeclared entry from class
   -- ########################################################################################
   test(
	function()
	   local c=lib.newclass() 
	   local u=c.undefined
	end,
	ERROR_class_undeclared_read("undefined"))

   -- ########################################################################################
   -- # Read undeclared entry from object
   -- ########################################################################################
   test(
	function()
	   local c=lib.newclass() 
	   local o=c:new()
	   local u=o.undefined
	end,
	ERROR_object_undeclared_read("undefined"))
   
   -- ########################################################################################
   -- # Overloading and calling of class functions
   -- ########################################################################################
   test(
	function()
	   local c=lib.newclass() 
	   c:declareClassfunction("fun")
	   local c2=lib.newclass(c)

	   c.fun=function() 
		    return "one"
		 end

	   local superclass_fun=c2.fun
	   c2.fun=function() 
		     return superclass_fun().."two" 
		  end

	   assert(c.fun()=="one")
	   assert(c2.fun()=="onetwo")	   
	end, 
	"OK")

   -- ########################################################################################
   -- # Overloading and calling of object methods functions
   -- ########################################################################################
   test(
	function()
	   local c=lib.newclass() 
	   c:declareObjectmethod("fun")
	   local c2=lib.newclass(c)
	   c.fun=function(self) return "one" end
	   local superclass_fun=c2:getObjectmethod("fun")
	   c2.fun=function(self) return superclass_fun().."two" end

	   local o1=c:new()
	   local o2=c2:new()
	   assert(o1.fun()=="one")
	   assert(o2.fun()=="onetwo")	   
	end, 
	"OK")

   -- ########################################################################################
   -- # Read and write object entry on object
   -- ########################################################################################
   test(
	function()
	   local c=lib.newclass() 
	   c:declareObjectdata("val")
	   local o1=c:new()
	   local o2=c:new()
	   o1.val="one" o2.val="two"
	   assert(o1.val=="one") assert(o2.val=="two")
	end, 
	"OK")
   
   -- ########################################################################################
   -- # Test serialization
   -- ########################################################################################
   test(
	function()
	   local c=lib.newclass()
	   c:declarePermanentObjectdata("multi")
	   c=lib.newclass(c) 
	   c:declarePermanentObjectdata("perm1")
	   c=lib.newclass(c)
	   c:declarePermanentObjectdata("perm2")
	   c=lib.newclass(c, c0)
	   c:declarePermanentObjectdata("perm3") 
	   lib.enable_serialize(c, "test") 
	   o=c:new()
	   o.perm1="a"
	   o.perm2="b"
	   o.perm3="c"
	   o.multi="d"
	   local ASCII=lib.serialize(o)
	   local o2=lib.deserialize(ASCII)
	   assert(o2.perm1=="a")
	   assert(o2.perm2=="b")
	   assert(o2.perm3=="c")
	   assert(o2.multi=="d")
	   
	end, 
	"OK")

   -- ########################################################################################
   -- # More complicated serialize / deserialize example:
   -- # binary tree with backward references
   -- # Serialize only simple tree structure,
   -- # regenerate redundant backreferences via deserialize_callback() (called whenever
   -- # an object is rebuilt from ASCII)
   -- ########################################################################################
   test(
	function()
	   local c=lib.newclass()
	   lib.enable_serialize(c, "test2") 

	   c:declarePermanentObjectdata("child1")
	   c:declarePermanentObjectdata("child2")
	   c:declareObjectdata("parent")
	   c:declareObjectmethod("checkIntegrity")
	   c:declareObjectmethod("zap")
	   
	   -- rebuild redundant data
	   c.deserialize_callback=
	      function(self) 
		 assert(self:objectIsA(c))
		 if self.child1 then self.child1.parent=self end
		 if self.child2 then self.child2.parent=self end
	      end
	   
	   -- constructor of superclass
	   local super_new=c:getClassfunction("new") -- c.new would also work
	   local localnew -- forward declaration because recursive
	   localnew=
	      function(actualclass, parent, nlevel)
		 local self=super_new(actualclass)
		 if nlevel > 1 then
		    self.child1=localnew(actualclass, self, nlevel-1)
		    self.child2=localnew(actualclass, self, nlevel-1)
		 end
		 self.parent=parent -- which may be nil		 
		 return self
	      end
	   -- overload constructor
	   c.new=localnew

	   c.checkIntegrity=
	      function(self)
		 local r=1
		 if self.child1 then 
		    assert(self.child1.parent==self) 
		    r=r+self.child1:checkIntegrity()
		 end
		 if self.child2 then
		    assert(self.child2.parent==self)
		    r=r+self.child1:checkIntegrity()
		 end
		 return r
	      end

	   -- Cleanup code: Break circular references, so that garbage
	   -- collection can free unused structures.
	   c.zap=
	      function(self)
		 if self.child1 then 
		    self.child1:zap()
		 end
		 if self.child2 then
		    self.child2:zap()
		 end
		 self.parent=nil
	      end

	   -- build tree structure with three levels
	   local o=c:new(nil, 4)
	   -- count number of nodes and check, whether backreferences are in order
	   assert(tostring(o:checkIntegrity())=="15")

	   local ASCII=lib.serialize(o)
	   local o2=lib.deserialize(ASCII)
	   assert(tostring(o2:checkIntegrity())=="15")
	   o:zap() 
	   o2:zap()	   
	end, 
	"OK")

   -- ########################################################################################
   -- # Error in deserialize (class not registered)
   -- ########################################################################################
   test(	
	function()
	   local o2=lib.deserialize("return function(storage) return storage.unknown{} end")	  
	end,
	ERROR_deserialize_unknown_class("unknown"))

   -- ########################################################################################
   -- # Error: Enable serialization twice for the same class
   -- ########################################################################################
   test(	
	function()
	   local c=lib.newclass()
	   lib.enable_serialize(c, "dupl") 
	   lib.enable_serialize(c, "dup2") 
	end,
	ERROR_serialize_duplicate_class2())

   -- ########################################################################################
   -- # Error: Use same serialization ID for two classes
   -- ########################################################################################
   test(	
	function()
	   local c1=lib.newclass(c)
	   local c2=lib.newclass(c)
	   lib.enable_serialize(c1, "dup") 
	   lib.enable_serialize(c2, "dup") 
	end,
	ERROR_serialize_duplicate_class1("dup"))

   -- ########################################################################################
   -- # private object functions
   -- ########################################################################################
   for tc, tcresult in pairs({
				tc1="OK", 
				tc3=ERROR_object_private_method("priv_fun"), 
				tc4=ERROR_object_private_method("priv_fun"), 
				tc5=ERROR_object_private_method("priv_fun2"),
				tc6=ERROR_private_env_write("undeclared"),
				tc7=ERROR_private_env_read("undeclared")}) do
      test(	
	   function()
	      local build_class=
		 function()
		    -- Create private env
		    local env=lib.createPrivateEnvironment()
		    setfenv(1, env)
		    
		    local c1=lib.newclass()	   
		    c1:declareObjectmethod("pub_fun", "priv_fun")
		    c1:setPrivate("priv_fun")
		    c1.pub_fun=function(self) self:priv_fun() return "one" end
		    c1.priv_fun=function() return "two" end
		    
		    local o=c1:new()
		    assert(o:pub_fun()=="one")
		    assert(o:priv_fun()=="two")

		    if tc == "tc6" then undeclared=1 end
		    if tc == "tc7" then local x=undeclared end

		    return c1
		 end
	      
	      local derive_class=
		 function(c)
		    
		    -- Create another private env
		    local env=lib.createPrivateEnvironment()
		    setfenv(1, env)
		    
		    local c2=lib.newclass(c)
		    c2:declareObjectmethod("pub_fun2", "priv_fun2")
		    c2:setPrivate("priv_fun2")
		    c2.priv_fun2=function(self) self:priv_fun() return "two2" end

		    local o=c2:new()
		    assert(o:priv_fun()=="two")
		    assert(o:priv_fun2()=="two2")

		    return c2
		 end
	      
	      local c=build_class()
	      local c2=derive_class(c)
	      local o=c:new()
	      
	      assert(o:pub_fun() == "one")
	      local o2=c2:new()

	      if tc == "tc3" then o:priv_fun() end	      
	      if tc == "tc4" then o2:priv_fun() end
	      if tc == "tc5" then o2:priv_fun2() end
	   end,
	   tcresult, tc)
   end -- for i

   -- ########################################################################################
   -- # private errors
   -- ########################################################################################
   test(	
	function()
	   local c1=lib.newclass()	   
	   c1:declareObjectmethod("priv_fun")
	   c1:setPrivate("priv_fun", "priv_fun")
	end,
	ERROR_sym_is_already_private("priv_fun")
     )

   test(	
	function()
	   local c1=lib.newclass()	   
	   c1:declareObjectdata("priv")
	   c1:setPrivate("priv")
	end,
	ERROR_private_must_be_objectmethod("priv")
     )

   test(	
	function()
	   local c1=lib.newclass()	   
	   c1:declareClassdata("priv")
	   c1:setPrivate("priv")
	end,
	ERROR_private_must_be_objectmethod("priv")
     )

   test(	
	function()
	   local c1=lib.newclass()	   
	   c1:declareClassfunction("priv")
	   c1:setPrivate("priv")
	end,
	ERROR_private_must_be_objectmethod("priv")
     )
	   
   io.stderr:write("mnoo/mnoo.lua: self test passed\n")
end -- self test

-- Store the evaluation result at a standard location for require() to look it up.
-- This is used to precompile libraries into the executable.
_LOADED["mnoo_current/mnoo.lua"]=lib
return lib