2014-10-01 18 views
5

In un normale PEG (parsing espressione grammaticale) è una grammatica valida:Creazione di un ricorsiva modello LPeg

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

Tuttavia, se provo a scrivere questo utilizzando LPeg la natura ricorsiva di tale regola fallisce:

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) 

Anche se in questo semplice esempio potrei riscrivere la regola per non utilizzare la ricorsione, ho alcune grammatiche esistenti che preferirei non riscrivere.

Come posso scrivere una regola autoreferenziale in LPeg?

risposta

5

Utilizzare un grammar.

Con l'uso di variabili Lua, è possibile definire i modelli in modo incrementale, con ogni nuovo modello utilizzando quelli precedentemente definiti. Tuttavia, questa tecnica non consente la definizione di schemi ricorsivi. Per schemi ricorsivi, abbiamo bisogno di grammatiche reali.

LPeg rappresenta grammatiche con tabelle, in cui ogni voce è una regola.

La chiamata lpeg.V (v) crea un modello che rappresenta il non terminale (o variabile) con indice v in una grammatica. Poiché la grammatica non esiste ancora quando viene valutata questa funzione, il risultato è un riferimento aperto alla rispettiva regola.

Una tabella viene fissata quando viene convertita in un modello (chiamando lpeg.P o utilizzando il modello in cui è previsto un modello). Quindi ogni riferimento aperto creato da lpeg.V (v) viene corretto per fare riferimento alla regola indicizzata da v nella tabella.

Quando una tabella è fissa, il risultato è un modello che corrisponde alla sua regola iniziale. La voce con indice 1 nella tabella definisce la sua regola iniziale. Se quella voce è una stringa, si presume che sia il nome della regola iniziale. In caso contrario, LPeg presume che la voce 1 stessa sia la regola iniziale.

A titolo di esempio, il seguente grammatica corrisponde stringhe di A e B è che hanno lo stesso numero di A e B è:

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 

E 'equivalente al seguente grammatica in notazione standard PEG:

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

Sicuramente la risposta giusta; ma posso davvero accettare una risposta che è una copia/incolla dal manuale, senza nemmeno un tentativo di formattazione? : p – Phrogz

+0

@Phrogz Dovresti aver davvero fatto una domanda direttamente indirizzata da quel manuale?=) E questo è "formattato", prova ad incollare quella citazione dal manuale direttamente in una risposta e poi blocca semplicemente citando tutto ciò che non ottieni sopra. =) –

+0

Non avrei dovuto, ma non ho riconosciuto questa parte del manuale come applicazione in un primo momento. Il mio ': p' avrebbe dovuto essere un':) '. Hai fatto bene, grazie. – Phrogz

0

so che questa è una risposta in ritardo, ma qui è un idea di come back-riferimento una regola

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') } 

Fondamentalmente una grammatica primitiva viene passata a lpeg.P (la grammatica è solo una tabella glorificata) che fa riferimento alla prima regola in base al numero anziché al nome, lpeg.V(1).

L'esempio aggiunge una semplice cattura lpeg.C sul terminale number e raccoglie tutti questi risultati nella tabella locale t per ulteriore utilizzo. (Si noti che non viene utilizzato lo lpeg.Ct che non è un grosso problema, ma ... parte del campione credo.)