mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-25 06:14:59 +00:00
137 lines
3.5 KiB
Lua
137 lines
3.5 KiB
Lua
-- JSON parser for loading test cases
|
|
-- Lightweight parser for test case data
|
|
|
|
---@class JSONParser
|
|
local M = {}
|
|
|
|
---Decode a JSON string into a Lua value
|
|
---@param str string JSON string to parse
|
|
---@return any Parsed value (table, string, number, boolean, or nil)
|
|
function M.decode(str)
|
|
local pos = 1
|
|
|
|
local function skip_whitespace()
|
|
while pos <= #str and str:sub(pos, pos):match("%s") do
|
|
pos = pos + 1
|
|
end
|
|
end
|
|
|
|
local function parse_string()
|
|
pos = pos + 1 -- skip opening quote
|
|
local start = pos
|
|
while pos <= #str do
|
|
local c = str:sub(pos, pos)
|
|
if c == '"' then
|
|
local result = str:sub(start, pos - 1)
|
|
pos = pos + 1
|
|
-- Handle escape sequences
|
|
result = result:gsub("\\n", "\n"):gsub("\\t", "\t"):gsub("\\r", "\r")
|
|
result = result:gsub('\\"', '"'):gsub("\\\\", "\\")
|
|
return result
|
|
elseif c == "\\" then
|
|
pos = pos + 2 -- skip escape sequence
|
|
else
|
|
pos = pos + 1
|
|
end
|
|
end
|
|
error("Unterminated string")
|
|
end
|
|
|
|
local function parse_number()
|
|
local start = pos
|
|
while pos <= #str and str:sub(pos, pos):match("[%d%.eE%+%-]") do
|
|
pos = pos + 1
|
|
end
|
|
return tonumber(str:sub(start, pos - 1))
|
|
end
|
|
|
|
local parse_value -- forward declaration
|
|
|
|
local function parse_array()
|
|
pos = pos + 1 -- skip [
|
|
local arr = {}
|
|
skip_whitespace()
|
|
if str:sub(pos, pos) == "]" then
|
|
pos = pos + 1
|
|
return arr
|
|
end
|
|
while true do
|
|
skip_whitespace()
|
|
arr[#arr + 1] = parse_value()
|
|
skip_whitespace()
|
|
local c = str:sub(pos, pos)
|
|
if c == "]" then
|
|
pos = pos + 1
|
|
return arr
|
|
elseif c == "," then
|
|
pos = pos + 1
|
|
else
|
|
error("Expected ',' or ']' in array at position " .. pos)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function parse_object()
|
|
pos = pos + 1 -- skip {
|
|
local obj = {}
|
|
skip_whitespace()
|
|
if str:sub(pos, pos) == "}" then
|
|
pos = pos + 1
|
|
return obj
|
|
end
|
|
while true do
|
|
skip_whitespace()
|
|
if str:sub(pos, pos) ~= '"' then
|
|
error("Expected string key at position " .. pos)
|
|
end
|
|
local key = parse_string()
|
|
skip_whitespace()
|
|
if str:sub(pos, pos) ~= ":" then
|
|
error("Expected ':' at position " .. pos)
|
|
end
|
|
pos = pos + 1
|
|
skip_whitespace()
|
|
obj[key] = parse_value()
|
|
skip_whitespace()
|
|
local c = str:sub(pos, pos)
|
|
if c == "}" then
|
|
pos = pos + 1
|
|
return obj
|
|
elseif c == "," then
|
|
pos = pos + 1
|
|
else
|
|
error("Expected ',' or '}' in object at position " .. pos)
|
|
end
|
|
end
|
|
end
|
|
|
|
parse_value = function()
|
|
skip_whitespace()
|
|
local c = str:sub(pos, pos)
|
|
if c == '"' then
|
|
return parse_string()
|
|
elseif c == "{" then
|
|
return parse_object()
|
|
elseif c == "[" then
|
|
return parse_array()
|
|
elseif c == "t" and str:sub(pos, pos + 3) == "true" then
|
|
pos = pos + 4
|
|
return true
|
|
elseif c == "f" and str:sub(pos, pos + 4) == "false" then
|
|
pos = pos + 5
|
|
return false
|
|
elseif c == "n" and str:sub(pos, pos + 3) == "null" then
|
|
pos = pos + 4
|
|
return nil
|
|
elseif c:match("[%d%-]") then
|
|
return parse_number()
|
|
else
|
|
error("Unexpected character '" .. c .. "' at position " .. pos)
|
|
end
|
|
end
|
|
|
|
return parse_value()
|
|
end
|
|
|
|
return M
|