我正在编写一个基于LPeg的解析器。如何使解析错误返回nil, errmsg
?
我知道我可以使用error()
,但据我所知,这会产生一个正常的错误,而不是nil, errmsg
。
代码很长,但相关部分是:
local eof = lpeg.P(-1)
local nl = (lpeg.P "r")^-1 * lpeg.P "n" + lpeg.P "\n" + eof -- r for winblows compat
local nlnoeof = (lpeg.P "r")^-1 * lpeg.P "n" + lpeg.P "\n"
local ws = lpeg.S(" t")
local inlineComment = lpeg.P("`") * (1 - (lpeg.S("`") + nl * nl)) ^ 0 * lpeg.P("`")
local wsc = ws + inlineComment -- comments count as whitespace
local backslashEscaped
= lpeg.P("\ ") / " " -- escaped spaces
+ lpeg.P("\\") / "\" -- escaped escape character
+ lpeg.P("\#") / "#"
+ lpeg.P("\>") / ">"
+ lpeg.P("\`") / "`"
+ lpeg.P("\n") -- \n newlines count as backslash escaped
+ lpeg.P("\") * lpeg.P(function(_, i)
error("Unknown backslash escape at position " .. i) -- this error() is what I wanna get rid of.
end)
local Line = lpeg.C((wsc + (backslashEscaped + 1 - nl))^0) / function(x) return x end * nl * lpeg.Cp()
当存在无效转义时,我希望Line:match(...)
返回nil, errmsg
。
LPeg本身没有提供特定的功能来帮助您报告错误。一个快速解决你的问题的方法是打一个受保护的电话(pcall)来匹配如下:
local function parse(text)
local ok, result = pcall(function () return Line:match(text) end)
if ok then
return result
else
-- `result` will contain the error thrown. If it is a string
-- Lua will add additional information to it (filename and line number).
-- If you do not want this, throw a table instead like `{ msg = "error" }`
-- and access the message using `result.msg`
return nil, result
end
end
然而,这也会捕获任何其他错误,这可能是您不想要的。更好的解决方案是使用LPegLabel。LPegLabel是LPeg的扩展,增加了对标记故障的支持。只需将require"lpeg"
替换为require"lpeglabel"
,然后使用lpeg.T(L)
来抛出标签,其中L
是1-255之间的整数(0用于常规PEG故障)。
local unknown_escape = 1
local backslashEscaped = ... + lpeg.P("\") * lpeg.T(unknown_escape)
现在,如果抛出了一个标签,Line:match(...)
将返回nil, label, suffix
(suffix
是剩余的未处理输入,您可以使用它来通过其长度计算错误位置)。这样,您就可以根据标签打印出相应的错误消息。对于更复杂的语法,您可能想要一种更系统的方法来映射错误标签和消息。请查看LPegLabel存储库的自述文件中的文档,查看如何做到这一点的示例。
LPegLabel还允许您顺便捕捉语法中的标签(通过标签选择);这对于实现诸如错误恢复之类的功能非常有用。有关标记故障和示例的更多信息,请查看文档。