Lua create multiple closure instances - lua

I have some lua code in a file. I want to create multiple closure instances of this code, each with a different _ENV upvalue. I can use luaL_loadfile to load the file and set the first upvalue, N times with different tables, to create N instances. But wouldn't this load and compile the file N times?
The lua equivalent of what i want to do is the following, except without the loadfile
func_list = {}
for i = 1, 10 do
local new_env = {hello=i, print=print}
func_list[i] = loadfile("Code.lua", "t", new_env)
end
for i = 1, 10 do
func_list[i]()
end
------ Code.lua ------
print(hello*hello)
is there a better way to do this?

Whenever you load a string/file in Lua, what you get in return is a function to call to actually run the file. What load does for you is just some additional processing to set the _ENV.
However, nothing prevents you from setting _ENV yourself. You could do it with something like this:
-- Code.lua --
_ENV = ...
print(hello * hello)
Then, you could load/compile the file just once, and use multiple instances as such:
local code = loadfile("Code.lua")
env_list = {}
for i = 1, 10 do
local new_env = {hello=i, print=print}
code(new_env)
env_list[i] = new_env
end
If you do not want the user to write _ENV = ... in every file, you could instead load the file into a string, prepend the line yourself and use load to compile the source. But this would not work on compiled files.

Use the IO libraries to load the file into a string, and then call loadstring on it.
Alternatively, just get one chunk and then change it's env prior to executing it

Related

LUA indexed table access via named constants

I am using LUA as embedded language on a µC project, so the ressources are limited. To save some cycles and memory I do always only indexed based table access (table[1]) instead og hash-based access (table.someMeaning = 1). This saves a lot of memory.
The clear drawback of this is approach are the magic numbers thrughtout the code.
A Cpp-like preprocessor would help here to replace the number with named-constants.
Is there a good way to achieve this?
A preprocessor in LUA itself, loading the script and editing the chunk and then loading it would be a variant, but I think this exhausts the ressources in the first place ...
So, I found a simple solution: write your own preprocessor in Lua!
It's probably the most easy thing to do.
First, define your symbols globally:
MySymbols = {
FIELD_1 = 1,
FIELD_2 = 2,
FIELD_3 = 3,
}
Then you write your preprocessing function, which basically just replace the strings from MySymbols by their value.
function Preprocess (FilenameIn, FilenameOut)
local FileIn = io.open(FilenameIn, "r")
local FileString = FileIn:read("*a")
for Name, Value in pairs(MySymbols) do
FileString = FileString:gsub(Name, Value)
end
FileIn:close()
local FileOut = io.open(FilenameOut, "w")
FileOut:write(FileString)
FileOut:close()
end
Then, if you try with this input file test.txt:
TEST FIELD_1
TEST FIELD_2
TEST FIELD_3
And call the following function:
Preprocess("test.txt", "test-out.lua")
You will get the fantastic output file:
TEST 1
TEST 2
TEST 3
I let you the joy to integrate it with your scripts/toolchain.
If you want to avoid attributing the number manually, you could just add a wonderful closure:
function MakeCounter ()
local Count = 0
return function ()
Count = Count + 1
return Count
end
end
NewField = MakeCounter()
MySymbols = {
FIELD_1 = NewField(),
FIELD_2 = NewField(),
FIELD_3 = NewField()
}

Passing session between two lua files

