it-swarm.it

Qual è la differenza tra analisi LR (0) e SLR?

Sto lavorando ai concetti dei miei compilatori, tuttavia sono un po 'confuso ... Google non mi ha portato da nessuna parte per una risposta definitiva.

I parser SLR e LR (0) sono la stessa cosa? In caso contrario, qual è la differenza?

71
Nitish Upreti

Entrambi i parser LR (0) e SLR (1) sono parser bottom-up, direzionali, predittivi. Ciò significa che

  • I parser tentano di applicare le produzioni al contrario per ridurre la frase di input al simbolo iniziale ( bottom-up)
  • I parser eseguono la scansione dell'input da sinistra a destra ( direzionale)
  • I parser tentano di prevedere quali riduzioni applicare senza necessariamente vedere tutto l'input ( predittivo)

Entrambi LR (0) e SLR (1) sono shift/riduci parser , il che significa che elaborano i token del flusso di input posizionandoli su uno stack e in ciascun punto si sposta un token spingendolo sulla pila o riducendo una sequenza di terminali e non terminali in cima allo stack di nuovo a un simbolo non finale. Si può dimostrare che qualsiasi grammatica può essere analizzata dal basso verso l'alto usando un parser shift/riduci, ma quel parser potrebbe non essere deterministico. Cioè, il parser potrebbe dover "indovinare" se applicare uno spostamento o una riduzione, e potrebbe finire per dover tornare indietro per rendersi conto che ha fatto la scelta sbagliata. Non importa quanto sia potente un parser deterministico di spostamento/riduzione che costruisci, non sarà mai in grado di analizzare tutte le grammatiche.

Quando un parser deterministico di spostamento/riduzione viene utilizzato per analizzare una grammatica che non è in grado di gestire, si traduce in spostamento/riduzione dei conflitti o ridurre/ridurre i conflitti , in cui il parser può entrare in uno stato in cui non può dire quale azione intraprendere. In un conflitto di spostamento/riduzione, non può dire se deve aggiungere un altro simbolo alla pila o eseguire una riduzione sui simboli in cima alla pila. In un conflitto di riduzione/riduzione, il parser sa che deve sostituire i simboli principali dello stack con alcuni non terminali, ma non può dire quale riduzione utilizzare.

Mi scuso se questa è una lunga esposizione, ma abbiamo bisogno di questo per essere in grado di affrontare la differenza tra analisi LR (0) e SLR (1). Un parser LR (0) è un parser di spostamento/riduzione che utilizza zero token di lookahead per determinare quale azione intraprendere (da qui lo 0). Ciò significa che in qualsiasi configurazione del parser, il parser deve avere un'azione inequivocabile da scegliere: sposta un simbolo specifico o applica una riduzione specifica. Se ci sono mai due o più scelte da fare, il parser fallisce e diciamo che la grammatica non è LR (0).

Ricordiamo che i due possibili conflitti di LR sono spostamento/riduzione e riduzione/riduzione. In entrambi questi casi, ci sono almeno due azioni che l'automa LR (0) potrebbe intraprendere e non può dire quale di esse usare. Poiché almeno una delle azioni in conflitto è una riduzione, una ragionevole linea di attacco sarebbe quella di provare a fare in modo che il parser stia più attento quando esegue una riduzione particolare. Più specificamente, supponiamo che al parser sia permesso guardare il prossimo token dell'input per determinare se dovrebbe spostarsi o ridursi. Se permettiamo al parser di ridurre solo quando "ha senso" farlo (per alcune definizioni di "ha senso"), allora potremmo essere in grado di eliminare il conflitto facendo in modo che l'automa scelga specificamente di spostare o ridurre in un passaggio particolare.

In SLR (1) ("LR semplificato (1)"), il parser può guardare un token di lookahead quando decide se deve spostarsi o ridursi. In particolare, quando il parser vuole provare a ridurre qualcosa della forma A → w (per A non terminale e stringa w), guarda il token successivo dell'input. Se quel token potrebbe apparire legalmente dopo la A non terminale in qualche derivazione, il parser si riduce. Altrimenti no. L'intuizione qui è che in alcuni casi non ha senso tentare una riduzione, perché dati i token che abbiamo visto finora e il token imminente, non è possibile che la riduzione possa mai essere corretta.

L'unica differenza tra LR (0) e SLR (1) è questa capacità aggiuntiva di aiutare a decidere quale azione intraprendere in caso di conflitti. Per questo motivo, qualsiasi grammatica che può essere analizzata da un parser LR (0) può essere analizzata da un parser SLR (1). Tuttavia, i parser SLR (1) possono analizzare un numero maggiore di grammatiche rispetto a LR (0).

