Buscar

compiladores granbery 2004

Prévia do material em texto

*
*
*
Linguagens Formais,
Lex & Yacc
Vladimir Oliveira Di Iorio 
*
*
*
Introdução
*
*
*
Linguagens formais são linguagens cuja sintaxe (e geralmente semântica) é precisamente definida, sem ambigüidades.
São diferentes de linguagens naturais, recheadas de imprecisões e ambigüidades.
Exemplos de linguagens formais: linguagens de programação como C, C++, Pascal, Java, linguagens de consulta como SQL.
Linguagens Formais
*
*
*
Uma definição para o comando UPDATE da linguagem SQL, usando estilo BNF:
	UPDATE [TRANSACTION transaction] {table | view} SET col = <val> [, col = <val> …] [WHERE <search_condition> | WHERE CURRENT OF cursor];
Exemplo de um comando válido:
	
	UPDATE CLIENTE SET DATA_INCLUSAO = CURRENT DATE;
Exemplo
*
*
*
Estudar fundamentos teóricos de linguagens formais:
alfabetos, palavras e linguagens;
expressões regulares;
gramáticas regulares e livres de contexto.
Apresentar ferramentas no estilo Lex, para processamento de entradas usando linguagens regulares.
Apresentar ferramentas no estilo Yacc, para processamento de entradas usando linguagens livres de contexo.
Dar exemplos de utilização de ferramentas Lex e Yacc, na construção de filtros, pequenos interpretadores e compiladores.
Objetivos do Curso
*
*
*
Expressões regulares são um formalismo usado para definir o formato correto de uma cadeia de caracteres.
São usados símbolos de um alfabeto qualquer, juntamente com operadores especiais, como '*' e '+'.
Um exemplo: a+ b c*
	Essa expressão define que as cadeias válidas são aquelas que iniciam com uma seqüência não vazia de símbolos 'a', seguidos de exatamente um símbolo 'b', seguido de uma seqüência possivelmente vazia de símbolos 'c'.
Visão Geral - Expressões Regulares
*
*
*
Autômatos finitos são um formalismo equivalente às expressões regulares, que usa representação em forma de grafo.
Autômato finito equivalente à expressão a+ b c* :
	
Visão Geral - Autômatos
a
a
b
c
*
*
*
Gramáticas são mais um formalismo usado para definir o formato correto de uma cadeia de caracteres.
Os símbolos são classificados como terminais e não terminais, e são usadas regras de reescrita para os símbolos não terminais.
Gramática equivalente à expressão a+ b c* :
		<S> -> a<A>
		<A> -> a<A> | b<B> | b
		<B> -> c<B> | c
Visão Geral - Gramáticas
*
*
*
Ferramentas no estilo Lex utilizam uma especificação baseada em expressões regulares.
A partir dessa definição, Lex gera automaticamente um programa que simula o comportamento de autômatos equivalentes às expressões fornecidas.
O programa lê uma entrada e verifica se essa entrada está no formato especificado.
Enquanto verifica a entrada, pode executar algumas ações (trechos de programa) desejadas.
Visão Geral - Lex
*
*
*
Ferramentas no estilo Yacc funcionam de maneira parecida com Lex - mas utilizam uma especificação baseada em gramáticas.
O formalismo de gramáticas é mais poderoso que o de expressões regulares e autômatos, assim é possível gerar programas que processam entradas mais complexas.
Da mesma forma que Lex, Yacc pode associar ações (trechos de programa) a cada regra da gramática. À medida que a entrada é processada, ações adequadas são executadas. Essas ações podem ser, por exemplo, a interpretação ou compilação da entrada.
Visão Geral - Yacc
*
*
*
Um compilador ou interpretador pode ser gerado rapidamente usando Lex e Yacc em conjunto.
Lex é geralmente usado para separar uma entrada em unidades léxicas. No caso de uma linguagem de programação, por exemplo, pode identificar um número real como "1.23" ou uma palavra-chave como "begin".
Yacc pode usar as unidades léxicas produzidas por Lex e verificar se essas unidades formam uma entrada válida. Enquanto isso pode, por exemplo, compilar a entrada.
Visão Geral - Lex & Yacc
*
*
*
Visão Geral - Lex & Yacc
Lex
Analisador
Léxico
regras
léxicas
entrada
Yacc
Analisador
Sintático
regras
sintáticas
saída
*
*
*
O primeiro compilador para uma linguagem de programação de alto nível foi desenvolvido para a linguagem FORTRAN, na década de 50.
Na época, a teoria de linguagens formais ainda não estava estabelecida (Chomsky, 1959).
Foi necessário um esforço gigantesco, e a utilização de um grande número de programadores.
História
*
*
*
Atualmente, com a utilização de ferramentas como Lex e Yacc, é possível construir rapidamente um compilador.
O processo de geração automática utilizado por essas ferramentas, em geral, produz analisadores quase tão rápidos quanto os escritos totalmente à mão.
História
*
*
*
Linguagens Formais
*
*
*
Um alfabeto é um conjunto finito de símbolos distintos.
Exemplo: Σ = {a,b,c} 
	é um alfabeto formado pelos símbolos a, b e c.
