Escolar Documentos
Profissional Documentos
Cultura Documentos
INTRODUÇÃO _
ESTRUTURA DE UM COMPILADOR _
1
Compiladores
ANÁLISE LÉXICA _
ANÁLISE SINTÁTICA _
ANÁLISE SEMÂNTICO _
2
Compiladores
para permitir tais chamadas. Logo, essa alteração de entidade de um tipo de dados
em outro.
AMBIGUIDADE _
NA LINGUAGEM ___________________________________________________________________
NA GRAMATICA____________________________________________________________________
Para retirar a ambiguidade temos que saber o que a linguagem está gerando, depois
disso vamos ter que reescrever a gramatica mantendo a mesma linguagem
RECURSIVIDADE _
RECURSIVIDADE DIRETA
Dependendo do parsing que vamos realizar, ele não aceita um determinado formato
das expressões regulares, com recursividade a esquerda. Os parsing são realizados
por chamadas recursivas, mas não é a chamada recursiva que gera ambiguidade.
REGRA:
EXEMPLO:........................................................................
Com isto, a imagem ao lado representa a solução pela regra inserindo duas regras,
onde essa recursividade vai ter um ponto de parada.
3
Compiladores
G: Gramatica
V: Variável
3: Alfabeto
RECURSIVIDADE INDIRETA
Na recursividade indireta, temos uma variável que chama outra variável, que por
sua vez chama a variável antecessor, ou seja, indiretamente.
O ALFABETO TEM QUE SER O MESMO EM AMBAS AS LINGUAGENS. V: vai ser diferente,
4
Compiladores
E: expressão
Com isso, agora temos que implementar a Gramatica-2 para este exemplo afim de
retirar a recursividade a esquerda.
FATORAR _
PARSING TOP-DOWN _
No PARSING DOWN, ele começa a análise da primeira até a última regra, de cima para
baixo. Com isso, ele tem o problema de desempenho, pois passa pelas tokens várias
vezes. Exemplo, ele entra dentro de uma regra, caso não dê certo, ele volta para
outra expressão, logo ele tem que ler o arquivo de entrada para cada regra.
Com isso, nestas regras acima, para evitar o problema no parsing top-down, temos
que trocar a Chamada do B, por A, logo implementando a recursividade. Só que,
nisto geramos ambiguidade, no PARSING TOP DOWN.
5
Compiladores
PODEMOS TER A RECURSIVIDADE A DIREITA. MAS NÃO PODEMOS TER RECURSIVIDADE A
ESQUERDA.
No Parsing Top Down é aquilo que faz uma derivação partindo do ciclo inicial até
tentar chegar no ciclo final, se chegar está sintaticamente correto se não
sintaticamente incorreto. Logo ele está construindo uma arvore de parsing a partir
da gramatica que você deu, logo ele vai em pré-ordem.
No TOP-DOWN a não pode ter que recursividade a esquerda, pois entre em um loop. A
gramatica começa como simplesmente legível e com recursividade a esquerda, ou
seja, é fácil implementar de forma legível e com as precedências necessários,
depois temos que retirar a recursividade a esquerda, trazendo para a direita.
PRECEDENCIA: A ideia é quem ta mais no fundo ter a precedência, pois ele funciona
em pré-ordem. Logo, NESTE EXEMPLO QUE TEM MAIOR PRECEDENCIA É QUE TA MAIS NO
FUNDO, COMO A MULTIPLICAÇÃO TEM MAIOR PRECEDENCIA QUE A SOMA, E NO ‘F’ O PARENTESE
TEM MAIOR PRECEDENCIA QUE A MULTIPLICAÇÃO.
O PARSING TOP-DOWN, PODE TER OU NÃO UM BACKTRANKING, POIS EM CASO MAIS SIMPLES
ELE TEM BACKTRACKING. EM OUTROS NÃO, MAS FICA INVIAVEL SUA IMPLEMENTAÇÃO.
AULA – 23/10/2017
6
Compiladores
No algoritmo, cada
variável vira uma
rotina, logo na imagem
ao lado, temos a
gramatica com duas
variáveis, logo duas
rotinas.
Cada regra da variável
simula a variável, sendo
que ele chama o lexan
para verificar a token,
se for verdadeiro ele
retorna 1, se falso,
retorna 0.
Na variável A, temos
duas opções para testar
a variável – regras,
logo caso a primeira não
de certo, ele tem que
voltar e testar na B, ou
seja, segurar o lexeme
para não avançar para o próximo.
PARSING PREDICTIVE _
Neste parsing é ser prever o futuro, preditivo que vai determinar algo, tipo estou
prevendo que vou passar em todas as matérias com nota alta. LOGO NÃO CONTEM
BACKTRACKING.
Esta implementação roda de 20 a 25% mais rápida que a não simplificada, logo,
aplicando os conceitos acima.
O problema de um parsing não predicitive, é que temos que testar vários caminhos.
Neste parsing, ele utiliza uma tabela de parsing para determinar a produção a ser
usada.
7
Compiladores
VANTAGEM DESTE PARSING, É QUE SE MUDAR A GRAMATICA, BASTA GERAR UMA NOVA TABELA E
NÃO TER QUE GERAR UM PARSING NOVO.
Componentes:
TABELA
Para gerar a tabela, utiliza-se duas funções sendo o FOLLOW e o FIRST. O First me
dá as primeiras tokens que uma variável pode derivar, ou seja, ele me dá a primeira
variável que a regra deriva.
Follow é seguinte ideia é que ele segue alguém, e no código, um elemento só pode
se anular se caso ele está sendo seguido por alguém. Logo, o Follow olha para a
variável e vem que a esta seguindo, por exemplo na imagem abaixo, quem está
seguindo a variável B são todos os símbolos gerados por C.(isto por causa da regra
em A,‘BC’ = B seguido de C). O Follow diz que só pode se anular quando ele tiver
certeza do que vem depois de você é gerado pelo próximo símbolo, caso contrário
não pode se anular.
A ideia do código é que, se caso uma entrada não ser resolvida na primeira regra
da Variável A ele passa para a segunda regra, caso não resolva ele se se anula
para outro poder resolver, no caso, se B seguido de C: se o B se anular, ele passa
a bola para o C gerar o símbolo.
Analisando o exemplo
abaixo, temos que o
First da Variável C
gera ‘d’ e lambida,
consequentemente as
outras também geram
seus próprios
símbolos. Detalhe que
na variável A está
seguido de outras variáveis, e essas variáveis também geram símbolos, logo tudo
que essas variáveis geram, o A também gera. Lembrando que o A por gerar lambida,
ele não gera direto a lambida, pois a variável C tem que se anular.
8
Compiladores
GERANDO FIRST E FOLLOW
REGRA: Tudo que segue a primeira variável, vai seguir também a sua derivada,
exemplo: tudo que segue E, vai seguir E’; Tudo que segue T, vai seguir T’...
9
Compiladores
GERANDO A TABELA
Os terminais são ‘a’, ‘b’ e ‘$’, estes são implementados na tabela com os terminais
na primeira linha. E as variáveis.
O algoritmo para isso, precisa de uma pilha e começa com A$, pois o A é o símbolo
inicial.
NESTE ALGORITIMO NÃO TEM CHAMADA DE FUNÇÃO E SIM UM LOOP, QUE FICA NA SEGUINTE
CONDIÇÃO: ENQUANTO NÃO TEM ERRO E NÃO ESTA NO TOPO DA PILHA O $, EU CONTINUO.
10
Compiladores
EXEMPLO – 2 ____________________________________________________________________
Neste exemplo temos a seguinte gramatica na imagem ao lado no qual foi retirado a
recursividade a esquerda, gerando a gramatica G2.
Caso seja necessário fatorar, teria que fatorar, mas neste caso vemos que não é
necessário.
DICA:
PARSING BUTTON-UP _
PARSING DESCENDENTE – TOPDOWN, nós temos um símbolo inicial que começa a fazer
derivações até chegar em uma String final. Qualquer que seja o top-down, temos
que retirar a recursividade a esquerda.
PARSING PREDICTIVE, temos que gerar a tabela, calcular First e Follow, temos
que fatorar a esquerda. Este fato de fatorar e tirar recursividade, não garante
que seja capaz de gerar este parsing pois caso a gramatica seja ambígua,
teríamos múltiplas entradas na tabela de parsing.
11
Compiladores
CONFLITOS
REDUCE-REDUCE
SHIFT-REDUCE
Neste conflito shift-reduce, ocorre quando uma opção de poder reduzir e também de
empilhar na máquina para poder ser encontrado. Um exemplo, é ter um elemento que
já foi encontrado e aí a máquina diz que podemos reduzir, mas também tem um ainda
está pendente de leitura, não encontramos, logo nos deparamos com um conflito de
adicionar e retirar da pilha. Quando temos este problema, nós decidimos ir para o
caminho de empilhar, ou seja, jogar fora o reduce e fazer o shift.
2. Temos que calcular o First e Follow. (TODA REDUÇÃO USA FOLLOW NA TABELA)
3. Gerar uma coleção de itens LR, que é da esquerda para a direita, se ela fosse
LR1 teríamos que ler pelo menos 1 elemento na entrada para saber o que fazer,
e assim por diante se fosse LR2, LR3.
4. Montar tabela AFD, essa tabela vai representar um autômato finito determinista,
baseado nas regras da gramatica. Ele vai mostra como a partir do símbolo
inicial nós vamos ir trocando até chegar no String final.
MONTAR TABELA
1. Aumentar a Gramatica
2. Gerar Coleção de Itens
3. Preencher a tabela com a coleção de itens, baseado em First e Follow.
12
Compiladores
Gerando a coleção de itens, através da imagem abaixo, temos que pegamos a gramatica
e geramos uma coleção de itens que vão ser trocados.
Sempre que chegar no PONTO e o ponto tiver enxergando o T por exemplo, temos que
expandir o T em outro estado, pegar um estado IX e
expandir todas as possibilidades que o T vai produzir.
13
Compiladores
Para montagem da tabela
temos que colocar Os
ESTADOS, sendo de 0 8.
Os terminais são:
+, *, K e $
Também, temos as
variáveis em transição:
E, T, F
ACTION:
Representa as transições
sobre os elementos do
alfabeto.
GO-TO:
Representa as transições
das variáveis. Se
estiver no estado 0 e
estiver lendo um E, vai
para o estado 1.
14
Compiladores
_______REVISÃO____________________________________________________________________
SIMBOLOGIA _
G: Gramatica
V: Variável
3: Alfabeto
EXPRESSÃO REGULAR _
+ PELO MENOS: Quando é inserido o (+), quer dizer que temos que pelo menos pegar
1 caractere dentro do (+).
EX: ∈ {𝒂, 𝒃}∗ - Se não entrar no conjunto, é gerado lambida, e ao entrar você pode
pegar quantos do alfabeto quiser.
EX: 𝑰𝑫𝑬𝑵𝑻𝑰𝑭𝑰𝑪𝑨𝑫𝑶𝑹 → 𝑳𝒆𝒕𝒓𝒂 {𝒍𝒆𝒕𝒓𝒂 | 𝑫𝒊𝒈𝒊𝒕𝒐} - Neste exemplo, estou dizendo que
meu identificador é composto por uma letra ou digito, no mínimo uma letra, logo
meu identificador tem somente 2 caracteres.
EX: 𝑰𝑫𝑬𝑵𝑻𝑰𝑭𝑰𝑪𝑨𝑫𝑶𝑹 → 𝑳𝒆𝒕𝒓𝒂 {𝒍𝒆𝒕𝒓𝒂 | 𝑫𝒊𝒈𝒊𝒕𝒐}∗ - Agora, estou dizendo que meu
identificador pode ter vários caracteres composto por letra ou digito.
EX: 𝑎∗ 𝑎∗ = 𝑎∗ ; 𝑎+ 𝑎+ = 𝑎𝑎+
PRESEDENCIA DE OPERADORES
Em uma expressão regular para regra de quem tem precedência, vai depender da
expressão. Primeiro normalmente é feito o (1)FECHO, (2)CONCATENAÇÃO, (3)UNIÃO,
isto em um exemplo apenas, vai depender de expressão para expressão.
+ + +
EX: 𝐶𝑜𝑛𝑠𝑡𝑎𝑛𝑡𝑒 → 𝐷 | 𝐷 . 𝐷 - Caso venha como entrada “1.42”, ele deverá
primeiro identificar como DIGITO(+).DIGITO(+), pois caso contrário, ele vai enviar
para a próxima fase como um caractere que não é identificado. Com isto, vemos a
ordem de precedência que foi a concatenação antes da união.
15
Compiladores
AUTÔMATOS _
Toda expressão regular é reconhecida pelo autômato, logo toda expressão regular
corresponde ao autômato finito determinista ‘m’ cuja linguagem de ‘m’ é essa
expressão. Toda vez que escrevemos essa expressão, temos que ter uma máquina que
reconheça essa expressão.
GRAMATICA _
Uma expressão denota uma linguagem (a*b, representa uma linguagem com =
aa,ab,abb...); Já a gramatica, ela deriva gera a partir de uma regra, algo que
possamos rever.
16
Compiladores
Seguindo o exemplo da
gramatica abaixo:
ALFABEO DO EXEMPLO É:
Identificador, +, * e k-
constante.
Definimos uma gramatica com algumas 2 expressões, que são elas: ATRIBUIÇÃO e
EXPRESSÃO. Dentro de cada expressão temos regras, que neste exemplo são 5, em que
são:
Com isso, nessa gramatica tem regras como não posso dividir ou
fazer outras coisas.
17
Compiladores
Neste mesmo exemplo, poderíamos ter gerado os seguintes resultados:
Exemplificando sobre as
constantes e os operadores, caso
seja recebido uma entrada do
tipo: 3++4 - Percebemos que é
composto por uma Constante;
Operador; Operador; Constante.
18
Compiladores
Logo percebemos que para nossa analise léxica, é preciso
conhecer o que está vindo e o já foi lido, ou seja, olhar para
a frente e também para trás, como neste caso que seria um erro
vir dois operadores seguidos.
Agora, temos que montar nosso grafo Lexo, que na verdade é um autômato
determinístico. Após isto que conseguimos gerar o programa, codificação.
1- Compostos de 2 tokens;
2- Composto por 2 maquinas;
3- Uma máquina de erro;
Lembrando, que poderia ser otimizado a máquina mas por motivo de respeitar as
regradas das tokens e também para manutenção, criamos essas maquinas separas sem
uma otimização que poderia ter no final apenas um grafo lexo, uma máquina.
19