Linguagens Formais e Tradutores Análise Sintática - 1

Slides:



Advertisements
Apresentações semelhantes
Software Básico Silvio Fernandes
Advertisements

Circuitos Lógicos e Organização de Computadores Capítulo 6 – Blocos com Circuitos Combinacionais Ricardo Pannain
ADT – Arvore Binária de Pesquisa
Percurso não recursivo e impressão em árvores binárias Drozdek.
MC542 Organização de Computadores Teoria e Prática
Listas Encadeadas Circulares Listas Duplamente Encadeadas
Listas Encadeadas Circulares Listas Duplamente Encadeadas
2008 LCG/UFRJ. All rights reserved. 1 Standard Template Library STL Claudio Esperança Paulo Roma Cavalcanti.
Capítulo 4 Controle de fluxo. 2Capítulo 4 – Controle de fluxo Controle de fluxo if / else switch / case while do / while for break / continue Instruções.
Construção de Compiladores
Auditoria de Segurança da Informação
Grupo Paralelismo – Unesp RC
FORTRAN 90 Denise Yumi Takamura.
SQL Procedural Junho/2006.
Funções de Linha 24/04/06. Funções de Linha Gerar ou construir novo valor não existente na tabela inicial. Valor construído a partir de dados de uma ou.
Sintaxe de Fortran 25/abril/2006. Comandos Fortran PROGRAM PRINT READ STOP END.
Exemplos de 3 posições relativas de prédio fronteiro
1. Equivalência entre portas 2. Derivação de expressões booleanas 3
Teste Estrutural de Software
Criptografia Quântica : Um Estudo
Teste Funcional de Software
Mais sobre classes Baseada no Livro: Deitel&Deitel - C++ How To program Cap. 7 Prentice Hall 1994 SCE 213 Programação Orientada a Objetos, ICMC - USP 2.
Introdução A fim de preparar a geração de código, deve-se relacionar o fonte estático do programa às ações em tempo de execução. Durante a execução, o.
Geração de Código Algoritmo de Escalonamento de instruções – List Scheduling.
1 Tradução Dirigida à Sintaxe Prof. Ricardo Santos.
Banco de Dados I Profa. Jiani Cardoso 2/2005
O Fluxo de Testes © Alexandre Vasconcelos
CES-11 LAB 03 Bitmap Quadtree
CES-41 COMPILADORES Capítulo IV Complementos de Análise Léxica.
Capítulo VII – Tipos Enumerativos e Estruturas 7.1 – Tipos enumerativos 7.2 – A necessidade de estruturas 7.3 – Manipulação dos campos de uma estrutura.
CES-10 INTRODUÇÃO À COMPUTAÇÃO Aulas Práticas – 2013 Capítulo III Comandos de Controle.
1.3 – Interpretadores – Compiladores versus Interpretadores
CES-41 COMPILADORES Aulas Práticas
CES-10 INTRODUÇÃO À COMPUTAÇÃO Aulas Práticas – 2013 Capítulo XI Encadeamento de Estruturas por Ponteiros.
Capítulo III Diagramas de Transições
Capítulo V Análise Sintática
1 Definição de Dicionário Dicionário é um sistema de informações: Equivalente a um conjunto de elementos não repetidos Equivalente a um conjunto de elementos.
5.5.4 – Métodos para a construção da tabela de análise LR
Curso Técnico em Informática La Salle - Canoas
TE 043 CIRCUITOS DE RÁDIO-FREQÜÊNCIA
Resolução de Sistemas Lineares- Parte 1
Laboratório de Programação Prof. Oscar Luiz Monteiro de Farias
Gerador de Analisadores Léxicos
Prof. Giovanny Lucero Introdução Prof. Giovanny Lucero
Linguagens Formais e Tradutores Linguagens Regulares (Revisão)
André Luis Meneses Silva
Tecnologia para Web JavaScript Enrique Pimentel Leite de Oliveira
Recursividade Estrutura de Dados.
Marco Antonio Montebello Júnior
Introdução à Probabilidade
The Data Warehouse Toolkit
Frações Professor: Graciano Pianezzer Beletti.
LINGUAGENS DE PROGRAMAÇÃO
Computação Gráfica Geometria de Transformações
Introdução Ciência da Computação estudo de algoritmos –ÊNFASE ao estudo de DADOS armazenamento manipulação refinamento (a partir de dados cru) estrutura.
Provas de Concursos Anteriores
Como aplicar leis da lógica
MECÂNICA - DINÂMICA Cinemática de uma Partícula Cap Exercícios.
1 António Arnaut Duarte. 2 Sumário: primeiros passos;primeiros passos formatar fundo;formatar fundo configurar apresentação;configurar apresentação animação.
Salas de Matemática.
MINISTÉRIO DO PLANEJAMENTO Projeto de Lei Orçamentária 2010 Ministro Paulo Bernardo Silva Brasília, 31 de agosto de 2009.
Principais operações em Listas TPA Listas Simples Inserção no Final 1.void insereNofinalDaLista(Lista *l, Elemento e){ 2.Lista paux,p; 3. p.
Aula 17 1 Análise Sintáctica Compiladores, Aula Nº 17 João M. P. Cardoso.
Análise Sintática – Parte 1
1 2 Observa ilustração. Cria um texto. Observa ilustração.
Grupo A – Azul Claro, Marrom, Laranja
Preleções Científicas Universidade Estadual do Ceará Pró-Reitoria de Extensão Integrais Múltiplas e Integrais de Volume Ministrante: Prof. K. David Sabóia.
CALENDÁRIO SEXY Ele & Ela. CALENDÁRIO SEXY Ele & Ela.
Rio Verde - Goiás - Brasil
GINÁSTICA LABORAL UM NOVO CAMINHO.
Transcrição da apresentação:

Linguagens Formais e Tradutores Análise Sintática - 1 Prof. André Luis Meneses Silva alms@ufs.br www.dsi.ufs.br

Análise Sintática (parsing) Verificar se um programa é sintaticamente válido Reconhecer a estrutura sintática do programa Produzir uma representação abstrata dos programas (sintaxe abstrata) Produzir mensagens de erros. Se recuperar. Programa fonte analisador léxico Tokens parser Árvore Abstrata Analisador Semântico

Especificação da Sintaxe Definições regulares não têm o poder de expressão suficiente Gramáticas livres de contexto = def. regulares + recursão Uma GLC G é definida por um conjunto de produções (ou regras) simbolo → simbolo simbolo .... simbolo Cada símbolo pode ser terminal ou não terminal O símbolo à esquerda de → é um não terminal Existe um símbolo destacado: símbolo inicial

Especificação da Sintaxe Exemplo E → E+E ou E → E+E E → E*E | E*E E → num | num E → (E) | (E) Derivação E  E+E  E + E + E  num + E + E  num + E + num  num + num + num

Árvore de derivação E + E E E num E + yield num num

Derivação mais à esquerda E  E+E  E + E + E  num + E + E  num + num + E  num + num + num Gramática é ambígua sse existe mais de uma árvore de derivação para uma mesma sentença (yield) sse existe mais de uma derivação mais à esquerda

E + E E E num E + num num E E + E E + E num num num

Gramáticas ambíguas Problemáticas para compilação Qual é a estrutura gramatical escolhida? Como lidar Adicionar regras extra-gramaticais precedência de operadores, associatividade, else opcional, ...; ou Usar GLC não ambígua equivalente com a original Associatividade à esquerda (direita) consegue-se com gramática recursiva à esquerda (direita) Precedência: com gramáticas por camadas

Gramáticas ambíguas Precedência e associatividade podem ser codificadas dentro da GLC E → E+T | E-T | T T → T*F | T/F | F F → id | num | (E) Regras recursivas à esq.  associatividade à esquerda Camada mais alta  precedência mais baixa Associatividade à esquerda (direita) consegue-se com gramática recursiva à esquerda (direita) Precedência: com gramáticas por camadas

Gramática por camadas - exemplo E → E + T | E - T | T T → T * F | T / F | F F → -F | +F | V V → V . id | V [ E ] | A A → id | num | (E) Em a + b.x, o “.” tem mais precedência Observem que permitimos, por exemplo: (2+3).x --- Isto é fixado na checagem de tipos

else opcional S  if E then S else S | if E then S | outro Qual é a árvore de if E1 then if E2 then S1 else S2 ? S' : comando sempre com else associado S" : comando sempre com else não associado A idéia: entre um then e um else sempre vai um comando associado (veja as prod. com then e else)

else opcional S  if E then S else S | if E then S | outro Qual é a árvore de if E1 then if E2 then S1 else S2 ? LPs adotam: else sempre casa com o if mais próximo. Podemos codificar isto numa GLC, assim: S  S' | S" S' if E then S' else S' | outro S" if E then S | if E then S' else S" S' : comando sempre com else associado S" : comando sempre com else não associado A idéia: entre um then e um else sempre vai um comando associado (veja as prod. com then e else)

Métodos de Análise Métodos universais são ineficientes (CYK e outros) Top Down (recursivos-descendentes) Constroem a árvore de deriv. em pré-ordem Caso particular: parser preditivos (LL(n)) Decidem que produção usar olhando n tokens de lookahead LL(1) são adequados para implementação manual Servem para a maioria das linguagens Exigem transformações na gramática original Bottom Up (pós-ordem) Cobrem uma classe mais ampla de gramáticas Menos transformações Usualmente construídos com ferramentas Menos transformaçaõ: gramática mais abstrata => mais próxima da sintaxe abstrata => mais modular e simples a geração das árvores abstratas LL 1ro L: varredura da entrada de esquerda para direita 2doL: derivação mais à esquerda

Parser Recursivo Descendente Pode envolver retrocesso (Backtracking) L → c A d A → ab | a O parser de cad exige retrocesso Ineficiente para ser usado na prática Em geral, LPs não precisam de retrocesso Construir a árvore

Análise preditivo Não precisam de retrocesso Porém, só servem para um tipo de gramáticas -- LL(n) não permitem recursão à esquerda LL(1): Se queremos expandir A e temos a na entrada Só deve ser possível usar uma única produção a é chamado de lookahead Exemplo: Sintaxes com palavras reservadas como S  if E then S else S | while E do S | begin L end Em gramáticas LL(n) usa-se n símbolos de lookahead

Parser preditivo Um procedimento para cada não terminal Um switch com um caso para cada produção S  if E then S else S | begin S L | print E L  end | ; S L E  num = num void S( ) { switch(tok) { case IF: proxToken( ); E( ); reconhecer(THEN); S( ); reconhecer(else); S( ); break; case BEGIN: proxToken( ); S( ); L( ); break; case PRINT: proxToken( ); E( ); break; default: erro( ); } }

void L( ) { switch(tok) { case END: proxToken( ); break; case SEMICOLON: proxToken( ); S( ); L( ); break; default: erro( ); } } void E( ) { switch(tok) { case NUM: proxToken( ); reconhecer(EQ); reconhecer(NUM); break; default : erro( ); } } void proxToken( ) { tok = scanner.proximoToken( ).codigo; } void reconhecer(int t) { if (t!=tok) erro( ); else tok = scanner.proximoToken( ).codigo; }

Gramáticas não LL V  V [ E ] | id O procedimento V( ) chama recursivamente V( ) sem consumir nenhum token. Entra em loop. E  num + E | num * E | num | (E) Com num inicialmente scaneado, que produção escolho? Porém, ela é LL(2). E  T + E | T T  F * T | F F  num | (E) Não é LL(2). (num)+(num) “( num” não determina a produção a escolher Na primeira, como dois lookahead poderia determinar a regra (porém não é LL(2) pois é rec. à esq.). Na segunda: que par de lookaheads determinam a escolha de E->num? ... “num $” e “num )”

Primeiro e Seguinte Primeiro()={a |  * a}  { |  * } Seguinte(A)={a | S * Aa} Permitem ver se a gramática é LL(1) Permitem construir a tabela sintática preditiva c/entrada contém a produção a ser aplicada para um par lookahead – terminal Exemplo Z  d Y   X  Y W  b | X Y W Z | c | a Primeiro(Z)=? Primeiro(Z)={d,a,c,b} Aa é chamada de forma sentencial

Algoritmo para Primeiro Inicializar Primeiro(a)={a}, para todo terminal a Se X  , adicionar  a Primeiro(X) Repetir até não poder adicionar mais Se X  Y1Y2...Yk,   Primeiro(Y1), ... ,   Primeiro(Yi-1) (i.e., Y1Y2..Yi-1  ), e a  Primeiro(Yi) então adicionar a a Primeiro(X) Primeiro(X1X2...Xn) = Primeiro(X1), se   Primeiro(X1); Primeiro(X1X2...Xn) = Primeiro(X1)...Primeiro(Xi+1) - {} se   Primeiro(Xk), para k =1...i , e   Primeiro(Xi+1); e Primeiro(X1X2...Xn) = Primeiro(X1)  ... Primeiro(Xn) se   Primeiro(Xk), para k = 1...n 1ro. Algoritmo: ITERATIVO e calcula primeiro para todos os símbolos (terminais e n/ao terminais) 2do. Calcula primeiro para cadeias (alfa)

a b c d X Y Z W a Z  d Y   X  Y W  b | X Y W Z | c | a b c d Primeiro a b c d X Y Z W a b c d , a, c , c a, b, c, d Construir a tabela no quadro, seguindo o algoritmo

Seguinte - Algoritmo Seguinte(A)={a | S * Aa} Colocar $ em Seguinte(S) Repetir até não poder adicionar mais Se existir AB adicionar Primeiro()-{ε} a Seguinte(B) Se existir AB ou AB e   Primeiro() adicionar Seguinte(A) a Seguinte(B)

S  Z $ c, b Z  d Y   X  Y W  b | X Y W Z | c | a $ a, b, c, d Seguinte X Y Z W c, b $ a, b, c, d Construir tabela no quadro, seguindo algoritmo

Tabela sintática preditiva A é uma entrada para (A,a) na tabela sse: Se A e a  Primeiro() Se A,   Primeiro() e a  Seguinte(A) Z  d Y   X  Y W  b | X Y W Z | c | a a b c d X Y W Z X  a X  Y Y  Y  c W  b Z  X Y W Z Z  d

G é LL(1) sse a sua tabela sintática preditiva é determinística LL(n): Com n lookaheads as entradas para as colunas serão cadeias de tamanho n Gramáticas não LL: Nenhuma G ambígua é LL Nenhuma G com recursão à esquerda é LL V  V [ E ] | id id  Primeiro(V[E]) Não é possível escolher a regra.

Eliminando recursão à Esquerda E → E+T | E-T | T Podemos escrever sem recursão à esquerda, assim E → T E' E'→ + T E' |  Em geral, se temos A → A1 | A2 |...| An | 1 | 2 |...| m Escrevemos A → 1A' | 2A' |...| mA' A' → 1A' | 2A' |...| nA' |  Dificuldades futuras Árvore de derivação associativa à direita Geração de árvore abstrata exige tratamento especial Recursão à esquerda direta. Ver teorema de Greibach para eliminar recursão à esquerda indireta.

Tabela preditiva da gramática de expressões anterior S → E $ E → T E' Adicionar produção S->E

Tirando em evidência (fatoração) à esquerda podemos escrever A → A' |... A' →  |  Por exemplo D → int Id ; | int Id ( A ) ; | outro pode ser re-escrito como D → int Id D' | outro D' → ; | ( A ) ; Pequena dificuldade: A estrutura gramatical foi mudada Inicialmente não sabemos que regra escolher pois o  aparece em duas regras. Solução: retardar a escolha para ser feita após o parser de  Mostrar exemplo de gra

Exemplos de fatoração E  id(E) | id[e] | id | num E  id E' | num A introdução de produções vazias pode introduzir conflitos. E  id E' | num E'  (E) | [e] | 

Xelse S e X S  if E then S else S | if E then S | outro Pode ser escrito como S  if E then S X | outro X  else S |  Porém, neste caso, a fatoração não é suficiente pois a nova gramática ainda é ambígua Em (X,else), a tabela vai ter duas entradas: Xelse S e X Solução ad-hoc: (adotada pela javacc, por ex.) Escolher sempre a primeira regra (neste caso, o else associa com o if mais próximo, como esperado). Não há prescrição geral. Não se aconselha usar soluções ad-hoc, salvo no caso bem conhecido do else opcional

Recuperação de erros em Parsers preditivos Quando acontecem? A( ) foi chamado com o lookahead a e a entrada (A,a) na tabela preditiva é vazia; ou Ao reconhecer(a) o lookahead é diferente de a Desespero trabalha bem Pular todos os tokens até achar um de sincronização Heurísticas para o conjunto de sincronização Ajustes por tentativa e erro Outros métodos Substituir o token. Exemplo: lookahead = "," e Reconhecer(";") falha Inserindo tokens (inseguro, pode entrar em loop) Inserir ";” quando detectar sua falta Compilador que não se recupera não é de muita utilidade na prática – Objetivo não é só reconhecer validez do prog. mas também dar mensagens de erro adequadas e se recuperar ... Conjunto de sincronização: pular até achar um de sincronização

Modalidade de despero -- Heurísticas A( ) foi chamado com lookahead a Ponto de partida: sincronização = Seguinte(A) e A( ) é resumido Acrescentar como sincronização as palavras chaves de construções mais altas ou da mesma altura (se houver seqüências). Por ex. Se faltar “;” e começar depois um while, o while poderia ser ignorado sem esta heurística Expressões estão dentro de comandos Usar produção vazia (ou que gera vazio) como default Ao reconhecer token Ignorar token (apagar) – sincroniza com todos (Em C, palavras reservadas não estão no próximo(cmd) pois “;” é finalizador (e “}” num comando composto (bloco))

Resumo Análise Sintática Gramáticas Ambigüidade Classificação dos métodos de análise Análise top-down Análise preditivo – Gramáticas LL Tabela sintática preditiva Fatoração e eliminação da recursão á esquerda Recuperação de erros

Bibliografia Seções 4.1-4.6 do livro do Dragão Seções 3.1 e 3.2 do livro do Tigre