Uma palavra sobre um alfabeto Σ é uma seqüência de comprimento finito formada com símbolos desse alfabeto.
Algumas palavras sobre o alfabeto Σ = {a,b,c} :
		abc , bcaabbac , b
A palavra de comprimento zero é representada por λ .
Alfabetos e Palavras
*
*
*
Seja Σ um alfabeto qualquer. O conjunto de todas as palavras sobre Σ é denotado por Σ* .
Por exemplo, se Σ = {a,b,c} , então
	Σ* = {λ, a, b, c, aa, ab, ac, ba, bb, bc, ca, cb, cc, aaa, ...}
Uma linguagem sobre um alfabeto Σ é qualquer subconjunto de Σ* .
Linguagens sobre Σ = {a,b,c} :
{a, ab, bbc}
{aa, ab, ac, ba, bb, bc, ca, cb, cc}
todas as palavras que começam com a
Linguagens
*
*
*
Outras linguagens sobre Σ = {a,b,c} :
todas as palavras de comprimento par
{λ, a, b, c} {λ}
{ } Σ* 
A concatenação de 2 palavras será representada por sua justaposição. Por exemplo, sejam uma palavra x = abc e uma palavra y = bb .
	A concatenação de x com y é representada por
		xy = abcbb .
Linguagens
*
*
*
Se X e Y são duas linguagens,
	a concatenação de X com Y, denotada por XY,
	é a seguinte linguagem:
		XY = { palavras xy tais que x  X e y  Y }
	
	Ou seja, são as palavras formadas pela concatenação de uma palavra qualquer de X
	com uma palavra qualquer de Y
	(prefixo pertence a X, sufixo pertence a Y).
Concatenação de linguagens
*
*
*
Por exemplo, sejam
		X = {a,b,c} e Y = {abb,ba} .
	Então
		XY = {aabb,aba,babb,bba,cabb,cba}
A concatenação de X consigo mesma n vezes é Xn :
		Xn = X X X ... X X (n vezes)
Por definição, X0 é o conjunto {λ}:
		X0 = {λ} 
Concatenação de linguagens
*
*
*
Outros exemplos, com X = {a,b,c} e Y = {abb,ba} :
	X0 = {λ}
	X1 = X = {a,b,c}
	X2 = XX = {aa,ab,ac,ba,bb,bc,ca,cb,cc}
	X3 = X2X = {aaa,aab,aac,aba,abb,abc,aca,acb,acc,
			baa,bab,bac,bba,bbb,bbc,bca,bcb,bcc,
			caa,cab,cac,cba,cbb,cbc,cca,ccb,ccc}
	Y2 = YY = {abbabb,abbba,baabb,baba}
Concatenação de linguagens
*
*
*
Considere o alfabeto Σ = {a,b,c}. Seja L1 a linguagem das
palavras que começam com o símbolo 'a', e L2 a linguagem
das palavras que terminam com o símbolo 'b'.
O conjunto {a,b,c} é uma linguagem sobre Σ ?
Qual a menor linguagem que pode ser criada com esse alfabeto (menor número de palavras) ?
Qual é o resultado da concatenação de L1 com L2 ?
Qual é o resultado da união de L1 com L2 ?
A união de 2 linguagens pode resultar em uma linguagem menor que as outras duas?
A concatenção de 2 linguagens pode resultar em uma linguagem menor que as outras duas?
Exercícios
*
*
*
A operação X*, denominada Fecho de Kleene de X,
	é definida como a união de Xi ,
	com i variando de 0 a infinito:
		X* = X0  X1  X2  X3  ... 
	Ou seja, X* consiste de todas as palavras que se pode construir a partir dos elementos de X .
Por exemplo, se X = {a,b,c} e Y = {abb,ba} :
	X* = {λ,a,b,c,aa,ab,ac,ba,bb,bc,ca,cb,cc,aaa,...}
	Y* = {λ,abb,ba,abbabb,abbba,baabb,baba,abbabbabb,...}
