2

我正在编写一个基于 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

4

1 回答 1

0

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(...)将返回(是剩余的未处理输入,您可以使用它通过其长度计算错误位置)。有了这个,您可以根据标签打印出适当的错误消息。对于更复杂的语法,您可能需要一种更系统的方式来映射错误标签和消息。请查看 LPegLabel 存储库的自述文件中的文档,以查看如何执行此操作的示例。nil, label, suffixsuffix

LPegLabel 还允许您顺便捕获语法中的标签(通过标签选择);这对于实现诸如错误恢复之类的事情很有用。有关标记故障和示例的更多信息,请查看文档。

于 2016-09-03T05:30:41.057 回答