2016-07-29 21 views
1

Ich schreibe einen LPeg-basierten Parser. Wie kann ich es so machen, dass ein Parsing-Fehler nil, errmsg zurückgibt?Wie kann ich Parsing-Fehler mit LPeg signalisieren?

Ich weiß, ich kann error() verwenden, aber soweit ich weiß, dass ein normaler Fehler erstellt, nicht nil, errmsg.

Der Code ist pretty long, aber der relevante Teil ist dies:

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() 

ich Line:match(...) wollen nil, errmsg zurückzukehren, wenn eine ungültige Flucht ist.

+0

was versuchen Sie zu erreichen? Ist es ein minimales Beispiel? Hast du versucht zurück zu kommen? – Jakuje

+0

Die 'error()' erzeugt einen Fehler anstelle von 'nil, errmsg'. Ich will 'Line: match()' und 'Data: match()', um 'nil, errmsg' zurückzugeben, wenn ein Fehler bei Backslash-Escapes auftritt. – SoniEx2

+0

@Jakuje Eigentlich brauche ich nur 'Line: match()', um 'nil, errmsg' zurückzugeben, wenn ein ungültiges Escape vorliegt. – SoniEx2

Antwort

0

LPeg selbst bietet keine spezifischen Funktionen, um Sie bei der Fehlerberichterstattung zu unterstützen. Eine schnelle Lösung für Ihr Problem wäre ein protected call (pcall) zu machen wie dies zum Spiel:

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 

Dies wird jedoch auch andere Fehler fangen, die Sie wahrscheinlich nicht wollen. Eine bessere Lösung wäre stattdessen LPegLabel zu verwenden. LPegLabel ist eine Erweiterung von LPeg, die Unterstützung für markierte Fehler hinzufügt. Ersetzen Sie einfach require"lpeg" durch require"lpeglabel" und verwenden Sie dann lpeg.T(L), um Etiketten zu werfen, wobei L eine ganze Zahl von 1-255 ist (0 wird für normale PEG-Fehler verwendet).

local unknown_escape = 1 
local backslashEscaped = ... + lpeg.P("\\") * lpeg.T(unknown_escape) 

Jetzt wird Line:match(...)nil, label, suffix zurück, wenn ein Etikett geworfen ist (suffix die verbleibende nicht verarbeitete Eingabe, die Sie für die Fehlerposition zu berechnen, über seine Länge verwenden). Damit können Sie die entsprechende Fehlermeldung basierend auf dem Etikett ausdrucken. Für komplexere Grammatiken würden Sie wahrscheinlich eine systematischere Art der Abbildung der Fehlerbeschriftungen und -nachrichten wünschen. Überprüfen Sie die Dokumentation in der Readme-Datei des LPegLabel-Repositorys, um Beispiele dafür zu sehen, wie dies möglich ist.

LPegLabel ermöglicht Ihnen auch, die Etiketten in der Grammatik übrigens zu fangen (über beschriftete Wahl); Dies ist nützlich, um Dinge wie die Fehlerbehebung zu implementieren. Weitere Informationen zu markierten Fehlern und Beispielen finden Sie in der Dokumentation.

+0

Ist es noch LPeg, wenn es nicht LPeg ist? Ich bin mir ziemlich sicher, dass ich LPegLabel nicht in Sachen verwenden kann, die nur mit Standard LPeg eingebaut sind. – SoniEx2

+0

@ SoniEx2 LPegLabel ist nicht LPeg, aber es kann als Drop-In-Ersatz dafür verwendet werden. Der erste Teil meiner Antwort (mit 'pcall') ist nur Standard-Lua, also können Sie das mit LPeg verwenden. – undecidabot

+0

Holen Sie Luvit, LPegLabel zu bündeln, und ich könnte den zweiten Teil dieser Antwort verwenden. (Können Sie LPegLabel nicht umschreiben, um es auf normales LPeg zu schreiben?) – SoniEx2