I want to call another lua script from my main script say like
session:execute("lua","/path/somefile.lua "..somearg1.." "..somearg2..)
its working fine and somefile.lua is executing but suppose i also want to use session there i.e. i am accessing a database in somefile.lua and want to speak a query result in somefile.lua by using session. (session:speak(queryResult)).
i also tried sending session as one of argument
session:execute("lua","/path/somefile.lua "..session)
but it gives a error "attempt to concatenate global 'session' (a userdata value)"
any advise..??
code of first lua file
session:answer();
session:setAutoHangup(false);
session:set_tts_params("flite","kal");
callerId = session:getVariable("caller_id_number");
session:execute("lua ","/etc/freeswitch/scripts/checkbal.lua "..callerId.." "..session);
session:destroy();
code for 2nd lua file
callerId=argv[1];
session=argv[2];
luasql = require "luasql.postgres";
env=assert(luasql:postgres());
con=assert(env:connect("mydb","postgres","password","127.0.0.1","5432"));
cur=assert(con:execute("select balance from bal where number='"..callerId.."'"));
session:set_tts_params("flite","kal");
row=cur:fetch({},"a");
res=row.balance;
session:speak(res);
Rig your second file to be a module that returns a function or a table of functions. Here is an example that has second file return a "speak" function that you can then re-use as many times as desired:
Code of first Lua file:
session:answer()
session:setAutoHangup(false)
session:set_tts_params("flite","kal")
callerId = session:getVariable("caller_id_number")
speak = require 'checkbal'
speak(session, callerId)
-- session:execute("lua ","/etc/freeswitch/scripts/checkbal.lua "..callerId.." "..session)
session:destroy()
Code for 2nd Lua file:
luasql = require "luasql.postgres"
local env=assert(luasql:postgres())
local con=assert(env:connect("mydb","postgres","password","127.0.0.1","5432"))
local function speak(session, callerId)
local cur = assert(con:execute("select balance from bal where number='"..callerId.."'"))
session:set_tts_params("flite","kal")
row=cur:fetch({},"a")
res=row.balance
session:speak(res)
end
return speak
Note: this is Lua: no need for semicolons.
I would consider making "session" an object (table with methods) with a "speak" method, but this is going beyond scope of this question and is not necessary, may just lead to more maintainable code later.

Importing a bunch of names in Lua

In my script I follow the practice of "importing" functions from another module. So I have code similar to the following at the start of my script:
local some_function = assert(require("utils").some_function)
local another_func = assert(require("utils").another_func)
local yet_another = assert(require("utils").yet_another)
local and_another = assert(require("utils").and_another)
But this chunk of code is not quite readable.
(The assert() is there to guard against spelling mistakes in the function names.)
I know that I can easily write my own function, say require_names(), and turn the above code into:
local some_function, another_func, yet_another, and_another
= require_names("utils", { "some_function", "another_func", "yet_another", "and_another" })
This looks much better. Still, it's not optimal: there's redundancy in this code: the names of the functions repeat twice.
Is there a way for me to write my require_names() so that it doesn't have the redundancy problem?
Or, do you you have any ideas for solving the readability problem in some other way?
(I need solutions that run on both Lua 5.1 and 5.2)
If you want only a subset of what is in utils module, yet only create locals, then you can't. Other answers give you the whole utils module, in that case I don't see why not just use require 'yourmodule'. If you can relinquish locals, then:
function require_names(modName, objNames)
for i,v in ipairs(objNames) do
_G[v] = assert(require(modName)[v])
end
end
works in both 5.1 :
> print(setn)
nil
> print(insert)
nil
> require_names("table", {"setn", "insert"})
> print(setn)
function: 005F7910
> print(insert)
function: 005F7890
The only non-globals option is to put what you want in a local table, to get only the subset you need:
function require_names(modName, objNames)
local mod = {}
for i,v in ipairs(objNames) do
mod[v] = assert(require(modName)[v])
end
return mod
end
local utils = require_names("utils", { 'a', 'b' })
utils.a = asdfasf
print(utils.b)
However, the only benefit of above compared to local utils=require 'utils' is that it documents what you module is going to use from the required module. But it is a little noisy with all the quotes and braces.
I would do this:
local u = require 'utils'
Then use it like this:
u.some_function(...)
This is very easy to type and very obvious.
If you really need locals, then I would not use a single function called require_names but two: the regular require + extract. Here's extract
local function extract(t, keys)
local values = {}
for i=1, #keys do values[i] = t[keys[i]] end
return unpack(values)
end
Usage:
local utils = require 'utils'
local some_function, another_func, yet_another, and_another =
extract(utils, { "some_function", "another_function", "yet_another", "and_another"})

Modify Lua Chunk Environment: Lua 5.2

