From e9d96b20d9c8d9e8f2372afeb212a2c6cde05ece Mon Sep 17 00:00:00 2001 From: tour Date: Fri, 13 Sep 2024 15:27:52 +0200 Subject: [PATCH] fix alg2 --- alg2.lua | 93 +++++++++++++++++++++++++++++++++++++++++--------------- init.lua | 4 +-- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/alg2.lua b/alg2.lua index b9eaf05..7c5794f 100644 --- a/alg2.lua +++ b/alg2.lua @@ -1,4 +1,26 @@ -local function stringToTable(str) +-- 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 @@ -11,66 +33,87 @@ local function stringToTable(str) elseif tonumber(s) then return tonumber(s) elseif s:match("^\".*\"$") or s:match("^'.*'$") then - return s:sub(2, -2):gsub("\\(.)", "%1") -- handle escape sequences - elseif s:match("^%b{}$") then - return stringToTable(s) + 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 inKey = true 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 buffer ~= "" then - value = buffer + elseif depth == 0 and not buffer:find("^%s*$") then + value = parseValue(buffer) buffer = "" end - - elseif char == ',' and depth == 1 then - if inKey then - key = parseValue(buffer) - else - value = parseValue(buffer) - tbl[key] = value + elseif (char == ',' or char == ";") and depth == 1 then + value = parseValue(buffer) + if key == nil then + key = getNextKey() end - buffer = "" - inKey = not inKey + tbl[key] = value + key, value = nil, nil + buffer = "" elseif char == '=' and depth == 1 then - key = parseValue(buffer) + key = parseKey(buffer) buffer = "" - inKey = false - else buffer = buffer .. char end - - if depth == 1 and i == #str and buffer ~= "" then - value = parseValue(buffer) - tbl[key] = value - 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 \ No newline at end of file diff --git a/init.lua b/init.lua index 9956ff8..2434039 100644 --- a/init.lua +++ b/init.lua @@ -22,7 +22,7 @@ local tests = { '{[-42.42e42] = 42.42E-42}', '{1, 2, 3, }', -- invalid tables, the code should return an error - -- --[[ + --[[ "{a = 1", "{'unclosed string\\'}", "{invalid key = 3}", @@ -48,6 +48,6 @@ local function test(alg) end -- test("s2t1") --- test("s2t2") +test("s2t2") -- test("s2t3") test("s2ttour") \ No newline at end of file