In pratica, tuttavia, SLR (1) è ancora un metodo di analisi abbastanza debole. Più comunemente, vedrai i parser LALR (1) ("Lookahead LR (1)") in uso. Funzionano anche loro cercando di risolvere i conflitti in un parser LR (0), ma le regole che usano per risolvere i conflitti sono molto più precise di quelle usate in SLR (1), e di conseguenza un numero molto maggiore di grammatiche sono LALR (1) di quanto lo siano le reflex (1). Per essere un po 'più specifici, i parser SLR (1) cercano di risolvere i conflitti osservando la struttura della grammatica per apprendere ulteriori informazioni su quando cambiare e quando ridurre. I parser LALR (1) guardano sia la grammatica che il parser LR (0) per ottenere informazioni ancora più specifiche su quando spostare e quando ridurre. Poiché LALR (1) è in grado di esaminare la struttura del parser LR (0), può identificare più precisamente quando determinati conflitti sono spuri. Le utility Linux yacc e bison, per impostazione predefinita, producono parser LALR (1).

Storicamente, i parser LALR (1) erano in genere costruiti con un metodo diverso che si basava sul parser LR (1) molto più potente, quindi vedrai spesso LALR (1) descritto in quel modo. Per capirlo, dobbiamo parlare dei parser LR (1). In un parser LR (0), il parser funziona tenendo traccia di dove potrebbe trovarsi nel mezzo di una produzione. Una volta scoperto che ha raggiunto la fine di una produzione, sa provare a ridurre. Tuttavia, il parser potrebbe non essere in grado di dire se si trova alla fine di una produzione e al centro di un'altra, il che porta a uno spostamento/riduzione del conflitto o in quale delle due diverse produzioni ha raggiunto la fine di (una riduzione/ridurre i conflitti). In LR (0), questo porta immediatamente a un conflitto e il parser fallisce. In SLR (1) o LALR (1), il parser prende quindi la decisione di spostare o ridurre in base al token successivo di lookahead.

In un parser LR (1), il parser tiene traccia di ulteriori informazioni mentre funziona. Oltre a tenere traccia di quale produzione il parser ritiene sia in uso, tiene traccia di quali possibili token potrebbero apparire dopo che la produzione è stata completata. Poiché il parser tiene traccia di queste informazioni in ogni fase e non solo quando deve prendere la decisione, il parser LR (1) è sostanzialmente più potente e preciso di qualsiasi LR (0), SLR (1) o Analizzatori LALR (1) di cui abbiamo parlato finora. LR (1) è una tecnica di analisi estremamente potente e può essere mostrato usando una matematica complicata che qualsiasi linguaggio che potrebbe essere analizzato in modo deterministico da qualsiasi spostamento/ridurre parser ha un po 'di grammatica che potrebbe essere analizzata con un automa LR (1). (Si noti che ciò non significa che tutte le grammatiche che possono essere analizzate in modo deterministico siano LR (1); ciò indica solo che un linguaggio che potrebbe essere analizzato in modo deterministico ha un po 'di grammatica LR (1)). Tuttavia, questo potere ha un prezzo e un parser LR (1) generato può richiedere così tante informazioni per funzionare che non può essere utilizzato nella pratica. Un parser LR (1) per un vero linguaggio di programmazione, ad esempio, potrebbe richiedere da decine a centinaia di megabyte di informazioni aggiuntive per funzionare correttamente. Per questo motivo, LR (1) in genere non viene utilizzato nella pratica e al suo posto vengono utilizzati parser più deboli come LALR (1) o SLR (1).

Più recentemente, un nuovo algoritmo di analisi chiamato GLR (0) ("Generalized LR (0)") ha guadagnato popolarità. Invece di provare a risolvere i conflitti che compaiono in un parser LR (0), il parser GLR (0) funziona invece provando tutte le possibili opzioni in parallelo. Utilizzando alcuni trucchi intelligenti, questo può essere fatto per funzionare in modo molto efficiente per molte grammatiche. Inoltre, GLR (0) può analizzare qualsiasi grammatica senza contesto, anche grammatiche che non possono essere analizzate da un parser LR (k) per qualsiasi k. Anche altri parser sono in grado di farlo (ad esempio, il parser Earley o un parser CYK), sebbene il GLR (0) tenda ad essere più veloce in pratica.

Se sei interessato a saperne di più, durante quest'estate ho tenuto un corso introduttivo sui compilatori e ho trascorso poco meno di due settimane a parlare delle tecniche di analisi. Se desideri avere un'introduzione più rigorosa a LR (0), SLR (1) e una serie di altre potenti tecniche di analisi, potresti apprezzare le diapositive delle lezioni e i compiti a casa sull'analisi. Tutti i materiali del corso sono disponibili qui sul mio sito personale .

Spero che sia di aiuto!

224
templatetypedef

Questo è quello che ho imparato. Di solito il parser LR (0) può avere ambiguità, ovvero una casella della tabella (derivata per la creazione del parser) può avere più valori (o) per dirla meglio: il parser porta a due stati finali con lo stesso input. Quindi il parser SLR viene creato per rimuovere questa ambiguità. Per costruirlo, trova tutte le produzioni che portano a stati di goto, trova il seguito per il simbolo di produzione sul lato sinistro e includi solo quegli stati di goto che sono presenti nel seguito. Questo inturn significa che non includi una produzione che non è possibile usando la grammatica originale (perché questo stato non è nel set seguente)

1
Yeswantth