Analisador de descida recursiva com retorno para a gramática

10

Alguém pode me esclarecer por que um analisador de descida recursiva com retrocesso que tenta as produções e S a a (nessa ordem) não reconhece o idioma formado pela gramática S a S a | a a .SaSaSaaSaSa | aa

Parece analisar apenas as palavras do idioma .{a2n | n1}

Gerei esse analisador usando este ABNF Parser Generator com a regra de produção S = "a" S "a" / "aa"e o analisador não reconhece a palavra aaaaaa, por exemplo.

Eu esperaria que ele usasse a produção até que a concatenação dos nós terminais da árvore de análise a partir da esquerda inicie com 7 's, e suba a árvore de análise escolhendo a produção S a a em vez disso até que a árvore pareça como isso:SaSaaSaa

   S 
 / | \
a  S  a
 / | \
a  S  a
  / \
 a   a
meribold
fonte
2
Por que você acha que não pode analisar esta palavra?
Yuval Filmus
@ Yuval, acho que deveria analisá-lo, então devo estar perdendo alguma coisa.
Meribold #
Ah, agora a pergunta faz mais sentido; obrigado pela edição! Se o que você escreve é ​​verdadeiro (não verifiquei), o gerador parece ter um bug. (Ou não é especificado para a sua gramática, eu acho que isso é improvável já que a gramática é fundamental e inequívoca.
Raphael
@ Rafael, eu editei a pergunta novamente (espero sem mudar o significado). Na verdade, tenho a tarefa de explicar por que esse analisador não reconhece a palavra aaaaaa.
meribold
Onde você conseguiu essa árvore? Eu não recebo muito desse gerador de analisador ABNF. A árvore que você dá não faz muito sentido. Mas a string aaaaaadeve analisar e não. Mas aaaaanalisa. Aparentemente, você está certo sobre os poderes de 2. A coisa deve estar com erros. ele analisa apenas aacom S = "aa" / "a" [S] "a". Você pode rastrear o que o analisador faz?
babou

Respostas:

6

Isso não é muita resposta, mas as árvores de análise não se encaixam nos comentários normais.

SaSa | aaaaaaaa

Mas a árvore de análise tem a seguinte forma:

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

ou se você preferir esta apresentação, com os terminais em linhas diferentes

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

Eu verifiquei que o gerador do analisador ABNF parece não funcionar, mas não sei como rastrear o que ele faz.

{a2n | n1}

É um pouco surpreendente ter um site tão elaborado em torno de um analisador de buggy, que além disso usa uma técnica de análise totalmente desinteressante.


Depois de uma análise mais aprofundada:

Eu acho que encontrei uma fonte do problema. Os colchetes significam opcional .

Portanto, sua gramática deve ser escrita S = "a" S "a" / "aa" ou S = "a" [S] "a". Então parece funcionar corretamente.

Mas o sistema aparentemente está perdido ao ter o dobro da mesma regra de formas diferentes. Não sei por quê.

Não encontrei uma página explicando esses problemas sintáticos para especificar a gramática.

Eu ainda considero esse carrinho.

babou
fonte
11
Ai. Sim. Não sei o que estava pensando quando escrevi essa árvore de análise. Vou editar minha pergunta e colar a sua.
Meribold #
Eu fiz encontrar uma outra descida recursiva, recuando gerador de analisador com uma linha de demonstração aqui e ele mostra o mesmo comportamento com esta regra:S ::= 'a'<S>'a' | 'a''a'
meribold
Ele ainda não é analisado aaaaaadurante o uso S = "a" S "a" / "aa", mas você parece estar certo quanto aos colchetes.
Meribold
Não vejo o ponto de explorar descida recursiva, analisando o backtracking.
Babou
você está certo sobre S = "a" S "a" / "aa"... testei muito rápido e cliquei em gerar em vez de analisar.
babou
3

s1()SaSatrues()s2()Saa

Considere analisar a palavra aaaaaanovamente. Em um ponto, a árvore de análise terá a seguinte aparência:

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

s()trueSSaa

   S 
 / | \
a  S  a
  / \
 a   a

Eu costumo considerar isso um problema com minha implementação e não com o retrocesso de analisadores de descida recursiva em geral.

#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";
}
meribold
fonte
2

É um recurso, não um bug

Veja de perto quando e onde ocorre o retorno:

     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

O ponto crucial aqui é que o analisador retorna atrás da posição, onde o último caractere correspondente foi encontrado. É por isso que "salta" da árvore 11 com l = aaaaaaaa para a 12ª árvore com l = aaaa usando S -> aa em l [7].

Sebbas
fonte
Finalmente tenho tempo para editá-lo! ;)
Sebbas 20/02