【问题标题】:How can I signal parsing errors with LPeg?如何使用 LPeg 发出解析错误信号?
【发布时间】:2016-12-04 12:36:04
【问题描述】:

我正在编写一个基于 LPeg 的解析器。我怎样才能让它解析错误返回nil, errmsg

我知道我可以使用error(),但据我所知,这会产生正常错误,而不是nil, errmsg

代码是pretty long,但相关部分是这样的:

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

【问题讨论】:

  • 你想达到什么目的?这是最小的例子吗?你试过return吗?
  • error() 产生错误而不是 nil, errmsg。如果反斜杠转义出现错误,我希望 Line:match()Data:match() 返回 nil, errmsg
  • @Jakuje 实际上,我只需要Line:match() 在出现无效转义时返回nil, errmsg

标签: error-handling lua lpeg


【解决方案1】:

LPeg 本身不提供帮助您报告错误的特定功能。快速解决您的问题的方法是使 protected call (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, suffixsuffix 是剩余的未处理输入,您可以使用它通过其长度计算错误位置)。有了这个,您可以根据标签打印出适当的错误消息。对于更复杂的语法,您可能需要一种更系统的方式来映射错误标签和消息。请查看 LPegLabel 存储库的自述文件中的文档,以查看如何执行此操作的示例。

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

【讨论】:

  • 如果不是LPeg,它还是LPeg吗?我很确定我不能在仅内置标准 LPeg 的东西中使用 LPegLabel。
  • @SoniEx2 LPegLabel 不是 LPeg,但它可以用作它的替代品。我的答案的第一部分(使用 pcall)只是标准 Lua,因此您可以将其与 LPeg 一起使用。
  • 让 Luvit 捆绑 LPegLabel,我可能会使用这个答案的第二部分。 (你不能重写 LPegLabel 以固定到普通的 LPeg 上吗?)
  • @SoniEx2 我想重写 LPegLabel 以固定到普通 LPeg 上并非不可能,但它的效率会非常低(更不用说笨拙了)。鉴于 LPegLabel 是 LabLua 下的一个项目,我希望有一天 LPegLabel 能被合并到 LPeg 中。
猜你喜欢
  • 1970-01-01
  • 2017-07-17
  • 2018-11-21
  • 2014-03-04
  • 1970-01-01
  • 2013-10-24
  • 1970-01-01
  • 2013-07-26
  • 2018-03-28
相关资源
最近更新 更多