Как я могу сигнализировать об ошибках синтаксического анализа с LPeg?

Я пишу парсер на основе 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 когда есть недопустимый побег.

1 ответ

Сам 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 это оставшийся необработанный ввод, который можно использовать для вычисления позиции ошибки по ее длине). При этом вы можете распечатать соответствующее сообщение об ошибке на основе метки. Для более сложных грамматик вы, вероятно, захотите более систематический способ отображения меток ошибок и сообщений. Пожалуйста, проверьте документацию, найденную в файле readme репозитория LPegLabel, чтобы увидеть примеры того, как это можно сделать.

LPegLabel также позволяет вам поймать метки в грамматике по пути (через выбор меток); это полезно для реализации таких вещей, как восстановление после ошибок. Для получения дополнительной информации о помеченных сбоях и примерах, пожалуйста, проверьте документацию.

Другие вопросы по тегам