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