Compiladores - Análise LL(1)

Documentos relacionados
Compiladores - Análise SLR

Compiladores - Análise Ascendente

Compiladores - Análise Ascendente

Reduce: reduz o que está imediatamente à esquerda do foco usando uma produção

Análise Sintática Bottom-up

Análise Sintática (Cap. 04) Análise Sintática Descendente

Compiladores - Análise Preditiva

Análise Sintática LL(1)

COMPILADORES. Análise sintática. Prof. Geovane Griesang Universidade de Santa Cruz do Sul UNISC Departamento de informática

Compiladores. Exemplo. Caraterísticas de Gramáticas. A αβ 1 αβ 2. A αx X β 1 β 2. Lembrando... Gramáticas Livres de Contexto

Compiladores - Gramáticas

Como construir um compilador utilizando ferramentas Java

Análise Sintática. Fabiano Baldo

Análise Sintática II. Eduardo Ferreira dos Santos. Outubro, Ciência da Computação Centro Universitário de Brasília UniCEUB 1 / 34

Compilação: Erros. Detecção de Erros: * Analisadores Top-Down - Preditivo Tabular (LL) - Feito a mão. * Analisadores Botton-Up: - Shift-Reduce (SLR)

Análise Sintática (Cap. 04) Análise Sintática Ascendente Analisador Sintático LR

Compiladores Analisador Sintático. Prof. Antonio Felicio Netto Ciência da Computação

Compiladores - Gramáticas

Análise Sintática Descendente

Compiladores - JACC. Fabio Mascarenhas

Compiladores Aula 6. Celso Olivete Júnior.

Análise Sintática I. Eduardo Ferreira dos Santos. Abril, Ciência da Computação Centro Universitário de Brasília UniCEUB 1 / 42

Construção de Compiladores Aula 18 - Análise Sintática Ascendente

Análise sintática. Análise sintática. Top-down ou descendente. Com retrocesso: por tentativa e erro. Preditiva: para gramáticas LL(1) 09/04/2012

Vantagens de uma Gramática. Sintaxe de uma Linguagem. Analisador Sintático - Parser. Papel do Analisador Sintático. Tiposde Parsers para Gramáticas

Análise sintática. Prof. Thiago A. S. Pardo. Análise sintática ascendente

Conceitos de Linguagens de Programação

Plano da aula. Compiladores. Os erros típicos são sintáticos. Análise Sintática. Usando Gramáticas. Os erros típicos são sintáticos

Análise Sintática - Final

Uma gramática é ambígua se existe alguma cadeia para qual ela tem mais de uma árvore sintática

Analisadores Sintáticos LR

Compiladores - Análise Léxica

Construção de Compiladores Aula 16 - Análise Sintática

Parsing Preditivo. Antes de ser abordado o Parsing Preditivo, será apresentado o Analisador Sintático Descendente Recursivo.

Compiladores - Análise Léxica

Compiladores. Fabio Mascarenhas

Compiladores - Autômatos

V Análise Sintática. V.1.1 Gramáticas Livres de Contexto Definições de GLC

Folha 4.1 Análise sintática descendente

Análise sintática. Análise sintática ascendente. Parte-se dos símbolos terminais em direção ao símbolo inicial da gramática. Derivação mais à direita

COMPILADORES. Análise sintática. Prof. Geovane Griesang Universidade de Santa Cruz do Sul UNISC Departamento de informática

Um Compilador Simples. Definição de uma Linguagem. Estrutura de Vanguarda. Gramática Livre de Contexto. Exemplo 1

Compiladores. Análise Sintática

COMPILADORES. Análise sintática. Prof. Geovane Griesang Universidade de Santa Cruz do Sul UNISC Departamento de informática

Compiladores. Top-Down x Bottom Up. Plano da aula. Redução exemplo 1. Redução exemplo 1. Lembrando: construir a tabela de análise LL(1) A Abc b B d

INE5318 Construção de Compiladores. AULA 4: Análise Sintática

Compiladores Análise Semântica

Tokens, Padroes e Lexemas

Introdução. Compiladores Análise Semântica. Introdução. Introdução. Introdução. Introdução 11/3/2008

Compiladores. Bruno Lopes. Bruno Lopes Compiladores 1 / 12. Instituto de C