Fecho de Kleene
*
*
*
Expressões Regulares
*
*
*
Para definir expressões regulares, vamos usar as seguintes abreviações:o conjunto {a} será representado simplesmente por a ;
{a,b,c,...} será representado por a  b  c ... 
O conjunto de expressões regulares sobre um alfabeto Σ qualquer é definido como:
os conjuntos { } e λ são expressões regulares;
para todo símbolo a de Σ, a é uma expressão regular;
se x e y são expressões regulares sobre Σ, então também são expressões regulares: x  y , xy e x* .
Expressões Regulares - Definição
*
*
*
a  b 
	linguagem formada apenas pelas palavras a e b
abb  aa  ac 
	linguagem formada exatamente pelas palavras abb, aa e ac, ou seja, {abb,aa,ac}		
a* 
	linguagem (infinita) de todas as palavras formadas apenas com o símbolo a 
		
Expressões Regulares - exemplos
*
*
*
(a  b) (ab  bb)
	 denota a linguagem {aab,abb,bab,bbb} 
(a  b)*
	a linguagem {λ,a,b,aa,ab,ba,bb,aaa,...} :
(aa)*
	palavras só com a, e com comprimento par (inclui a palavra nula)
Expressões Regulares - exemplos
*
*
*
Outras abreviações:
x+ é usado para abreviar xx* ;
x2 é usado para abreviar xx ; 
x3 para abreviar x2x etc.
(a2)+ 
	palavras só com a, e com comprimento par maior que zero (não inclui a palavra nula)
a (a  b  c)*
	Linguagem sobre {a,b,c} das palavras que começam com o símbolo a
Expressões Regulares - exemplos
*
*
*
a (a  b  c)* b 
	Linguagem sobre {a,b,c} das palavras que começam com o símbolo a e terminam com b	
(b  c) (a  b  c)*  λ
	Linguagem das palavras sobre {a,b,c} que não começam com o símbolo a
Expressões Regulares - exemplos
*
*
*
Que linguagens são essas?
 (a  b)* b (a  b)*
 (a  b)* a (a  b)* a (a  b)*
 (a  b  c) (a  b  c) (a  b  c) 
 (b* a b* a b*) *
Expressões Regulares - exercícios
*
*
*
Construa expressões regulares para:
Linguagem das palavras de comprimento par sobre o alfabeto {a,b,c}.
Linguagem das palavras sobre o alfabeto {a,b,c} que contenham exatamente um símbolo c .
Expressões Regulares - exercícios
*
*
*
As linguagens que podem ser especificadas por expressões regulares são chamadas de
		linguagens regulares.
Mas expressões regulares não podem ser usadas
	para especificar qualquer linguagem desejada.
Muitas linguagens não conseguem ser especificadas com expressões regulares, necessitando de formalismos mais poderosos.
Expressões Regulares - restrições
*
*
*
Exemplos de linguagens que não são regulares:
Linguagem cujas palavras têm o formato anbn,
para qualquer n. Ou seja:
			{ λ, ab, aabb, aaabbb, ... }
Linguagem sobre alfabeto { a, b, (, ) } com
aninhamento correto de parêntesis abrindo e fechando.
Expressões Regulares - restrições
*
*
*
Lex - Gerador de
Analisadores Léxicos
*
*
*
Ajuda a escrever programas cuja entrada pode ser descrita por meio de expressões regulares.
Principais utilizações:
transformações simples e extração de padrões em textos;
separação da entrada em unidades léxicas, como preparação para um analisador sintático.
Os exemplos utilizados nesta apresentação seguirão o formato do programa JFlex, um clone de Lex que gera código em Java.
Lex - Gerador de
Analisadores Léxicos
*
*
*
Um texto fonte para LEX é composto de:
lista de expressões regulares;
fragmentos de programa associados a cada expressão.
Quando o fonte é submetido ao LEX, um programa é automaticamente gerado. Esse programa:
lê um arquivo de entrada;
particiona a entrada em cadeias que "casam" com as expressões regulares definidas;
executa os fragmentos de programa associados às expressões "casadas";
escreve no arquivo de saída.
Lex - fonte
*
*
*
Lex - esquema de funcionamento
Lex
Yylex
fonte
Yylex
(programa
gerado)
entrada
saída
*
*
*
Um exemplo, usando o gerador JFlex:
Lex - exemplo com JFlex
Como na maioria dos clones de Lex, o fonte é dividido em 3 seções, separadas por "%%".
No JFlex, as seções são:
 código do usuário a ser incluído;
 opções e declarações;
 regras e ações associadas.
