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
Related
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
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
How can I match "words" mixed parenthesis delimited strings, on the basis that they are separated by whitespaces. EG:
split_words_and_parenthesis("1791 (AR6K Async) S 2 ") --> {"1791","AR6K Async","S","2"}
Here's my attempt:
str = "1791 (AR6K Async) S 2 "
for val in str:gmatch("%S+") do
if str:gmatch("(" )then
str:gsub("%b()" , function(s) val=s end)
print(val)
else
print(val)
end
end
output:
(AR6K Async)
(AR6K Async)
(AR6K Async)
(AR6K Async)
(AR6K Async)
Can be solved using string.match if you know the format:
str = "1791 (AR6K Async) S 2 "
s1 = str:match("(%d%d%d%d)%s%(.*%)%s.+%s.+")
s2 = str:match("%d%d%d%d%s(%(.*%))%s.+%s.+")
s3 = str:match("%d%d%d%d%s%(.*%)%s(.+)%s.+")
s4 = str:match("%d%d%d%d%s%(.*%)%s.+%s(.+)")
print(s1)
print(s2)
print(s3)
print(s4)
Another solution that is generic and allows for variable number of entries (try it: simply past in a lua interpreter):
function get_separate_words(str)
local i = 1
local words = {}
function get_parentheses_content(str,is_recursively_called)
local i = 1
local function split(s, sep)
local fields = {}
local sep = sep or ":"
local pattern = string.format("([^%s]+)", sep)
string.gsub(s, pattern, function(c) fields[#fields + 1] = c end)
return fields
end
for j = 1,#str do
local c = string.sub(str,j,j)
local d = string.sub(str,j+1,j+1)
if j <= i then
elseif c == "(" then
i = j + #get_parentheses_content(string.sub(str,j+1,#str),true) + 2
elseif c == ")" and (is_recursively_called or (d == " ") or (not d)) then
print('c')
local parentheses_content = string.sub(str,1,j-1)
return {parentheses_content}
end
end
local parentheses_content = string.match(str,"^(.*)%)%s+[^)]*$")
if parentheses_content then print('a') end
parentheses_content = parentheses_content or string.match(str,"^(.*)%)$")
if parentheses_content then
print("A")
return {parentheses_content}
else
print("B")
return split("("..str," ")
end
end
local function merge(table_a, table_b)
table_a = table_a or {}
table_b = table_b or {}
for k_b, v_b in pairs(table_b) do
if type(v_b) == "table" and type(table_a[k_b] or false) == "table" then
merge(table_a[k_b], table_b[k_b])
else
table_a[k_b] = v_b
end
end
return table_a
end
for j = 1,#str do
local c = string.sub(str,j,j)
if j < i then
elseif c == " " or j == #str then
local word = string.gsub(string.sub(str,i,j)," ","")
if #word > 0 then
table.insert(words, word)
print(word)
end
i = j+1
elseif c == "(" then
local all_characters_after_opening_parentheses = string.sub(str,j+1,#str)
local parentheses_content = get_parentheses_content(all_characters_after_opening_parentheses)[1]
table.insert(words, parentheses_content)
j= j+#parentheses_content+2
i = j
end
end
return words
end
separate_words = get_separate_words("1791 (AR6(K As)ync) S 2 )")
for k,v in ipairs(separate_words) do print(k,v) end
I'm trying to edit a lua script to add on my Garry's Mod Server, but I get this error and I dont know what to do.
Error:
[ERROR] --/sh_worlditemspawner.lua:56: attempt to index local 'v' (a nil value)
Code:
local PLUGIN = PLUGIN
PLUGIN.name = "World Item Spawner"
PLUGIN.author = "Black Tea"
PLUGIN.desc = "World Item Spawner."
PLUGIN.itempoints = PLUGIN.itempoints or {}
PLUGIN.spawngroups = {
["default"] = {
{"bleach"},
},
["example"] = {
{"ration"},
},
["junks"] = {
{"junk_ws"},
{"junk_wj"},
{"junk_be"},
{"junk_bt"},
{"junk_p"},
{"junk_ss"},
{"junk_bl"},
{"junk_k"},
{"junk_p"},
{"junk_hp"},
{"junk_ec"},
{"junk_ej"},
}
}
PLUGIN.spawnrate = 30
PLUGIN.maxitems = 10
PLUGIN.itemsperspawn = 2
PLUGIN.spawneditems = PLUGIN.spawneditems or {}
if SERVER then
local spawntime = 1
function PLUGIN:Think()
if spawntime > CurTime() then return end
spawntime = CurTime() + self.spawnrate
for k, v in ipairs(self.spawneditems) do
if (!v:IsValid()) then
table.remove(self.spawneditems, k)
end
end
if #self.spawneditems >= self.maxitems then return end
for i = 1, self.itemsperspawn do
if #self.spawneditems >= self.maxitems then return end
local v = table.Random(self.itempoints)
if #self.spawneditems > self.maxitems then
return
end
local data = {}
data.start = v[1]
data.endpos = data.start + Vector(0, 0, 1)
data.filter = client
data.mins = Vector(-16, -16, 0)
data.maxs = Vector(16, 16, 16)
local trace = util.TraceHull(data)
if trace.Entity:IsValid() then
continue
end
local idat = table.Random(self.spawngroups[v[2]]) or self.spawngroup["default"]
local item = nut.item.Spawn(v[1] + Vector( math.Rand(-8,8), math.Rand(-8,8), 10 ), AngleRand(), idat[1], idat[2] or {})
table.insert( self.spawneditems, item )
end
end
function PLUGIN:LoadData()
self.itempoints = nut.util.ReadTable("itempoints")
end
function PLUGIN:SaveData()
for k, v in ipairs(self.spawneditems) do
v:Remove()
end
nut.util.WriteTable("itempoints", self.itempoints)
end
else
netstream.Hook("nut_DisplaySpawnPoints", function(data)
for k, v in pairs(data) do
local emitter = ParticleEmitter( v[1] )
local smoke = emitter:Add( "sprites/glow04_noz", v[1] )
smoke:SetVelocity( Vector( 0, 0, 1 ) )
smoke:SetDieTime(10)
smoke:SetStartAlpha(255)
smoke:SetEndAlpha(255)
smoke:SetStartSize(64)
smoke:SetEndSize(64)
smoke:SetColor(255,186,50)
smoke:SetAirResistance(300)
emitter:Finish()
end
end)
end
nut.command.Register({
adminOnly = true,
onRun = function(client, arguments)
local trace = client:GetEyeTraceNoCursor()
local hitpos = trace.HitPos + trace.HitNormal*5
local spawngroup = arguments[1] or "default"
table.insert( PLUGIN.itempoints, { hitpos, spawngroup } )
nut.util.Notify( "You added ".. spawngroup .. " item spawner." )
end
}, "itemspawnadd")
nut.command.Register({
adminOnly = true,
onRun = function(client, arguments)
local trace = client:GetEyeTraceNoCursor()
local hitpos = trace.HitPos + trace.HitNormal*5
local range = arguments[1] or 128
local mt = 0
for k, v in pairs( PLUGIN.itempoints ) do
local distance = v[1]:Distance( hitpos )
if distance <= tonumber(range) then
PLUGIN.itempoints[k] = nil
mt = mt + 1
end
end
nut.util.Notify( mt .. " item spawners has been removed.")
end
}, "itemspawnremove")
nut.command.Register({
adminOnly = true,
onRun = function(client, arguments)
if SERVER then
netstream.Start(client, "nut_DisplaySpawnPoints", PLUGIN.itempoints)
nut.util.Notify( "Displayed All Points for 10 secs." )
end
end
}, "itemspawndisplay")
This is because
table.Random(self.itempoints)
returns nil. Did you mean math.random? If you post code for that table.Random func I can give more info.
The output for the below script is:
AD[1] = [variable not found]
AD['2'] = bar
How can I modify the function getfield to return a value for v for both cases?
function getfield (f)
local v = _G
for w in string.gfind(f, "[%w_]+") do
v = v[w]
end
return v
end
AD = {[1] = 'foo', ['2'] = 'bar'}
data = {"AD[1]","AD['2']"}
for i,line in ipairs(data) do
s = getfield(line)
if s then
print(line .. " = " .. s)
else
print(line .. " = [variable not found]")
end
end
UPDATE:
I'm 90% sure, this is going to work for me:
function getfield (f)
local v = _G
for w in string.gfind(f, "['%w_]+") do
if (string.find(w,"['%a_]")==nil) then
w = loadstring('return '..w)()
else
w = string.gsub(w, "'", "")
end
v=v[w]
end
return v
end
This happens to work
function getfield (f)
local v = _G
for w in string.gfind(f, "['%w_]+") do
local x = loadstring('return '..w)()
print(w,x)
v = v[x] or v[w]
end
return v
end
AD = {[1] = 'foo', ['2'] = 'bar'}
data = {"AD[1]","AD['2']"}
for i,line in ipairs(data) do
s = getfield(line)
if s then
print(line .. " = " .. s)
else
print(line .. " = [variable not found]")
end
end
but it's pretty fragile.
Note that I added ' to the pattern.
The difficulty is that sometimes w is a string representing a name (key), and sometimes it's a string representing a number. In the second case it needs to be converted from string to number. But you need the context or some syntax to decide.
Here's the kind of fragility I mean:
> data = {"math[pi]","AD['2']"}
>
> for i,line in ipairs(data) do
>> s = getfield(line)
>> if s then
>> print(line .. " = " .. s)
>> else
>> print(line .. " = [variable not found]")
>> end
>> end
math table: 0x10ee05100
pi nil
math[pi] = 3.1415926535898
AD table: 0x10ee19ee0
'2' 2
AD['2'] = bar
> pi = 3
> math[3] = 42
> data = {"math[pi]","AD['2']"}>
> for i,line in ipairs(data) do
>> s = getfield(line)
>> if s then
>> print(line .. " = " .. s)
>> else
>> print(line .. " = [variable not found]")
>> end
>> end
math table: 0x10ee05100
pi 3
math[pi] = 42
AD table: 0x10ee19ee0
'2' 2
AD['2'] = bar
math[pi] is unchanged, but getfield interprets pi in the global context and gets 3 so the wrong field of math is returned.
You'll get the strings '1' and "'2'". You have to evaluate it to turn it into whatever object it is:
v = v[loadstring('return ' .. w)()]
Don't do this if the string came from an untrusted source though (like a user input or something) because they could execute arbitrary code.