Os analisadores SLR, LALR e LR podem ser implementados usando exatamente o mesmo maquinário baseado em tabelas.
Fundamentalmente, o algoritmo de análise coleta o próximo token de entrada T e consulta o estado atual S (e as tabelas de lookahead, GOTO e redução associadas) para decidir o que fazer:
- SHIFT: Se a tabela atual diz para SHIFT no token T, o par (S, T) é colocado na pilha de análise, o estado é alterado de acordo com o que a tabela GOTO diz para o token atual (por exemplo, GOTO (T) ), outro token de entrada T 'é buscado e o processo se repete
- REDUZIR: Cada estado tem 0, 1 ou muitas reduções possíveis que podem ocorrer no estado. Se o analisador for LR ou LALR, o token é verificado em relação aos conjuntos de antevisão para todas as reduções válidas para o estado. Se o token corresponde a um conjunto de antevisão para uma redução da regra gramatical G = R1 R2 .. Rn, ocorre uma redução e deslocamento da pilha: a ação semântica de G é chamada, a pilha é removida n (de Rn) vezes, o par ( S, G) é colocado na pilha, o novo estado S 'é definido como GOTO (G), e o ciclo se repete com o mesmo token T. Se o analisador for um analisador SLR, há no máximo uma regra de redução para o estado e, portanto, a ação de redução pode ser feita às cegas, sem pesquisar para ver qual redução se aplica. É útil para um analisador SLR de saber se existe éuma redução ou não; isso é fácil de dizer se cada estado registra explicitamente o número de reduções associadas a ele e essa contagem é necessária para as versões L (AL) R na prática de qualquer maneira.
- ERROR: Se nem SHIFT nem REDUCE for possível, um erro de sintaxe é declarado.
Então, se todos eles usam o mesmo maquinário, de que adianta?
O valor alegado em SLR é sua simplicidade na implementação; você não precisa examinar as reduções possíveis verificando os conjuntos de antevisão porque há no máximo um, e esta é a única ação viável se não houver SHIFT saídas do estado. A redução que se aplica pode ser anexada especificamente ao estado, para que a máquina de análise SLR não precise procurá-la. Na prática, os analisadores L (AL) R lidam com um conjunto útil de idiomas maior e é tão pouco trabalho extra para implementar que ninguém implementa SLR, exceto como um exercício acadêmico.
A diferença entre LALR e LR tem a ver com o gerador de tabela. Os geradores de analisador LR controlam todas as reduções possíveis de estados específicos e seu conjunto de lookahead preciso; você termina com estados nos quais cada redução está associada a seu conjunto de lookahead exato a partir de seu contexto esquerdo. Isso tende a construir conjuntos bastante grandes de estados. Os geradores do analisador LALR estão dispostos a combinar estados se as tabelas GOTO e conjuntos de lookhead para reduções forem compatíveis e não entrarem em conflito; isso produz um número consideravelmente menor de estados, ao preço de não ser capaz de distinguir certas sequências de símbolos que LR consegue distinguir. Portanto, os analisadores LR podem analisar um conjunto maior de linguagens do que os analisadores LALR, mas têm tabelas de analisador muito maiores. Na prática, pode-se encontrar gramáticas LALR que estão próximas o suficiente dos idiomas-alvo para que o tamanho da máquina de estado valha a pena otimizar;
Portanto: todos os três usam a mesma máquina. SLR é "fácil" no sentido de que você pode ignorar um pouquinho da máquina, mas simplesmente não vale a pena. LR analisa um conjunto mais amplo de idiomas, mas as tabelas de estado tendem a ser bem grandes. Isso deixa o LALR como a escolha prática.
Dito tudo isso, é importante saber que os analisadores GLR podem analisar qualquer linguagem livre de contexto, usando máquinas mais complicadas, mas exatamente as mesmas tabelas (incluindo a versão menor usada pelo LALR). Isso significa que o GLR é estritamente mais poderoso que o LR, LALR e SLR; basicamente, se você puder escrever uma gramática BNF padrão, o GLR irá analisar de acordo com ela. A diferença no mecanismo é que o GLR está disposto a tentar análises múltiplas quando há conflitos entre a tabela GOTO e / ou conjuntos de lookahead. (Como o GLR faz isso de forma eficiente é pura genialidade [não minha], mas não caberá neste post SO).
Isso para mim é um fato extremamente útil. Eu construo analisadores de programa e transformadores e analisadores de código são necessários, mas "desinteressantes"; o trabalho interessante é o que você faz com o resultado analisado e, portanto, o foco está em fazer o trabalho de pós-análise. Usar o GLR significa que posso construir gramáticas funcionais com relativa facilidade, em comparação com hackear uma gramática para entrar na forma utilizável do LALR. Isso é muito importante ao tentar lidar com idiomas não acadêmicos, como C ++ ou Fortran, onde você literalmente precisa de milhares de regras para lidar bem com a linguagem inteira, e não quer gastar sua vida tentando hackear as regras gramaticais para atender às limitações de LALR (ou mesmo LR).
Como um exemplo famoso, C ++ é considerado extremamente difícil de analisar ... por caras que fazem análise LALR. C ++ é simples de analisar usando o maquinário GLR usando praticamente as regras fornecidas no final do manual de referência do C ++. (Eu tenho precisamente esse analisador, e ele lida não apenas com o C ++ vanilla, mas também com uma variedade de dialetos de fornecedores. Isso só é possível na prática porque estamos usando um analisador GLR, IMHO).
[EDITAR novembro de 2011: estendemos nosso analisador para lidar com todo o C ++ 11. O GLR tornou isso muito mais fácil de fazer. EDITAR agosto de 2014: agora lidando com todo o C ++ 17. Nada quebrou ou piorou, GLR ainda é o miau do gato.]