Lua local require of modules in different files - lua

I learned that require only loads a file once it's loaded (in package.loaded). However I see the following behavior and would like to get some pointers on what is happening.
Say I have a file lib/q.lua:
local q = {}
local function dep()
return 1;
end
q.dep = dep;
return q;
Another file lib/p.lua:
local q = require('./lib/q');
local p = {}
local function result()
return q;
end
p.result = result;
return p;
Another file main.lua:
local p = require('./lib/p');
local q = require('./lib/q');
local assert = require('luassert');
local result = p.result();
print(result);
print(package.loaded['./lib/q']);
print(q);
assert.is_equal(q, result);
The value of package.loaded['./lib/q'] is the same as result. However its value is different from the local q in main.lua. I expected them to be the same. Is there documentation on this or I'm missing something.
New to Lua. Any help is greatly appreciated. Thanks.

Related

Lua: Workaround for boolean conversion of a class variable when enclosed in parentheses

In the below code, can anyone explain why does t1:print() works but (t1):print fails. I am attempting to make something like (t1 * 3):print() work without using an intermediate variable.
function classTestTable(members)
members = members or {}
local mt = {
__metatable = members;
__index = members;
}
function mt.print(self)
print("something")
end
return mt
end
TestTable = {}
TestTable_mt = ClassTestTable(TestTable)
function TestTable:new()
return setmetatable({targ1 = 1}, TestTable_mt )
end
TestTable t1 = TestTable:new()
t1:print() -- works fine.
(t1):print() -- fails with error "attempt to call a boolean value"
Lua expressions can extend over multiple lines.
print
(3)
Will print 3
So
t1:print()
(t1):print()
actually is equivalent to
t1:print()(t1):print()
or
local a = t1:print()
local b = a(t1)
b:print()
So you're calling the return value of t1:print()
To avoid that follow Egors advice and separate both statements with a semicolon.
t1:print();(t1):print()

Load Lua function chunk, modify environment in 5.2 with sandbox

