Lua displaying table - lua

I'v made a quiz with multiple choices. Before starting the game, a user has to use an identifier and then the user is added to the table and ready to play:
function addUser(msg)
local id = msg.from.username
if (userScore == nil) then
userScore = {}
end
if (userScore[id]) then
return "user already in Game"
else
userScore[id] = 100
return id
end
This adds the points in the table:
function addScore(msg)
local id = msg.from.username
if (userScore[id] == nil) then
return "user unknown. start het spel!"
end
if (game == "on") then
if (userScore[id]) then
userScore[id] = userScore[id] - 1
return id .. ", punje eraf!"
else
return id .. " is not yet a user! where to start huh?"
end
else
return "Game mode is off"
end
end
Then with !score the users can see the score:
elseif (matches[1] == "!score") then
for k, v in pairs(userScore) do
return k .. " : " .. v
end
The issue I have is that I only see one line in the table, knowing that other users are added to the table. What am I doing wrong?

From the comments it seems you want to return a single string that contains the userScore table's key-value pairs on each line.
You can do this by constructing a string that has the lines.
For example like this:
local res = {}
for k, v in pairs(userScore) do
table.insert(res, k .. " : " .. v)
end
return table.concat(res, "\n")

Related

How to implement the exercise 15.5 in pil4?

I am working on this exercise in pil4.
Exercise 15.5:
The approach of avoiding constructors when saving tables with cycles is too radical. It is
possible to save the table in a more pleasant format using constructors for the simple case, and to use
assignments later only to fix sharing and loops. Reimplement the function save (Figure 15.3, “Saving
tables with cycles”) using this approach. Add to it all the goodies that you have implemented in the previous
exercises (indentation, record syntax, and list syntax).
I have tried this with the code below, but it seems not to work on the nested table with a string key.
local function basicSerialize(o)
-- number or string
return string.format("%q",o)
end
local function save(name,value,saved,indentation,isArray)
indentation = indentation or 0
saved = saved or {}
local t = type(value)
local space = string.rep(" ",indentation + 2)
local space2 = string.rep(" ",indentation + 4)
if not isArray then io.write(name," = ") end
if t == "number" or t == "string" or t == "boolean" or t == "nil" then
io.write(basicSerialize(value),"\n")
elseif t == "table" then
if saved[value] then
io.write(saved[value],"\n")
else
if #value > 0 then
if indentation > 0 then io.write(space) end
io.write("{\n")
end
local indexes = {}
for i = 1,#value do
if type(value[i]) ~= "table" then
io.write(space2)
io.write(basicSerialize(value[i]))
else
local fname = string.format("%s[%s]",name,i)
save(fname,value[i],saved,indentation + 2,true)
end
io.write(",\n")
indexes[i] = true
end
if #value > 0 then
if indentation > 0 then io.write(space) end
io.write("}\n")
else
io.write("{}\n")
end
saved[value] = name
for k,v in pairs(value) do
if not indexes[k] then
k = basicSerialize(k)
local fname = string.format("%s[%s]",name,k)
save(fname,v,saved,indentation + 2)
io.write("\n")
end
end
end
else
error("cannot save a " .. t)
end
end
local a = { 1,2,3, {"one","Two"} ,5, {4,b = 4,5,6} ,a = "ddd"}
local b = { k = a[4]}
local t = {}
save("a",a,t)
save("b",b,t)
print()
And I got the wrong ouput.
a = {
1,
2,
3,
{
"one",
"Two",
}
,
5,
{
4,
5,
6,
}
a[6]["b"] = 4
,
}
a["a"] = "ddd"
b = {}
b["k"] = a[4]
How could I make the text ' a[6]["b"] = 4 ' jump out of the table constructor?

Add to table only 10 values

