test_string_to_table/alg2.lua

118 lines
3.1 KiB
Lua

-- based on https://www.lua.org/pil/20.4.html
local function code(s)
-- mask all escaped characters
return s:gsub("\\(.)", function (c)
return string.format("\\%03d", string.byte(c))
end):gsub("([\"'])(.-)%1", function (_, match) -- detect all strings
return "'" .. match:gsub("([,;{}%[%]=])", function (c)
return string.format("\\%03d", string.byte(c))
end) .. "'"
end)
end
local function decode(s)
return s:gsub("\\(%d%d%d)", function (d)
return string.char(d)
end)
end
local function codedStringToTable(str)
-- special case: hadle empty tables (our parser would break here)
if str:find("^{%s*}$") then return {} end
local function parseValue(s)
s = s:match("^%s*(.-)%s*$") -- trim whitespace
if s == "nil" then
return nil
elseif s == "true" then
return true
elseif s == "false" then
return false
elseif tonumber(s) then
return tonumber(s)
elseif s:match("^\".*\"$") or s:match("^'.*'$") then
return decode(s:sub(2, -2))
elseif s:match("^{.*}$") then
return codedStringToTable(s)
else
error("Unrecognized value: " .. s)
end
end
local function parseKey(s)
s = s:match("^%s*(.-)%s*$") -- trim whitespace
if s:match("^%[(.*)%]$") then
return parseValue(s:match("^%[(.*)%]$"))
elseif s:find("^[%a_][%a%d_]*$") then -- make sure the string is a valid lua identifier
return s
else
error("Unrecognized key: " .. s)
end
end
local tbl = {}
local key, value
local next_key = 1
local depth = 0
local buffer = ""
local function getNextKey()
while tbl[next_key] ~= nil do
next_key = next_key + 1
end
key = next_key
next_key = next_key + 1
return key
end
for i = 1, #str do
local char = str:sub(i, i)
if char == '{' then
if depth > 0 then
buffer = buffer .. char
end
depth = depth + 1
elseif char == '}' then
depth = depth - 1
if depth > 0 then
buffer = buffer .. char
elseif depth == 0 and not buffer:find("^%s*$") then
value = parseValue(buffer)
buffer = ""
end
elseif (char == ',' or char == ";") and depth == 1 then
value = parseValue(buffer)
if key == nil then
key = getNextKey()
end
tbl[key] = value
key, value = nil, nil
buffer = ""
elseif char == '=' and depth == 1 then
key = parseKey(buffer)
buffer = ""
else
buffer = buffer .. char
end
end
if key == nil then
key = getNextKey()
end
tbl[key] = value
return tbl
end
local function stringToTable(str)
str = code(str)
return codedStringToTable(str)
end
function test_string_to_table.s2t2(str)
return stringToTable(str)
end