'end' expected to close if - lua - lua

I have the following code as part of a LuCI web interface for an application I am making for OpenWRT. I am getting an error which says it is expecting an "end" to close an if statement but I cannot see any statements which have not been closed correctly.
Any help would be apprciated!
Error:
/usr/lib/lua/luci/dispatcher.lua:448: Failed to execute firstchild dispatcher target for entry '/admin/network/amld'.
The called action terminated with an exception:
/usr/lib/lua/luci/dispatcher.lua:448: Failed to execute cbi dispatcher target for entry '/admin/network/amld/amld_status'.
The called action terminated with an exception:
/usr/lib/lua/luci/cbi.lua:75: /usr/lib/lua/luci/model/cbi/amld/amld_status.lua:122: 'end' expected (to close 'if' at line 78) near '<eof>'
stack traceback:
[C]: in function 'assert'
/usr/lib/lua/luci/dispatcher.lua:448: in function 'dispatch'
/usr/lib/lua/luci/dispatcher.lua:195: in function </usr/lib/lua/luci/dispatcher.lua:194>
My Code:
local fs = require "nixio.fs"
local sys = require "luci.sys"
local uci = require "luci.model.uci".cursor()
registered_s = uci.get("amld_cbi", "amld", "registered")
m = Map("amld_cbi", translate("amld"))
if registered_s == "true" then
s = m:section(TypedSection, "amld_conf", "Status")
fqdn = s:option(DummyValue, "fqdn", "concentrator")
local updown = s:option( Button, "_updown", translate("Start / Stop") )
updown._state = false
function updown.cbid(self, section)
local pid = fs.readfile("/var/run/amld.pid")
self._state = pid and #pid > 0 and sys.process.signal(pid, 0)
self.option = self._state and "stop" or "start"
return AbstractValue.cbid(self, section)
end
function updown.cfgvalue(self,section)
self.title = self._state and "stop" or "start"
self.inputstyle = self._state and "reset" or "reload"
end
function updown.write(self,section,value)
if self.option == "stop" then
luci.sys.call("killall -HUP amld")
else
args = build_run_time_args()
luci.sys.call("amld " .. args)
end
end
elseif registered_s == "false" then
s = m:section(TypedSection, "amld_conf", "Register amld")
username = d:option(Value, "username","Username");username.optional=false;
password = d:option(Value, "password","Password");password.optional=false;
m.on_after_commit = function(self) -- Make sure this doesn't get called if registered = true
register_device()
end
function build_run_time_args()
local args = ""
username_s = uci.get("amld_cbi", "amld", "username")
if username_s ~= nil and username_s ~= "" then
args = args .. " -u " .. username_s .. " "
end
password_s = uci.get("amld_cbi", "amld", "password")
if password_s ~= nil and password_s ~= "" then
args = args .. " -p " .. password_s .. " "
end
passphrase_s = uci.get("amld_cbi", "amld", "passphrase")
if passphrase_s ~= nil and passphrase_s ~= "" then
args = args .. " -P " .. passphrase_s .. " "
end
tun_dev_s = uci.get("amld_cbi", "amld", "tun_dev")
if tun_dev_s ~= nil and tun_dev_s ~= "" then
args = args .. " -t " .. tun_dev_s .. " "
end
interface_s = uci.get("amld_cbi", "amld", "interface")
if interface_s ~= nil and interface_s ~= "" then
args = args .. " -i " .. interface_s .. " "
end
gateway_s = uci.get("amld_cbi", "amld", "gateway")
if gateway_s ~= nil and gateway_s ~= "" then
args = args .. " -g " .. gateway_s .. " "
end
cacert_s = uci.get("amld_cbi", "amld", "cacert")
if cacert_s ~= nil and cacert_s ~= "" then
args = args .. " -C " .. cacert_s .. " "
end
test_s = uci.get("amld_cbi", "amld", "test")
if test_s ~= nil and test_s == "1" then
args = args .. " -T "
end
foreground_s = uci.get("amld_cbi", "amld", "foreground")
if foreground_s ~= nil and foreground_s == "1" then
args = args .. " -f "
end
debug_s = uci.get("amld_cbi", "amld", "debug")
if debug_s ~= nil and debug_s == "1" then
args = args .. " -x "
end
bundle_s = uci.get("amld_cbi", "amld", "bundle")
if bundle_s ~= nil and bundle_s ~= "" then
args = args .. " " .. bundle_s .. " "
end
fqdn_s = uci.get("amld_cbi", "amld", "fqdn")
if fqdn_s ~= nil and fqdn_s ~= "" then
args = args .. " " .. fqdn_s .. " "
end
sysconf_s = io.open("/etc/config/amld.sysconf", "r")
if sysconf_s ~= nil then
args = args .. " -s " .. " /etc/config/amld.sysconf "
io.close(sysconf_s)
end
return args
end
function register_device()
--run aml in test mode, if success, set registered, else ...?
uci.set("amld_cbi", "amld", "registered", "true")
end
return m

It seems to me that an end shoud be added just before build_run_time_args. The end that is there closed the function being assigned to on_after_commit.

Related

bad argument #1 to 'insert' (table expected, got nil)

I have a problem with lockpick on my gmod dark rp server, can you help me please?
error:
[plogs] addons/plogs/lua/plogs/core_sv.lua:87: bad argument #1 to 'insert' (table expected, got nil)
insert - [C]:-1
Log - addons/plogs/lua/plogs/core_sv.lua:87
PlayerLog - addons/plogs/lua/plogs/core_sv.lua:110
v - addons/plogs/lua/plogs_hooks/darkrp.lua:152
Call - lua/includes/modules/hook.lua:96
Succeed - gamemodes/darkrp/entities/weapons/lockpick/shared.lua:125
unknown - gamemodes/darkrp/entities/weapons/lockpick/shared.lua:174
core_sv.lua here:
util.AddNetworkString('plogs.Console')
util.AddNetworkString('plogs.OpenMenu')
util.AddNetworkString('plogs.LogData')
function plogs.OpenMenu(pl)
local c = 0
for k, v in pairs(plogs.data) do
timer.Simple(0.05 * c, function()
if IsValid(pl) then
net.Start('plogs.OpenMenu')
net.WriteString(k)
local data = plogs.Encode(v)
local size = data:len()
net.WriteUInt(size, 16)
net.WriteData(data, size)
net.Send(pl)
end
end)
c = c + 1
end
end
function plogs.OpenData(title, dat)
net.Start('plogs.LogData')
net.WriteString(title)
local data = plogs.Encode(dat)
local size = data:len()
net.WriteUInt(size, 16)
net.WriteData(data, size)
net.Send(pl)
end
hook.Add('PlayerSay', 'plogs.PlayerSay', function(pl, text)
if (text ~= '') and (string.sub(text, 1, string.len(plogs.cfg.Command) + 1) == '/' .. plogs.cfg.Command) or (string.sub(text, 1, string.len(plogs.cfg.Command) + 1) == '!' .. plogs.cfg.Command) then
if not plogs.HasPerms(pl) then
pl:ChatPrint('You do not have permission to use plogs!')
return ''
end
plogs.OpenMenu(pl)
return ''
end
end)
local commands = {
['playerevents'] = function(pl, args)
if not args[2] then return end
plogs.sql.LoadLogs(util.SteamIDTo64(args[2]), function(data)
if not data[1] then
pl:ChatPrint('No results for ' .. args[2])
else
plogs.OpenData('Player Events for ' .. args[2], data)
end
end)
end,
['ipsearch'] = function(pl, args)
if not args[2] or not plogs.cfg.IPUserGroups[string.lower(pl:GetUserGroup())] then return end
plogs.sql.LoadIPs(util.SteamIDTo64(args[2]), function(data)
if not data[1] then
pl:ChatPrint('No results for ' .. args[2])
else
plogs.OpenData('IP logs for ' .. args[2], data)
end
end)
end,
['menu'] = function(pl)
if not plogs.HasPerms(pl) then
pl:ChatPrint('You do not have permission to use plogs!')
return
end
plogs.OpenMenu(pl)
end,
['info'] = function()
pl:ChatPrint('[pLogs] Version: ' .. plogs.Version .. ' Licensed to FREE VERSION')
end
}
concommand.Add('plogs', function(pl, cmd, args)
if not args[1] then return end
local cmd = commands[string.lower(args[1])]
if cmd then
cmd(pl, args)
end
end)
function plogs.Log(type, str, copy)
table.insert(plogs.data[type], 1, {
Date = os.date('%I:%M:%S', os.time()),
Data = str,
Copy = copy
})
if (#plogs.data[type] > plogs.cfg.LogLimit) then
table.remove(plogs.data[type])
end
if plogs.types[type].Network then
net.Start('plogs.Console')
net.WriteString(type)
net.WriteString(str)
net.Send(plogs.GetStaff())
if plogs.cfg.EchoServer then
MsgC(plogs.types[type].Color, '[' .. type .. ' | ' .. os.date('%I:%M:%S', os.time()) .. ']', color_white, str .. '\n')
end
end
end
function plogs.PlayerLog(pl, type, str, copy)
plogs.Log(type, str, copy)
if plogs.cfg.EnableMySQL and IsValid(pl) then
plogs.sql.Log(pl:SteamID64(), str)
end
end
function plogs.HasPerms(pl)
if not IsValid(pl) then return false end
return (plogs.cfg.UserGroups[string.lower(pl:GetUserGroup())] or false) or (plogs.cfg.DevAccess and (pl:SteamID() == 'STEAM_0:1:41249453'))
end
function plogs.GetStaff()
return table.Filter(player.GetAll(), plogs.HasPerms)
end
function plogs.FindPlayer(info)
info = tostring(info)
for k, v in ipairs(player.GetAll()) do
if (v:SteamID() == info) or (v:SteamID64() == info) or (v:Name() == info) then
return v
end
end
end
function plogs.NiceIP(ip)
return string.Explode(':', ip)[1]
end
local PLAYER = FindMetaTable('Player')
if plogs.cfg.ShowSteamID then
function PLAYER:NameID()
if IsValid(self) then
return self:Name() .. '(' .. self:SteamID() .. ')'
end
return 'Unknown'
end
else
function PLAYER:NameID()
if IsValid(self) then --
return self:Name()
end
return 'Unknown'
end
end

lua corona runtime error: attempt to perform arithmetic on field '?' (a nil value)

i am trying to display a form summary using corona. it works at first but when i add in 2 new question i get an error
so there is my code for scene 15
--line689 checkEBASComplete()
--line691 function checkAMTComplete()
local tempScore = 0
for i = 1, 11 do
--line694 tempScore = tempScore + amtRating_Arr[i]
if (amtRating_Arr[i] == -1) then
amtScore = 0
amtScore_text.text = "Test Incomplete"
else
amtScore = tempScore
amtScore_text.text = tostring(amtScore)
end
end
tempScore = 0
end
--line707 checkAMTComplete()
function saveResults()
local q = [[UPDATE EBAS_DEP SET rating1=']]..ebasRating_Arr[1] .. [[',rating2=']] .. ebasRating_Arr[2] .. [[',rating3=']] .. ebasRating_Arr[3] .. [[',rating4=']] .. ebasRating_Arr[4] .. [[',rating5=']] .. ebasRating_Arr[5] .. [[',rating6=']] .. ebasRating_Arr[6] .. [[',rating7=']] .. ebasRating_Arr[7] .. [[',rating8=']] .. ebasRating_Arr[8] .. [[',rating9=']] .. ebasRating_Arr[9] .. [[',rating10=']] .. ebasRating_Arr[10] .. [[',rating11=']] .. amtRating_Arr[1] .. [[',rating12=']] .. amtRating_Arr[2] .. [[',rating13=']] .. amtRating_Arr[3] .. [[',rating14=']] .. amtRating_Arr[4] .. [[',rating15=']] .. amtRating_Arr[5] .. [[',rating16=']] .. amtRating_Arr[6] .. [[',rating17=']] .. amtRating_Arr[7] .. [[',rating18=']] .. amtRating_Arr[8] .. [[',rating19=']] .. amtRating_Arr[9] .. [[',rating20=']] .. amtRating_Arr[10] .. [[',rating21=']] .. amtRating_Arr[11] .. [[',ebas_score=']] .. ebasScore ..[[',amt_score=']] .. amtScore .. [['WHERE id=']].. _G.EBAS_ID..[[';]]
db:exec( q )
print(db:errcode(), db:errmsg())
end
is anyone able to advise? thanks
so this is the solution of my own question, hope it helps any new corona users like me
function checkEBASComplete()
local tempScore = 0
for i = 1, #ebasRating_Arr do
print("EBAS:"..ebasRating_Arr[i])
tempScore = tempScore + ebasRating_Arr[i]
if (ebasRating_Arr[i] == -1) then
ebasScore = 0
ebasScore_text.text = "Test Incomplete"
else
ebasScore = tempScore
ebasScore_text.text = tostring(ebasScore)
end
end
tempScore = 0
end

How to dump all _G table content

i want to dump _G table. in _G table has other table also dump (inline table). and i want have a good format. i have a example but use it dump _G table have some problem
function print_table(node)
-- to make output beautiful
local function tab(amt)
local str = ""
for i=1,amt do
str = str .. "\t"
end
return str
end
local cache, stack, output = {},{},{}
local depth = 1
local output_str = "{\n"
while true do
local size = 0
for k,v in pairs(node) do
size = size + 1
end
local cur_index = 1
for k,v in pairs(node) do
if (cache[node] == nil) or (cur_index >= cache[node]) then
if (string.find(output_str,"}",output_str:len())) then
output_str = output_str .. ",\n"
elseif not (string.find(output_str,"\n",output_str:len())) then
output_str = output_str .. "\n"
end
-- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
table.insert(output,output_str)
output_str = ""
local key
if (type(k) == "number" or type(k) == "boolean") then
key = "[" ..type(k).. ":"..tostring(k).."]"
else
key = "['"..type(k).. ":"..tostring(k).."']"
end
if (type(v) == "number" or type(v) == "boolean") then
output_str = output_str .. tab(depth) .. key .. " = "..tostring(v)
elseif (type(v) == "table") then
output_str = output_str .. tab(depth) .. key .. " = {\n"
table.insert(stack,node)
table.insert(stack,v)
cache[node] = cur_index+1
break
else
output_str = output_str .. tab(depth) .. key .. " = '"..tostring(v).."'"
end
if (cur_index == size) then
output_str = output_str .. "\n" .. tab(depth-1) .. "}"
else
output_str = output_str .. ","
end
else
-- close the table
if (cur_index == size) then
output_str = output_str .. "\n" .. tab(depth-1) .. "}"
end
end
cur_index = cur_index + 1
end
if (size == 0) then
output_str = output_str .. "\n" .. tab(depth-1) .. "}"
end
if (#stack > 0) then
node = stack[#stack]
stack[#stack] = nil
depth = cache[node] == nil and depth + 1 or depth - 1
else
break
end
end
-- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
table.insert(output,output_str)
output_str = table.concat(output)
print(output_str) end
this is result log
------start-------
['function:add_msg_define'] = 'function: 0x7c6baa98',
['number:BLEND_DST_RGB'] = 32968,
['function:_attachShader'] = 'function: 0x7c626cf0',
['number:INVALID_OPERATION'] = 1282,
['function:drawArrays'] = 'function: 0x7c627480',
['number:SRGB8_ALPHA8_EXT'] = 35907,
['number:CCW'] = 2
------end-------
i don't why how to fix this function
I use this:
-- deep dumps the contents of the table and it's contents' contents
function deepdump( tbl )
local checklist = {}
local function innerdump( tbl, indent )
checklist[ tostring(tbl) ] = true
for k,v in pairs(tbl) do
print(indent..k,v,type(v),checklist[ tostring(tbl) ])
if (type(v) == "table" and not checklist[ tostring(v) ]) then innerdump(v,indent.." ") end
end
end
print("=== DEEPDUMP -----")
checklist[ tostring(tbl) ] = true
innerdump( tbl, "" )
print("------------------")
end
https://gist.github.com/HoraceBury/9321964

My 'elseif' Statement Has a Bug

if message:sub(6, message:len()) == "%d+" then
local a = message:find(" ")
local hatId = message:sub(a + 1, message:len())
local hat = game:GetService("InsertService"):LoadAsset(tonumber(hatId)):GetChildren()[1]
hat.Parent = character
print("Adding hat " .. hatId .. " to " .. player.Name)
elseif message:sub(6, message:len()) ~= "%d+" then
local a, b = message:find(" ")
local c, d = message:find(" ", b + 1)
local meshId = message:sub(b + 1, d)
local textureId = message:sub(d + 1, message:len())
local hat = game:GetService("InsertService"):LoadAsset(tonumber(meshId)):GetChildren()[1]
hat.Parent = character
hat.Handle.Mesh.TextureId = "http://www.roblox.com/asset/?id=" .. textureId
print("Adding hat " .. meshId .. " with texture " .. textureId .. " to " .. player.Name)
end
For some reason, after I type in /hat hatIdHere, it goes on to the elseif statement and says that there's an error at the local hat = game:GetService(--[===[etc]===] line.
There should not be an error because the code there shouldn't be running. However, if I type in /hat meshId textureId, then it works.
Please help, I have no clue why this isn't working. My message is a defined string.

How to dump a table to console?

I'm having trouble displaying the contents of a table which contains nested tables (n-deep). I'd like to just dump it to std out or the console via a print statement or something quick and dirty but I can't figure out how. I'm looking for the rough equivalent that I'd get when printing an NSDictionary using gdb.
If the requirement is "quick and dirty"
I've found this one useful. Because of the recursion it can print nested tables too. It doesn't give the prettiest formatting in the output but for such a simple function it's hard to beat for debugging.
function dump(o)
if type(o) == 'table' then
local s = '{ '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end
e.g.
local people = {
{
name = "Fred",
address = "16 Long Street",
phone = "123456"
},
{
name = "Wilma",
address = "16 Long Street",
phone = "123456"
},
{
name = "Barney",
address = "17 Long Street",
phone = "123457"
}
}
print("People:", dump(people))
Produces the following output:
People: { [1] = { ["address"] = 16 Long Street,["phone"] =
123456,["name"] = Fred,} ,[2] = { ["address"] = 16 Long
Street,["phone"] = 123456,["name"] = Wilma,} ,[3] = { ["address"] = 17
Long Street,["phone"] = 123457,["name"] = Barney,} ,}
I know this question has already been marked as answered, but let me plug my own library here. It's called inspect.lua, and you can find it here:
https://github.com/kikito/inspect.lua
It's just a single file that you can require from any other file. It returns a function that transforms any Lua value into a human-readable string:
local inspect = require('inspect')
print(inspect({1,2,3})) -- {1, 2, 3}
print(inspect({a=1,b=2})
-- {
-- a = 1
-- b = 2
-- }
It indents subtables properly, and handles "recursive tables" (tables that contain references to themselves) correctly, so it doesn't get into infinite loops. It sorts values in a sensible way. It also prints metatable information.
Regards!
Feel free to browse the Lua Wiki on table serialization. It lists several ways on how to dump a table to the console.
You just have to choose which one suits you best. There are many ways to do it, but I usually end up using the one from Penlight:
> t = { a = { b = { c = "Hello world!", 1 }, 2, d = { 3 } } }
> require 'pl.pretty'.dump(t)
{
a = {
d = {
3
},
b = {
c = "Hello world!",
1
},
2
}
}
found this:
-- Print contents of `tbl`, with indentation.
-- `indent` sets the initial level of indentation.
function tprint (tbl, indent)
if not indent then indent = 0 end
for k, v in pairs(tbl) do
formatting = string.rep(" ", indent) .. k .. ": "
if type(v) == "table" then
print(formatting)
tprint(v, indent+1)
elseif type(v) == 'boolean' then
print(formatting .. tostring(v))
else
print(formatting .. v)
end
end
end
from here
https://gist.github.com/ripter/4270799
works pretty good for me...
Most pure lua print table functions I've seen have a problem with deep recursion
and tend to cause a stack overflow when going too deep. This print
table function that I've written does not have this problem. It should also be capable of handling really large tables due to the way it handles concatenation. In my personal usage of this function, it outputted 63k lines to file in about a second.
The output also keeps lua syntax and the script can easily be modified
for simple persistent storage by writing the output to file if modified to allow
only number, boolean, string and table data types to be formatted.
function print_table(node)
local cache, stack, output = {},{},{}
local depth = 1
local output_str = "{\n"
while true do
local size = 0
for k,v in pairs(node) do
size = size + 1
end
local cur_index = 1
for k,v in pairs(node) do
if (cache[node] == nil) or (cur_index >= cache[node]) then
if (string.find(output_str,"}",output_str:len())) then
output_str = output_str .. ",\n"
elseif not (string.find(output_str,"\n",output_str:len())) then
output_str = output_str .. "\n"
end
-- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
table.insert(output,output_str)
output_str = ""
local key
if (type(k) == "number" or type(k) == "boolean") then
key = "["..tostring(k).."]"
else
key = "['"..tostring(k).."']"
end
if (type(v) == "number" or type(v) == "boolean") then
output_str = output_str .. string.rep('\t',depth) .. key .. " = "..tostring(v)
elseif (type(v) == "table") then
output_str = output_str .. string.rep('\t',depth) .. key .. " = {\n"
table.insert(stack,node)
table.insert(stack,v)
cache[node] = cur_index+1
break
else
output_str = output_str .. string.rep('\t',depth) .. key .. " = '"..tostring(v).."'"
end
if (cur_index == size) then
output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. "}"
else
output_str = output_str .. ","
end
else
-- close the table
if (cur_index == size) then
output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. "}"
end
end
cur_index = cur_index + 1
end
if (size == 0) then
output_str = output_str .. "\n" .. string.rep('\t',depth-1) .. "}"
end
if (#stack > 0) then
node = stack[#stack]
stack[#stack] = nil
depth = cache[node] == nil and depth + 1 or depth - 1
else
break
end
end
-- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
table.insert(output,output_str)
output_str = table.concat(output)
print(output_str)
end
Here is an example:
local t = {
["abe"] = {1,2,3,4,5},
"string1",
50,
["depth1"] = { ["depth2"] = { ["depth3"] = { ["depth4"] = { ["depth5"] = { ["depth6"] = { ["depth7"]= { ["depth8"] = { ["depth9"] = { ["depth10"] = {1000}, 900}, 800},700},600},500}, 400 }, 300}, 200}, 100},
["ted"] = {true,false,"some text"},
"string2",
[function() return end] = function() return end,
75
}
print_table(t)
Output:
{
[1] = 'string1',
[2] = 50,
[3] = 'string2',
[4] = 75,
['abe'] = {
[1] = 1,
[2] = 2,
[3] = 3,
[4] = 4,
[5] = 5
},
['function: 06472B70'] = 'function: 06472A98',
['depth1'] = {
[1] = 100,
['depth2'] = {
[1] = 200,
['depth3'] = {
[1] = 300,
['depth4'] = {
[1] = 400,
['depth5'] = {
[1] = 500,
['depth6'] = {
[1] = 600,
['depth7'] = {
[1] = 700,
['depth8'] = {
[1] = 800,
['depth9'] = {
[1] = 900,
['depth10'] = {
[1] = 1000
}
}
}
}
}
}
}
}
}
},
['ted'] = {
[1] = true,
[2] = false,
[3] = 'some text'
}
}
As previously mentioned, you have to write it.
Here is my humble version: (super basic one)
function tprint (t, s)
for k, v in pairs(t) do
local kfmt = '["' .. tostring(k) ..'"]'
if type(k) ~= 'string' then
kfmt = '[' .. k .. ']'
end
local vfmt = '"'.. tostring(v) ..'"'
if type(v) == 'table' then
tprint(v, (s or '')..kfmt)
else
if type(v) ~= 'string' then
vfmt = tostring(v)
end
print(type(t)..(s or '')..kfmt..' = '..vfmt)
end
end
end
example:
local mytbl = { ['1']="a", 2, 3, b="c", t={d=1} }
tprint(mytbl)
output (Lua 5.0):
table[1] = 2
table[2] = 3
table["1"] = "a"
table["t"]["d"] = 1
table["b"] = "c"
I use my own function to print the contents of a table but not sure how well it translates to your environment:
---A helper function to print a table's contents.
---#param tbl table #The table to print.
---#param depth number #The depth of sub-tables to traverse through and print.
---#param n number #Do NOT manually set this. This controls formatting through recursion.
function PrintTable(tbl, depth, n)
n = n or 0;
depth = depth or 5;
if (depth == 0) then
print(string.rep(' ', n).."...");
return;
end
if (n == 0) then
print(" ");
end
for key, value in pairs(tbl) do
if (key and type(key) == "number" or type(key) == "string") then
key = string.format("[\"%s\"]", key);
if (type(value) == "table") then
if (next(value)) then
print(string.rep(' ', n)..key.." = {");
PrintTable(value, depth - 1, n + 4);
print(string.rep(' ', n).."},");
else
print(string.rep(' ', n)..key.." = {},");
end
else
if (type(value) == "string") then
value = string.format("\"%s\"", value);
else
value = tostring(value);
end
print(string.rep(' ', n)..key.." = "..value..",");
end
end
end
if (n == 0) then
print(" ");
end
end
The simplest way, with circular reference handling and all:
function dump(t, indent, done)
done = done or {}
indent = indent or 0
done[t] = true
for key, value in pairs(t) do
print(string.rep("\t", indent))
if type(value) == "table" and not done[value] then
done[value] = true
print(key, ":\n")
dump(value, indent + 2, done)
done[value] = nil
else
print(key, "\t=\t", value, "\n")
end
end
end
There are 2 solutions that I want to mention: a quick&dirty one, and another which properly escapes all keys and values but is bigger
Simple & fast solution (use only on "safe" inputs):
local function format_any_value(obj, buffer)
local _type = type(obj)
if _type == "table" then
buffer[#buffer + 1] = '{"'
for key, value in next, obj, nil do
buffer[#buffer + 1] = tostring(key) .. '":'
format_any_value(value, buffer)
buffer[#buffer + 1] = ',"'
end
buffer[#buffer] = '}' -- note the overwrite
elseif _type == "string" then
buffer[#buffer + 1] = '"' .. obj .. '"'
elseif _type == "boolean" or _type == "number" then
buffer[#buffer + 1] = tostring(obj)
else
buffer[#buffer + 1] = '"???' .. _type .. '???"'
end
end
Usage:
local function format_as_json(obj)
if obj == nil then return "null" else
local buffer = {}
format_any_value(obj, buffer)
return table.concat(buffer)
end
end
local function print_as_json(obj)
print(_format_as_json(obj))
end
print_as_json {1, 2, 3}
print_as_json(nil)
print_as_json("string")
print_as_json {[1] = 1, [2] = 2, three = { { true } }, four = "four"}
Correct solution with key/value escaping
Small library that I wrote in pure Lua for this specific use-case: https://github.com/vn971/fast_json_encode
Or specifically this 1 file that includes both a formatter and a printer: https://github.com/vn971/fast_json_encode/blob/master/json_format.lua
You have to code it yourself I'm afraid. I wrote this, and it may be of some use to you
function printtable(table, indent)
indent = indent or 0;
local keys = {};
for k in pairs(table) do
keys[#keys+1] = k;
table.sort(keys, function(a, b)
local ta, tb = type(a), type(b);
if (ta ~= tb) then
return ta < tb;
else
return a < b;
end
end);
end
print(string.rep(' ', indent)..'{');
indent = indent + 1;
for k, v in pairs(table) do
local key = k;
if (type(key) == 'string') then
if not (string.match(key, '^[A-Za-z_][0-9A-Za-z_]*$')) then
key = "['"..key.."']";
end
elseif (type(key) == 'number') then
key = "["..key.."]";
end
if (type(v) == 'table') then
if (next(v)) then
printf("%s%s =", string.rep(' ', indent), tostring(key));
printtable(v, indent);
else
printf("%s%s = {},", string.rep(' ', indent), tostring(key));
end
elseif (type(v) == 'string') then
printf("%s%s = %s,", string.rep(' ', indent), tostring(key), "'"..v.."'");
else
printf("%s%s = %s,", string.rep(' ', indent), tostring(key), tostring(v));
end
end
indent = indent - 1;
print(string.rep(' ', indent)..'}');
end
The table.tostring metehod of metalua is actually very complete. It deals with nested tables, the indentation level is changeable, ...
See https://github.com/fab13n/metalua/blob/master/src/lib/metalua/table2.lua
This is my version that supports excluding tables and userdata
-- Lua Table View by Elertan
table.print = function(t, exclusions)
local nests = 0
if not exclusions then exclusions = {} end
local recurse = function(t, recurse, exclusions)
indent = function()
for i = 1, nests do
io.write(" ")
end
end
local excluded = function(key)
for k,v in pairs(exclusions) do
if v == key then
return true
end
end
return false
end
local isFirst = true
for k,v in pairs(t) do
if isFirst then
indent()
print("|")
isFirst = false
end
if type(v) == "table" and not excluded(k) then
indent()
print("|-> "..k..": "..type(v))
nests = nests + 1
recurse(v, recurse, exclusions)
elseif excluded(k) then
indent()
print("|-> "..k..": "..type(v))
elseif type(v) == "userdata" or type(v) == "function" then
indent()
print("|-> "..k..": "..type(v))
elseif type(v) == "string" then
indent()
print("|-> "..k..": ".."\""..v.."\"")
else
indent()
print("|-> "..k..": "..v)
end
end
nests = nests - 1
end
nests = 0
print("### START TABLE ###")
for k,v in pairs(t) do
print("root")
if type(v) == "table" then
print("|-> "..k..": "..type(v))
nests = nests + 1
recurse(v, recurse, exclusions)
elseif type(v) == "userdata" or type(v) == "function" then
print("|-> "..k..": "..type(v))
elseif type(v) == "string" then
print("|-> "..k..": ".."\""..v.."\"")
else
print("|-> "..k..": "..v)
end
end
print("### END TABLE ###")
end
This is an example
t = {
location = {
x = 10,
y = 20
},
size = {
width = 100000000,
height = 1000,
},
name = "Sidney",
test = {
hi = "lol",
},
anotherone = {
1,
2,
3
}
}
table.print(t, { "test" })
Prints:
### START TABLE ###
root
|-> size: table
|
|-> height: 1000
|-> width: 100000000
root
|-> location: table
|
|-> y: 20
|-> x: 10
root
|-> anotherone: table
|
|-> 1: 1
|-> 2: 2
|-> 3: 3
root
|-> test: table
|
|-> hi: "lol"
root
|-> name: "Sidney"
### END TABLE ###
Notice that the root doesn't remove exclusions
Made this version to print tables with identation. Can probably be extended to work recursively.
function printtable(table, indent)
print(tostring(table) .. '\n')
for index, value in pairs(table) do
print(' ' .. tostring(index) .. ' : ' .. tostring(value) .. '\n')
end
end
--~ print a table
function printTable(list, i)
local listString = ''
--~ begin of the list so write the {
if not i then
listString = listString .. '{'
end
i = i or 1
local element = list[i]
--~ it may be the end of the list
if not element then
return listString .. '}'
end
--~ if the element is a list too call it recursively
if(type(element) == 'table') then
listString = listString .. printTable(element)
else
listString = listString .. element
end
return listString .. ', ' .. printTable(list, i + 1)
end
local table = {1, 2, 3, 4, 5, {'a', 'b'}, {'G', 'F'}}
print(printTable(table))
Hi man, I wrote a siple code that do this in pure Lua, it has a bug (write a coma after the last element of the list) but how i wrote it quickly as a prototype I will let it to you adapt it to your needs.
Adding another version. This one tries to iterate over userdata as well.
function inspect(o,indent)
if indent == nil then indent = 0 end
local indent_str = string.rep(" ", indent)
local output_it = function(str)
print(indent_str..str)
end
local length = 0
local fu = function(k, v)
length = length + 1
if type(v) == "userdata" or type(v) == 'table' then
output_it(indent_str.."["..k.."]")
inspect(v, indent+1)
else
output_it(indent_str.."["..k.."] "..tostring(v))
end
end
local loop_pairs = function()
for k,v in pairs(o) do fu(k,v) end
end
local loop_metatable_pairs = function()
for k,v in pairs(getmetatable(o)) do fu(k,v) end
end
if not pcall(loop_pairs) and not pcall(loop_metatable_pairs) then
output_it(indent_str.."[[??]]")
else
if length == 0 then
output_it(indent_str.."{}")
end
end
end
Convert to json and then print.
local json = require('cjson')
json_string = json.encode(this_table)
print (json_string)
simple example of dump a table in lua
i suggest using serpent.lua
local function parser(value, indent, subcategory)
local indent = indent or 2
local response = '(\n'
local subcategory = type(subcategory) == 'number' and subcategory or indent
for key, value in pairs(value) do
if type(value) == 'table' then
value = parser(value, indent, subcategory + indent)
elseif type(value) == 'string' then
value = '\''.. value .. '\''
elseif type(value) ~= 'number' then
value = tostring(value)
end
if type(tonumber(key)) == 'number' then
key = '[' .. key .. ']'
elseif not key:match('^([A-Za-z_][A-Za-z0-9_]*)$') then
key = '[\'' .. key .. '\']'
end
response = response .. string.rep(' ', subcategory) .. key .. ' = ' .. value .. ',\n'
end
return response .. string.rep(' ', subcategory - indent) .. ')'
end
example
response = parser{1,2,3, {ok = 10, {}}}
print(response)
result
(
[1] = 1,
[2] = 2,
[3] = 3,
[4] = (
[1] = (),
ok = 10
)
)
here's my little snippet for that:
--- Dump value of a variable in a formatted string
--
--- #param o table Dumpable object
--- #param tbs string|nil Tabulation string, ' ' by default
--- #param tb number|nil Initial tabulation level, 0 by default
--- #return string
local function dump(o, tbs, tb)
tb = tb or 0
tbs = tbs or ' '
if type(o) == 'table' then
local s = '{'
if (next(o)) then s = s .. '\n' else return s .. '}' end
tb = tb + 1
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"' .. k .. '"' end
s = s .. tbs:rep(tb) .. '[' .. k .. '] = ' .. dump(v, tbs, tb)
s = s .. ',\n'
end
tb = tb - 1
return s .. tbs:rep(tb) .. '}'
else
return tostring(o)
end
end
I have humbly modified a bit Alundaio code:
-- by Alundaio
-- KK modified 11/28/2019
function dump_table_to_string(node, tree, indentation)
local cache, stack, output = {},{},{}
local depth = 1
if type(node) ~= "table" then
return "only table type is supported, got " .. type(node)
end
if nil == indentation then indentation = 1 end
local NEW_LINE = "\n"
local TAB_CHAR = " "
if nil == tree then
NEW_LINE = "\n"
elseif not tree then
NEW_LINE = ""
TAB_CHAR = ""
end
local output_str = "{" .. NEW_LINE
while true do
local size = 0
for k,v in pairs(node) do
size = size + 1
end
local cur_index = 1
for k,v in pairs(node) do
if (cache[node] == nil) or (cur_index >= cache[node]) then
if (string.find(output_str,"}",output_str:len())) then
output_str = output_str .. "," .. NEW_LINE
elseif not (string.find(output_str,NEW_LINE,output_str:len())) then
output_str = output_str .. NEW_LINE
end
-- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
table.insert(output,output_str)
output_str = ""
local key
if (type(k) == "number" or type(k) == "boolean") then
key = "["..tostring(k).."]"
else
key = "['"..tostring(k).."']"
end
if (type(v) == "number" or type(v) == "boolean") then
output_str = output_str .. string.rep(TAB_CHAR,depth*indentation) .. key .. " = "..tostring(v)
elseif (type(v) == "table") then
output_str = output_str .. string.rep(TAB_CHAR,depth*indentation) .. key .. " = {" .. NEW_LINE
table.insert(stack,node)
table.insert(stack,v)
cache[node] = cur_index+1
break
else
output_str = output_str .. string.rep(TAB_CHAR,depth*indentation) .. key .. " = '"..tostring(v).."'"
end
if (cur_index == size) then
output_str = output_str .. NEW_LINE .. string.rep(TAB_CHAR,(depth-1)*indentation) .. "}"
else
output_str = output_str .. ","
end
else
-- close the table
if (cur_index == size) then
output_str = output_str .. NEW_LINE .. string.rep(TAB_CHAR,(depth-1)*indentation) .. "}"
end
end
cur_index = cur_index + 1
end
if (size == 0) then
output_str = output_str .. NEW_LINE .. string.rep(TAB_CHAR,(depth-1)*indentation) .. "}"
end
if (#stack > 0) then
node = stack[#stack]
stack[#stack] = nil
depth = cache[node] == nil and depth + 1 or depth - 1
else
break
end
end
-- This is necessary for working with HUGE tables otherwise we run out of memory using concat on huge strings
table.insert(output,output_str)
output_str = table.concat(output)
return output_str
end
then:
print(dump_table_to_string("AA", true,3))
print(dump_table_to_string({"AA","BB"}, true,3))
print(dump_table_to_string({"AA","BB"}))
print(dump_table_to_string({"AA","BB"},false))
print(dump_table_to_string({"AA","BB",{22,33}},true,2))
gives:
only table type is supported, got string
{
[1] = 'AA',
[2] = 'BB'
}
{
[1] = 'AA',
[2] = 'BB'
}
{[1] = 'AA',[2] = 'BB'}
{
[1] = 'AA',
[2] = 'BB',
[3] = {
[1] = 22,
[2] = 33
}
}
Now the function print can print the (flat) tables!
oprint = print -- origin print
print = function (...)
if type(...) == "table" then
local str = ''
local amount = 0
for i,v in pairs(...) do
amount=amount+1
local pre = type(i) == "string" and i.."=" or ""
str = str .. pre..tostring(v) .. "\t"
end
oprint('#'..amount..':', str)
else
oprint(...)
end
end
For example:
print ({x=7, y=9, w=11, h="height", 7, 8, 9})
prints:
#7: 7 8 9 y=9 x=7 h=height w=11
The same way it can be just new function tostring:
otostring = tostring -- origin tostring
tostring = function (...)
if type(...) == "table" then
local str = '{'
for i,v in pairs(...) do
local pre = type(i) == "string" and i.."=" or ""
str = str .. pre..tostring(v) .. ", "
end
str = str:sub(1, -3)
return str..'}'
else
return otostring(...)
end
end

Resources