How i can add to table only max 10 users ?
Because i saving scoretbl in txt, and this file have more 100 lines :/ So i want save only 10 users.
I don't know how can i check there is username in the table, and don't add this user or not this username in the table and add it?
Example:
local scoretbl = {}
local num = 0
for i=1, 10 do
table.insert(scoretbl,{'Name '..i, 100 + num})
num = num + 100
end
local function AddToTable(name, score)
if table.HasValue(scoretbl,name) then return end // hmm its not work ?
table.insert(scoretbl,{name, score})
end
AddToTable('User 55', 5454)// 11 user
AddToTable('User 55', 5454)// check: only one username in table
AddToTable('User 32', 5454)// 12 user
local function ShowOnly10()
table.sort( scoretbl, function( a, b ) return a[2] > b[2] end )
//table.remove(scoretbl,#scoretbl) remove last index in table, if i need only 10 value, i need delete in cycle ?
for k, v in pairs(scoretbl) do
print(k ,v[1], v[2])
end
end
ShowOnly10()
// upd: maybe its fix username ?
local function AddToTable(name, score)
for k, v in pairs(scoretbl) do
if v[1] == name then return false end
end
table.insert(scoretbl,{name, score})
end
I recommend that you make use of Lua's hashtable, v.name and v.score are easier to read than v[1] and v[2].
The function table.HasValue does not exist. You have to write your own.
When you want to print only the first ten elements, you should only iterate over the first ten (or up to the length of the table if it is less than ten elements long).
Line comments in Lua start with --, not //.
local scoretbl = {}
for i = 1,10 do
table.insert(scoretbl, { name = 'Name '..i, score = 100*i })
end
local function AddToTable(name, score)
-- Walk the whole table to find whether a name exists
for i,v in ipairs(scoretbl) do
if v.name == name then
-- if the record is present, update it
scoretbl[i].score = score
return
end
end
-- Insert new record
table.insert(scoretbl, { name = name, score = score })
end
AddToTable('User 55', 5454) -- 11 users
AddToTable('User 55', 5454) -- check: only one username in table
AddToTable('User 32', 5454) -- 12 users
local function ShowOnly10()
table.sort(scoretbl,function(a,b) return a.score > b.score end)
for i = 1,math.min(#scoretbl,10) do
print(i, scoretbl[i].name, scoretbl[i].score)
end
end
ShowOnly10()

List object fields in Lua

I want to create a very simple object oriented program. How can I list object fields (e.g., all customer names)? What is wrong with the following code? for k, v in ipairs() didn't work.
do
local Account = {}
function Account:Current()
return self.B
end
function Account:Deposit(D)
self.B = self.B + D
end
function Account:Withdraw(W)
self.B = self.B - W
end
function BankAccount(Id, Name, N)
return {B=N,Current=Account.Current,Deposit=Account.Deposit,Withdraw=Account.Withdraw,AccountName=Name,AccountId=Id}
end
end
local Id = 1
local CustomerDatabase = {}
while true do
print("Select an option or press q to quit")
print("1. Create new customer entry")
print("5. List current customer database")
local Option = io.read("*line")
if Option == "q" then break
elseif Option == "1" then
print("Enter the name")
local Name = io.read("*line")
print("Enter initial amount")
local InitialAmount = io.read("*line")
BankAccount(Id, Name, InitialAmount)
table.insert(CustomerDatabase, BankAccount)
Id = Id + 1
elseif Option == "5" then
for k, v in ipairs(CustomerDatabase) do
print(k .. v.AccountName)
end
end
end
BankAccount(Id, Name, InitialAmount)
table.insert(CustomerDatabase, BankAccount)
Here, BankAccount is a function, you are inserting into the table a function. That's why v.AccountName is invalid, functions can't be indexed.
What you should do is to add the account object:
local account = BankAccount(Id, Name, InitialAmount)
table.insert(CustomerDatabase, account)

Lua: Help function: Can I extract the name of a function?

I would like to learn by developing a help function. The code below outlines my plan, and it can be summarized as:
For functions defined in Lua files - look for comments in the source.
For built-in functions and DLLs - look for a text file in .\help\function.txt.
For libraries (if no comments in the source) - look for a text file in .\lib\help\function.txt.
At the end of my code you can see an attempt to build an index of function names using their unique tostring(function) return value. Surely I should be able to do this in a loop?
function learn()
-- learn()
-- Make it easier to learn Lua, based upon Matlab console functions
-- help() print for help text in function source or help directory
-- who() print defined variables (exlcuding those in loaded modules)
-- what() print defined functions (exlcuding those in loaded modules)
-- which() print path to function source file if present
-- list() print the file to the console
-- edit() edit "filename" or function source if present
-- note: edit(_) can be used after any call to list(), help(func),
-- Helper functions
-- table.name() returns table name as string
-- table.length() this is difficult
-- table.keylist() returns a list of keys
-- table.keytype() returns a list of key types
-- edit_source() process function names
-- edit_new() create new "filename" (will use but not make subdirectories)
-- string.split() returns a table from a string
-- io.exists() test if a filename exists
-- io.newfile() creates an empty file
--
-- global variables
-- editor = "P:\\MyPrograms\\EDITORS\\Addins\\Editor2\\editor2.exe "
-- helpindex a list of the names of the inbuilt functions - see end of file
-- topics a table of help topics see topics.lua
-- web = "web"
web = "web"
-- webhelp = "http://www.nongnu.org/gsl-shell/doc/"
webhelp = "http://www.nongnu.org/gsl-shell/doc/"
-- editor = "P:\\MyPrograms\\EDITORS\\Addins\\Editor2\\editor2.exe "
editor = "P:\\MyPrograms\\EDITORS\\Addins\\Editor2\\editor2.exe "
-- required packages
-- lfs - lua file system (binary from lua-files)
require("lfs")
-- topics - for the help system
require("topics")
end
learn()
function who(t,i)
-- who(table)
-- searches the table (or defaults to _G) to print a list of table keys + types
-- the second parameter is to add a prefix for a recursive call to search "sub" tables
-- still having difficulty with the "loaded" table as this is self referencing and sets up an infinate loop!
-- designed for the console, but could be modified to return a table
--
if type(t)~="table" then
t=_G
end
if type(i)~="string" then
i=""
end
local s={}
local u={}
s = table.keylist(t)
u = table.keytype(t)
for k,v in ipairs(s) do
if u[k]=="table" and s[k]~="_G" and s[k]~="loaded" then
who(t[s[k]],i..v..".")
else
if u[k]~="table" and u[k]~="function" and u[k]~="cdata" then
print(u[k], i..v)
end
end
end
end
function what(t,i)
-- what(table)
-- searches the table (or defaults to _G) to print a list of function names
-- the second parameter is to add a prefix for a recursive call to search "sub" tables
-- still having difficulty with the "loaded" table as this is self referencing and sets up an infinate loop!
-- designed for the console, but could be modified to return a table
--
if type(t)~="table" then
t=_G
end
if type(i)~="string" then
i=""
end
local s={}
local u={}
s = table.keylist(t)
u = table.keytype(t)
for k,v in ipairs(s) do
if u[k]=="table" and s[k]~="_G" and s[k]~="loaded" then
what(t[s[k]],i..v..".")
else
if u[k]=="function" then
print(u[k], i..v)
end
end
end
end
function which(funcname)
-- which(funcname)
-- identifies the source for the current definition of funcname
-- designed for the console, but could be modified to return a string
--
if type(funcname)~="function" then return end
local filename = _G.debug.getinfo(funcname).short_src
if filename=="[C]" then
print(tostring(funcname))
else
return filename
end
end
function help(funcname)
-- help(object)
-- for functions prints help text (from source or help\function.txt)
-- adding help text to source as ^--comments is recommended,
-- for builtin functions use a subdirectory from the executable,
-- for uncommented source add a sibling help directory
-- for table prints table name, size and list of contents
-- for variables prints the type of the object
--
if type(funcname)=="boolean" then
io.write("boolean: ")
print(funcname)
return
end
if type(funcname)=="string" then
if funcname=="web" then
os.launch(webhelp)
else
print("string: "..funcname)
end
return
end
if type(funcname)=="number" then
print("number: "..funcname)
return
end
if type(funcname) == 'userdata' then
print(tostring(funcname))
io.write("metadata: ")
print(getmetatable(funcname))
end
if type(funcname) == 'cdata' then
print(tostring(funcname))
-- *** Unfinished
end
if type(funcname)=="table" then
print(tostring(funcname)..", size: "..table.length(funcname))
who(funcname)
what(funcname)
return
end
if type(funcname)=="function" then
-- Test for a source file
local filename = _G.debug.getinfo(funcname).short_src
if io.exists(filename) then
local codestart = _G.debug.getinfo(funcname).linedefined
local codeend = _G.debug.getinfo(funcname).lastlinedefined
if codestart < 1 then
print("Start is less than 1")
codestart = 1
end
if codeend< 1 then
print("End is less than 1")
codeend= 100
end
-- Try to read comments from the source
local output = 0
local count = 0
for line in io.lines(filename) do
count = count+1
if count > codestart and count < codeend then
if line:match("^%-%-") then
print(line)
output = output + 1
end
end
end
if output>0 then
io.write("From : ")
return filename -- to be used with edit(_)
end
-- Test for a help file as a sibling of the source
if output==0 then
-- No comments in the source file so look for a help file
local t = string.split(filename, "\\")
local helppath = table.concat(t,"\\",1,table.length(t)-1).."\\help\\"..t[table.length(t)]
helppath = string.gsub(helppath, "%.lua$" , ".txt")
if io.exists(helppath) then
local filename = list(helppath)
io.write("From : ")
return filename -- to be used with edit(_)
else
print("No help in source file : "..filename)
io.write("No help in: ")
return helppath -- to be used with edit_new(_)
end
end
end
-- Test for a help file in the generic help directory
if helpindex[tostring(funcname)] then
local helppath = "help\\"..helpindex[tostring(funcname)]..".txt"
if io.exists(helppath) then
local filename = list(helppath)
io.write("From : ")
return filename -- to be used with edit(_)
else
io.write("Built in function, but no help in: ")
return helppath -- to be used with edit_new(_)
end
else
print("No help index entry for "..tostring(funcname))
return
end
end
end
function list(filename)
if type(filename)=="function" then
print("list will only accept a string with a valid file name")
return
end
if type(filename)~="string" then
print("list will only accept a string with a valid file name")
return
end
if io.exists(filename) then
for line in io.lines(filename) do
print(line)
end
return filename
else
io.write("Can't find file: ")
return filename
end
end
function edit(filename, linenum)
-- edit(filename[, linenum])
-- loads the file into my editor (defined as global editor)
-- the linenum parameter will move the cursor to linenum
-- you will need to edit the global "editor" and the source command line below
-- or download Editor² from http://www.zabkat.com
--
if type(filename)=="function" then
filename = edit_source(filename)
return filename
end
if type(filename)~="string" then return end
if type(linenum)~="number" then linenum=1 end
if io.exists(filename) then
os.launch(editor.." /P /L:"..linenum.." \""..filename.."\"", " /P /L:"..linenum.." \""..filename.."\"")
else
print("To make a new file edit_new('filename')")
io.write("Can't find file: ")
return filename
end
end
function edit_source(funcname)
if type(funcname)~="function" then return end
local filename = _G.debug.getinfo(funcname).short_src
if io.exists(filename) then
local linenum = _G.debug.getinfo(funcname).linedefined
if linenum < 1 then
linenum = 1
end
edit(filename, linenum)
io.write("Editing : ")
return filename
end
end
function edit_new(filename)
if type(filename)~="string" then return end
io.newfile(filename)
edit(filename) -- This will check for a valid file name
io.write("Editing : ")
return filename
end
function table.name(table)
if type(table)~="table" then return end
for k, v in pairs(_G) do
if v == table then
return k
end
end
return nil
end
function table.length(table)
if type(table)~="table" then return end
local len = 0
for _ in pairs(table) do
len = len + 1
end
if type(len)=="number" then
return len
else
return nil
end
end
function table.keylist(table)
if type(table)~="table" then return end
local keylist={}
local n=0
for key in pairs(table) do
n=n+1
keylist[n]=key
end
return keylist
end
function table.keytype(table)
if type(table)~="table" then return end
local keytype={}
local n=0
for key in pairs(table) do
n=n+1
keytype[n]=type(table[key])
end
return keytype
end
function table.tablelist(table)
if type(table)~="table" then return end
local tablelist={}
local n=0
for key in pairs(table) do
if type(table[key])=="table" then
n=n+1
tablelist[n]=key
end
end
return tablelist
end
function string.split(inputstr, sep)
if sep == nil then
sep = "%s"
end
local t={}
local i=1
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
t[i] = str
i = i + 1
end
return t
end
function io.newfile(filename)
-- io.newfile(filename)
-- Will create a file if this is a valid filename
-- relative paths will work
-- files will not be overwritten
if type(filename)~="string" then
print("This function requires a string")
return
end
if io.exists(filename) then
io.write("This file already exists : ")
return filename
end
file, errormsg = io.open(filename, "w")
if errormsg then
print(errormsg)
else
file:write()
file:close()
io.write("New file created : ")
return filename
end
end
function io.exists(filename)
if type(filename)~="string" then return false end
local f=io.open(filename,"r")
if f~=nil then
io.close(f) return true
else
return false
end
end
function os.launch(command,params)
-- Via a dos box works - but flashes up a dos console
-- would love a way round this problem
command = "start "..command
os.execute(command)
end
helpindex = {
[tostring(assert)] = "assert",
[tostring(collectgarbage)] = "collectgarbage",
[tostring(dofile)] = "dofile",
[tostring(error)] = "error",
[tostring(getfenv)] = "getfenv",
[tostring(getmetatable)] = "getmetatable",
[tostring(ipairs)] = "ipairs",
[tostring(load)] = "load",
[tostring(loadfile)] = "loadfile",
[tostring(loadstring)] = "loadstring",
[tostring(next)] = "next" ,
[tostring(pairs)] = "pairs" ,
[tostring(pcall)] = "pcall" ,
[tostring(rawequal)] = "rawequal" ,
[tostring(rawget)] = "rawget" ,
[tostring(rawset)] = "rawset" ,
[tostring(select)] = "select" ,
[tostring(setfenv)] = "setfenv" ,
[tostring(setmetatable)] = "setmetatable" ,
[tostring(tonumber)] = "tonumber" ,
[tostring(tostring)] = "tostring" ,
[tostring(type)] = "type" ,
[tostring(unpack)] = "unpack" ,
[tostring(xpcall)] = "xpcall" ,
[tostring(coroutine.create)] = "coroutine.create" ,
[tostring(coroutine.resume)] = "coroutine.resume" ,
[tostring(coroutine.running)] = "coroutine.running" ,
[tostring(coroutine.status )] = "coroutine.status ",
[tostring(coroutine.wrap)] = "coroutine.wrap" ,
[tostring(coroutine.yield)] = "coroutine.yield" ,
[tostring(string.byte)] = "string.byte" ,
[tostring(string.char)] = "string.char" ,
[tostring(string.dump)] = "string.dump" ,
[tostring(string.find )] = "string.find",
[tostring(string.format)] = "string.format" ,
[tostring(string.gmatch)] = "string.gmatch" ,
[tostring(string.gsub)] = "string.gsub" ,
[tostring(string.len)] = "string.len" ,
[tostring(string.lower)] = "string.lower" ,
[tostring(string.match)] = "string.match" ,
[tostring(string.rep)] = "string.rep" ,
[tostring(string.reverse)] = "string.reverse" ,
[tostring(string.sub)] = "string.sub" ,
[tostring(string.upper)] = "string.upper" ,
[tostring(table.concat)] = "table.concat" ,
[tostring(table.insert)] = "table.insert" ,
[tostring(table.maxn)] = "table.maxn" ,
[tostring(table.remove)] = "table.remove" ,
[tostring(table.sort)] = "table.sort" ,
[tostring(math.abs)] = "math.abs" ,
[tostring(math.acos)] = "math.acos" ,
[tostring(math.asin)] = "math.asin" ,
[tostring(math.atan)] = "math.atan" ,
[tostring(math.atan2)] = "math.atan2" ,
[tostring(math.ceil)] = "math.ceil" ,
[tostring(math.cos)] = "math.cos" ,
[tostring(math.cosh)] = "math.cosh" ,
[tostring(math.deg)] = "math.deg" ,
[tostring(math.exp)] = "math.exp" ,
[tostring(math.floor)] = "math.floor" ,
[tostring(math.fmod)] = "math.fmod" ,
[tostring(math.frexp)] = "math.frexp" ,
[tostring(math.ldexp)] = "math.ldexp" ,
[tostring(math.log)] = "math.log" ,
[tostring(math.log10)] = "math.log10" ,
[tostring(math.max)] = "math.max" ,
[tostring(math.min)] = "math.min" ,
[tostring(math.modf)] = "math.modf" ,
[tostring(math.pow)] = "math.pow" ,
[tostring(math.rad)] = "math.rad" ,
[tostring(math.random)] = "math.random" ,
[tostring(math.randomseed)] = "math.randomseed" ,
[tostring(math.sin)] = "math.sin" ,
[tostring(math.sinh)] = "math.sinh" ,
[tostring(math.sqrt)] = "math.sqrt" ,
[tostring(math.tan)] = "math.tan" ,
[tostring(math.tanh)] = "math.tanh" ,
[tostring(io.close)] = "io.close" ,
[tostring(io.flush)] = "io.flush" ,
[tostring(io.input)] = "io.input" ,
[tostring(io.lines)] = "io.lines" ,
[tostring(io.open)] = "io.open" ,
[tostring(io.output)] = "io.output" ,
[tostring(io.popen)] = "io.popen" ,
[tostring(io.read)] = "io.read" ,
[tostring(io.tmpfile)] = "io.tmpfile" ,
[tostring(io.type)] = "io.type" ,
[tostring(io.write)] = "io.write" ,
[tostring(os.clock)] = "os.clock" ,
[tostring(os.date)] = "os.date" ,
[tostring(os.difftime)] = "os.difftime" ,
[tostring(os.execute)] = "os.execute" ,
[tostring(os.exit)] = "os.exit" ,
[tostring(os.getenv)] = "os.getenv" ,
[tostring(os.remove)] = "os.remove" ,
[tostring(os.rename)] = "os.rename" ,
[tostring(os.setlocale)] = "os.setlocale" ,
[tostring(os.time)] = "os.time" ,
[tostring(os.tmpname)] = "os.tmpname" ,
[tostring(debug.debug)] = "debug.debug" ,
[tostring(debug.getfenv)] = "debug.getfenv" ,
[tostring(debug.gethook)] = "debug.gethook" ,
[tostring(debug.getinfo)] = "debug.getinfo" ,
[tostring(debug.getlocal)] = "debug.getlocal" ,
[tostring(debug.getmetatable)] = "debug.getmetatable" ,
[tostring(debug.getregistry)] = "debug.getregistry" ,
[tostring(debug.getupvalue)] = "debug.getupvalue" ,
[tostring(debug.setfenv)] = "debug.setfenv" ,
[tostring(debug.sethook)] = "debug.sethook" ,
[tostring(debug.setlocal)] = "debug.setlocal" ,
[tostring(debug.setmetatable)] = "debug.setmetatable" ,
[tostring(debug.setupvalue)] = "debug.setupvalue" ,
[tostring(debug.traceback)] = "debug.traceback" ,
[tostring(module)] = "module" ,
[tostring(package.loadlib)] = "package.loadlib" ,
[tostring(package.seeall)] = "package.seeall" ,
[tostring(print)] = "print" ,
[tostring(require)] = "require" ,
[tostring(graph.fxplot)] = "graph.fxplot"
}
Revised code:
function help(funcname)
-- help(object)
-- for functions prints help text (from source or help\function.txt)
-- adding help text to source as ^--comments is recommended,
-- for builtin functions use a subdirectory from the executable,
-- for uncommented source add a sibling \help directory and function.txt
-- (note that the source file may contain several functions)
-- for table prints table name, size and list of contents
-- for variables prints the type of the object
--
if type(funcname)=="boolean" then
io.write("boolean: ")
print(funcname)
return
end
if type(funcname)=="string" then
if funcname=="web" then
os.launch(webhelp)
else
print("string: "..funcname)
end
return
end
if type(funcname)=="number" then
print("number: "..funcname)
return
end
if type(funcname) == 'userdata' then
print(tostring(funcname))
io.write("metadata: ")
print(getmetatable(funcname))
end
if type(funcname) == 'cdata' then
print(tostring(funcname))
-- *** Unfinished
end
if type(funcname)=="table" then
print(tostring(funcname)..", size: "..table.length(funcname))
who(funcname)
what(funcname)
return
end
if type(funcname)=="function" then
-- Test for a source file
local filename = _G.debug.getinfo(funcname).short_src
if io.exists(filename) then
local codestart = _G.debug.getinfo(funcname).linedefined
local codeend = _G.debug.getinfo(funcname).lastlinedefined
if codestart < 1 then
print("Start is less than 1")
codestart = 1
end
if codeend< 1 then
print("End is less than 1")
codeend= 100
end
-- Try to read comments from the source
local output = 0
local count = 0
for line in io.lines(filename) do
count = count+1
if count > codestart and count < codeend then
if line:match("^%-%-") then
print(line)
output = output + 1
end
end
end
if output>0 then
io.write("From : ")
return filename -- to be used with edit(_)
end
-- Try to read comments from \help\function.txt
if output==0 then
-- No comments in the source file so look for a help file
local t = string.split(filename, "\\")
local helppath = table.concat(t,"\\",1,table.length(t)-1).."\\help\\"..helpindex[funcname]..".txt"
if io.exists(helppath) then
local filename = list(helppath)
io.write("From : ")
return filename -- to be used with edit(_)
else
print("No help in source file : "..filename)
io.write("No help in: ")
return helppath -- to be used with edit_new(_)
end
end
end
-- Test for a help file in the generic help directory
if helpindex[funcname] then
local helppath = "help\\"..helpindex[funcname]..".txt"
if io.exists(helppath) then
local filename = list(helppath)
io.write("From : ")
return filename -- to be used with edit(_)
else
io.write("Built in function, but no help in: ")
return helppath -- to be used with edit_new(_)
end
else
print("No help index entry for "..helpindex[funcname])
return
end
end
end
-- helpindex as a [function literal -> string] mapping of names.
-- many thanks to Ryan Stein
-- http://stackoverflow.com/questions/20269173/lua-help-function-can-i-extract-the-name-of-a-function
helpindex = {}
do
local function indexfn(t, n)
if n == '_G' then n = '' else n = n .. '.' end
for k, v in pairs(t) do
if type(v) == 'function' then
helpindex[v] = n .. k
end
end
end
for k, v in pairs(_G) do -- Iterate all tables in global scope.
if type(v) == 'table' then
indexfn(v, k)
end
end
end
Perhaps this may be what you're looking for:
local helpindex = {}
do
local function indexfn(t, n)
if n == '_G' then n = '' else n = n .. '.' end
for k, v in pairs(t) do
if type(v) == 'function' then
helpindex[v] = n .. k
end
end
end
for k, v in pairs(_G) do -- Iterate all tables in global scope.
if type(v) == 'table' then
indexfn(v, k)
end
end
end
-- helpindex is now a [function literal -> string] mapping of names.
You don't need to convert the functions to strings to use them as table keys, since anything other than nil can be used as a table key in Lua. The functions themselves work just fine.

lua - calling a function from a string

From what I've read on this site the below should work.
Can some kindly soul please point out where I'm going wrong?
I've embedded more description and print returns in the code hopefully to make easier reading
local m = {
{opt = "Solar Panels", cmd = "solarPanel"}
-- There are many more options here.
}
function doTheMenu()
print("Welcome to Spyder's Factory")
print("")
print("What would you like to make?")
local n = 1
local l = #m - 1
while true do --This while loop may or may not be relevant to the question, it's the menu
term.clear() --this is ComputerCraft lua, the term function is defined
term.setCursorPos(1,2) --elsewhere in an API
for i, j in pairs(m) do
if i == n then
if i < 10 then print(i, " ["..j.opt.."]") else print(i, " ["..j.opt.."]") end
fsel = j.cmd --set fsel to the function name I require in case of a break
tsel = j.opt --Ditto, tsel, human-friendly name
else
if i < 10 then print(i, " "..j.opt) else print(i, " "..j.opt) end
end
end
local a, b = os.pullEvent("key")
if b == 200 and n > 1 then n = n - 1 end
if b == 208 and n <= l then n = n + 1 end
if b == 28 then break end
end
write("\nSure, how many "..tsel.."? ")
qty = tonumber(read())
req[fsel] = req[fsel] + qty
str = fsel.."("..qty..")"
print("Loading function '"..fsel.."("..qty..")'") --Returns "Loading function 'solarPanel(1)'"
func = loadstring(str)
print(func) --Returns "function: 2cdfc5a7"
print("Loading function")
func() --The error line, Returns "string:1: attempt to call nil"
--tellUserWhatNeed()
--makeItHappen()
end
doTheMenu()
The issue is the code fails to run with the error:
string:1 attempt to call nil
Also what is term variable, if that's all your code, term is not defined and is null)
That said: either _G[fsel] is nil or fsel is nil ?
Are you sure you have function declared in _G with the name stored in fsel?
e.i. call before the problem line print (_G[fsel]) to see what it gives you.
This was the solution that ended up working:
local f = loadstring(str)
if f then
setfenv(f, getfenv())
f()
end
This replaces the lines above:
print("Loading function '"..fsel.."("..qty..")'")
func = loadstring(str)
print(func)
print("Loading function")
func()
As well as adding basic error handling for loading the function

Resources