Linguagens Formais e Autômatos P. Blauth Menezes

Apresentação. !! Familiarização com os métodos de construção de compiladores de linguagens e com as técnicas de compilação mais habituais.

COMPILADORES. Análise sintática. Prof. Geovane Griesang Universidade de Santa Cruz do Sul UNISC Departamento de informática

Compiladores Aula 4. Celso Olivete Júnior.

O uso de gramáticas traz vantagens para projetistas de linguagens e escritores de compiladores pelas seguintes razões:

Análise Sintática Introdução

Compiladores Análise Semântica

Compiladores I. Caracterizar um método de análise bottom-up. Compreender os mecanismos básicos envolvidos no processo de análise LR.

Linguagens Livres de Contexto

CAP. VI ANÁLISE SEMÂNTICA

INE5317 Linguagens Formais e Compiladores AULA 9: Propriedades e Reconhecimento das Linguagens Livres do Contexto

Linguagens Livres do Contexto. Adaptado de H. Brandão

Análise Sintática II: Analisadores Descendentes Preditivos

Construção de Compiladores Aula 17 - Análise Sintática Descendente

Gramáticas Livres de Contexto Parte 1

INE5416 Paradigmas de Programação. Ricardo Azambuja Silveira INE CTC UFSC E Mail: URL:

Transcrição:

Compiladores - Análise LL(1) Fabio Mascarenhas - 2013.1 http://www.dcc.ufrj.br/~fabiom/comp

Gramáticas LL(1) Uma gramática é LL(1) se toda predição pode ser feita examinando um único token à frente Muitas construções em gramáticas de linguagens de programação são LL(1), ou podem ser tornadas LL(1) com alguns jeitinhos A vantagem é que um analisador LL(1) é bastante fácil de construir, e muito eficiente Como a análise LL(1) funciona?

Análise LL(1) Conceitualmente, o analisador LL(1) constrói uma derivação mais à esquerda para o programa, partindo do símbolo inicial A cada passo da derivação, o prefixo de terminais da forma sentencial tem que casar com um prefixo da entrada Caso exista mais de uma regra para o não-terminal que vai gerar o próximo passo da derivação, o analisador usa o primeiro token após esse prefixo para escolher qual regra usar Esse processo continua até todo o programa ser derivado ou acontecer um erro (o prefixo de terminais da forma sentencial não casa com um prefixo do programa)

Uma gramática LL(1) simples: PROG -> CMD ; PROG PROG -> CMD -> id = EXP CMD -> print EXP EXP -> id EXP -> num EXP -> ( EXP + EXP ) Vamos analisar id = ( num + id ) ; print num ;

O terminal entre é o lookahead, usado para escolher qual regra usar PROG id = ( num + id ) ; print num ;

O terminal entre é o lookahead, usado para escolher qual regra usar PROG -> CMD ; PROG id = ( num + id ) ; print num ;

Um prefixo de terminais na forma sentencial desloca o lookahead PROG -> CMD ; PROG -> id = EXP ; PROG id = ( num + id ) ; print num ;

Um prefixo de terminais na forma sentencial desloca o lookahead PROG -> CMD ; PROG -> id = EXP ; PROG -> id = ( EXP + EXP ) ; PROG id = ( num + id ) ; print num ;

Um prefixo de terminais na forma sentencial desloca o lookahead PROG -> CMD ; PROG -> id = EXP ; PROG -> id = ( EXP + EXP ) ; PROG -> id = ( num + EXP ) ; PROG id = ( num + id ) ; print num ;

Um prefixo de terminais na forma sentencial desloca o lookahead PROG -> CMD ; PROG -> id = EXP ; PROG -> id = ( EXP + EXP ) ; PROG -> id = ( num + EXP ) ; PROG -> id = ( num + id ) ; PROG id = ( num + id ) ; print num ;

Um prefixo de terminais na forma sentencial desloca o lookahead PROG -> CMD ; PROG -> id = EXP ; PROG -> id = ( EXP + EXP ) ; PROG -> id = ( num + EXP ) ; PROG -> id = ( num + id ) ; PROG -> id = ( num + id ) ; CMD ; PROG id = ( num + id ) ; print num ;