*
*
*
Lex - exemplo com JFlex
*
*
*
Lex - exemplo com JFlex
Essa regra "casa" com um caractere "\t" (tabulação) da entrada. Se isso acontecer, o código entre chaves é executado.
Se a entrada não casa com nenhuma regra, a ação default é executada: escrever o caractere diretamente na saída.
Ou seja, esse "programa" Lex serve para ler uma entrada e trocar as tabulações por "TAB".
*
*
*
Supondo que o fonte abaixo esteja no arquivo ex1.flex:
> jflex ex1.flex
Reading "ex1.flex"
Constructing NFA : 6 states in NFA
Converting NFA to DFA : ..
4 states before minimization, 3 states in minimized DFA
Writing code to "Yylex.java"
> javac Yylex.java
Executa jflex,
gerando Yylex.java
gera Yylex.class
Lex - usando o JFlex
*
*
*
Supondo que o texto abaixo esteja no arquivo teste.txt:
> java Yylex teste.txt
teste...
TABEsta linha comeca com tab
TABTABEsta com 2 tabs
teste...
		Esta linha comeca com tab
			Esta com 2 tabs
fim.
Para gerar a saída no arquivo saida.txt:
> java Yylex teste.txt > saida.txt
Lex - executando analisador gerado
*
*
*
Expressões regulares usadas em regras:
caracteres de texto: letras, dígitos, caracteres especiais como '\t', '\n' etc.
operadores: usados como funções especiais das expressões regulares.
Na maioria das versões de Lex, os operadores são:
		" \ [ ] ˆ - ? . * + | ( ) $ / { } % < >
