Análise Sintática LR (parte 1)
"As minhas ovelhas ouvem a minha voz; eu as conheço, e elas me seguem. Eu lhes
dou a vida eterna e elas jamais perecerão."
Análise Sintática LR
Também chamados de parsers shift-reduce
É uma técnica de análise sintática bottom-up
(ascendente)
Usada em vários geradores automáticos de
parsers
Analisador Sintática LR
Existem diversos tipos de analisadores LR(k)
Left-to-right – a ordem de leitura dos tokens é da
esquerda para a direita
Rigthmost derivation – encontra uma derivação mais
à direita (porém, de trás para a frente)
Analisador Sintática LR
O tipo que veremos, inicialmente, é chamado
Parser LR(0)
Veremos o Parser SLR(1) na próxima aula
Esclarecendo...
Classificação “maior”: parser bottom-up
Sub-classificação: parser LR (shift-reduce)
Assuntos
1. Princípios de funcionamento
2. Construção do autômato LR(0)
3. Construção das tabelas
1. Princípios de
Funcionamento
Funcionamento
Objetivo: dar uma ideia geral do funcionamento
de todos os parsers shift-reduce
Símbolos terminais (tokens) lidos de uma
entrada (lexer)
Símbolos terminais e não-terminais podem ser
Funcionamento
À medida que lê os terminais (tokens), o parser
poderá realizar uma destas ações
SHIFT
REDUCE
ACCEPT
Ações
Ação REDUCE <X>
Realizada quando os símbolos no topo da pilha
formam a cadeia , e o parser “percebeu” que eles foram gerados por X
Ação principal
Na árvore, equivale a tornar X o pai de cada símbolo
da cadeia
Executa dois passos:
1. Remove toda a cadeia da pilha 2. Insere o símbolo X
Ações
Ação SHIFT
Pega o próximo terminal (token) da entrada e
“guarda” na pilha
Realizada quando o parser precisa de mais
informações sobre a entrada antes de formar um “ramo” da árvore
Ações
Ação ACCEPT
Indica que a entrada terminou e está correta (toda a
árvore foi identificada/criada)
Ação ERROR
Funcionamento
O grande problema é:
O parser LR(0), basicamente, vai escolher com
base no estado atual de um “autômato LR(0)”
Mas pode ser necessário ver o token para decidir o
próximo estado
Funcionamento
Antes de prosseguir, precisamos ver como criar
um Autômato LR(0)
Veremos, a seguir, como construí-lo a partir de
2. Construção do Autômato
LR(0)
Construção do Autômato
O primeiro passo é criar um gramática
estendida
Acrescentar a produção S’ C (ou S’ C$)
C é o não-terminal inicial original e S’ vai ser um novo
não-terminal inicial
$ é tratado como um terminal, e representa fim de
arquivo (EOF)
Esta vai ser a única “produção inicial”, o que facilita disparar o ACCEPT.
Itens LR(0)
Indicam em que parte de uma produção a
análise sintática pode estar, num certo instante
Um ponto é usado para indicar a posição
Para um produção XYZW , temos estes itens
possíveis:
X·YZW
XY·ZW
XYZ·W
Autômato LR(0)
Um Autômato LR(0) é assim:
Cada estado é uma coleção de itens LR(0)
As transições entre estados acontecem quando um
símbolo (terminal ou não-terminal) é “lido”
Não existe estado final
Ou: você pode entender o estado que tem S’ C$· como
Autômato LR(0)
O estado inicial será o item S’ ·C
Para todo item X·Y , sendo Y não-terminal,
adicionar ao mesmo estado os itens
Y. , para toda produção de Y
Para todo item X·Y , criar transição que:
Lê o símbolo Y (terminal ou não)
Autômato LR(0)
Depois de concluído o autômato, os estados
devem ser numerados, para facilitar
O autômato pode, então, ser usado para criar as
Exemplo: A Gramática
E → E+T | T
T → n
Mais Sobre a Pilha
Este autômato será simulado por meio da pilha
A pilha guardará pares “símbolo / estado”
Cada símbolo (terminal ou não-terminal) inserido na
pilha levará o autômato a um novo estado
O estado atual será o estado no topo da pilha
3. Construção das Tabelas
de um Parser LR(0)
Tabelas de Parsing
Um Parser LR(0) é dirigido por certas tabelas,
construídas a partir do autômato
Como as tabelas são organizadas? Duas
formas:
Forma intuitiva
3.1 Forma Intuitiva
Uma tabela de ações
Para cada estado, indica uma ação
Ações possíveis: REDUCE ou ACCEPT ou SHIFT
(sem o próximo estado)
Depois do SHIFT, olharia o token para decidir o próximo
estado
Uma tabela das transições do autômato
Para cada estado e cada símbolo (terminal ou
não-terminal), indica o próximo estado
3.1 Forma Intuitiva - Exemplo
(Mostrar tabelas “intuitivas” baseadas no
autômato anterior)
Daria certo assim.
Porém, na prática, as tabelas
são organizadas de forma
3.2 Forma Padrão
Nesta forma, cada ação SHIFT já informa o
próximo estado:
SHIFT <próximo estado>
Assim, a tabela de transição só precisa guardar
3.2 Forma Padrão
Tabela ACTION
Com base no estado atual e no próximo token, diz
qual ação tomar
Tabela REDUCE-GOTO (ou GOTO)
Usada durante um “REDUCE <X>” , após
remover todo o da pilha
Com base no estado e no não-terminal X, diz o
3.2 Forma Padrão
Para criar ambas as tabelas, é preciso analisar
os itens LR(0) de cada estado do autômato
Descreveremos apenas as situações de
entradas corretas
Nos demais casos, subentende-se que a ação é
Tabela REDUCE-GOTO
É a mais simples de construir, descreve
transições do autômato para não-terminais
Se o estado s tiver um item X·N, com um
ponto antes de um não-terminal N, faremos:
REDUCE-GOTO [s, N] = estado contendo o item
XN· (com o ponto depois do N)
Exemplo:
Tabela ACTION (caso 1)
Se o estado s tiver um item com um ponto antes
de um terminal t assim: X·t
Seja s’ o estado que temo ponto depois de t assim:
Xt·
Então: ACTION[ s, t ] = “SHIFT <s’>”
Traduzindo: no estado s, quando o token for t, faça uma
Tabela ACTION (caso 2)
Se o estado s tiver um item com um ponto no
final da produção assim: X·
Mandar fazer uma redução desta produção,
independente do próximo símbolo:
ACTION[ s, * ] = “REDUCE< X >”
Mais Sobre o REDUCE
Aplicando uma ação REDUCE< X > :
1. O parser desempilha
2. Olha o estado atual s (no topo da pilha, após a
remoção de )
3. Consulta a tabela REDUCE-GOTO no estado s com
o símbolo X, para decidir o próximo estado s’,
Tabela ACTION (caso 3)
Um caso especial será definido quando o estado
x tiver o item S’ C· (com ponto no final)
Seria uma redução, porém, esta é a produção inicial
da gramática
Neste caso, a ação será ACCEPT, mas apenas para
o símbolo $ (fim de arquivo): ACTION[ x, $ ] = “ACCEPT”
Tabela ACTION (caso 3 alt.)
ALTERNATIVA: Caso você trate S’ C$ como
a produção inicial:
No estado do item S’ C·$ , ao ler $, a ação será
um SHIFT levando ao estado que tem S’ C$.
Pelo caso 1 normal
Então, no estado S C$· , basta fazer o ACCEPT
independentemente do próximo símbolo
Comentários
Observe que, na forma intuitiva, para escolher a
próxima ação (SHIFT, REDUCE ou ACCEPT), não é necessário olhar nenhum token da entrada
Ou seja, olha 0 de lookahead
Isso explica porque ele é LR(0) e não LR(1)
Porém, na forma padrão, nas ações de SHIFT, é
necessário olhar o próximo token – parser LR(0.5) ? Ainda assim, é considerado LR(0), porque não precisa olhar o
4. Exemplos de Execução de
um Parser LR(0)
Exemplos 1 e 2
Mostrar como o parser gerado ao longo da aula
processa estas entradas (sequências de tokens vindas do lexer):
“n”
Exemplo 1: “n”
Feito em sala...
Tabela PILHA x ENTRADA x AÇÃO
Entenda a pilha como uma “lista” em que se
Por que LR mesmo ?
Relembrando: porque o parser faz passos que
lembram uma derivação mais à direita
Porém invertida – bottom-up
No exemplo 2, observe:
A cadeia formada por PILHA + ENTRADA,
Em cada ponto de uma ação REDUCE ou ACCEPT
Referências
“Compiladores: Princípios, Técnicas e
Ferramentas” (Alfred Aho, et. al.)
1ª edição: seção 3.7 (até a página 100) 2ª edição: seção 3.6
“Compiladores: Princípios e Práticas” (Kenneth
Louden)