Analisador (parser) Recursivo-Descendente Preditivo
Last updated
Last updated
Sobre o alfabeto {a,b}, considere linguagem
L = {a^n * b^n : n >= 0}
descrita pela gramática
S -> a S b | end
Construa-se um programa com lookahead de 1, em que o símbolo não terminal S seja uma função recursiva, que reconheça a linguagem L
No programa anterior:
lookahead é uma variável que representa o próximo à entrada.
adv() é uma função que avança na entrada, colocando em lookahead o próximo símbolo.
eat(c) é uma função que verifica se no lookahead está o símblo c, gerando erro se não estiver, e avança para o próximo.
Há duas produções da gramática com cabeça S, sendo a decisão central do programa a escolha de qual usar face ao valor do lookahead.
deve escolher-se S -> a S b se o lookahead for a.
e S -> end se o lookahead for $ ou b
No programa anterior, o símbolo $, marcador de fim de entrada, corresponde ao \n
Uma palavra é aceite pelo programa se e só se
S(); eat($), não der erro.
Sobre o alfabeto {a; b}, considere linguagem
descrita pela gramática
S -> end | a B S | b A S A -> a | b A A B -> a B B | b
Construa um programa em que os símbolos não terminais sejas funções recursivas que reconheça a linguagem L.
O programa terá 3 funções recursivas, A, B, S, semelhantes à função S do exemplo anterior.
Em A, deve escolher-se A -> a se lookahead for a e A -> b AA se for b.
Em B, deve escolher-se B -> b se lookahead for b e B -> a B B se for a.
Em S, deve escolher-se S -> a B S se lookahead for a, S -> b A S se for b e S -> end se for $ (este último, mais tarde saber-se-á porquê).
Sobre o alfabeto {a; b}, considere linguagem
descrita pela gramática
S -> end | a B | b A A -> a S | b A A B -> a B B | b S
Construa um programa em que os símbolos não terminais sejas funções recursivas que reconheça a linguagem L.
O programa terá 3 funções recursivas, A, B e S, semelhantes á funcão S do exemplo anterior, exceto no critério de escolha da produção S -> end.
Escolhar S -> end quando lookahead for $ pode não resolver.
Por exemplo, com o lookahead igual a a, há situações em que se tem de escolher S -> a B e outras S -> end.
É o que acontece com a entrada bbaa
S => b A => bb AA => bba S A => ... momento em que o S tem de ser expandido para o end e o lookahead é a.
Sobre o alfabeto {a; b}, considere linguagem
descrita pela gramática
S -> end | a B | b A A -> a S | b A A B -> a B B | b S
Construa um programa em que os símbolos não terminais sejas funções recursivas que reconheça a linguagem L.
Tal como no caso anterior, escolher S -> end quando lookahead for $ pode não resolver.
Sobre o alfabeto {a,b}, considere linguagem
descrita pela gramática
S -> a S b | a b
Construa um programa em que o símbolo não terminal S seja uma função recursiva que reconheça a linguagem L.
Como escolher entre as duas produções se ambas começam pelo mesmo símbolo ?
Há duas abordagens:
Pôr em evidência o a à esquerda, transformando a gramática para
S -> a X X -> S b | b
Aumentar o número do símbolos de lookahead para 2
se for aa, escolhe-se S -> a S b
se for ab, escolhe-se S -> a b
Sobre o alfabeto {a, b}, considere linguagem
descrita pela gramática
S -> a S b | a b
Construa um programa em que o símbolo não terminal S seja uma função recursiva quem reconheça a linguagem L.
Há duas abordagens:
Pôr em evidência o a à esquerda, transformando a gramática para
S -> a X X -> S b | b
Aumentar o número de símbolo de lookahead para 2
se for aa, escolhe-se S -> a S b
se for aa, escolhe-se S -> a b
Sobre o alfabeto {a, b}, considere linguagem
descrita pela gramática
S -> S a b | a b
Construa um programa em que o símbolo não terminal S seja uma função recursiva que reconheça a linguagem L.
Escolher a primeira produção cria um ciclo infinito, por causa da recursividade à esquerda
O ANTLR consegue lidar com este tipo (simples) de recursividade à esquerda, mas falha com outros tipos.
Mas, em geral os reconhecedores descendetes não lidam com recursividades à esquerda.
Solução geral: eliminar a recursividade à esquerda