151 lines
4.6 KiB
Lua
151 lines
4.6 KiB
Lua
local function stringToTable(str)
|
|
local parseTable -- defined later, but we need it in parseValue
|
|
|
|
local function parseValue(s, idx)
|
|
s = s:match("^%s*(.-)%s*$", idx) -- Trim leading/trailing spaces
|
|
|
|
if s:sub(1, 1) == "{" then
|
|
return parseTable(s)
|
|
elseif s:sub(1, 4) == "true" then
|
|
return true, 4
|
|
elseif s:sub(1, 5) == "false" then
|
|
return false, 5
|
|
elseif s:sub(1, 3) == "nil" then
|
|
return nil, 3
|
|
elseif s:sub(1, 1) == '"' or s:sub(1, 1) == "'" then
|
|
local quote = s:sub(1, 1)
|
|
local value = ""
|
|
for i = 2, #s do
|
|
if s:sub(i, i) == quote then
|
|
local j = i - 1
|
|
local escaped = false
|
|
while (s:sub(j,j)) == "\\" do
|
|
escaped = not escaped
|
|
j = j - 1
|
|
end
|
|
if not escaped then
|
|
value = value:gsub("\\(.)", "%1")
|
|
return value, i
|
|
end
|
|
end
|
|
value = value .. s:sub(i, i)
|
|
end
|
|
error("Unclosed string")
|
|
end
|
|
local _, next_i = s:find("^[+-]?%d+%.?%d*[eE][+-]?%d+%s*")
|
|
if not next_i then
|
|
_, next_i = s:find("^[+-]?%d+%.?%d*%s*")
|
|
end
|
|
if next_i then
|
|
return tonumber(s:match("^[+-]?%d+%.?%d*[eE][+-]?%d+") or s:match("^([+-]?%d+%.?%d*)")), next_i
|
|
else
|
|
print(s)
|
|
error("Invalid value")
|
|
end
|
|
end
|
|
|
|
local lua_keywords = {
|
|
["and"] = true,
|
|
["break"] = true,
|
|
["do"] = true,
|
|
["else"] = true,
|
|
["elseif"] = true,
|
|
["end"] = true,
|
|
["false"] = true,
|
|
["for"] = true,
|
|
["function"] = true,
|
|
["goto"] = true,
|
|
["if"] = true,
|
|
["in"] = true,
|
|
["local"] = true,
|
|
["nil"] = true,
|
|
["not"] = true,
|
|
["or"] = true,
|
|
["repeat"] = true,
|
|
["return"] = true,
|
|
["then"] = true,
|
|
["true"] = true,
|
|
["until"] = true,
|
|
["while"] = true,
|
|
}
|
|
|
|
local function parseKey(s, i)
|
|
s = s:match("^%s*(.-)%s*$", i) -- Trim leading/trailing spaces
|
|
if s:find("^[%a_]") then
|
|
local key = s:match("^[%a%d_]*")
|
|
if not lua_keywords[key] then
|
|
return key, #key
|
|
end
|
|
end
|
|
end
|
|
|
|
function parseTable(s)
|
|
local tbl = {}
|
|
local i = 2 -- Start after the opening '{'
|
|
local key, value
|
|
local value_found -- we cannot check for value, bc {nil} is valid
|
|
local next_key = 1
|
|
local next_i
|
|
|
|
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
|
|
|
|
while i <= #s do
|
|
local char = s:sub(i, i)
|
|
if char == "}" then
|
|
return tbl, i
|
|
elseif char == " " then
|
|
i = i + 1
|
|
elseif char == "," or char == ";" then
|
|
if key ~= nil or (not value_found) then
|
|
error("value expected")
|
|
end
|
|
value_found = false
|
|
i = i + 1
|
|
elseif char == "[" then
|
|
if key ~= nil then
|
|
error("Expected value, not a key")
|
|
end
|
|
key, next_i = parseValue(s:sub(i + 1))
|
|
i = i + next_i + 1
|
|
if s:sub(i, i) ~= "]" then
|
|
error("Expected closing bracket")
|
|
end
|
|
i = i + 1
|
|
elseif key == nil then
|
|
key, next_i = parseKey(s:sub(i))
|
|
if not key then -- not a valid key
|
|
value, next_i = parseValue(s:sub(i))
|
|
key = getNextKey()
|
|
tbl[key] = value
|
|
value_found = true
|
|
key = nil
|
|
end
|
|
i = i + next_i
|
|
elseif char == "=" then
|
|
i = i + 1
|
|
value, next_i = parseValue(s:sub(i))
|
|
tbl[key] = value
|
|
value_found = true
|
|
key = nil
|
|
i = i + next_i + 1
|
|
else
|
|
error("Unexpected character: " .. s:sub(i, i))
|
|
end
|
|
end
|
|
error("Unclosed table")
|
|
end
|
|
|
|
local result, _ = parseValue(str)
|
|
return result
|
|
end
|
|
|
|
function test_string_to_table.s2t1(str)
|
|
return stringToTable(str)
|
|
end |