70 lines
1.8 KiB
Lua
70 lines
1.8 KiB
Lua
--- simple s expressions parser
|
|
|
|
local parse, parse_exp, parse_atom
|
|
|
|
-- expression: starts with ( and ends with ), contains a whietspace separated list of expressions and tokens
|
|
-- s has no leading whitespace, starts with (
|
|
-- returns exp, r (r has no leading whitespace)
|
|
-- returns nil, err
|
|
parse_exp = function(s)
|
|
local r = s:match("^%(%s*(.*)$")
|
|
if not r then return nil, "no expression found" end
|
|
local exp = {}
|
|
repeat
|
|
local item, r_item = parse(r)
|
|
if item then
|
|
table.insert(exp, item)
|
|
r = r_item
|
|
end
|
|
until not item
|
|
if not r:match("^%)") then
|
|
return nil, "expected closing )"
|
|
end
|
|
return exp, r:match("^%)%s*(.-)$")
|
|
end
|
|
|
|
-- atom: litteral delimited by whitespace, ), or (; and with escaping using \
|
|
-- s has no leading whitespace
|
|
-- returns exp, r (r has no leading whitespace)
|
|
-- returns nil, err
|
|
parse_atom = function(s)
|
|
local atom = {}
|
|
local n, r = s:match("^([^%s%(%)\\]*)(.-)$")
|
|
if #n > 0 then table.insert(atom, n) end
|
|
while r:match("^\\") do
|
|
table.insert(atom, r:match("^\\(.)"))
|
|
n, r = r:match("^\\.([^%s%(%)\\]*)(.-)$")
|
|
if #n > 0 then table.insert(atom, n) end
|
|
end
|
|
if #atom == 0 then return nil, "no atom found" end
|
|
return table.concat(atom), r:match("^%s*(.-)$")
|
|
end
|
|
|
|
-- s has no leading whitespace
|
|
-- returns exp, r (r has no leading whitespace)
|
|
-- returns nil, err
|
|
parse = function(s)
|
|
local i, r = parse_exp(s)
|
|
if i then return i, r end
|
|
i, r = parse_atom(s)
|
|
if i then return i, r end
|
|
return nil, "no expression found"
|
|
end
|
|
|
|
local function test(s)
|
|
local trimmed = s:match("^%s*(.-)$")
|
|
local parsed, r = parse(s)
|
|
if not parsed then
|
|
print(r)
|
|
elseif r:match(".") then
|
|
print(("unexpected %q at end of expression"):format(r))
|
|
else
|
|
print(require("inspect")(parsed))
|
|
end
|
|
end
|
|
|
|
test("((str (Hel\\)lo world) sa mère) (lol))")
|
|
|
|
test("\\lol\\ wut")
|
|
|
|
test("()")
|