Parser ricorsivo di discesa con backtracking per la grammatica


10

Qualcuno può illuminarmi perché un parser di discesa ricorsivo con backtracking che prova le produzioni e (in quell'ordine) non riconosce la lingua formata dalla grammatica .S a a S a S a | a aSun'Sun'Saun'Sun'Sun' | un'un'

Sembra analizzare solo le parole dalla lingua .{un'2n | n1}

Ho generato un simile parser usando questo generatore di parser ABNF con la regola di produzione S = "a" S "a" / "aa"e il parser non riconosce la parola aaaaaa, per esempio.

Mi aspetto che utilizzi la produzione fino a quando la concatenazione dei nodi terminali dell'albero di analisi da sinistra inizia con 7 ', quindi salgo l'albero di analisi scegliendo la produzione invece fino a quando l'albero sembra Questo:S a aSun'Sun'aSaun'

   S 
 / | \
a  S  a
 / | \
a  S  a
  / \
 a   a

2
Perché pensi che non possa analizzare questa parola?
Yuval Filmus,

@Yuval, penso che dovrebbe analizzarlo, quindi mi devo perdere qualcosa.
Meribold,

Ah, ora la domanda ha più senso; grazie per la modifica! Se ciò che scrivi è vero (non ho verificato) il generatore sembra avere un bug. (O non è specificato per la tua grammatica; penso che sia improbabile poiché la grammatica è elementare e inequivocabile.
Raffaello

@Raphael, ho modificato di nuovo la domanda (si spera senza cambiare il significato). In realtà sono incaricato di spiegare perché un tale parser non riconosce la parola aaaaaa.
Meribold

Dove hai preso quell'albero. Non ottengo molto da quel generatore di parser ABNF. L'albero che dai non ha molto senso. Ma la stringa aaaaaadovrebbe analizzare e non lo fa. Ma aaaaanalizza. Apparentemente hai ragione sui poteri di 2. La cosa deve essere infastidita. analizza solo aacon S = "aa" / "a" [S] "a". Riesci a rintracciare cosa fa il parser?
babou,

Risposte:


6

Questa non è una grande risposta, ma gli alberi di analisi non si adattano ai normali commenti.

La tua grammatica dovrebbe analizzare la stringa a a a a a a .SaSa | aaaaaaaa

Ma l'albero di analisi ha la seguente forma:

      S 
     /|\
    / S \
   / /|\ \
  / / S \ \
 / / / \ \ \
a a a   a a a

o se preferisci questa presentazione, con i terminali su linee diverse

     S 
   / | \
  a  S  a
   / | \
  a  S  a
    / \
   a   a

Ho verificato che il generatore di parser ABNF non sembra funzionare, ma non so come rintracciarlo.

Sembra in effetti ricongiungere il set che non è quello che definisce la grammatica.{a2n | n1}

È un po 'sorprendente avere un sito così elaborato attorno a un parser buggy, che utilizza inoltre una tecnica di analisi totalmente poco interessante.


Dopo un'ulteriore occhiata:

Penso di aver trovato una fonte del problema. Le parentesi quadre significano facoltativo .

Quindi la tua grammatica dovrebbe essere scritta S = "a" S "a" / "aa" o S = "a" [S] "a". Quindi sembra funzionare correttamente.

Ma il sistema sembra essere perso quando si ha il doppio della stessa regola in forme diverse. Non sono sicuro del perché.

Non ho trovato una pagina che spieghi questi problemi sintattici per specificare la grammatica.

Considero ancora quel passeggino.


1
Ahia. Si. Non so cosa stavo pensando quando ho scritto quell'albero di analisi. Modificherò la mia domanda e incollerò la tua.
Meribold,

Ho trovato un'altra discesa ricorsiva, un generatore di parser con backtracking con una demo online qui e mostra lo stesso comportamento con questa regola:S ::= 'a'<S>'a' | 'a''a'
meribold,

Non viene ancora analizzato aaaaaadurante l'utilizzo S = "a" S "a" / "aa", ma sembra che tu abbia ragione tra parentesi.
meribold

Non vedo il punto di esplorare la discesa ricorsiva, il parser di backtracking.
babou,

hai ragione su S = "a" S "a" / "aa"... Ho testato troppo in fretta e ho cliccato su generate invece di analizzare.
babou,

3

s1()SaSatrues()s2()Saa

Valuta di analizzare aaaaaanuovamente la parola . Ad un certo punto, l'albero di analisi sarà simile al seguente:

   S 
 / | \
a  S  a
 / | \
a  S  a    <--
 / | \
a  S  a
  / \
 a   a

s()trueSSaun'

   S 
 / | \
a  S  a
  / \
 a   a

Tendo a considerare questo un problema con la mia implementazione e non con i parser di discesa ricorsivi di backtracking in generale.

#include <iostream>

char* next;    
bool term(char token) {
    if (*next != '\0')
        return *next++ == token;
    else
        return false;
}

bool s();    
bool s1() {
    return term('a') && s() && term('a');
}    
bool s2() {
    return term('a') && term('a');
}    
bool s() {
    auto save = next;
    return s1() or (next = save, s2());
}    

int main(int argc, char* argv[]) {
    next = "aaaaaa";
    if (s() && *next == '\0') {
        std::cout << "match";
    }
    else
        std::cout << "no match";
}

2

È una caratteristica non un bug

Dai un'occhiata da vicino quando e dove si verifica il backtracking:

     1.           2.          3.          4.          5.          6.          7.          8.          9.          10.         11.         12.

     S            S           S           S           S           S           S           S           S           S           S           S      
   / | \        / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \
  a  S  a      a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a
                / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       /   \
               a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a     a
                            / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \       / | \
                           a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a
                                        / | \       / | \       / | \       / | \       / | \       / | \       / | \       /   \
                                       a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a     a
                                                    / | \       / | \       / | \       / | \       / | \       /   \
                                                   a  S  a     a  S  a     a  S  a     a  S  a     a  S  a     a     a
                                                                / | \       / | \       / | \       /   \   
                                                               a  S  a     a  S  a     a  S  a     a     a
                                                                            / | \       /   \
                                                                           a  S  a     a     a



w[] = 'aaaaaa'  //input
l[] = ''        //current tree leafs


 1. tree:   The parser starts with the start symbol S and tries first alternative S->aSa:       Result: w[0]  = l[0]     w = aaaaaa    l = aSa
 |          -- S->aSa works                                                                         | |     | | 
 6. tree:   The parser matches a after a:                                                       Result: w[6]  = l[6]     w = aaaaaa    l = aaaaaaSaaaaaa
 7. tree:   The parser tries S->aSa again but there is no match!                                Result: w[7] != l[7]     w = aaaaaa    l = aaaaaaaSaaaaaaa 
 8. tree:   The parser tries S->aa but there is still no match!                                 Result: w[7] != l[7]     w = aaaaaa    l = aaaaaaaaaaaaaa
 9. tree:   Backtracking after the last symbol that matched => Backtracking at l[7]             Result: w[7] != l[7]     w = aaaaaa    l = aaaaaaaaaaaa
10. tree:   Backtracking after the last symbol that matched => Backtracking at l[7]             Result: w[7] != l[7]     w = aaaaaa    l = aaaaaaaaaa
11. tree:   Backtracking after the last symbol that matched => Backtracking at l[7]             Result: w[7] != l[7]     w = aaaaaa    l = aaaaaaaa
12. tree:   Backtracking after the last symbol that matched => Backtracking at l[7]             Result: w[7] != l[7]     w = aaaaaa    l = aaaa

Il punto cruciale qui è che il parser torna indietro dopo la posizione, dove è stato trovato l'ultimo carattere corrispondente. Ecco perché "salta" dall'albero 11 con l = aaaaaaaa al dodicesimo albero con l = aaaa usando S -> aa at l [7].


Finalmente ho tempo di modificarlo! ;)
Sebbas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.