"Object" generating function in Lua - lua

I use this to generate "class" in Lua.
function Class(...)
local cls = {};
local bases = {arg};
for i, base in ipairs(bases) do
for k, v in pairs(base) do
cls[k] = v
end
end
cls.__index = cls;
cls._is_a = {[cls] = true}
for i, base in pairs(bases) do
for c in pairs(base._is_a) do
cls._is_a[c] = true
end
cls._is_a[base] = true
end
function cls.IsType(obj,cmptype)
if cmptype then
return obj._is_a[cmptype] or false;
else
if type(obj) == 'table' then
return cls._is_a[obj.__index] or false;
else
return false;
end
end
end
cls._init = function() end;
setmetatable(cls, {
__call = function (c, ...)
local instance = setmetatable({}, c)
instance._init(instance, ...);
return instance
end
})
return cls
end
In zeroBane Studio this code launches, and i can do stuff like
Test1 = Class();
function Test1:_init()
end
Test2 = Class(Test1);
function Test2:_init()
Test1._init(self);
end
But when i try to do something similar in Starbound, it will complain about Test1._init(self); with message "attempt to index a function value". I can bet that it has to do with metatables, but i'm not good enough to solve it by myself.
Is there a way to fix this, so that i will be able to call functions using '.'?

Related

Setting multiplication operator through metatable in specific environment in Lua

local names = setmetatable({},
{__mul = function(a,b) return a|b end}
)
names={i=0,j=1}
tr=load("return i*j",nil,"t",names)()
print(tr)
It prints tr as 0. The expected answer is 1 as 0|1 results to 1. Where is the code wrong?
Try:
local mt_obj = {
__tostring = function(o) return tostring(o[1]) end,
}
local function get(o)
if type(o) == "table" then return o[1] else return o end
end
local function new(v)
return setmetatable({v}, mt_obj)
end
function mt_obj.__mul(a,b)
return new(get(a)|get(b))
end
local mt_env = {
__index = function(t,k) return new(t.variables[k]) end,
__newindex = function(t,k,v) t.variables[k] = v end,
}
local names = {i=0,j=1}
local env = setmetatable({variables = names}, mt_env)
tr=get(load("return i*j*8",nil,"t",env)())
print(tr)

How to change a table's metatable but inherit it's own methods in Lua

in Lua we do OO programming like this:
MyClass = {}
function MyClass:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
obj.hello = "hello world"
return obj
end
function MyClass:sayHi()
print(self.hello)
end
function main()
local obj = MyClass:new()
obj:sayHi()
end
When working with more compelx stuff, usually I take advantage of Lua's metamethods to proxy function calls and do whatever I need with it, like arguments parsing etc, by adding this:
MyClassMeta = {}
function MyClassMeta.__index(obj, funcName)
return function (self, ...)
//do some stuff
print("you called " .. funcName .. " with args", ...)
end
end
and changing the line:
setmetatable(obj, self)
to:
setmetatable(obj, MyClassMeta)
every function I call with an instance of MyClass will execute the code implemented in MyClassMeta.__index metamethod.
What I want to do now is inherit the MyClass existing methods, and execute MyClassMeta.__index only for functions that are not part of MyClass.
In the above example, the code will always execute the MyClassMeta.__index metamethod even when calling MyClass:sayHi():
function main()
local obj = MyClass:new()
obj:sayHi("hello")
end
you called sayHi with args hello
When you set __index to a table, it will look for properties on that table and return them if they don't exist on the instance. Since sayHi exists on the MyClass table it is used.
self.__index = self
When you set __index to a function, it can return anything for properties that don't exist on the instance. You can check if the key exists on the MyClass table and return it, then do something else if it doesn't:
MyClass = {}
MyMetatable = {
__index = function(obj, key)
if MyClass[key] ~= nil then return MyClass[key] end
return function(self, ...)
print("you called "..tostring(key))
print(" self.hello is '"..tostring(self.hello).."'")
print(" with args", ...)
end
end
}
function MyClass:new()
local obj = {}
setmetatable(obj, MyMetatable)
obj.hello = "hello world"
return obj
end
function MyClass:sayHi()
print(self.hello)
end
function main()
local obj = MyClass:new()
obj:sayHi()
end
local obj = MyClass:new()
obj:sayHi("hello")
obj:somethingElse(1, 2, 3)
Version with Egor's comment
MyClass = {}
setmetatable(MyClass, {
-- if it's not found on MyClass, return a function
__index = function(self, funcName)
return function(self, ...)
print("you called "..funcName.." with args", ...)
end
end
})
function MyClass:new()
local obj = {}
-- if it's not found on obj, try self (MyClass)
setmetatable(obj, { __index = self })
obj.hello = "hello world"
return obj
end
function MyClass:sayHi()
print(self.hello)
end
local obj = MyClass:new()
obj:sayHi()
obj:somethingElse(1, 2, 3)
When creating an object this sets the __index of the new object's metatable to MyClass, and MyClass's metatable's index to the function that is a fallback. So if the property isn't on your object or on MyClass, it will use the fallback.