It is my understanding that in Lua 5.2 that environments are stored in upvalues named _ENV. This has made it really confusing for me to modify the environment of a chunk before running it, but after loading it.
I would like to load a file with some functions and use the chunk to inject those functions into various environments. Example:
chunk = loadfile( "file" )
-- Inject chunk's definitions
chunk._ENV = someTable -- imaginary syntax
chunk( )
chunk._ENV = someOtherTable
chunk( )
Is this possible from within Lua? The only examples I can find of modifying this upvalue are with the C api (another example from C api), but I am trying to do this from within Lua. Is this possible?
Edit: I'm unsure of accepting answers using the debug library. The docs state that the functions may be slow. I'm doing this for efficiency so that entire chunks don't have to be parsed from strings (or a file, even worse) just to inject variable definitions into various environments.
Edit: Looks like this is impossible: Recreating setfenv() in Lua 5.2
Edit: I suppose the best way for me to do this is to bind a C function that can modify the environment. Though this is a much more annoying way of going about it.
Edit: I believe a more natural way to do this would be to load all chunks into separate environments. These can be "inherited" by any other environment by setting a metatable that refers to a global copy of a chunk. This does not require any upvalue modification post-load, but still allows for multiple environments with those function definitions.
The simplest way to allow a chunk to be run in different environments is to make this explicit and have it receive an environment. Adding this line at the top of the chunk achieves this:
_ENV=...
Now you can call chunk(env1) and later chunk(env2) at your pleasure.
There, no debug magic with upvalues.
Although it will be clear if your chunk contains that line, you can add it at load time, by writing a suitable reader function that first sends that line and then the contents of the file.
I do not understand why you want to avoid using the debug library, while you are happy to use a C function (neither is possible in a sandbox.)
It can be done using debug.upvaluejoin:
function newEnvForChunk(chunk, index)
local newEnv = {}
local function source() return newEnv end
debug.upvaluejoin(chunk, 1, source, 1)
if index then setmetatable(newEnv, {__index=index}) end
return newEnv
end
Now load any chunk like this:
local myChunk = load "print(x)"
It will initially inherit the enclosing _ENV. Now give it a new one:
local newEnv = newEnvForChunk(myChunk, _ENV)
and insert a value for 'x':
newEnv.x = 99
Now when you run the chunk, it should see the value for x:
myChunk()
=> 99
If you don't want to modify your chunk (per LHF's great answer) here are two alternatives:
Set up a blank environment, then dynamically change its environment to yours
function compile(code)
local meta = {}
local env = setmetatable({},meta)
return {meta=meta, f=load('return '..code, nil, nil, env)}
end
function eval(block, scope)
block.meta.__index=scope
return block.f()
end
local block = compile('a + b * c')
print(eval(block, {a=1, b=2, c=3})) --> 7
print(eval(block, {a=2, b=3, c=4})) --> 14
Set up a blank environment, and re-set its values with your own each time
function compile(code)
local env = {}
return {env=env, f=load('return '..code, nil, nil, env)}
end
function eval(block, scope)
for k,_ in pairs(block.env) do block.env[k]=nil end
for k,v in pairs(scope) do block.env[k]=v end
return block.f()
end
local block = compile('a + b * c')
print(eval(block, {a=1, b=2, c=3})) --> 7
print(eval(block, {a=2, b=3, c=4})) --> 14
Note that if micro-optimizations matter, the first option is about 2✕ as slow as the _ENV=... answer, while the second options is about 8–9✕ as slow.

How to load a file data in memory,and don't reload again. just like static field in java?

I have a function :it's load a file data and build a table.
I want to load the file only once , everytime call the function and get return table in memory,don't load file again(you know io operation repeat is bad).
the goal is just like static field in java,only build once , and use it .
how to do ?
You can do this in one of several ways. For example:
local myInternalLocal = nil
function FuncName(...)
if(not myInternalLocal) then
myInternalLocal = --Do stuff that builds the table.
end
return myInternalLocal
end
Alternatively, you can do it by replacement, which better hides the internal variable, and is (very slightly) faster:
function FuncName(...)
local myInternalLocal = --Do stuff that builds the table.
FuncName = function()
return myInternalLocal
end
end
The downside here has to do with modules. If this is inside of a module, then you may need to change how it rebinds the function. And of course, if someone stores the old function before calling it, they can keep calling the original version.
I think you want to use require to load the file.

Resources