Os operadores podem ser utilizados como caracteres de texto, se vierem precedidos de um caractere de escape ('\') ou se estiverem entre aspas duplas.
Nas slides seguintes, veremos o funcionamento de alguns operadores.
Lex - expressões regulares
*
*
*
Classes de caracteres:
	[abc]	casa com um dos caracteres a, b ou c
	[a-zA-Z]	casa com uma letra minúscula ou maiúscula
	[^a-zA-Z]	casa com qualquer coisa, exceto letras
Caractere arbitrário:
	.		casa com qualquer caractere, exceto newline
Exclusivos de JFlex:
	[:letter:] 		isLetter( )
	[:digit:] 		isDigit( )
	[: lowercase:] 	isLowerCase( )
	[:uppercase:] 	isUpperCase( )
Lex - expressões regulares
*
*
*
Expressões repetidas (* e +):
	a*		casa com qualquer número consecutivo de a's 				(incluindo zero repetições)
	a+		casa com uma ou mais ocorrências de a
	[A-Za-z][A-Za-z0-9]* 
			definição usual de "identificador"
Alternação e agrupamento ( | ):
	(ab|cd)	casa com ab ou cd
	
Lex - expressões regulares
*
*
*
Expressão opcional (?):
		ab?c	casa com ac ou abc;
	(ab|cd+)?(ef)*
			casa com abefef , efefef , cdef, ou cddd 					mas não com abc , abcd , ou abcdef
Macros e repetições:
		{digit}	casa com a definição da expressão regular
			digit (seção de definições)
		a{n}	casa com n vezes a concatenação de a
Lex - expressões regulares
*
*
*
Como ocorre a escolha das regras para casamento?
À medida que a entrada é lida, o analisador procura a regra que possibilita o casamento com a mais longa possível seqüência de caracteres da entrada.
Se mais de uma regra possibilita o casamento com a seqüência mais longa, a escolha é feita pela ordem em que as regras aparecem na fonte.
	
Lex - escolha de regras
*
*
*
Supondo que o fonte abaixo esteja no arquivo ex2.flex:
> jflex ex2.flex
Reading "ex2.flex"
Constructing NFA : 16 states in NFA
Converting NFA to DFA : ......
8 states before minimization, 6 states in minimized DFA
Writing code to "Yylex.java"
> javac yylex.java
Lex - escolha de regras
*
*
*
Supondo que o texto abaixo esteja no arquivo teste.txt:
> java Yylex teste.txt
abcde
abc
abc123
123abcd
123abc
O que será impresso???
Lex - escolha de regras
*
*
*
abcde
abc
abc123
123abcd
123abc
-----id
-----abc
-----id
123-----id
123-----abc
Entrada:
Saída:
Lex - escolha de regras
*
*
*
Dentro das ações associadas às regras, alguns objetos e métodos podem ser acessados pelos trechos de programa.
Esses objetos e métodos, gerados automaticamente pelo analisador, possuem nomes que iniciam com "yy", para evitarconflito com objetos do código inserido pelo usuário.
Exemplos: o texto que foi "casado" com a regra, a linha e coluna atuais etc.
Nos próximos slides, veremos esses e outros exemplos de objetos e métodos acessíveis dentro das ações associadas às regras.
Lex - objetos acessados nas ações
*
*
*
String yytext() : retorna a seqüência de caracteres que foi casada com a regra.
int yylength() : retorna o comprimento da seqüência casada.
int yyline : contém o número da linha atual.
int yycolumn : contém o número da coluna atual, na linha atual.
Lex - objetos acessados nas ações
*
*
*
*
*
*
abcde
// linha comentada
abc
abc123
123abcd
123abc
id=abcde linha= 0 coluna = 0
id=abc linha= 2 coluna = 0
id=abc123 linha= 3 coluna = 0
int=123
id=abcd linha= 4 coluna = 3
int=123
id=abc linha= 8 coluna = 3
Entrada:
Saída:
*
*
*
Inserir um caractere inválido (ex: "@") no arquivo de entrada e executar a especificação anterior.
Responder: é possível construir uma especificação
	Lex que reconheça fórmulas matemáticas sintaticamente
	corretas, com operandos, operadores e parêntesis
	aninhados?
	Exemplo:
		12*((5 + 2) / (3 - 1))
Lex - exercícios
*
*
*
No exemplos exibidos até agora, Lex é usado para gerar um programa que processa toda a entrada de uma vez.
Como explicado anteriormente, outra importante utilização de Lex é na separação da entrada em unidades léxicas, para ser usado em conjunto com um analisador sintático.
Lex - uso com analisador sintático
*
*
*
Lex
Yylex
regras
léxicas
entrada
Yacc
Yyparse
regras
sintáticas
saída
Lex - uso com analisador sintático
*
*
*
Quando Lex é usado em conjunto com um analisador sintático, uma classe Main não é gerada.
Lex gera um método yylex que retorna a unidade léxica que foi extraída do texto de entrada.
O método yylex deve ser seguidamente executado pelo analisador sintático, para obter essas unidades sintáticas, chamadas tokens.
Algumas ações devem conter comandos return, que retornam o token que foi extraído, para o analisador sintático.
Lex - uso com analisador sintático
*
*
*
Usa classe Symbol
definida em java_cup
Usa java_cup
Diretiva para teste: cria
uma função Main artificial
Retorna um token.
O código do token é definido
no analisador sintático.
*
*
*
Gramáticas
*
*
*
Gramáticas são um formalismo utilizado para especificação de linguagens.
Em uma gramática, os símbolos são classificados como terminais (símbolos que formam as palavras da linguagem) e não terminais.
Os símbolos não terminais são também chamados de variáveis.
O processo de geração de palavras é denominado derivação, e consiste em regras de reescrita dos símbolos não terminais.
Gramáticas - Definição
*
*
*
Exemplo:
		<S> -> a<A>
		<A> -> a<A> | b<B> | b
		<B> -> c<B> | c
No exemplo acima, símbolos entre < > são considerados como não terminais ( <S>, <A>, <B> ). Os demais são os símbolos terminais ( a, b, c ).
Cada regra é composta de um lado esquerdo e um lado direito, separados por -> .
O único tipo de gramática que vamos estudar são as gramáticas livres de contexto, em que o lado esquerdo é apenas uma variável (não terminal).
Gramáticas - Exemplo
*
*
*
Exemplo:
		<S> -> a<A>
		<A> -> a<A> | b<B> | b
		<B> -> c<B> | c
As regras <B> -> c<B> | c são uma abreviação para as regras <B> -> c<B> e <B> -> c .
Uma das variáveis é denominada símbolo inicial. Para simplificar, vamos supor que seja o símbolo do lado esquerdo da primeira regra (neste caso, <S> ).
Outra simplificação: geralmente, usa-se maiúsculas para variáveis e minúsculas para terminais, dispensando <> .
Gramáticas - Exemplo
*
*
*
Exemplo:
		S -> aA
		A -> aA | bB | b
		B -> cB | c
Uma derivação é uma seqüência de palavras formadas por símbolos terminais e variáveis, onde cada palavra é derivada da anterior.
Uma palavra y é derivada de x (escrevemos x => y) se y pode ser obtida substituindo uma variável v de x por algum lado direito das regras de v.
Por exemplo, são válidas as seguintes relações :
	aabB => aabc , aaA => aaaA , ABAB => AcAB
Gramáticas - Derivação
*
*
*
Exemplo:
		S -> aA
		A -> aA | bB | b
		B -> cB | c
Exemplo de derivação :
	aaA => aaaA => aaaaA => aaaabB => aaaabcB
Usa-se a abreviação =>* para indicar derivação em 0 ou mais passos: aaA =>* aaaabcB
Derivações de interesse, para uma gramática, são aquelas cuja primeira palavra é o símbolo inicial, e a última palavra contém apenas símbolos terminais.
Gramáticas - Derivação
*
*
*
Exemplo:
		S -> aA
		A -> aA | bB | b
		B -> cB | c
Derivações de palavras com terminais a partir de S :
	S => aA => aaA => aab
	S => aA => abB => abcB => abccB => abccc
	S =>* aaabcc
A linguagem gerada por uma gramática é o conjunto de palavras apenas com símbolos terminais, derivadas a partir do símbolo inicial.
Linguagem de uma gramática
*
*
*
Usando a gramática abaixo:
		S -> aA
		A -> aA | bB | b
		B -> cB | c
	mostre todos os passos das seguintes derivações:
		S =>* ab
		S =>* aaabcc
Qual é a palavra de menor comprimento, apenas com símbolos terminais, que pode ser derivada a partir do símbolo inicial da gramática acima?
Qual é a linguagem gerada pela gramática acima? 
Exercícios
*
*
*
Exemplo:
		S -> aA
		A -> aA | bB | b
		B -> cB | c
A linguagem gerada pela gramática acima é o conjunto de palavras sobre {a,b,c} que começam com a (uma ou mais ocorrências), seguido de exatamente um b, seguido de qualquer número de c. 
Ou seja, a linguagem gerada por essa gramática é: 	a+bc* 
MAS: gramáticas podem gerar linguagens bem mais complexas que as expressas por expressões regulares!
Linguagem de uma gramática
*
*
*
Gramáticas com regras cujo lado esquerdo é apenas uma variável são chamadas gramáticas livres de contexto.
As linguagens geradas por gramáticas livres de contextro são chamadas linguagens livres de contexto.
As gramáticas regulares são um subconjunto das gramáticas livres de contexto. São aquelas com regras cujo lado direito é λ, ou é composto apenas de um terminal, ou então um terminal seguido de uma variável.
A gramática usada nos exemplos anteriores é regular.
As linguagens geradas por gramáticas regulares são chamadas de linguagens regulares.
Regulares X Livres de Contexto
*
*
*
Exemplo:
		S -> aSb | λ
		
Algumas palavras geradas pela gramática acima:
	
Gramáticas Livres de Contexto
	S => λ
	S => aSb => ab
	S => aSb => aaSbb => aabb
	S => aSb => aaSbb => aaaSbbb => aaabbb
Qual é a linguagem gerada pela gramática acima?
Resposta: o conjunto de palavras sobre {a,b} com o formato anbn , n ≥ 0.
*
*
*
Exemplo:
		S -> aSB | λ
		B -> bb
		
Algumas palavras geradas pela gramática acima:
	
Gramáticas Livres de Contexto
	S => λ
	S => aSB => aB => abb
	S => aSB => aaSBB => aaBB => aabbB => aabbbb
	S => aSB => aaSBB => aaaSBBB => aaaBBB => aaabbBB =>
	 => aaabbbbB => aaabbbbbb
Qual é a linguagem gerada pela gramática acima?
É o conjunto de palavras sobre {a,b} com o formato anb2n , n ≥ 0.
*
*
*
Exemplo:
		S -> aSbb | A
		A -> Ac | λ
		
Algumas palavras geradas pela gramática acima:
	
Gramáticas Livres de Contexto
	S => A => λ
	S => aSbb => aAbb => abb
	S => aSbb => aaSbbbb => aaAbbbb => aacbbbb
	S => aSbb => aAbb => aAcbb => aAccbb => aAcccbb => 
	 => aAcccbb => acccbb
Qual é a linguagem gerada pela gramática acima?
 É o conjunto de palavras sobre {a,b,c} com o formato ancmb2n , m,n ≥ 0.
*
*
*
Construir uma gramática sobre o alfabeto
		{ a, b, c, +, -, (, ) }
	para gerar fórmulas matemáticas sintaticamente corretas em que a, b e c são operandos, + e - são operadores, e os parêntesis são corretamente aninhados.
Exercício
*
*
*
Exemplo:
		S -> aSB | λ
		B -> bb
		
Árvore de derivação para a palavra aaabbbbbb:
	
Árvores de DerivaçãoS
a S B
a S B
a S B
λ
bb
bb
bb
*
*
*
Exemplo:
		S -> aSB | λ
		B -> bb
		
Árvore de derivação para a palavra aaabbbbbb:
	
Árvores de Derivação
S
a S B
a S B
a S B
λ
bb
bb
bb
*
*
*
Exemplo:
		S -> aSB | λ
		B -> bb
		
Árvore de derivação para a palavra aaabbbbbb:
	
Árvores de Derivação
S
a S B
a S B
a S B
λ
bb
bb
bb
*
*
*
Exemplo:
		S -> aSB | λ
		B -> bb
		
Árvore de derivação para a palavra aaabbbbbb:
	
Árvores de Derivação
S
a S B
a S B
a S B
λ
bb
bb
bb
*
*
*
Exemplo:
		S -> aSB | λ
		B -> bb
		
Árvore de derivação para a palavra aaabbbbbb:
	
Árvores de Derivação
S
a S B
a S B
a S B
λ
bb
bb
bb
*
*
*
Exemplo:
		S -> aSB | λ
		B -> bb
		
Árvore de derivação para a palavra aaabbbbbb:
	
Árvores de Derivação
S
a S B
a S B
a S B
λ
bb
bb
bb
*
*
*
Exemplo:
		S -> aSB | λ
		B -> bb
		
Árvore de derivação para a palavra aaabbbbbb:
	
Árvores de Derivação
S
a S B
a S B
a S B
λ
bb
bb
bb
*
*
*
Exemplo:
		S -> aSB | λ
		B -> bb
		
Árvore de derivação para a palavra aaabbbbbb:
	
Árvores de Derivação
S
a S B
a S B
a S B
λ
bb
bb
bb
*
*
*
Exemplo:
		S -> XC | AY
		X -> aXb | λ
		Y -> bYc | λ
		A -> aA | λ
		C -> cC | λ
Árvores de Derivação e Ambigüidade
S
X C
a X b
λ
c C
S
A Y
b Y c
λ
c
a A
λ
*
*
*
Algumas gramáticas são ambíguas. Isso significa que uma mesma palavra (só com terminais) pode ser derivada de duas formas diferentes, mesmo que seja sempre escolhida uma mesma ordem para substituição das variáveis.
Duas gramáticas diferentes podem gerar a mesma linguagem, sendo uma das gramáticas ambígua e a outra não ambígua.
Gramáticas ambíguas não são muito adequadas para a construção automática de analisadores sintáticos.
Algumas linguagens são inerentemente ambíguas. Isso quer dizer que é impossível construir uma gramática não ambígua que gere essas linguagens.
Ambigüidade
*
*
*
Yacc - Gerador de
Analisadores Sintáticos
(Yacc =Yet Another Compiler-Compiler)
*
*
*
Auxilia no processo de definição do formato da entrada para um programa.
Uma especificação Yacc:
regras que descrevem a estrutura da entrada;
ações que devem ser executadas quando cada regra é utilizada;
Os exemplos utilizados nesta apresentação seguirão o formato do programa Javacup, um clone de Yacc que gera código em Java.
Yacc - Gerador de
Analisadores Sintáticos
*
*
*
Yacc - exemplo usando Javacup
Declaração de símbolos
terminais e não terminais
Gramática: lista de regras
separadas por ";" .
*
*
*
A especificação apresentada tem como símbolos terminais: PTVIRG, MAIS, MENOS e INTEIRO.
Se esse símbolos corresponderem a ';', '+', '-' e números inteiros, respectivamente, então a linguagem reconhecida é uma lista de expressões separadas por ';'. O operadores são '+' e '-' .
Para gerar um analisador sintático, vamos usar o Javacup (supondo especificação em um arquivo ex1.cup) :
Yacc - geração de um analisador
> java java_cup.Main ex1.cup
java_cup é um programa
escrito em Java!
*
*
*
A gramática utilizada possui várias ambigüidades, que causam conflitos no analisador sintático.
Yacc - conflitos na gramática
> java java_cup.Main ex1.cup
Opening files...
Parsing specification from standard input...
...
*** Shift/Reduce conflict found in state #9
...
*** More conflicts encountered than expected -- parser generation aborted
...
*
*
*
Por exemplo, considere a entrada: 1 - 2 + 3;
Yacc - conflitos na gramática
expr_list
expr_ptv
expr ;
expr - expr
expr + expr
INTEIRO
2
3
expr_list
expr_ptv
expr ;
expr + expr
expr - expr
3
1
2
OU
1
INTEIRO
INTEIRO
INTEIRO
INTEIRO
INTEIRO
*
*
*
Yacc - reescrevendo especificação
Ambigüidades eliminadas
*
*
*
Supondo nova especificação em um arquivo ex2.cup :
Yacc - reescrevendo especificação
> java java_cup.Main ex2.cup
Opening files...
Parsing specification from standard input...
Checking specification...
...
Writing parser...
Closing files...
------- CUP v0.10k Parser Generation Summary -------
 0 errors and 0 warnings
...
 0 productions never reduced.
 0 conflicts detected (0 expected).
 Code written to "parser.java", and "sym.java".
----------------------------------------------------
*
*
*
Os arquivos gerados pelo Javacup foram parser.java e sym.java. O primeiro contém o analisador sintático (parser). O segundo contém uma definição para símbolos terminais.
Conteúdo do arquivo sym.java:
Yacc - arquivos gerados
*
*
*
Falta especificar um procedimento que extrai os tokens do arquivo de entrada, classificando-os como PTVIRG, MAIS, MENOS ou INTEIRO.
Esse procedimento é geralmente chamado de scanner.
Isso será feito usando um analisador léxico automaticamente gerado com JFlex.
Yacc - especificando "scanner"
*
*
*
Yacc - especificando "scanner"
Usa classe Symbol, definida
em java_cup.runtime,
e os códigos de sym.java.
*
*
*
Finalmente, falta especificar uma função Main, que irá fazer as inicializações necessárias e chamar o analisador sintático.
O analisador sintático, por sua vez, irá chamar o analisador léxico para extrair os tokens do arquivo de entrada.
Yacc - especificando "Main"
*
*
*
Yacc - especificando "Main"
*
*
*
Suponha que tenhamos então os seguintes arquivos:
ex2.cup : especificação que será submetida ao Javacup;
ex2.flex : especificação que será submetida ao JFlex;
Main.java : contém a função Main.
O procedimento completo para gerar um analisador sintático será:
Yacc - procedimento completo
> java java_cup.Main ex2.cup
...
> jflex ex2.flex
...
> javac Main.java
*
*
*
Yacc - usando o analisador gerado
> java Main
1+2;
1-2+3;
^Z
> java Main
1+2;
1-2+3;
1 1;
Syntax error
Couldn't repair and continue parse
...
*
*
*
Um analisador que só verifica a sintaxe de uma entrada tem pouca utilidade.
Ferramentas como Javacup permitem associar valores aos símbolos e ações às regras.
Uma ação é um trecho de programa que é associado a uma regra. Pode ser executado em qualquer ponto da derivação, quando a regra é utilizada.
Um valor pode ser associado a um símbolo terminal no analisador léxico. No analisador sintático, um valor pode ser associado a um símbolo não terminal, recursivamente.
Yacc - valores e ações
*
*
*
A classe Symbol possui construtores que permitem a especificação de um valor (Object) associado ao token.
Trecho da nova especificação para o JFlex:
Yacc - valores no JFlex
Valor associado
ao token.
*
*
*
No Javacup, na declaração de símbolos terminais e não terminais, pode-se especificar um TIPO associado.
Trecho da nova especificação para o Javacup:
Yacc - valores no Javacup
Tipo associado
ao símbolo.
*
*
*
As ações são trechos de programa delimitados por {: ... :}.
A cada token, é possível associar um identificador, para que o seu valor possa ser acessado dentro da ação.
Trecho da nova especificação para o Javacup:
Yacc - valores no Javacup
*
*
*
A palavra-chave RESULT indica o valor do símbolo da esquerda da regra sendo utilizada.
Trecho da nova especificação para o Javacup:
Yacc - valores no Javacup
*
*
*
Suponha especificações em ex3.cup e ex3.flex:
Yacc - usando o analisador gerado
> java java_cup.Main ex3.cup
...
> jflex ex3.flex
...
> javac Main.java
> java Main
1;
= 1
1+1;
= 2
1-1;
= 0
1-2-3;
= 2
^Z
*
*
*
Usando a gramática abaixo:
expr_list ->
	 expr_list expr_ptv 
	| expr_ptv
expr_ptv -> expr PTVIRG
expr ->
 INTEIRO MAIS expr
 | INTEIRO MENOS expr
 | INTEIRO
	construir a árvore de derivação de : 1 - 2 - 3;
Exercício
*
*
*
Derivação de: 1 - 2 - 3;
Yacc - associatividade
expr_ptv
expr ;
INTEIRO - expr
INTEIRO - expr
1
2
3
INTEIRO
Associatividade
à direita!
*
*
*
Trecho da novaespecificação para o Javacup, implementando associatividade à esquerda para os operadores '+' e '-' :
Yacc - alterando associatividade
*
*
*
Derivação de: 1 - 2 - 3;
Yacc - associatividade à esquerda
expr_ptv
expr ;
expr - INTEIRO
expr - INTEIRO
3
2
1
INTEIRO
Associatividade
à esquerda!
*
*
*
Suponha especificações em ex4.cup e ex4.flex:
Yacc - usando o analisador gerado
> java java_cup.Main ex4.cup
...
> jflex ex4.flex
...
> javac Main.java
> java Main
1;
= 1
1+1;
= 2
1-1;
= 0
1-2-3;
= -4
^Z
*
*
*
A associatividade de operadores é uma característica tão importante em gramáticas que a maioria dos clones de Yacc possuem diretivas para simplificar sua especificação.
Suponha que, além dos operadores representados por MAIS e MENOS, a gramática possua VEZES e DIVISAO.
As diretivas abaixo determinam a precedência e a associatividade desses operadores:
Yacc - associatividade e precedência
*
*
*
Usando as diretivas apresentadas, não é mais necessário construir as regras de modo que a derivação defina a associatividade e precedência.
Trecho da nova especificação: 
Yacc - associatividade e precedência
*
*
*

Continue navegando