Converting Python function to Lua function

I am trying to convert existing python function into lua function. But my lua function is not producing same result as python function. Any help is appreciated.
Python function:
import json
test = '{"http://localhost:8080/":{"phone":{"-detail/phone detail.template.html":"5167n,a,7,2","s/motorola-xoom-with-wifi.json":"516a0,5,4,3"},"favicon.ico":"016ad,3,3,2","img/phones/motorola-xoom-with-wi-fi.":{"1.jpg":"*02s,2s,4v,h3|116da,o,l,6","2.jpg":"*02s,2s,4v,kp|116da,j,i,8","3.jpg":"*02s,2s,4v,ob|116da,o,m,8,7,,7,7,7","4.jpg":"*02s,2s,4v,rx|116da,o,m,9,8,,7,7,7","5.jpg":"*02s,2s,4v,vj|116da,p,m,a,8,,7,7,7"}}}'
def tri(param):
t = {}
for key in param:
if key not in param:
continue
if isinstance(param[key], dict) and param[key] is not None:
flat = tri(param[key])
for x in flat:
if x not in flat:
continue
t[key + x] = flat[x]
else:
t[key] = param[key]
return t
print(tri(json.loads(test)))
Lua code ( which is not producing same result as python function)
local json = require('cjson')
local test = '{"http://localhost:8080/":{"phone":{"-detail/phone-detail.template.html":"5167n,a,7,2","s/motorola-xoom-with-wi-fi.json":"516a0,5,4,3"},"favicon.ico":"016ad,3,3,2","img/phones/motorola-xoom-with-wi-fi.":{"1.jpg":"*02s,2s,4v,h3|116da,o,l,6","2.jpg":"*02s,2s,4v,kp|116da,j,i,8","3.jpg":"*02s,2s,4v,ob|116da,o,m,8,7,,7,7,7","4.jpg":"*02s,2s,4v,rx|116da,o,m,9,8,,7,7,7","5.jpg":"*02s,2s,4v,vj|116da,p,m,a,8,,7,7,7"}}}'
local function tri(param)
t = {}
for key in pairs(param) do
if param[key] == nil then end
if type(param[key]) == "table" then
flat = tri(param[key])
for k in pairs(flat) do
t[key .. k] = flat[k]
end
else
t[key] = param[key]
end
end
return t
end
print(json.encode(tri(json.decode(test))))
local function tri(param)
t = {} -- every time we call tri t will be "reset" to an empty table
for key in pairs(param) do
if param[key] == nil then end
if type(param[key]) == "table" then
flat = tri(param[key]) -- here we call tri, but we still need t!
for k in pairs(flat) do
t[key .. k] = flat[k]
end
else
t[key] = param[key]
end
end
return t
end
Making at least t global should solve that problem. But there is also no reason for flat to be global so we make it local too.
local function tri(param)
local t = {}
for key in pairs(param) do
if param[key] == nil then end
if type(param[key]) == "table" then
local flat = tri(param[key])
for k in pairs(flat) do
t[key .. k] = flat[k]
end
else
t[key] = param[key]
end
end
return t
end
Your task could be done a bit easier using json.traverse() function from this Lua JSON module.
Traversing lets you perform arbitrary operations with JSON elements on-the-fly.
This code concatenates element's path (for every JSON element except JSON containers: arrays/objects) and uses it as a key for Lua table.
local json = require'json'
local t = {}
local function callback(path, json_type, value)
if value ~= nil then -- value == nil for containers (arrays/objects)
t[table.concat(path)] = value
end
end
local test = '{"http://localhost:8080/":{"phone":{"-detail/phone detail.template.html":"5167n,a,7,2","s/motorola-xoom-with-wifi.json":"516a0,5,4,3"},"favicon.ico":"016ad,3,3,2","img/phones/motorola-xoom-with-wi-fi.":{"1.jpg":"*02s,2s,4v,h3|116da,o,l,6","2.jpg":"*02s,2s,4v,kp|116da,j,i,8","3.jpg":"*02s,2s,4v,ob|116da,o,m,8,7,,7,7,7","4.jpg":"*02s,2s,4v,rx|116da,o,m,9,8,,7,7,7","5.jpg":"*02s,2s,4v,vj|116da,p,m,a,8,,7,7,7"}}}'
json.traverse(test, callback)
-- Now t == {
-- ["http://localhost:8080/favicon.ico"] = "016ad,3,3,2",
-- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.1.jpg"] = "*02s,2s,4v,h3|116da,o,l,6",
-- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.2.jpg"] = "*02s,2s,4v,kp|116da,j,i,8",
-- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.3.jpg"] = "*02s,2s,4v,ob|116da,o,m,8,7,,7,7,7",
-- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.4.jpg"] = "*02s,2s,4v,rx|116da,o,m,9,8,,7,7,7",
-- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.5.jpg"] = "*02s,2s,4v,vj|116da,p,m,a,8,,7,7,7",
-- ["http://localhost:8080/phone-detail/phone detail.template.html"] = "5167n,a,7,2",
-- ["http://localhost:8080/phones/motorola-xoom-with-wifi.json"] = "516a0,5,4,3"
-- }

