Importing a bunch of names in Lua - 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"})

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()
}

Is there a possiblity to create a variable with a string for everything inside of a table?

Just wondering about that, because i don't find a solution for it. Pretty sure because I'm new to this c: Thanks for your help.
Edit: for explanation im gonna explain it with the code a bit.
local FileList = fs.list("") --Makes a Table with all the files and directories available (as strings) on the PC it's running on (Inside of a mod called computercraft for minecraft)
for _, file in ipairs(FileList) do
--Here I need a function which assigns every string from the table to a variable, just for the explanation I'll call it unknown.function
unknown.function
end
while true do
print(a) --a is for example one of the different variables made from the "unknown.function"
sleep(1)
end
LIKE this?
AllFiles = {}
function Crazyfunction(file)
AllFiles[table.getn(AllFiles)+1] = file
end
local FileList = fs.list("")
for _, file in ipairs(FileList) do
Crazyfunction(file)
end
while true do
print(AllFiles[NUMBE_OF_FILE_HERE])
sleep(1)
end
You mean like this?
local FileList = fs.list("")
for _, file in ipairs(FileList) do
-- file is the variable which contains a string from the table.
print(file)
sleep(1)
end
What you want is... already what your loop does. You just added an extra loop for no reason.

Prevent a file being written to more than once without file:close()

I'm currently working on a logging system. My problems arise when using for loops and file writing. Here is a small example:
file = io.open("text.txt","a") --text.txt can be literally anything
for i=1,8 do
if x == true then
file:write("X is true.")
elseif y == true then
file:write("Y is true.")
end
end
Is there a way to stop the file from being written to multiple times without using file:close()? I have a huge number of different file:write sections, and adding file:close() after all of them would be a massive problem.
If file:close() every time is a massive problem, then this is the custom logic you need.
myFileMetatable = {} --implement all necessary file operations here
function myFileMetatable.write(self, str)
if not self.written then
self.written = true
self.f:write(str)
end
end
function myFileMetatable.close(self)
self.f:close()
end
myFile = {}
function myFile.open(filename, mode)
local t = {f = io.open(filename, mode)}
setmetatable(t, {__index = myFileMetatable})
return t
end
--now you can do
file = myFile.open("test", "w")
file:write("test")
file:write("hello")
file:write("world")
file:close() --and only "test" will be written
Note that this is probably much better than replacing file:write(str) with something file_write(file, str), since you need to store somewhere the fact that the file has already been written to, which you cannot store inside the FILE* object and using a global variable for that will break when using multiple files. That's why I wrap the FILE* object in a table and use myFileMetatable to implement my own methods that I will need.
However, if you need just one file at a time and don't mind the global variable then this is more efficient.
file_written = false
function file_write(file, str)
if not file_written then
file_written = true
file:write(str)
end
end
file = io.open("test", "w")
file_write(file, "test")
file_write(file, "hello")
file_write(file, "world")
file:close()
Mind that it's not as pretty as the first example and you might face a problem in the future, if you decide to expand beyond one file.
How Egor Skriptunoff already said, I'll recommend you to write your own writing function. I'm normally using sth. like this:
local function writeFile(filePath, str)
local outfile = io.open(filePath, 'w')
outfile:write(str)
outfile:close()
end
For appending to the file easily change the mode from w to a.
For my specific case, I've found a solution - since I just want a single option to print, and it's the last option (rather than the first, and I should've specified this), I can just set a variable to what I want my output to be and write that at the end.
log = ""
if x == 2 then
log = "X is 2."
elseif y == 2 then
log = "Y is 2."
end
file:write(log)
For the last option, I'd refer anyone to the accepted answer which should be perfect.

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.

lua - get the list of parameter names of a function, from outside the function

I'm generating some (non-html) documentation for a Lua library that I developed. I will generate the documentation by hand, but I'd appreciate some kind of automation if possible (i.e. generating skeletons for each function so I can fill them in)
I'd like to know if there's a way for lua to know the names of the parameters that a function takes, from outside it.
For example, is there a way to do this in Lua?
function foo(x,y)
... -- any code here
end
print( something ... foo ... something)
-- expected output: "x", "y"
Thanks a lot.
ok,here is the core code:
function getArgs(fun)
local args = {}
local hook = debug.gethook()
local argHook = function( ... )
local info = debug.getinfo(3)
if 'pcall' ~= info.name then return end
for i = 1, math.huge do
local name, value = debug.getlocal(2, i)
if '(*temporary)' == name then
debug.sethook(hook)
error('')
return
end
table.insert(args,name)
end
end
debug.sethook(argHook, "c")
pcall(fun)
return args
end
and you can use like this:
print(getArgs(fun))
Try my bytecode inspector library. In Lua 5.2 you'll be able to use debug.getlocal.
Take a look at debug.getinfo, but you probably need a parser for this task. I don't know of any way to fetch the parameters of a function from within Lua without actually running the function and inspecting its environment table (see debug.debug and debug.getlocal).
function GetArgs(func)
local args = {}
for i = 1, debug.getinfo(func).nparams, 1 do
table.insert(args, debug.getlocal(func, i));
end
return args;
end
function a(bc, de, fg)
end
for k, v in pairs(GetArgs(a)) do
print(k, v)
end
will print
1 bc
2 de
3 fg
Basically we use debug.getinfo to retrieve the nparams attribute (which gives us the information of how many parameters the function takes) and debug.getlocal to access the name of the parameters.
Tested and working with Lua 5.4
Take a look at the luadoc utility. It is sort of like Doxygen, but for Lua. It is intended to allow the documentation to be written in-line with the source code, but it could certainly be used to produce a template of the documentation structure to be fleshed out separately. Of course, the template mechanism will leave you with a maintenance issue down the road...

Resources