diff --git a/assets/.lua/csexp.lua b/assets/.lua/csexp.lua new file mode 100644 index 0000000..b321b30 --- /dev/null +++ b/assets/.lua/csexp.lua @@ -0,0 +1,66 @@ +local function _parse(str, init) + local pos = init + if string.find(str, "^%(", pos) then + pos = pos + 1 + local list = {}; + while true do + if string.find(str, "^%)", pos) then + pos = pos + 1 + return list, pos + end + local child, new_pos = _parse(str, pos) + if not child then + error(string.format("expected ')', '(', or atom at index %d", pos)) + end + table.insert(list, child) + pos = new_pos + end + else + local pos = init + local len_str = string.match(str, "^[1-9]%d*", pos) + if not len_str then + error(string.format("expected '(' or atom at index %d", pos)) + end + local len = tonumber(len_str) + pos = pos+#len_str + + if not string.find(str, "^:", pos) then error(string.format("expected ':' at index %d", pos)) end + pos = pos + 1 + local value = string.sub(str, pos, pos + len - 1) + if #value < len then error(string.format("unexpected end of string at index %d", pos)) end + pos = pos + len + + return value, pos + end +end + +local function parse(str) + local value, pos = _parse(str, 1) + if pos <= #str then + error(string.format("extra characters at index %d", pos)) + end + return value +end + +local function _serialize(tokens, obj) + if type(obj) == "table" then + table.insert(tokens, "(") + for _, item in ipairs(obj) do + _serialize(tokens, item) + end + table.insert(tokens, ")") + else + local s = tostring(obj) + table.insert(tokens, tostring(#s)) + table.insert(tokens, ":") + table.insert(tokens, s) + end +end + +local function serialize(obj) + local tokens = {} + _serialize(tokens, obj) + return table.concat(tokens) +end + +return {parse = parse, serialize = serialize}