infinite recursion in __index metamethod

As I understand lua don't call __index unless the key wasn't found in the table so I have that code and it suffers from infinite recursion in the __index part which I don't get as both values used inside the __index function already exist in the table!?
This is basically a test script for trying to save the size of the table in a memory to retreive when # is called
do
local lenKey,originalKey = {},{}
fastKey = {}
fastKey.__len = function(t) return t[lenKey] end
fastKey.__index = function (t,k)
t[lenKey] = t[lenKey] +1
return t[oroginalKey][k]
end
fastKey.__newindex = function(t,k,v) t[originalKey][k] = v end
fastKey.__pairs = function ()
return function (t, k)
return next(t[oroginalKey], k)
end
end
function fastLen(t)
local proxy = {}
local c = 0
for _ in pairs(t) do
c=c+1
end
proxy[lenKey] = c
proxy[originalKey] = t
setmetatable(proxy,fastKey)
return proxy
end
end
n = fastLen{1,2,3,x=5}
--n:insert(1) -- here the __index is called and gets stackoverflow
print(#n)
You've got two typos in there: both the __index and __pairs functions contain oroginalKey instead of originalKey.

Is This A lua Variable Scope Issue (and how can it be solved)?

A very strange error, showing an object is nil.
the code in subject is
while pbs:HasNext() do
local char = self.DecodeCharacter(pbs)
...
One would think, that if pbs:HasNext() is true, it means that, pbs is not nil, whatsoever.
However, the print(pbs) - the first line of HTMLEntityCodec:DecodeCharacter prints nil
function HTMLEntityCodec:DecodeCharacter(pbs)
print(pbs)
...
The entire file dumped below, it was stripped from 1800+ lines to 110 so it can be clear for SO users to get he context. But that stripping took away all logic from the code, so do not get confused by that.
#!/usr/bin/env lua
function Inherits( baseClass )
local new_class = {}
local class_mt = { __index = new_class }
function new_class:create()
local newinst = {}
setmetatable( newinst, class_mt )
return newinst
end
if baseClass then
setmetatable( new_class, { __index = baseClass } )
end
return new_class
end
-------------------------------------------
-- PushbackString
-------------------------------------------
PushbackString = Inherits({})
function PushbackString:Init(input)
self.input = input
self.pushback = nil
self.temp = nil
self.index = 0
self.mark = 0
end
-- Mark the current index, so the client can reset() to it if need be.
function PushbackString:HasNext()
return true
end
function PushbackString:Mark ()
self.temp = self.pushback
self.mark = self.index
end
BaseCodec = Inherits({})
function BaseCodec:Decode(input)
local buff = ''
local pbs = PushbackString:create()
pbs:Init(input)
while pbs:HasNext() do
local char = self.DecodeCharacter(pbs)
if char ~= nil then
buff = buff .. char
else
buff = buff .. pbs:Next()
end
end
return buff
end
HTMLEntityCodec = Inherits(BaseCodec)
-- HTMLEntityCodec.classname = ('HTMLEntityCodec')
function HTMLEntityCodec:DecodeCharacter(pbs)
print(pbs)
pbs:Mark()
end
DefaultEncoder = Inherits({})
function DefaultEncoder:Init(codecs)
self.html_codec = HTMLEntityCodec:create()
end
function DefaultEncoder:TestInput(input , strict)
print ("\n----------------8<----------------8<----------------\n")
print ("Input:\t" .. input)
-- default value
if strict == nil then strict = true end
-- nothing to do
if input == nil then return nil end
local working = input
local codecs_found = {}
local found_count = 0
local clean = false
while not clean do
clean = true
old = working
working = self.html_codec:Decode( working )
if old ~= working then
print ("Warning:\tINTRUSION DETECTED")
end
end
print ("Output:\t".. working)
return working
end
local default_encoder = DefaultEncoder:create()
default_encoder:Init()
default_encoder:TestInput("%25", true)
----------8<-----------8<--------------8<----------------
END OF FILE
Console Output:
tzury#1005:~/devel/lua$ lua problem.lua
----------------8<----------------8<----------------
Input: %25
nil
lua: problem.lua:70: attempt to index local 'pbs' (a nil value)
stack traceback:
problem.lua:70: in function 'DecodeCharacter'
problem.lua:54: in function 'Decode'
problem.lua:96: in function 'TestInput'
problem.lua:109: in main chunk
[C]: ?
In your code, the crash happens on this line:
local char = self.DecodeCharacter(pbs)
The problem is that you are calling DecodeCharacter with incorrect number of arguments.
Solution: call it like this (notice the colon):
local char = self:DecodeCharacter(pbs)
Explanation:
When you define functions in Lua using the colon (:), you are using a syntax sugar which hides an implicit first argument named self. Definitions like:
function HTMLEntityCodec:DecodeCharacter(pbs) ... end
Are actually 'translated' to this:
HTMLEntityCodec.DecodeCharacter = function (self, pbs) ... end
When you call the function, you either need to pass the self argument yourself, or use the colon call to supply it automatically. In your code (self.DecodeCharacter(pbs)), you are passing pbs which ends up as self in HTMLEntityCodec.DecodeCharacter, and pbs ends up being nil. Both following calls are equivalent and should solve the issue:
local char = self.DecodeCharacter(self, pbs)
local char = self:DecodeCharacter(pbs)

Resources