This question is similar to Modify Lua Chunk Environment, but with a twist.
I want to load strings as reusable Lua functions, where they all share the same sandbox for evaluation, but where each invocation I can pass in different values for the variables.
For example, say I want to evaluate the equation round(10*w/h) + a * b, with round, w, and h all values in a common sandbox environment, but where a and b are values that I want to modify each time I evaluate the equation.
This question shows how to dynamically change the environment for a compiled function. However, it does so setting the ENTIRE environment for the function, without the fallback sandbox that I have.
What's an efficient way to achieve my goals? Note that I almost only care about time needed to evaluate the function, not the setup time.
The way I have things currently, users write CSS expressions like:
box {
left: #x;
width: viewwidth - #w;
}
...where #x and #w are attributes of the box element (the 'local' variables) and viewwidth is a sheet-level variable set up elsewhere (my sandbox). Each of the property values—the portion after the :—is parsed out as a string to be compiled into a Lua function. They use normal Lua syntax, except where I currently swap the # with _el. in order to dereference the element table.
For answers to this question it is acceptable to keep this same syntax and require a differentiation between the local and sheet variables, BUT it is also acceptable to have a solution the gets rid of the # symbols and treats all variables the same.
I came up with 6 techniques for accomplishing the goal. The bottom of this post benchmarks their performance for evaluation speed. The fastest techniques require differentiating the local variables from sandbox variables in code:
local SANDBOX = {
w=1920,
h=1080,
round=function(n) return math.floor(n+0.5) end
}
local CODE = "round(10*w/h) + #a * #b"
function compile(code, sandbox)
local munged = 'local _el=... return '..code:gsub('#', '_el.')
return load(munged, nil, nil, sandbox)
end
local f = compile(CODE, SANDBOX)
print(f{a=1, b=2}) --> 20
print(f{a=3, b=4}) --> 30
If you don't want to differentiate the changing variables from those in the sandbox, or don't want to use a fragile sub() like the above, the next fastest is to mutate the __index of your locals and then pass it as the environment. You can wrap this in a helper function to make it easier:
local CODE = "round(10*w/h) + a * b"
function compile(code, sandbox)
local meta = {__index=sandbox}
return {meta=meta, f=load('_ENV=... return '..code)}
end
function eval(block, locals)
return block.f(setmetatable(locals, block.meta))
end
local f = compile(CODE, SANDBOX)
print(eval(f, {a=1, b=2})) --> 20
print(eval(f, {a=3, b=4})) --> 30
Here is the benchmark results of all the techniques below. Note that the fastest one can be even faster because unlike all other techniques the compile function returns a function that can be invoked directly, instead of wrapped in an evaluation helper script:
scope as a table, 2 : 0.9s (0.22µs per eval)
scope as a table : 1.1s (0.27µs per eval)
Use __ENV, change scope meta : 1.3s (0.32µs per eval)
blank env, change meta of scope : 1.6s (0.41µs per eval)
copy values over environment : 2.8s (0.70µs per eval)
setfenv, change scope meta : 3.0s (0.74µs per eval)
local SANDBOX = {
w = 1920,
h = 1080,
round = function(n) return math.floor(n+0.5) end
}
local TESTS = {
{env={a=1, b=2}, expected=18+2},
{env={a=4, b=3}, expected=18+12},
{env={a=9, b=7}, expected=18+63},
{env={a=4, b=5}, expected=18+20},
}
-- https://leafo.net/guides/setfenv-in-lua52-and-above.html
local function setfenv(fn, env)
local i = 1
while true do
local name = debug.getupvalue(fn, i)
if name == "_ENV" then
debug.upvaluejoin(fn, i, (function() return env end), 1)
break
elseif not name then
break
end
i = i + 1
end
return fn
end
local techniques = {
["copy values over environment"]={
code="round(10*w/h) + a*b",
setup=function(code, fallback)
local env = setmetatable({},{__index=fallback})
return {env=env,func=load("return "..code,nil,nil,env)}
end,
call=function(block, kvs)
for k,v in pairs(block.env) do block.env[k]=nil end
for k,v in pairs(kvs) do block.env[k]=v end
return block.func()
end
},
["blank env, change meta of scope"]={
code="round(10*w/h) + a*b",
setup=function(code, fallback)
local kvsmeta = {__index=fallback}
local envmeta = {}
local env = setmetatable({},envmeta)
return {envmeta=envmeta,meta=meta,kvsmeta=kvsmeta,func=load("return "..code,nil,nil,env)}
end,
call=function(block, kvs)
block.envmeta.__index=kvs
setmetatable(kvs, block.kvsmeta)
return block.func()
end
},
["setfenv, change scope meta"]={
code="round(10*w/h) + a*b",
setup=function(code, fallback)
return {meta={__index=fallback}, func=load("return "..code)}
end,
call=function(block, kvs)
setmetatable(kvs,block.meta)
setfenv(block.func, kvs)
return block.func()
end
},
["Use __ENV, change scope meta"]={
code="round(10*w/h) + a*b",
setup=function(code, fallback)
local meta = {__index=fallback}
return {meta=meta, func=load("_ENV=... return "..code)}
end,
call=function(block, kvs)
return block.func(setmetatable(kvs, block.meta))
end
},
["scope as a table"]={
-- NOTE: requires different code than all other techniques!
code="round(10*w/h) + _el.a * _el.b",
setup=function(code, fallback)
local env = setmetatable({},{__index=fallback})
return {env=env,func=load("return "..code,nil,nil,env)}
end,
call=function(block, kvs)
block.env._el=kvs
return block.func()
end
},
["scope as a table, 2"]={
-- NOTE: requires different code than all other techniques!
code="round(10*w/h) + _el.a * _el.b",
setup=function(code, fallback)
return load("local _el=... return "..code,nil,nil,fallback)
end,
call=function(func, kvs)
return func(kvs)
end
},
}
function validate()
for name,technique in pairs(techniques) do
local f = technique.setup(technique.code, SANDBOX)
for i,test in ipairs(TESTS) do
local actual = technique.call(f, test.env)
if actual~=test.expected then
local err = ("%s test #%d expected %d but got %s\n"):format(name, i, test.expected, tostring(actual))
io.stderr:write(err)
error(-1)
end
end
end
end
local N = 1e6
function benchmark(setup, call)
for name,technique in pairs(techniques) do
local f = technique.setup(technique.code, SANDBOX)
local start = os.clock()
for i=1,N do
for i,test in ipairs(TESTS) do
technique.call(f, test.env)
end
end
local elapsed = os.clock() - start
print(("%-33s: %.1fs (%.2fµs per eval)"):format(name, elapsed, 1e6*elapsed/#TESTS/N))
end
end
validate()
benchmark()

How to use strtok in luajit?

My code are as follow:
local ffi = require "ffi"
local ffi_C = ffi.C
local ffi_typeof = ffi.typeof
local ffi_new = ffi.new
local ffi_string = ffi.string
local NULL = ngx.null
local tostring = tostring
ffi.cdef[[
char * strtok(char * str, const char * delimiters);
]]
local p_char_type = ffi_typeof("char[?]")
function split(src, c)
local result = {}
local pch = ffi_new(p_char_type, 1)
local psrc = ffi_new(p_char_type, #src)
local pc = ffi_new(p_char_type, #c)
ffi.copy(psrc, src)
ffi.copy(pc, c)
pch = ffi_C.strtok(psrc, pc)
while pch do
table.insert(result, ffi_string(pch))
pch = ffi_C.strtok(NULL, pc)
ngx.log(ngx.ERR, "pch ok")
end
ngx.log(ngx.ERR, "split ok")
return result
end
When I run my nginx, there are something wrong happened!
After return by the while loop, the nginx worker process crashed with signal 11.
The last ngx.log can not run.
How can I deal with it?
local psrc = ffi_new(p_char_type, #src)
ffi.copy(psrc, src)
ffi.copy when given a string source also copies a null terminator, but your array is too small to hold it, resulting an overflow.
Also, instead of using strtok, consider using Lua patterns. They are safer, easier to use, and don't depend on the FFI.

Lua: getting the latest line in external txt file

I am having a lua function to reading and writing a txt file, I need every time lua write in at a new line instead of replacing the previous write in. How do I do that? Do I need to read in and get the lines 1st every time before I write in?
Here is my code:
local function FileOutput(name)
local f = io.open(name, "w+")
local meta = {
__call = function(t, str) f:write(str .. '\n') end,
__gc = function() f:close() end
}
return setmetatable({}, meta)
end
function writeRec()
LOG("writing")
local testfile = FileOutput(getScriptDirectory()..'/textOutput.txt')
testfile('oh yes!')
testfile = nil
end
Have you tried a+ instead of w+?
http://www.lua.org/manual/5.1/manual.html#pdf-io.open

How can I change every index into a table using a metatable?

I'm trying to write a metatable so that all indexes into the table are shifted up one position (i.e. t[i] should return t[i+1]). I need to do this because the table is defined using index 1 as the first element, but I have to interface with a program that uses index 0 as the first element. Since reading Programming in Lua, I think that I can accomplish what I want with a proxy table, but I can't seem to get it working. So far, I have this:
t = {"foo", "bar"}
local _t = t
t = {}
local mt = {
__index = function(t, i)
return _t[i+1]
end
}
setmetatable(t, mt)
However, this does not create the expected result. In fact, it doesn't return any values at all (every lookup is nil). Is there a better way to do this, or am I just missing something?
t = {"foo", "bar"}
local _t = t
t = {}
local mt = {
__index = function(t, i)
return _t[i+1]
end
}
setmetatable(t, mt)
print(t[0])
outputs "foo" for me when run here: http://www.lua.org/cgi-bin/demo

Resources