Setting multiplication operator through metatable in specific environment in Lua - 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)

Related

self as param, and setting scope?

Can someone please help me with this.
local function TestPrice()
local obj1 = require("myObj")
local obj2 = require("myObj")
obj1:setPrice(30)
obj2:setPrice(40)
print(obj1.price) -- this prints '40'. Setting price on obj2 changes the price in obj1
end
and
-- myObj.lua
local M = {
price = -1;}
local function _setPrice(self, newprice)
self.price = newprice
-- todo other stuff
end
M.setPrice = _setPrice
return M
I thought that by setting self as a param it set the scope. Why does calling this function on obj2 update the value of obj1?
You need a function to create new object
-- myObj.lua
local M = {}
local function _setPrice(self, newprice)
self.price = newprice
-- todo other stuff
end
M.setPrice = _setPrice
M.__index = M
local function create_new_obj()
local obj = {price = -1}
setmetatable(obj, M)
return obj
end
return create_new_obj
-- main.lua
local function TestPrice()
local obj1 = require("myObj")()
local obj2 = require("myObj")()
obj1:setPrice(30)
obj2:setPrice(40)
print(obj1.price, obj2.price)
end
TestPrice()
In your code require load once, and second require give you same object. You should implement some kind of copy method.
-- myObj.lua
local M = {
price = -1;}
local function _setPrice(self, newprice)
self.price = newprice
-- todo other stuff
end
function M:copy()
return {["price"] = self.price, ["setPrice"]=_setPrice, ["copy"] = self.copy}
end
M.setPrice = _setPrice
return M

Read only iterable table in lua?

I want to have a read only table in my Lua program. If ever a key is removed or a key is associated with a new value, an error must be thrown.
function readonly(table)
local meta = { } -- metatable for proxy
local proxy = { } -- this table is always empty
meta.__index = table -- refer to table for lookups
meta.__newindex = function(t, key, value)
error("You cannot make any changes to this table!")
end
setmetatable(proxy, meta)
return proxy -- user will use proxy instead
end
It works great.
t = { }
t["Apple"] = "Red"
t[true] = "True!"
t[51] = 29
for k,v in pairs(t) do
print(v)
end
t = readonly(t)
t[51] = 30
Prints
Red
True!
29
input:7: You cannot make any changes to this table!
Problem
for k, v in pairs(t) do
print(v)
end
Will print nothing under all circumstances now. That's because the proxy table will never have anything inside of it. pairs apparently never calls index and thus cannot retrieve anything from the actual table.
What can I do to make this readonly table iterable?
I'm on Lua 5.1 and have access to these metamethods:
Lua 5.1 Manual
You can modify standard Lua function pairs to work correctly with your read-only tables.
local function readonly_newindex(t, key, value)
error("You cannot make any changes to this table!")
end
function readonly(tbl)
return
setmetatable({}, {
__index = tbl,
__newindex = readonly_newindex
})
end
local original_pairs = pairs
function pairs(tbl)
if next(tbl) == nil then
local mt = getmetatable(tbl)
if mt and mt.__newindex == readonly_newindex then
tbl = mt.__index
end
end
return original_pairs(tbl)
end
Usage:
t = { }
t["Apple"] = "Red"
t[true] = "True!"
t[51] = 29
for k,v in pairs(t) do
print(k, v)
end
t = readonly(t)
for k,v in pairs(t) do
print(k, v)
end
t[51] = 30
One solution is to create a wholly custom iterator for the table.
function readonly(table)
local meta = { } -- metatable for proxy
local proxy = { } -- this table is always empty
meta.__index = table -- refer to table for lookups
meta.__newindex = function(t, key, value)
error("You cannot make any changes to this table!")
end
local function iter()
return next, table
end
setmetatable(proxy, meta)
return proxy, iter -- user will use proxy instead
end
Usage:
t = { }
t["Apple"] = "Red"
t[true] = "True!"
t[51] = 29
for k,v in pairs(t) do
print(v)
end
t, tIter = readonly(t)
t[51] = 30
for k, v in tIter do
print(v)
end

"Object" generating function in 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 '.'?

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"
-- }

attaching metatables within tabes

I have parser that parses a config file and produces a table.
The resulting table can look something like:
root = {
global = {
},
section1 = {
subsect1 = {
setting = 1
subsubsect2 = {
}
}
}
}
The goal is to have a table I can read settings from and if the setting doesn't exist, it'll try to grab it from it's parent. At the top level it will grab from global. If it's not in global it'll return nil.
I attach metatables to root like this:
local function attach_mt(tbl, parent)
for k,v in pairs(tbl) do
print(k, v)
if type(v) == 'table' then
attach_mt(v, tbl)
setmetatable(v, {
__index = function(t,k)
print("*****parent=", dump(parent))
if parent then
return tbl[k]
else
if rawget(tbl, k) then
return rawget(tbl, k)
end
end
print(string.format("DEBUG: Request for key: %s: not found", k))
return nil
end
})
end
end
end
attach_mt(root)
However, when requesting keys it doesn't work. What appears to be the case is that is always nil. How do I read from the parent table?
local function attach_mt(tbl, parent)
setmetatable(tbl, {__index = parent or root.global})
for k, v in pairs(tbl) do
if type(v) == 'table' then
attach_mt(v, tbl)
end
end
end
attach_mt(root)
setmetatable(root.global, nil)

Resources