2014-10-01 8 views
5

In einem normalen PEG (Packrat Parser) ist dies eine gültige Grammatik:ein rekursive LPeg Muster erstellen

values <- number (comma values)* 
number <- [0-9]+ 
comma <- ',' 

aber wenn ich versuche, dies mit LPeg die rekursive Natur dieser Regel zu schreiben, schlägt fehl:

local lpeg = require'lpeg' 

local comma = lpeg.P(',') 
local number = lpeg.R('09')^1 
local values = number * (comma * values)^-1 
--> bad argument #2 to '?' (lpeg-pattern expected, got nil) 

Obwohl ich in diesem einfachen Beispiel die Regel umschreiben könnte, um keine Rekursion zu verwenden, habe ich einige existierende Grammatiken, die ich lieber nicht umschreiben würde.

Wie kann ich eine Selbstreferenzierungsregel in LPeg schreiben?

Antwort

5

Verwenden Sie eine grammar.

Mit der Verwendung von Lua-Variablen ist es möglich, Muster inkrementell zu definieren, wobei jedes neue Muster zuvor definierte Muster verwendet. Diese Technik erlaubt jedoch nicht die Definition von rekursiven Mustern. Für rekursive Muster benötigen wir echte Grammatiken.

LPeg stellt Grammatiken mit Tabellen dar, wobei jeder Eintrag eine Regel ist.

Der Aufruf lpeg.V (v) erstellt ein Muster, das das Nonterminal (oder die Variable) mit Index v in einer Grammatik darstellt. Da die Grammatik bei der Auswertung dieser Funktion noch nicht existiert, ist das Ergebnis ein offener Verweis auf die jeweilige Regel.

Eine Tabelle wird fixiert, wenn sie in ein Muster konvertiert wird (entweder durch Aufrufen von lpeg.P oder durch Verwendung eines Musters, in dem ein Muster erwartet wird). Dann wird jede durch lpeg.V (v) erzeugte offene Referenz so korrigiert, dass sie sich auf die in der Tabelle durch v indizierte Regel bezieht.

Wenn eine Tabelle fixiert ist, ist das Ergebnis ein Muster, das der ursprünglichen Regel entspricht. Der Eintrag mit Index 1 in der Tabelle definiert seine Anfangsregel. Wenn dieser Eintrag eine Zeichenfolge ist, wird angenommen, dass dies der Name der ursprünglichen Regel ist. Andernfalls nimmt LPeg an, dass der Eintrag 1 selbst die anfängliche Regel ist.

Als Beispiel wird die folgende Grammatik entspricht Saiten einer und bs, die die gleiche Anzahl von a und bs haben:

equalcount = lpeg.P{ 
    "S"; -- initial rule name 
    S = "a" * lpeg.V"B" + "b" * lpeg.V"A" + "", 
    A = "a" * lpeg.V"S" + "b" * lpeg.V"A" * lpeg.V"A", 
    B = "b" * lpeg.V"S" + "a" * lpeg.V"B" * lpeg.V"B", 
} * -1 

Es entspricht der folgenden Grammatik in Standard-PEG-Notation:

S <- 'a' B/'b' A/'' 
    A <- 'a' S/'b' A A 
    B <- 'b' S/'a' B B 
+1

Definitiv die richtige Antwort; Aber kann ich wirklich eine Antwort akzeptieren, die ein Kopieren/Einfügen aus dem Handbuch ist, ohne auch nur einen Formatierungsversuch durchzuführen? : p – Phrogz

+0

@Phrogz Sollten Sie wirklich eine Frage gestellt haben, die direkt in diesem Handbuch behandelt wird?=) Und das ist "formatiert", versuche, dieses Zitat aus dem Handbuch direkt in eine Antwort einzufügen und blockiere dann einfach alles, was du nicht erhältst. =) –

+0

Ich hätte das nicht, aber ich habe diesen Teil des Handbuchs nicht als zuerst anerkennend erkannt. Mein ': p' hätte ein':) 'sein sollen. Du hast es gut gemacht, danke. – Phrogz

0

ich weiß, dass dies eine späte Antwort, aber hier ist eine Idee, wie

local comma = lpeg.P(',') 
local number = lpeg.R('09')^1 
local values = lpeg.P{ lpeg.C(number) * (comma * lpeg.V(1))^-1 } 

local t = { values:match('1,10,20,301') } 
eine Regel-Referenz

Grundsätzlich wird eine primitive Grammatik an lpeg.P übergeben (Grammatik ist nur eine verherrlichte Tabelle), die die erste Regel durch die Nummer anstelle des Namens referenziert, d. H. lpeg.V(1).

Das Beispiel fügt einfach eine einfache lpeg.C Erfassung am number Terminal hinzu und sammelt alle diese Ergebnisse in der lokalen Tabelle t zur weiteren Verwendung. (Beachten Sie, dass keine lpeg.Ct verwendet wird, die keine große Sache ist, aber immer noch ... Teil der Probe, denke ich.)