Um prefixo de terminais na forma sentencial desloca o lookahead PROG -> CMD ; PROG -> id = EXP ; PROG -> id = ( EXP + EXP ) ; PROG -> id = ( num + EXP ) ; PROG -> id = ( num + id ) ; PROG -> id = ( num + id ) ; CMD ; PROG -> id = ( num + id ) ; print EXP ; PROG id = ( num + id ) ; print num ;

O lookahead agora está no final da entrada (EOF) PROG -> CMD ; PROG -> id = EXP ; PROG -> id = ( EXP + EXP ) ; PROG -> id = ( num + EXP ) ; PROG -> id = ( num + id ) ; PROG -> id = ( num + id ) ; CMD ; PROG -> id = ( num + id ) ; print EXP ; PROG -> id = ( num + id ) ; print num ; PROG id = ( num + id ) ; print num ;

Chegamos em uma derivação para o programa, sucesso! PROG -> CMD ; PROG -> id = EXP ; PROG -> id = ( EXP + EXP ) ; PROG -> id = ( num + EXP ) ; PROG -> id = ( num + id ) ; PROG -> id = ( num + id ) ; CMD ; PROG -> id = ( num + id ) ; print EXP ; PROG -> id = ( num + id ) ; print num ; PROG -> id = ( num + id ) ; print num ; id = ( num + id ) ; print num ;

FIRST, FOLLOW e FIRST+ Como o analisador LL(1) sabe qual regra aplicar, dado o lookahead? Examinando os conjuntos de lookahead (FIRST+) de cada regra FIRST+(A -> w) = FIRST(w) U FOLLOW(A) - { '' }, se '' em FIRST(w) FIRST(w), caso contrário E quem são os conjuntos FIRST e FOLLOW? Revisão de linguagens formais! FIRST(w) = { x é terminal w -*-> xv, para alguma string v } U { '' w -*-> '' } FOLLOW(A) = { x é terminal ou EOF S -*-> waxv para algum w e v }

A condição LL(1) Uma gramática é LL(1) se os conjuntos FIRST+ das regras de cada nãoterminal são disjuntos Por que isso faz a análise LL(1) funcionar? Vejamos um passo LL(1):...At... -*->...t... ou...a... -*->...tw... No primeiro caso isso quer dizer que t está no FOLLOW(A) No segundo caso, t está no FIRST da regra de A que foi usada A derivação é mais à esquerda, então o primeiro... é um prefixo de terminais, logo t é o lookahead!

Analisador LL(1) de tabela No analisador LL(1) recursivo, o contexto de análise (onde estamos na árvore sintática) é mantido pela pilha de chamadas da linguagem Mas podemos escrever um analisador LL(1) genérico (que funciona para qualquer gramática LL(1)), mantendo esse contexto em uma pilha explícita O analisador funciona a partir de uma tabela LL(1) As linhas da tabela são os não-terminais, as colunas são terminais As células são a regra escolhida para aquele não-terminal, dado o terminal como lookahead

Uma gramática LL(1) simples: PROG -> CMD ; PROG PROG -> CMD -> id = EXP CMD -> print EXP EXP -> id EXP -> num EXP -> ( EXP + EXP ) Vamos construir a tabela LL(1)

Conjuntos FIRST+ Calculados a partir dos conjuntos FIRST e FOLLOW das regras PROG -> CMD ; PROG -> [id, print] PROG -> -> [<<EOF>>] CMD -> id = EXP -> [id] CMD -> print EXP -> [print] EXP -> id -> [id] EXP -> num -> [num] EXP -> ( EXP + EXP ) -> [(]

Tabela LL(1) id num ; + ( ) print = EOF PROG PROG -> CMD ; PROG PROG -> CMD ; PROG PROG -> CMD CMD -> id = EXP CMD -> print EXP EXP EXP -> id EXP -> num EXP -> ( EXP + EXP )

Algoritmo Pilha começa com <<EOF>> e o símbolo inicial Enquanto a pilha não está vazia retiramos o topo da pilha e: Se for um terminal: se casa com o lookahead, avançamos o lookahead, senão dá erro Se for um não-terminal: consultamos a tabela LL(1) e empilhamos o lado direito da produção correspondente, na ordem reversa Para o algoritmo construir uma árvore, é só empilhar nós ao invés de termos, e acrescentar os filhos ao nó que saiu da pilha