Skip to content

Instantly share code, notes, and snippets.

@notpeelz
Last active May 20, 2018 09:35
Show Gist options
  • Save notpeelz/ac7ff23fa1c8498720628d38e9c99528 to your computer and use it in GitHub Desktop.
Save notpeelz/ac7ff23fa1c8498720628d38e9c99528 to your computer and use it in GitHub Desktop.
Lua implementation of tostring(table) docs for OpenComputers
local serialization = require('serialization')
local serialize, deserialize = serialization.serialize, serialization.deserialize
local stringBuilder = require('stringBuilder')
local MAX_LINES = 10
local docsBuilder = {}
docsBuilder.MAX_LINES = MAX_LINES
function formatValueEntry(value)
local sb = stringBuilder()
local mspace = ''
for s in string.gmatch(value, '[^\r\n]+') do
sb.append(mspace .. s)
mspace = '\n '
end
return sb.value
end
function isShortKeyAble(key)
if type(key) ~= 'string' then return false end
if string.match(key, '[0-9].*') == key then return false end
if string.match(key, '[a-zA-Z0-9_]+') == key then return true end
return false
end
return setmetatable(docsBuilder, {
__call = function(_, obj, maxLines)
assert(type(obj) == 'table', 'bad argument #1 (table expected, got ' .. type(obj) .. ')')
assert(
type(maxLines) == 'number' or type(maxLines) == 'boolean' or maxLines == nil,
'bad argument #2 (number, boolean or nil expected, got ' .. type(maxLines) .. ')'
)
maxLines = maxLines or MAX_LINES
if type(maxLines) == 'boolean' then
maxLines = maxLines and math.huge or MAX_LINES
else
assert(maxLines > 0, 'bad argument #2 (nuber greater than 0 expected)')
end
local builder = {}
return setmetatable(builder, {
__tostring = function()
local sb = stringBuilder()
local lineCount = 1
sb.append('{')
local i = 0
local mspace = ''
function appendMember(name, value)
if i ~= #obj and lineCount ~= 1 then
sb.append(',\n')
end
local shortKey = isShortKeyAble(name)
local newEntry
if shortKey then
newEntry = mspace .. name .. '=' .. formatValueEntry(value)
else
if type(name) == 'boolean' or type(name) == 'number' then
newEntry = mspace .. '[' .. name .. ']=' .. formatValueEntry(value)
else
newEntry = mspace .. '["' .. name .. '"]=' .. formatValueEntry(value)
end
end
if lineCount == 1 then
mspace = shortKey and '\x20' or '\x20\x20'
end
local lines = (function()
local t = {}
local c = 0
for v in string.gmatch(newEntry, '[^\r\n]+') do
c = c + 1
t[c] = v
end
return t
end)()
local lineNumber = 0
while lineCount <= maxLines do
lineNumber = lineNumber + 1
local line = lines[lineNumber]
if line == nil then break end
sb.append(line)
if lineNumber < #lines then
sb.append('\n')
end
lineCount = lineCount + 1
end
end
for memberName, member in pairs(obj) do
i = i + 1
if lineCount > maxLines then break end
local docEntry = builder[memberName]
if docEntry == true or docEntry == nil then
if type(member) == 'table' then
appendMember(memberName, serialize(member, maxLines))
else
appendMember(memberName, tostring(member))
end
elseif type(docEntry) == 'string' then
appendMember(memberName, docEntry)
end
end
if lineCount > maxLines then
sb.append(',\n...')
else
sb.append('}')
end
return sb.value
end
})
end
})
local stringBuilder = {}
local keyBlacklist = {internal = true}
return setmetatable({}, {
__call = function()
local t = {}
local props = {
value = function()
return table.concat(t)
end
}
local methods = {
append = function(...)
for _, v in ipairs({...}) do
table.insert(t, v)
end
end,
remove = function(n)
return table.remove(t, n)
end,
compile = function(...)
return table.concat(t, ...)
end,
len = function()
return #t
end
}
local builder = {internal = { t = t }}
return setmetatable({}, {
__index = function(_, key)
if builder[key] then return builder[key] end
if props[key] then return props[key]() end
return methods[key]
end,
__newindex = function(_, key, value)
builder[key] = value
end,
__pairs = function()
local prevKey
local enum = 'builder'
local enumProps, enumMethods, enumBuilder
enumProps = function()
local propKey, propValue = next(props, prevKey)
if propKey then
prevKey = propKey
return propKey, propValue
end
prevKey = nil
enum = 'methods'
return enumMethods()
end
enumMethods = function()
local methodKey, methodValue = next(methods, prevKey)
if methodKey then
prevKey = methodKey
return methodKey, methodValue
end
prevKey = nil
enum = 'builder'
return -- return nothing (terminates the iterator)
end
enumBuilder = function()
local builderKey, builderValue = next(builder, prevKey)
prevKey = builderKey
while keyBlacklist[builderKey] ~= nil do
builderKey, builderValue = next(builder, prevKey)
prevKey = builderKey
end
if builderKey ~= nil then return builderKey, builderValue end
prevKey = nil
enum = 'props'
return enumProps()
end
return function()
if enum == 'builder' then return enumBuilder() end
if enum == 'props' then return enumProps() end
if enum == 'methods' then return enumMethods() end
error('unexpected stringBuilder iterator error')
end
end,
__len = function(self)
local count = 0
for _ in pairs(self) do count = count + 1 end
return count
end
})
end
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment