1 CIn / UFPE Parsing Tarciana Dias / Gustavo Carvalho / Março 2012.

Slides:



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

Tópicos em Compiladores
Compiladores Claudio Benossi
I - Noções dum compilador
Compiladores I Cristiano Damiani Vasconcellos
Linguagem de Montagem Visão geral.
Deyvisson, Rafael M.P., Renato, Robson C.F., Rodolfo
Análise Sintática Ascendente ­
Elsa Carvalho 49 Universidade da Madeira Departamento de Matemática e Engenharias Programação em Lógica e Funcional (2000/01) (Actualizado em 2005/06)
Software Básico Silvio Fernandes
Padrão de Projeto Interpreter
Compiladores Prof. Yandre Maldonado Compiladores - Prof. Yandre - 1.
Linguagens Livres de Contexto
JavaCC e JJTree Geração de compiladores implementados em Java
Curso Sistemas de Informação Disciplina: Arquitetura de Software
YACC.
Análise Sintática Ascendente
Relações em uma Gramática
Construção de Compiladores
Construção de Compiladores
Prof. Bruno Moreno Aula 4 – 11/03/2011
Implementar Listas em Linguagem Funcional II
Análise léxica e sintática
Aula prática – geração de código (.NET)
Aula prática - análise contextual
Definição de tipos - LF3Definição de tipos - LF3 PLP - Apresentação 1.
Compiladores, Aula Nº 9 João M. P. Cardoso
Linguagens Livres de Contexto
Gramáticas Livres de Contexto
Teoria e Implementação de Linguagens Computacionais
Análise Sintática – Parte 1
Teoria e Implementação de Linguagens Computacionais – IF688
Sintaxe e Semântica Prof.: Gláucya Carreiro Boechat
Estudo dos Conceitos e Paradigmas de Programação
Análises léxica e sintática
Análise Léxica Primeira fase de um compilador
Análise Léxica e Sintática
1 JavaCC Prof. Alexandre Monteiro Baseado em material cedido pelo Prof. Euclides Arcoverde Recife.
Capítulo II Gramáticas e Linguagens
Analise sintática aula-07-analise-sintática.pdf.
Análises léxica e sintática
Aula sobre JavaCC Parsing Tarciana Dias Abril 2013.
Compiladores.
Análise Sintática Prof. Alexandre Monteiro
Tradução Dirigida por Sintaxe
Mas para que serve um analisador sintático? Verificar se a estrutura gramatical do programa está correta Escrito de outra forma: O texto segue as regras.
Análise Sintática de Descida Recursiva
Faculdade Pernambucana - FAPE Setembro/2007
Aula de SableCC Teoria e Implementação de Linguagens Computacionais (Compiladores) - IF688 – Artur Ribeiro de Aquino – ara Allan Jefferson – ajss.
COMPILADORES 04 Prof. Marcos.
Um Tradutor Dirigido por Sintaxe Simples
Fundamentos de linguagens de programação
Semântica de Linguagens de Programação
Compiladores – IF688 Professor: André Santos
Copyright 2000, Departamento de Informática, UFPE. Todos os direitos reservados sob a legislação em vigor. Orientação a Objetos e Java.
JavaCC.
Sintaxe de uma Linguagem
PLP – JavaCC Java Compiler Compiler
Teoria e Implementação de Linguagens Computacionais Revisão 1º EE André Ricardo Schäffer Lopes – Rodrigo Diego Melo Amorim–
Analisador sintático: Tipos de análises sintáticas
1 CIn / UFPE Algoritmos de Parsing Gustavo Carvalho Março 2011.
Compilador Software que traduz o texto (linguagem fonte) que representa um programa para código máquina(linguagem alvo) capaz de ser executado pelo.
Faculdade Pernambuca - FAPE Compiladores Abril/2007 Compiladores Abril/2007.
Informática Teórica Engenharia da Computação. Teoria da Computação Contexto do que vamos começar a estudar As linguagens também podem ser definidas formalmente.
COMPILADORES 03 Prof. Marcos.
Análise Contextual Mauro Araújo Teoria e Implementação de Linguagens Computacionais - IF688 Centro de Informática – CIn Universidade Federal de Pernambuco.
1 Programação Multi-Paradigma Cristiano Bertolini – Ellison Siqueira – Guilherme Avelino –
1 CIn / UFPE Introdução a Parsing Gustavo Carvalho Abril 2015.
Transcrição da apresentação:

1 CIn / UFPE Parsing Tarciana Dias / Gustavo Carvalho / Março 2012

2 Roteiro Processo de Compilação Conceitos Básicos Estratégias de Parsing Gramáticas LL / Recursive Descent Algorithm Referências

3 Processo de Compilação Análise Léxica (Scanning) Análise Sintática (Parsing) Análise Semântica Ger. Código Intermediário Otimização Cód. Interm. Geração de CódigoOtimização do Cód. Gerado Tokens AST AST decorada Cód. Interm. Cód. Interm. OtimizadoCód. ObjetoCód. Objeto Otimizado Front-end Back-end Compilador (1 ou N passos) Erro

4 Processo de Interpretação Análise Léxica (Scanning) Análise Sintática (Parsing) Análise Semântica Ger. Código Intermediário Otimização Cód. Interm. Geração de CódigoOtimização do Cód. Gerado Tokens Front-end Back-end Interpretador Erro Fetch => Analyze => Execute Saídas de forma imediata Não traduz programa fonte para código objeto (a priori) Ideal (melhor desempenho): instruções com formato simplificado (bytecode) Execução

5 Roteiro Processo de Compilação Conceitos Básicos Estratégias de Parsing Gramáticas LL / Recursive Descent Algorithm Referências

6 Conceitos Básicos Gramáticas livres de contexto (GLC) –Conjunto finito de símbolos não-terminais (V) Uma classe particular de frases de uma linguagem Ex.: Programa, Expressao, Valor –Conjunto finito de símbolos terminais (Σ), disjunto de V Símbolos atômicos Ex.: ‘ 23 ’, ‘ + ’, ‘ - ‘, ‘ and ’ –Conjunto finito de regras de produção (R) –Símbolo inicial (um dos símbolos de V) (S) Ex.: Programa

7 Conceitos Básicos Exemplo –Terminais +, -, not, length, and, or, ==, ++, 0, …, 9, a, …, z, A, …, Z –Não-terminais Programa, Expressao, Valor, ExpUnaria, ExpBinaria, ValorConcreto, ValorInteiro, ValorBooleano, ValorString –Produções Programa ::= Expressao Expressao ::= Valor | ExpUnaria | ExpBinaria Valor ::= ValorConcreto ValorConcreto ::= ValorInteiro | ValorBooleano | ValorString ExpUnaria ::= "-" Expressao | "not" Expressao | "length" Expressao ExpBinaria ::= Expressao "+" Expressao | Expressao "-" Expressao | Expressao "and" Expressao | Expressao "or" Expressao | Expressao "==" Expressao | Expressao "++" Expressao ValorInteiro ::= [1-9] [0-9]* ValorBooleano ::= "true" | "false" ValorString ::= ([A-Za-z] | [0-9])*

8 Conceitos Básicos Uma árvore sintática para uma gramática G –Árvore com labels em que: As folhas são símbolos terminais Os nós são símbolos não-terminais Uma frase de G –Seqüência de terminais de uma árvore sintática (esquerda p/ direita) Exemplo: (onde o todo é ) Uma sentença –Frase cuja árvore começa a partir do símbolo inicial Exemplo: (onde o todo é 2 + 3) Linguagem gerada por G: todas as sentenças de G

9 Conceitos Básicos Gramáticas ambíguas –Podem gerar 2 árvores distintas para a mesma expressão –Exemplo: Suponha que usemos um único não-terminal string e que não façamos distinção entre dígitos e listas: Expressão: 9 – string string → string + string | string – string | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 (9 – 5 ) – (5 + 2)

10 Conceitos Básicos Expressões Regulares (REs) –Notação conveniente para expressar um conjunto de strings de símbolos terminais. ‘|’ separa alternativas; ‘*’ indica que o item anterior pode ser repetido 0 ou mais vezes; ‘(’ e ‘)’ são parênteses agrupadores. Exemplos: –Mr | Ms gera {Mr, Ms} –M(r | s) gera {Mr, Ms} –ps*t gera {pt, pst, psst, pssst,...} –ba(na)* gera {ba, bana, banana, bananana,...} –M(r | s)* gera {M, Mr, Ms, Mrr, Mrs, Msr, Mss, Mrrr,...} Assim RE são capazes de gerar linguagens simples, chamadas de linguagens regulares

11 Conceitos Básicos No entanto, linguagens de programação completas são self- embedding –Por exemplo, as expressões representadas por LE1 ou LE2 (vistas em sala de aula), ex.: a * (b+c) / d contém outra subexpressão embedded, (b + c). –O comando if x > y then m := x else m := y contém o subcomando embedded, ‘m := x’ Portanto, self-embedding nos permitem escrever expressões complexas, comandos, etc; Uma linguagem regular, ou seja, que não exibe self-embedding pode ser representada através de REs. Já uma linguagem que exiba self-embedding não pode ser gerada por REs. Para isso, nós escrevemos regras de produção recursivas usando ou BNF ou EBNF.

12 Conceitos Básicos EBNF é uma combinação de BNF com REs. Uma produção EBNF é da forma N := X, onde N é um símbolo não- terminal e X é uma RE estendida, ou seja, uma RE construída a partir de ambos os símbolos terminais e não-terminais. –Diferentemente de uma BNF, o lado direito de uma produção EBNF pode usar não somente ‘|’ mas também ‘*’ além de ‘(‘ e ‘)’. –Diferentemente de uma RE ordinária, o lado direito pode conter símbolos não- terminais como também símbolos terminais. Logo, podemos escrever regras de produção recursivas, e uma EBNF é capaz de gerar uma linguagem com self-embedding.

13 Conceitos Básicos Exemplo de gramática expressa em EBNF: Expression ::= primary-Expression (Operator primary-Expression)* primary-Expression ::= Identifier | (Expression) Identifier ::= a | b | c | d | e Operator ::= + | - | * | / Esta gramática gera expressões como: a + b a – b – c a + (b * c) a * (b + c) / d a – (b – (c – (d - e)))

14 Conceitos Básicos Transformações de Gramática –Fatoração à esquerda –Ex;: XY | XZ equivale à X (Y | Z) single-Command ::= V-name := Expression | if Expression then single-Command else single-Command single-Command ::= V-name := Expression | if Expression then single-Command ( ε | else single-Command)

15 Conceitos Básicos Transformações de Gramática –Eliminação de recursão à esquerda N ::= X | NY, onde N é um símbolo não-terminal e X e Y são REs estendidas. Esta produção é recursiva à esquerda. N ::= X(Y)* Substituindo por uma regra EBNF equivalente

16 Conceitos Básicos Transformações de Gramática Identifier ::= Letter | Identifier Letter | Identifier Digit Identifier ::= Letter | Identifier (Letter | Digit) Identifier ::= Letter (Letter | Digit)* Fatorando à esquerda Eliminando recursão à esquerda

17 Conceitos Básicos A necessidade da eliminação de recursão à esquerda é melhor entendida depois que se vai usar a abordagem top-down;

18 Roteiro Processo de Compilação Conceitos Básicos Estratégias de Parsing Gramáticas LL / Recursive Descent Algorithm Referências

19 Estratégias de Parsing Objetivo –Reconhecimento de uma string de entrada (seqüência de tokens) e decisão se esta é ou não uma sentença de G –Determinação de sua estrutura de frases (pode ser representada por uma árvore sintática) –Gramática não ambígua: cada sentença tem exatamente uma syntax tree Top-Down –Examina os símbolos terminais da esquerda para a direita –Forma a ST (syntax tree) de cima para baixo –Parsing ok: string de entrada totalmente conectada à ST L(eft-to-right) L(eft-most-derivation) => LL Bottom-Up –Examina os símbolos terminais da esquerda para a direita –Forma a ST (syntax tree) de baixo (nós terminais) para cima(nó raiz) –Parsing ok: string de entrada reduzida a uma S-tree S(imple) L(eft-to-right) R(ight-most-derivation) => SLR L(eft-to-right) R(ight-most-derivation) => LR L(ook) A(head) L(eft-to-right) R(ight-most-derivation) => LALR

20 Estratégia Bottom-Up Exemplo: Sentence ::= Subject Verb Object Subject ::= I | a Noun | the Noun Object ::= me | a Noun | the Noun Noun ::= cat | mat | rat Verb ::= like | is | see | sees the cat sees a rat. Noun Subject Verb Noun Object Sentence Aqui ele não poderia ter escolhido um Subject?

21 Estratégia Top-Down Exemplo: Sentence ::= Subject Verb Object Subject ::= I | a Noun | the Noun Object ::= me | a Noun | the Noun Noun ::= cat | mat | rat Verb ::= like | is | see | sees the cat sees a rat. Noun Subject Verb Noun Object Sentence

22 Estratégias de Parsing Qual estratégia de parsing devemos usar? –Isso vai depender do tipo de gramática ! –Sempre é necessário escolher qual regra de produção aplicar Isto é feito de acordo com o algoritmo de Parsing –Recursive Descent é um algoritmo de parsing top-down –Consiste de um grupo de métodos parseN, um para cada símbolo não-terminal N de G. Estes métodos cooperam para fazer o parse das sentenças completas parseSubject parseVerbparseObject parseSentence the cat sees a rat. parseNoun

23 Roteiro Processo de Compilação Conceitos Básicos Estratégias de Parsing Gramáticas LL / Recursive Descent Algorithm Referências

24 Gramáticas LL(1) Deve-se expressar a gramática em EBNF, com uma regra de produção simples para cada símbolo não-terminal, e realizar as transformações de gramática necessárias, por exemplo, sempre eliminar recursão à esquerda e fatorizar à esquerda sempre que possível Gramáticas LL(1): –Se a gramática contém X | Y, starters[[ X ]] e starters[[ Y ]] devem ser disjuntos –Se a gramática contém X*, starters[[ X ]] deve ser disjunto do conjunto de tokens que podem seguir X* Na prática quase toda gramática de uma linguagem de programação pode ser transformada em LL(1), sem mudar a linguagem que a gramática gera Recursive-descent parsing é somente adequado para gramáticas LL(1) –Em geral, o projetista da linguagem projeta a sua sintaxe para ser adequada à um parsing recursive-descent.

25 Gramáticas não-LL(1) Exemplo de uma Gramáticas não LL(1): Program ::= single-Command Command ::= single-Command (; single-Command)* V-name ::= Identifier single-Command ::= V-name := Expression | Identifier ( Expression ) | if Expression then single-Command else single-Command … Expression ::= primary-Expression (Operator primary-Expression)* primary-Expression ::= Integer-Literal | Identifier …

26 Gramáticas não-LL(1) Desenvolvimento do método parseSingleCommand: private void parseSingleCommand(){ switch(currentToken.kind){ case Token.IDENTIFIER: { parseVname(); accept(Token.BECOMES); parseExpression(); } break; case Token.IDENTIFIER: { parseIdentifier (); accept(Token.LPAREN); parseExpression(); accept(Token.RPAREN); } break; } Uma simples fatoração à esquerda resolve o problema! … V-name ::= Identifier single-Command ::= V-name := Expression | Identifier ( Expression ) | … … single-Command ::= Identifier (:= Expression | ( Expression ) )

27 Gramáticas não-LL(1) Exemplo de uma Gramáticas não LL(1): Aqui, starters[[ ;Declaration ]] = {;} e o conjunto de terminais que seguem (; Declaration)* neste contexto também é {;} –Como não são disjuntos, então a gramática não é LL(1) … Block::= begin Declaration (; Declaration)* ; Command end Declaration ::= integer Identifier (, Identifier)* … private void parseBlock(){ accept(Token.BEGIN){ parseDeclaration(); while (currentToken.kind == Token.SEMICOLON) acceptIt(); parseDeclaration(); } accept(Token.SEMICOLON); parseCommand(); accept(Token.END); } … Block::= begin Declaration ; (Declaration;)* Command end … Como resolver?

28 Recursive Descent Parser –Algoritmo de parsing para gramáticas LL –Visão geral Para cada produção N, crie um método parseN Crie uma classe parser com um atributo currentToken –E os métodos parseN –E os métodos auxiliares: accept e acceptIt –E um método público parse que chama parseS O código de cada método parseN depende da produção N A árvore é dada implicitamente pela chamada dos métodos –Pode ser criada explicitamente

29 Recursive Descent Parser Programa ::= Expressao Expressao ::= ExpCondicionalOr ExpCondicionalOr ::= ExpCondicionalAnd ( ("or" ExpCondicionalAnd)* | ε)? ExpCondicionalAnd ::= ExpIgualdade ( ("and" ExpIgualdade)* | ε)? ExpIgualdade ::= ExpAritmetica ( ("==" ExpAritmetica)? | ε)? ExpAritmetica ::= ExpConcatenacao ( (("+" | "-") ExpConcatenacao)* | ε)? ExpConcatenacao ::= ExpUnaria ( ("++" ExpUnaria)* | ε)? ExpUnaria ::= "-" Expressao | "not" Expressao | "length" Expressao | ValorConcreto ValorConcreto ::= ValorInteiro | ValorBooleano | ValorString accept(int type) { if ( currentToken.getType() == type ) { currentToken = scanner.getNextToken(); } else { // ERRO } acceptIt() { currentToken = scanner.getNextToken(); } Métodos auxiliares

30 Recursive Descent Parser parsePrograma() parseExpressao(); } parseExpressao() { parseExpCondicionalOr(); } parseExpCondicionalOr() { parseExpCondicionalAnd(); while ( currentToken.getType() == Token.OR ) { acceptIt(); parseExpCondicionalAnd(); } parseExpIgualdade() { parseExpAritmetica(); if ( currentToken.getType() == Token.EQUAL ) { acceptIt(); parseExpAritmetica(); } Programa ::= Expressao Expressao ::= ExpCondicionalOr ExpCondicionalOr ::= ExpCondicionalAnd ( ("or" ExpCondicionalAnd)* | ε)? ExpCondicionalAnd ::= ExpIgualdade ( ("and" ExpIgualdade)* | ε)? ExpIgualdade ::= ExpAritmetica ( ("==" ExpAritmetica)? | ε)? ExpAritmetica ::= ExpConcatenacao ( (("+" | "-") ExpConcatenacao)* | ε)? ExpConcatenacao ::= ExpUnaria ( ("++" ExpUnaria)* | ε)? ExpUnaria ::= "-" Expressao | "not" Expressao | "length" Expressao | ValorConcreto ValorConcreto ::= ValorInteiro | ValorBooleano | ValorString parse () { parsePrograma(); if ( currentToken.getType() != Token.EOT ) { // ERRO }

31 Recursive Descent Parser parseExpUnaria() { if ( currentToken.getType() == Token.MINUS ) { acceptIt(); parseExpressao(); } else if ( currentToken.getType() == Token.NOT ) { acceptIt(); parseExpressao(); } else if ( currentToken.getType() == Token.LENGTH ) { acceptIt(); parseExpressao(); } else { parseValorConcreto(); } Programa ::= Expressao Expressao ::= ExpCondicionalOr ExpCondicionalOr ::= ExpCondicionalAnd ( ("or" ExpCondicionalAnd)* | ε)? ExpCondicionalAnd ::= ExpIgualdade ( ("and" ExpIgualdade)* | ε)? ExpIgualdade ::= ExpAritmetica ( ("==" ExpAritmetica)? | ε)? ExpAritmetica ::= ExpConcatenacao ( (("+" | "-") ExpConcatenacao)* | ε)? ExpConcatenacao ::= ExpUnaria ( ("++" ExpUnaria)* | ε)? ExpUnaria ::= "-" Expressao | "not" Expressao | "length" Expressao | ValorConcreto ValorConcreto ::= ValorInteiro | ValorBooleano | ValorString parseValorConcreto() { if ( currentToken.getType() == Token.INT ) { acceptIt(); } else if ( currentToken.getType() == Token.BOOLEAN ) { acceptIt(); } else { accept(Token.STRING); }

32 Roteiro Processo de Compilação Conceitos Básicos Estratégias de Parsing Gramáticas LL / Recursive Descent Algorithm Referências

33 Referências WATT, D.; BROWN, D. Programming Language Processors in Java. –Capítulo 4 Foco maior na abordagem LL AHO, A.; LAM, M.; SETHI, R.; ULLMAN, J. Compilers: Principles, Techniques & Tools. –Capítulo 4 Foco maior na abordagem LR

34 CIn / UFPE Parsing Tarciana Dias / Gustavo Carvalho / Março 2012