• Nenhum resultado encontrado

Compiladores: P ASCAL jr

N/A
N/A
Protected

Academic year: 2021

Share "Compiladores: P ASCAL jr"

Copied!
89
0
0

Texto

(1)

Compiladores: P ASCAL

jr

Rog´erio Eduardo da Silva, M.Sc.

2005/2

(2)

Sum´

ario

1 Introdu¸c˜ao 1

1.1 Evolu¸c˜ao das Linguagens de Programa¸c˜ao . . . 1

1.2 Introdu¸c˜ao `a Compila¸c˜ao . . . 2

1.2.1 Fases da Compila¸c˜ao . . . 3

1.3 Ferramentas para Gera¸c˜ao de Compiladores . . . 6

2 Um Compilador Simples de uma Passagem 7 2.1 Defini¸c˜ao da Sintaxe . . . 7

2.2 An´alise Gramatical . . . 8

2.2.1 Exerc´ıcios Propostos . . . 9

2.3 Caracter´ısticas da linguagem P ASCALjr . . . 10

2.3.1 Exerc´ıcios Propostos . . . 12

3 An´alise L´exica 13 3.1 O Papel do Analisador L´exico . . . 13

3.2 Buferiza¸c˜ao de Entrada . . . 14

3.3 Gram´aticas e Linguagens Regulares . . . 15

3.3.1 Exerc´ıcios Propostos . . . 16

3.4 Especifica¸c˜ao e Reconhecimento de Tokens . . . 17

3.4.1 Trabalho Pr´atico #1 . . . 19

4 An´alise Sint´atica 21 4.1 O Papel do Analisador Sint´atico . . . 21

4.2 An´alise Sint´atica Ascendente - BOTTOM UP . . . 23

4.2.1 Algoritmo “Empilhar-e-Reduzir” . . . 23

4.3 An´alise Sint´atica Descendente - TOP DOWN . . . 24

4.3.1 An´alise Sint´atica Preditiva . . . 25

4.3.2 Exerc´ıcios Propostos . . . 26

4.4 Reconhecedor de Gram´aticas Preditivas Descendentes . . . 27

4.4.1 Algoritmo para Constru¸c˜ao da Tabela de An´alise . . . 29

4.4.2 Projeto de uma Gram´atica para um Analisador Sint´atico Preditivo Ascendente . . . 30

4.4.3 Projeto de uma Gram´atica para um Analisador Sint´atico Preditivo Descendente . . . 31

4.4.4 Exerc´ıcios Propostos . . . 38

(3)

5 An´alise Semˆantica 41

5.1 Tradu¸c˜ao Dirigida pela Sintaxe . . . 41

5.1.1 Defini¸c˜oes L-Atribu´ıdas . . . 43

5.1.2 Verifica¸c˜oes de Contexto . . . 44

5.2 Tabela de S´ımbolos . . . 46

5.2.1 Atributos dos Nomes dos Identificadores . . . 47

5.2.2 Hashing . . . 47

5.3 Projeto das Regras Semˆanticas . . . 50

5.3.1 Trabalho Pr´atico #3 . . . 56

6 Gera¸c˜ao de C´odigo Intermedi´ario 59 6.1 Linguagens Intermedi´arias . . . 59

6.1.1 Representa¸c˜oes Gr´aficas . . . 59

6.1.2 Nota¸c˜ao P´os (e Pr´e) Fixadas . . . 60

6.1.3 C´odigo de Trˆes-Endere¸cos . . . 61

6.2 BackPatching (Retrocorre¸c˜ao) . . . 64

7 Otimiza¸c˜ao de C´odigo 67 7.1 Otimiza¸c˜ao Peephole . . . 67

7.2 Otimiza¸c˜ao de Blocos Sequenciais atrav´es de grafos . . . 68

7.2.1 Algoritmo para Construir o GAD de um bloco . . . 69

7.2.2 Algoritmo para Ordena¸c˜ao de um GAD . . . 70

8 Gera¸c˜ao de C´odigo Objeto 71 8.1 M´aquina Objeto . . . 72

8.1.1 Regras para Gera¸c˜ao de C´odigo Objeto . . . 76

(4)

Lista de Figuras

1.1 Processo de Compila¸c˜ao . . . 2

1.2 Fases da Compila¸c˜ao . . . 3

1.3 Arvore resultante da an´alise de um comando de atribui¸c˜ao em PASCAL . .´ 4

2.1 Representa¸c˜ao da ´arvore gramatical da produ¸c˜ao A→XYZ . . . . 8

2.2 Ambig¨uidade Gramatical . . . 8

3.1 O papel do analisador l´exico . . . 13

3.2 Buffer de entrada para um analisador l´exico . . . 15

3.3 Autˆomato finito de reconhecimento de n´umeros inteiros e reais . . . 17

3.4 AFD de reconhecimento de identificadores simples . . . 18

3.5 AFD de reconhecimento de strings . . . 18

4.1 Exemplo de ´Arvore Sint´atica . . . 22

4.2 Deriva¸c˜ao `a Esquerda e `a Direita . . . 22

4.3 An´alise descendente com backtracking . . . 25

4.4 Exemplos de Recurs˜ao `a Esquerda e `a Direita . . . 27

4.5 Funcionamento de um Analisador Sint´atico Descendente . . . 28

5.1 Exemplo de ´Arvore Decorada para a Express˜ao 3*5+4 . . . 42

5.2 Grafo de Dependˆencias . . . 43

5.3 Tipos Simples e Construtor de Tipos . . . 44

5.4 Hashing com Encadeamento . . . 49

6.1 Exemplo de Representa¸c˜ao Gr´afica de Operadores para a=b*c+b*2 . . . . 60

6.2 Backpatching para express˜oes l´ogicas . . . 65

(5)

Cap´ıtulo 1

Introdu¸c˜

ao

Entende-se por linguagem como uma forma eficiente de comunica¸c˜ao entre pessoas. Na verdade a linguagem ´e um conjunto de palavras usadas, segundo certas regras, para a forma¸c˜ao de frases compreens´ıveis por ambos os interlocutores (falantes).

Quando um dos interlocutores ´e o computador, se faz necess´ario o uso de uma lingua-gem especial denominada lingualingua-gem de programa¸c˜ao que permite a comunica¸c˜ao entre homem e m´aquina atrav´es da defini¸c˜ao de comandos.

Uma L. P. ´e ser dita de baixo n´ıvel, se esta somente aceitar comandos na pr´opria linguagem da m´aquina (0’s e 1’s) que ´e de dif´ıcil aplica¸c˜ao. J´a as linguagens ditas de alto n´ıvel, s˜ao representadas por a¸c˜oes pr´oximas ao problema a ser resolvido que s˜ao, posteriormente, traduzidas para a linguagem de m´aquina, atrav´es de um agente especial denominado compilador ou interpretador.

Concluindo: compilador ´e um programa capaz de traduzir um certo programa fonte (escrito em uma linguagem fonte) para outro programa objeto (escrito em uma linguagem objeto) geralmente a pr´opria linguagem de m´aquina.

1.1

Evolu¸c˜

ao das Linguagens de Programa¸c˜

ao

Cronologicamente, as L. P.’s s˜ao classificadas em cinco gera¸c˜oes: (1a) linguagens de

m´aquina; (2a) linguagens simb´olicas (Assembly); (3a) linguagens orientadas ao usu´ario;

(4a) linguagens orientadas `a aplica¸c˜ao e (5a) linguagens de conhecimento.

As duas primeiras s˜ao consideradas linguagens de baixo n´ıvel, enquanto que as demais de alto n´ıvel.

Os primeiros computadores s´o podiam ser programados atrav´es da sua pr´opria lingua-gem de m´aquina (c´odigo bin´ario), onde cada opera¸c˜ao possu´ıa sua representa¸c˜ao bin´aria que era passada `a m´aquina atrav´es de circuitos el´etricos. Esse processo, al´em de extrema-mente dif´ıcil e cansativo, era altaextrema-mente sujeito a erros devido a sua grande complexidade de execu¸c˜ao.

A seguir, como uma primeira tentativa de simplifica¸c˜ao, surgem as linguagens simb´oli-cas ou de montagem (Assembly). Agora, extensas seq¨uˆencias bin´arias s˜ao substitu´ıdas por

mnemˆonicos que s˜ao “palavras especiais” que representam certas a¸c˜oes b´asicas. Exemplo

MOV, JMP, etc. Os mnemˆonicos precisavam ser traduzidos para a linguagem de m´aquina antes da sua execu¸c˜ao.

(6)

A 3a gera¸c˜ao surgiu na d´ecada de 60, com as linguagens procedimentais como

FOR-TRAN, PASCAL e ALGOL e declarativas como LISP e PROLOG. Nas linguagens pro-cedimentais, um programa especifica uma seq¨uˆencia de passos a serem seguidos para a solu¸c˜ao do problema. J´a as linguagens declarativas s˜ao subdivididas em funcionais e l´ogicas. A programa¸c˜ao funcional se baseia na teoria das fun¸c˜oes recursivas, enquanto que, as linguagens l´ogicas se baseiam em proposi¸c˜oes da l´ogica de predicados (fatos e regras).

Devido ao fato de programas escritos em linguagens de 3a gera¸c˜ao serem muito

ex-tensos e de dif´ıcil manuten¸c˜ao, surgiram as linguagens de aplica¸c˜ao (4a gera¸c˜ao), onde o

desenvolvedor deixa de se preocupar com “atividades secund´arias” e trata apenas da co-difica¸c˜ao do problema (foco do programador deixa de ser a coco-difica¸c˜ao para ser a an´alise do problema). Aspectos como: interface de entrada e sa´ıda, relat´orios, etc. s˜ao resol-vidos pela pr´opria linguagem atrav´es de um banco de dados e dicion´arios associados `as aplica¸c˜oes desenvolvidas.

A 5a gera¸c˜ao das linguagens de programa¸c˜ao atua em problemas altamente

comple-xos onde a representa¸c˜ao de conhecimento se faz necess´aria para sua solu¸c˜ao, como os problemas enfrentados pela inteligˆencia artificial. A linguagem PROLOG ´e aceita como pertencente a esta gera¸c˜ao.

1.2

Introdu¸c˜

ao `

a Compila¸c˜

ao

Conforme j´a dito, um compilador nada mais ´e do que um programa tradutor respons´avel por converter uma certa linguagem fonte em outra linguagem objeto (ver Figura 1.1). Usualmente a linguagem objeto ´e a pr´opria linguagem de m´aquina, mas n˜ao necessaria-mente.

Programa

Fonte COMPILADOR ProgramaObjeto Mensagem

de Erro

Figura 1.1: Processo de Compila¸c˜ao

Existem dois tipos b´asicos de tradutores: os compiladores e os interpretadores. Os primeiros fazem uma an´alise completa sobre o programa fonte, caso n˜ao encontre erros faz a tradu¸c˜ao de todo o c´odigo fonte para a linguagem objeto que ser´a posteriormente executado em uma m´aquina capaz de fazˆe-lo. J´a os interpretadores n˜ao tˆem essa preo-cupa¸c˜ao hol´ıstica (an´alise completa) sobre o programa fonte. Um interpretador traduz um comando fonte por vez e o executa em uma m´aquina virtual (programa que simula o funcionamento de um computador) sem a necessidade da cria¸c˜ao do programa objeto.

Interpretadores s˜ao mais simples de serem implementados, por´em, compiladores geram execu¸c˜oes mais r´apidas de programas, pois n˜ao h´a a perda de tempo de tradu¸c˜oes virtuais a cada nova instru¸c˜ao executada.

(7)

1.2.1

Fases da Compila¸c˜

ao

O processo de compila¸c˜ao pode ser dividido em dois grupos de etapas: as etapas de an´alise e as etapas de s´ıntese. Na an´alise, o programa fonte ´e percorrido em busca de erros de programa¸c˜ao (inconsistˆencias com a linguagem fonte), j´a na etapa de s´ıntese (ap´os a verifica¸c˜ao da corretude do programa de origem), efetua-se a tradu¸c˜ao, propriamente dita, do c´odigo fonte para a linguagem objeto em quest˜ao. A figura 1.2 abaixo ilustra todo o processo: Tabela de Símbolos Análise Léxica Análise Sintática Análise Semântica

Geração de Código Intermediário

Otimização de Código Geração de Código Objeto

Manipulador de Erros ANÁLISE SÍNTESE Programa Fonte Programa Objeto

Figura 1.2: Fases da Compila¸c˜ao

A an´alise l´exica ou scanning ´e a primeira etapa do processo de compila¸c˜ao. Ela

´e respons´avel por analisar linearmente os caracteres do programa fonte e agrup´a-los em unidades l´exicas denominadas tokens. O token ´e o elemento mais b´asico da programa¸c˜ao; ele ´e representado por um conjunto de caracteres que apresentam um significado claro para o programa. Exemplo: Para o seguinte c´odigo em PASCAL:

Media := Nota1 + Nota2 * 2 Os caracteres poderiam ser agrupados da seguinte forma: 1. O identificador “Media” 2. O s´ımbolo de atribui¸c˜ao “:=” 3. O identificador “Nota1” 4. O sinal de adi¸c˜ao “+” 5. O identificador “Nota2” 6. O sinal de multiplica¸c˜ao “*”

(8)

7. O n´umero “2”

Os espa¸cos em branco presentes na senten¸ca s˜ao ignorados durante a an´alise.

O resultado da an´alise l´exica ´e uma lista contendo todos os tokens encontrados no programa fonte. Essa lista l´exica ´e ent˜ao o elemento de entrada para a an´alise sint´atica

ou an´alise gramatical (parsing ), onde ´e verificado se os tokens podem ser agrupados em senten¸cas v´alidas (comandos, express˜oes, etc.) da linguagem fonte. Normalmente, esses agrupamentos s˜ao realizados atrav´es da constru¸c˜ao de uma ´arvore sint´atica conforme ´e apresentado na figura 1.3:

Comando de Atribuição Identificador Símbolo de

Atribuição Expressão

Expressão AritméticoOperador Expressão

Expressão AritméticoOperador Expressão

Media :=

Nota1 +

Nota2 * 2

Figura 1.3: ´Arvore resultante da an´alise de um comando de atribui¸c˜ao em PASCAL A estrutura hier´arquica de um programa ´e usualmente expressa por regras recursivas. Por exemplo, poder´ıamos ter as seguintes regras como parte defini¸c˜ao de express˜oes:

1. Qualquer identificador ´e uma express˜ao 2. Qualquer n´umero ´e uma express˜ao

3. Se express˜ao1 e express˜ao2 s˜ao express˜oes v´alidas, ent˜ao express˜ao1 “op. aritm´etico”

express˜ao2 tamb´em ´e

A estrutura utilizada para a representa¸c˜ao dessas regras ´e a gram´atica livre de contexto (GLC), normalmente apresentada na Forma Normal de Backus (BNF).

Exemplo:

hcomandoi ::= hwhilei | hatribuic¸˜aoi | . . .

hwhilei ::= while hexpr booli do hcomandoi hatribuic¸˜aoi ::= identificador := hexpr aritmi

hexpr booli ::= hexpr aritmi op.L´ogico hexpr aritmi

hexpr aritmi ::= hexpr aritmi op.Aritm htermoi | htermoi htermoi ::= n´umero | identificador

Ap´os a an´alise sint´atica, tem-se a certeza de que o programa est´a escrito corretamente (respeita as regras gramaticais da linguagem fonte), por´em, ser´a que o programa escrito faz algum sentido? Ou seja, executa de forma apropriada?

(9)

A an´alise semˆantica tem por objetivo validar os comandos e express˜oes atrav´es de an´alises como compatibilidade de tipos e escopo de identificadores. Esta etapa analisa, por exemplo, se um identificador declarado como vari´avel ´e usado como tal, ou se uma express˜ao atribu´ıda a uma vari´avel retorna um tipo compat´ıvel com o qual foi declarada a vari´avel (em algumas linguagens, uma vari´avel inteira n˜ao pode receber uma express˜ao real).

At´e aqui foi realizada a etapa de an´alise do programa fonte, ou seja, a procura por erros de programa¸c˜ao. Caso nenhum erro seja encontrado, o processo de compila¸c˜ao passa ent˜ao para a etapa de s´ıntese, ou seja, a constru¸c˜ao do programa objeto.

A gera¸c˜ao do c´odigo intermedi´ario ´e a primeira fase da constru¸c˜ao do programa objeto. O que ela faz ´e a representa¸c˜ao do programa fonte em uma linguagem inter-medi´aria simplificada (m´aquina abstrata), o que permite a realiza¸c˜ao da pr´oxima etapa mais facilmente.

A pr´oxima etapa ´e a otimiza¸c˜ao de c´odigo, que tem por objetivo tentar modificar o c´odigo intermedi´ario no intuito de melhorar a velocidade de execu¸c˜ao, bem como a utiliza¸c˜ao do espa¸co de mem´oria, fazendo com isso, um uso mais racional dos recursos da m´aquina.

A ´ultima etapa do processo de compila¸c˜ao ´e a gera¸c˜ao de c´odigo objeto propria-mente dita. Esta fase tem como objetivos: produ¸c˜ao de c´odigo objeto, reserva de mem´oria para constantes e vari´aveis, sele¸c˜ao de registradores, etc. ´E a fase mais dif´ıcil, pois re-quer uma sele¸c˜ao cuidadosa das instru¸c˜oes e dos registradores da m´aquina alvo a fim de produzir c´odigo objeto eficiente.

Exemplo de gera¸c˜ao de c´odigo para o c´odigo fonte: While I < 100 do I := J + I

C´odigo Intermedi´ario Otimiza¸c˜ao C´odigo Objeto L0: if I<100 goto L1 L0: if I ≥ 100 goto L2 L0: MOV AX, I goto L2 I := J+I CMP AX, 100

goto L0 JGE L2 MOV AX, J MOV BX, I ADD BX MOV I, AX JMP L0 L1: Temp := J+I L2: . . . L2: . . . I := Temp goto L0 L2: . . .

Al´em dessas fases, h´a tamb´em os m´odulos de gerenciamento de tabelas e mani-pula¸c˜ao de erros.

O gerenciamento de tabelas consiste de um conjunto de tabelas e rotinas associadas que s˜ao utilizadas por quase todas as fases do tradutor. A principal estrutura deste m´odulo ´e a Tabela de S´ımbolos, que ´e respons´avel por armazenar informa¸c˜oes acerca dos identificadores do programa sob an´alise, como por exemplo: declara¸c˜ao das vari´aveis, procedimentos e sub-rotinas, lista de parˆametros, etc..

(10)

Os dados a serem armazenados dependem do projeto do tradutor, mas os mais co-muns s˜ao: identificador, classe (vari´avel, parˆametro, procedimento, etc.), tipo, endere¸co, tamanho.

A tabela de s´ımbolos deve ser estruturada de uma forma que permita r´apida inser¸c˜ao e extra¸c˜ao de informa¸c˜oes, por´em deve ser t˜ao compacta quanto poss´ıvel.

O m´odulo de manipula¸c˜ao de erros tem por objetivo “tratar os erros” que s˜ao de-tectados em todas as fases de an´alise do programa fonte e deve dispor de mecanismos (recupera¸c˜ao de erros) que permitam que o processo de an´alise prossiga mesmo que erros tenham sido detectados.

1.3

Ferramentas para Gera¸c˜

ao de Compiladores

Existem diversas ferramentas para auxiliar a constru¸c˜ao de compiladores chamadas de

geradores de compiladores ou sistemas de escritas de tradutores. A seguir s˜ao apresentados

alguns exemplos:

Geradores de Analisadores Gramaticais respons´aveis por desenvolver analisadores sint´aticos, normalmente a partir de entrada baseada numa gram´atica livre de con-texto.

Geradores de Analisadores L´exicos geram automaticamente analisadores l´exicos a partir de uma especifica¸c˜ao baseada em express˜oes regulares.

Dispositivos de tradu¸c˜ao dirigida pela sintaxe produzem cole¸c˜oes de rotinas que percorrem uma ´arvore gramatical, gerando c´odigo intermedi´ario.

Geradores autom´aticos de c´odigo tal ferramenta toma uma cole¸c˜ao de regras que definem a tradu¸c˜ao de cada opera¸c˜ao da linguagem intermedi´aria para linguagem alvo. Tais regras precisam incluir detalhamento suficiente para que possamos lidar com os diferentes m´etodos de acesso poss´ıveis para os dados.

Dispositivos de fluxo de dados Ferramentas que auxiliam na etapa de otimiza¸c˜ao de c´odigo.

N˜ao ´e de escopo desta disciplina o estudo de ferramentas de implementa¸c˜ao de com-piladores, mais detalhes podem ser obtidos na bibliografia de apoio.

(11)

Cap´ıtulo 2

Um Compilador Simples de uma

Passagem

2.1

Defini¸c˜

ao da Sintaxe

A especifica¸c˜ao da sintaxe de uma linguagem de programa¸c˜ao pode ser obtida atrav´es de uma gram´atica livre de contexto.

Exemplo: Seja o comando condicional da forma:

IF Express˜ao THEN Comando ELSE Comando

se Expr denotar a constru¸c˜ao de uma express˜ao e Cmd denotar um comando (ou enun-ciado), pode-se usar as regras de produ¸c˜ao de uma GLC1 para representar tal estrutura

da seguinte forma:

< Cmd >⇒ IF < Expr > THEN < Cmd > ELSE < Cmd >

as palavras-chave como IF, THEN e ELSE representam os s´ımbolos terminais, enquanto que os termos Cmd e Expr, representam os n˜ao-terminais.

Exemplo de uma GLC simples para definir express˜oes aritm´eticas baseadas apenas em adi¸c˜ao e subtra¸c˜ao:

< Lista > ⇒ < Lista > + < Digito > < Lista > ⇒ < Lista > − < Digito > < Lista > ⇒ < Digito >

< Digito > ⇒ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

onde os s´ımbolos 0 a 9 e + ou - s˜ao os elementos terminais, enquanto que Lista e Digito representam os n˜ao-terminais. Convencionalmente, o primeiro n˜ao-terminal representa o axioma da gram´atica. Express˜oes exemplo: 1+1, 3-6+9, 1+2+3+4+5+6

(12)

2.2

An´

alise Gramatical

A an´alise gramatical ´e feita atrav´es de deriva¸c˜oes de cadeias a partir do axioma da gram´atica. Se um n˜ao-terminal A possui uma produ¸c˜ao A ⇒ XYZ ent˜ao, uma ´arvore gramatical pode ter um n´o rotulado de A com 3 filhos X, Y e Z da esquerda para a direita, conforme a figura 2.1.

A

X

Y

Z

Figura 2.1: Representa¸c˜ao da ´arvore gramatical da produ¸c˜ao A→XYZ

Formalmente, segundo uma GLC, a ´arvore gramatical resultante apresenta as seguintes propriedades:

• A raiz ´e rotulada pelo s´ımbolo de partida (axioma); • Cada folha ´e rotulada por um terminal ou por ε;

• Cada n´o interno ´e rotulado por um elemento n˜ao-terminal;

• Se A ⇒ X1X2. . . Xn´e uma produ¸c˜ao ent˜ao, algum n´o interno da ´arvore ser´a rotulado

por A sendo X1X2. . . Xn os r´otulos dos filhos desse n´o.

Ambiguidade

Uma gram´atica pode ter mais de uma ´arvore gramatical gerando uma dada cadeia, neste caso, ela ´e dita ser amb´ıgua. Ambos os exemplos da figura 2.2 geram a senten¸ca 9-5+2.

Cadeia

Cadeia

+

Cadeia

Cadeia

-

Cadeia

9

5

2

Cadeia

Cadeia

Cadeia

+

Cadeia

Cadeia

-9

5

2

(13)

Associatividade de Operadores

Convencionalmente, 9+5+2 ´e equivalente `a (9+5)+2, pois, ao analisarmos o operando 5 precisamos decidir qual opera¸c˜ao ser´a realizada primeiro. Pela conven¸c˜ao da matem´atica a adi¸c˜ao ´e associativa `a esquerda, sendo assim o resultado (9+5)+2 ´e obtido. Na maioria das linguagens de programa¸c˜ao, as quatro opera¸c˜oes b´asicas (adi¸c˜ao, subtra¸c˜ao, multiplica¸c˜ao e divis˜ao) s˜ao associativas `a esquerda.

A exponencia¸c˜ao ´e um exemplo de operador associativo `a direita (em Fortran) 5**2**3 ´e equivalente a 5**(2**3). Outro exemplo ´e o operador de atribui¸c˜ao, onde a express˜ao a=b=c (em linguagem C) ´e tratada como a=(b=c).

Precedˆencia de Operadores

Considere a express˜ao 9+5∗2. Existem duas interpreta¸c˜oes poss´ıveis: (9+5)∗2 e 9+(5∗2). Quando mais de um tipo de operadores estiverem presentes em uma express˜ao ´e necess´ario se definir a ordem de precedˆencia entre eles.

Na aritm´etica, os operadores ∗ e / tem precedˆencia mais alta do que + e -; assim, na express˜ao anterior o operador de multiplica¸c˜ao ´e capturado antes da adi¸c˜ao.

2.2.1

Exerc´ıcios Propostos

1. Prova, atrav´es da constru¸c˜ao da ´arvore de deriva¸c˜ao, que os exemplos anteriores s˜ao v´alidos para a gram´atica de express˜oes aritm´eticas vista.

2. Considere a gram´atica livre de contexto: S → SS+ | SS∗ | a

(a) Mostre que a cadeia aa+a∗ pode ser gerada por esta gram´atica. (b) Construa a ´arvore gramatical para esta cadeia.

(c) Qual ´e a linguagem gerada por esta gram´atica? Justifique sua resposta. 3. Quais s˜ao as linguagens geradas pelas seguintes gram´aticas?

(a) S → 0S1 | 01 (b) S → +SS | -SS | a

(c) S → S(S)S | ε (d) S → aSbS | bSaS | ε

(e) S → a | S+S | SS | S* | (S)

4. Construa uma gram´atica livre de contexto para os n´umeros romanos (1 a 10). 5. Construa uma G.L.C. para as express˜oes aritm´eticas de inteiros e identificadores

(14)

2.3

Caracter´ısticas da linguagem P ASCAL

jr

1. N˜ao ´e caso sensitivo (‘A’ = ‘a’)

2. Suporta os tipos: integer, real, char, string e boolean 3. Comandos:

• Atribui¸c˜ao com operadores: “:=”, “+=”, “-=”, “*=”, “/=”, “++”, “- -” • Entrada com o comando read( )

• Sa´ıda com os comandos write( ) e writeln() • Condicional com o comando if - then - else • Repeti¸c˜oes:

– Pr´e teste com o comando while - do – P´os teste com o comando repeat - until – Contada com o comando for - to - do

• Sub-rotinas atrav´es dos comando procedure e function.

– Retorno de fun¸c˜oes com o comando result

– Nome de identificador de subrotinas inicia obrigatoriamente com “ ” (ex.: Tela)

4. Constantes caracteres delimitados por (‘ ’) e constantes strings por (“ ”) 5. Operadores relacionais: “=”, “>=”, “<=”, “>”, “<”, “<>”

6. Operadores l´ogicos “and”, “or”, “xor” (ou exclusivo), “not” 7. Operadores aritm´eticos: “+”, “-”, “*”, “/”, “**” (potencia¸c˜ao) 8. Suporta operadores tern´arios: Expr “?” valor1 “:” valor2 9. Precedˆencia de operadores:

(a) “=”, “+=”, “-=”, “*=”, “/=”, “++”, “- -” (b) “and”, “or”, “xor”

(c) “=”, “>=”, “<=”, “>”, “<”, “<>” (d) “not” (e) “+”, “-” (f) “*”, “/” (g) “**” (h) “(” “)” (i) “-” 10. S´ımbolos especiais: “,”, “:”, “;”, “(”, “)”, “.”

(15)

11. Bloco de comandos delimitados por “begin” e “end” 12. Coment´ario de linha com operador “//”

13. Coment´ario de bloco com os delimitadores “{” e “}”

14. Lista de Palavras Reservadas: var, const, while, do, for, read, write, writeln, if, then, else, true, false, integer, real, char, string, boolean, result, procedure, function,and,or,xor,not,to,repeat,until,program,downto

Exemplos de programas a serem reconhecidos pela linguagem P ASCALjr:

{

PILOTO.TXT

Exemplo completo de programa na linguagem PASCALjr Desenvolvido por Rogerio Eduardo da Silva

Agosto, 2005 }

Program Piloto;

// declara¸c~oes de variaveis e constantes globais

var: integer cont; real Nota1, Nota2, Media_das_medias, med; const: integer total = 10;

// Subrotina de preenchimento de tela procedure _Tela()

begin

writeln("******** ENTRADA DE DADOS ***************"); writeln("Digite os valores da entrada:");

end;

// Calculo da media aritmetica entre duas notas func real _Media(real a , b)

var: real media; begin

media := (a+b)/2.0; result := media; end;

// Inicio do Programa Principal begin

Media_das_medias := 0; for cont=0 to total do begin

_Tela();

read(Nota1, Nota2);

med := _Media(Nota1, Nota2); Media_das_medias += med;

(16)

write("Media = ",med); end;

write("Media Geral = ",Media_das_medias/total); end.

2.3.1

Exerc´ıcios Propostos

Usando a linguagem P ASCALjrfa¸ca:

1. Um programa para c´alculo do fatorial de N.

(17)

Cap´ıtulo 3

An´

alise L´

exica

3.1

O Papel do Analisador L´

exico

A an´alise l´exica ´e a primeira fase de um compilador e tem por objetivo fazer a leitura do programa fonte, caracter a caracter, e traduzi-lo para uma seq¨uˆencia de s´ımbolos l´exicos denominados tokens, os quais s˜ao utilizados pelo analisador sint´atico. Exemplos de tokens s˜ao os identificadores, palavras reservadas, operadores da linguagem, etc.

A intera¸c˜ao entre an´alise l´exica e sint´atica ´e normalmente implementada fazendo-se com que o analisador l´exico seja uma sub-rotina ou co-rotina do parser (ver figura 3.1). Ao receber do parser um comando do tipo “obter pr´oximo token”, o analisador l´exico lˆe os caracteres de entrada at´e que possa identificar o pr´oximo token.

Programa Fonte Analisador Léxico Analisador Sintático Tabela de Símbolos Token Obter Próximo Token

Figura 3.1: O papel do analisador l´exico

Um analisador l´exico cl´assico pode ser entendido como um sistema de estados fini-tos e, portanto, utiliza-se um autˆomato finito para sua implementa¸c˜ao. As principais caracter´ısticas desse autˆomato:

• O alfabeto de entrada s˜ao os caracteres pertencentes ao arquivo fonte

• Cada estado final reconhece uma classe espec´ıfica de tokens da linguagem fonte

´

E denominado erro l´exico a qualquer evento (durante o processo de an´alise l´exica) que impossibilite a interpreta¸c˜ao de um token.

Uma lista de tokens ´e o resultado do processo de an´alise l´exica, caso nenhum erro l´exico tenha sido encontrado.

(18)

Porque efetuar an´alise l´exica?

Simplifica¸c˜ao de Projeto ´e mais simples implementar dois analisadores distintos (para tarefas distintas) do que um analisador sint´atico que fa¸ca todo trabalho de forma unificada;

Melhor Eficiˆencia a an´alise l´exica ´e potencialmente mais lenta que a sint´atica (pois efetua leitura de caracteres em disco). T´ecnicas de buferiza¸c˜ao de leitura podem acelerar significativamente este processo;

Portabilidade as peculiaridades do alfabeto de entrada de cada linguagem podem ser tratadas exclusivamente pelo scanner.

Tokens, Padr˜oes e Lexemas

Um token ´e um s´ımbolo terminal da gram´atica da linguagem fonte sob an´alise. Em geral, existem diversas cadeias de caracteres para as quais o mesmo token ´e gerado.

Essas cadeias respeitam um determinado padr˜ao ou regra associada a esse token. Um lexema ´e um conjunto de caracteres que ´e reconhecido pelo padr˜ao de um deter-minado token.

Exemplo:

const pi = 3.14159;

a subcadeia pi ´e um lexema para o token “identificador”, pois respeita o padr˜ao para os identificadores (letra)(letra | digito)∗.

Atributos para os tokens

Um token ´e comumente representado como um par [LEXEMA, CLASSE], onde a classe indica qual foi o padr˜ao utilizado para reconhecer o lexema.

Outras informa¸c˜oes adicionais podem ser incorporadas `a descri¸c˜ao do token, de acordo com as necessidades das fases subseq¨uentes, como por exemplo, n´umero da linha e co-luna onde o token foi reconhecido no arquivo fonte e n´umero de caracteres lidos at´e o reconhecimento, seria exemplos de informa¸c˜oes adicionais ´uteis caso um erro l´exico seja detectado.

3.2

Buferiza¸c˜

ao de Entrada

Conforme j´a visto, o processo de an´alise l´exica ´e normalmente realizado efetuando-se uma leitura do arquivo fonte de entrada, caracter a caracter, o que resulta em um processo significativamente lento.

Existem 3 alternativas de implementa¸c˜ao de analisadores l´exicos (listados em ordem crescente de complexidade de implementa¸c˜ao):

1. Usar ferramentas de constru¸c˜ao de analisadores l´exicos (como o Lex), atrav´es de express˜oes regulares;

(19)

2. Escrever um programa numa linguagem de programa¸c˜ao convencional, usando seus recursos de entrada e sa´ıda;

3. Escrever um programa numa linguagem de montagem e manipular explicitamente a entrada e a sa´ıda.

Alguns aspectos a serem considerados no projeto de implementa¸c˜ao de um scanner : Buffer

Em muitas linguagens, existem momentos que o analisador l´exico precisa examinar v´arios caracteres `a frente do lexema, antes que seja anunciado um reconhecimento.

Os caracteres que foram lidos e n˜ao foram “aproveitados” no lexema sob an´alise, s˜ao ent˜ao, devolvidos ao fluxo de entrada para que possam ser lidos novamente na an´alise de outro lexema posterior.

Assim sendo, um buffer de entrada que acumula v´arios caracteres ´e criado, conforme a figura 3.2. O processo de an´alise l´exica ´e realizado sobre este buffer. Os tokens que foram reconhecidos s˜ao eliminados do buffer e novos caracteres s˜ao adicionados a ele at´e que todo o arquivo fonte seja lido e analisado.

E = m * c * c eof

apontador

Figura 3.2: Buffer de entrada para um analisador l´exico

Em casos mais simples, a entrada pode ser realizada caracter a caracter, contendo apenas um buffer de armazenamento dos caracteres lidos.

3.3

Gram´

aticas e Linguagens Regulares

A seguir, ser˜ao revisados alguns conceitos importantes da disciplina linguagens formais e m´aquinas (LFM) para ent˜ao prosseguir na an´alise l´exica.

Gram´atica

Uma gram´atica ´e um mecanismo gerador de senten¸cas de uma dada linguagem. ´E definida pela qu´adrupla (VN, VT, P, S), onde: VN representa o conjunto de s´ımbolos n˜ao-terminais

da linguagem; VT representa o conjunto de s´ımbolos terminais ou alfabeto; P ´e um

con-junto de regras de produ¸c˜ao e S ´e o axioma da gram´atica (s´ımbolo inicial).

As regras de produ¸c˜ao s˜ao definidas na forma α ⇒ β1 | β2 | . . . | βN, onde α representa

um s´ımbolo n˜ao-terminal e os βN representam senten¸cas podendo conter tanto s´ımbolos

(20)

Seq¨uˆencia de Deriva¸c˜ao

Entende-se por deriva¸c˜ao ao processo de substitui¸c˜ao de α por um dos βN na regra de

produ¸c˜ao, desta forma obtendo-se uma nova senten¸ca que por sua vez, pode ser novamente derivada por outra regra. Uma seq¨uˆencia de deriva¸c˜ao ´e uma s´erie de deriva¸c˜oes sucessivas que permitem a gera¸c˜ao de uma determinada senten¸ca da linguagem.

Gram´atica Regular

Uma gram´atica ´e dita ser regular se todas as suas regras de produ¸c˜ao respeitam a forma

A → αB ou A → α, onde A,B s˜ao s´ımbolos n˜ao-terminais e α ´e uma senten¸ca contendo

somente s´ımbolos terminais.

Gram´atica Linearmente `a Esquerda e `a Direita

Quando uma regra de produ¸c˜ao ´e da forma A → αB, ou seja, novos s´ımbolos n˜ao-terminais s˜ao inseridos `a direita da senten¸ca, diz-se se tratar de uma gram´atica linearmente `a direita.

Se a produ¸c˜ao for da forma A → Bα denomina-se como linearmente `a esquerda. Express˜oes Regulares

Uma express˜ao regular representa uma determinada linguagem atrav´es de ‘f´ormulas’ in-dutivas.

Simbologia adotada:

ε = senten¸ca vazia (comprimento = 0);

a | b = representa uma sele¸c˜ao entre a senten¸ca a ou b;

A∗ = conjunto de todas as senten¸cas de comprimento ≥ 0 sobre A;

A+ = A∗− {ε} = fechamento positivos sobre A

A? = representa que a express˜ao A ocorre zero ou uma vez.

Exemplos: Digito(Digito)∗ = representa a descri¸c˜ao de n´umeros inteiros. Letra(Letra|Digito)∗ = representa a descri¸c˜ao de identificadores.

3.3.1

Exerc´ıcios Propostos

1. Defina express˜oes regulares e sua respectiva gram´atica regular para as seguintes linguagens:

• todas as palavras contendo a e/ou b.

• todas as palavras contendo a e/ou b com sufixo aa.

• todas as palavras contendo a e/ou b com aaa como sub-palavra. • todas as palavras contendo a e/ou b com exatamente dois b.

(21)

P: S → 0S | A

A → A1 | B B → 0 | 1 | ε

3.4

Especifica¸c˜

ao e Reconhecimento de Tokens

A especifica¸c˜ao de tokens ´e feita atrav´es de express˜oes regulares e reconhecida atrav´es dos reconhecedores de gram´aticas regulares chamados de autˆomatos finitos.

Exemplo:

< Numero > → < Digitos >< Frac Opc >< Exp Opc > < Frac Opc > → . < Digitos >| ε

< Exp Opc > → (E | e)(+ | − | ε) < Digitos >| ε < Digitos > → < Digito >< Digitos >|< Digito >

< Digito > → 0 | 1 | 2 | . . . | 9

esta gram´atica ´e capaz de reconhecer n´umeros inteiros como 1, 100, 1234, etc. e tamb´em n´umeros reais expressos ou n˜ao por nota¸c˜ao exponencial como: 1.5, 10.34, 1.3e15, 1E+2; por´em, ´e incapaz de reconhecer n´umeros como 1., sem a parte fracion´aria. A figura 3.3 reconhece esta gram´atica, enquanto que a figura 3.4 reconhece identificadores simples1.

0

1

8

DÍGITO DIGITO . INICIO * Retornar(Num_Inteiro, Obter_Token())

2

DIGITO

3

DÍGITO

4

E | e + | -

5

DIGITO

6

DÍGITO

7

* Retornar(Num_Real, Obter_Token()) OUTRO OUTRO E | e DIGITO OUTRO

Figura 3.3: Autˆomato finito de reconhecimento de n´umeros inteiros e reais

O reconhecimento de strings ´e apresentado na figura 3.5, onde “caracteres v´alidos” representa o alfabeto v´alido para strings, geralmente letras, n´umeros, espa¸cos e sinais ortogr´aficos.

Exerc´ıcio: Criar um AFD capaz de reconhecer os tokens da linguagem P ASCALjr:

s´ımbolos (Dois Pontos, Ponto e V´ırgula, V´ırgula, Abre e Fecha Parˆenteses, Atribui¸c˜ao), Operadores Relacionais e Aritm´eticos, Constante Caracter e identificadores de sub-rotinas

(22)

0

1

2

LETRA OU DÍGITO

LETRA OUTRO

INICIO

* Retornar(ID, Obter_Token())

Figura 3.4: AFD de reconhecimento de identificadores simples

0

1

CARACTERES VÁLIDOS " INICIO

2

Retornar(String,Obter_Token()) "

Figura 3.5: AFD de reconhecimento de strings

(iniciam obrigatoriamente com ‘ ’ e tem pelo menos 2 caracteres), e ainda, ser capaz de tratar os caracteres nulos: espa¸cos, enter, tab e coment´arios, sem reconhecer token.

Reconhecendo palavras reservadas como identificadores simples:

Criar uma fun¸c˜ao de identifica¸c˜ao de palavras reservadas (enumera¸c˜ao) que retorna a classe palavra reservada ou identificador.

USO: Retornar(ObterClasse(Lexema),Lexema) Erros L´exicos

1. ‘Caracter Inv´alido’ : uso de um caracter (simbolo) de entrada (arquivo fonte)

que n˜ao perten¸ca ao alfabeto da linguagem. Exemplo: # ou %

2. ‘Delimitador N˜ao Balanceado’ : defini¸c˜ao de uma cadeia literal (ou constante

caracter) sem o correto balanceamento das aspas. Exemplo: “Entrada de Dados 3. ‘N´umero Real Inv´alido’ : defini¸c˜ao incorreta ou incompleta de um n´umero real.

Exemplos: 1., 1.0e3, .8, 1e+

O c´odigo abaixo apresenta algum erro l´exico? Apresente a lista l´exica. begin ; <>media==10.5E-5

/===//%_ M´edia 1.P Teste?

Solu¸c˜ao:

begin Palavra Reservada ; S´ımbolo Ponto e Virgula

(23)

<> Operador Relacional Diferente

media Identificador

= Operador Relacional de Igualdade = Operador Relacional de Igualdade 10.5E-5 N´umero Real

/= S´ımbolo de Atribui¸c˜ao

= Operador Relacional de Igualdade = Operador Relacional de Igualdade Teste Identificador

? S´ımbolo Interroga¸c˜ao

3.4.1

Trabalho Pr´

atico #1

Implementar um m´odulo (sub-rotina) analisador l´exico para um prot´otipo de compilador para a linguagem P ASCALjr vista em aula.

Caracter´ısticas: Do m´odulo scanner:

• A sub-rotina retorna um token (classe e lexema) cada vez que for chamada. • Considera que o programa fonte para an´alise j´a est´a aberto.

• N˜ao retorna nada quando atingir o fim de arquivo (flag de controle). • Implementa um AFD para o reconhecimento de tokens.

Do programa a ser criado:

• Abre um arquivo fonte para an´alise.

• Chama (sucessivas vezes) a rotina de scanner e exibe o valor do token. • Fecha o arquivo fonte ao final da compila¸c˜ao.

• P´ara o processo de compila¸c˜ao caso um erro seja encontrado. • Exibe erros de compila¸c˜ao (se ocorrerem) ou mensagem de sucesso.

Crit´erios de Avalia¸c˜ao:

• Implementa¸c˜ao usando linguagem C ou C++.

• Entrega de fontes e execut´avel (em um arquivo zipado) via disquete/CD ou e-mail: rsilva@joinville.udesc.br ou professor.rogerio@gmail.com

(24)

• Grupo de 02 alunos (m´aximo).

• Valor do trabalho: 10.0 (25% da nota pr´atica). • Data de Entrega: A Definir

• Puni¸c˜oes:

– de 10% por cada an´alise incorreta.

– de 20% do valor do trabalho por dia de atraso.

– de 20% do valor do trabalho para a entrega n˜ao conforme dos arquivos pedidos. – de 50% do valor do trabalho para o caso de n˜ao executar ou travar (ap´os teste

em 2 computadores, sendo um o do professor).

– de 100% do valor do trabalho para o caso de c´opias (mesmo de trabalhos de semestres anteriores).

• Prazo m´aximo para defesa e argui¸c˜ao sobre o trabalho: 5 dias letivos ap´os entrega. • Puni¸c˜oes:

– de 25% para argui¸c˜ao n˜ao respondida ou respondida incorretamente. Obs.: A argui¸c˜ao ´e individual.

(25)

Cap´ıtulo 4

An´

alise Sint´

atica

4.1

O Papel do Analisador Sint´

atico

A an´alise sint´atica constitui a segunda etapa de um tradutor. Sua fun¸c˜ao ´e verificar se as constru¸c˜oes usadas no programa est˜ao gramaticalmente corretas. Normalmente, as estruturas sint´aticas v´alidas s˜ao especificadas atrav´es de uma gram´atica livre de contexto. Dada uma GLC e uma senten¸ca (programa fonte) s, o objetivo do parser ´e verificar se s pertence a GLC, atrav´es da constru¸c˜ao de uma ´arvore de deriva¸c˜ao.

O processo de constru¸c˜ao dessa ´arvore pode ser feito de forma expl´ıcita (construindo-se o TDA) ou impl´ıcita, atrav´es de chamadas recursivas das rotinas que aplicam as regras de produ¸c˜ao da gram´atica durante o reconhecimento.

Existem duas estrat´egias b´asicas: Descendente (Top-Down) e Ascendente

(Bottom-Up). Na estrat´egia top-down constr´oi-se a ´arvore a partir da raiz em dire¸c˜ao `as folhas

(tokens), enquanto que na bottom-up, o processo ´e invertido e a constru¸c˜ao ´e realizada partindo-se das folhas, agrupando-se os tokens at´e que a raiz da ´arvore seja gerada.

A ´arvore gramatical ´e ent˜ao a sa´ıda para as pr´oximas fases da compila¸c˜ao. Revis˜ao sobre Gram´aticas Livre de Contexto (GLC)

Uma gram´atica livre de contexto ´e qualquer gram´atica da forma: A → α, onde A ´e um s´ımbolo n˜ao-terminal e α um elemento pertencente a (VN ∪ VT).

Exemplo de produ¸c˜oes de uma G.L.C.: S → SS+ | SS∗ | a. ´

Arvores de Deriva¸c˜ao ´

Arvore de deriva¸c˜ao ´e a representa¸c˜ao gr´afica de uma deriva¸c˜ao de senten¸ca.

Exemplo: Considerando a gram´atica abaixo, gerar ´arvore de deriva¸c˜ao que comprova que a senten¸ca 45 ´e v´alida (ver Figura 4.1).

(26)

< Numero > → < Num >

< Num > → < Num >< Digito >|< Digito > < Digito > → 0 | 1 | 2 | . . . | 9

<Numero>

<Num>

<Num>

<Digito>

<Digito>

4

5

Figura 4.1: Exemplo de ´Arvore Sint´atica Deriva¸c˜ao mais `a Esquerda e mais `a Direita

Deriva¸c˜ao mais `a esquerda ´e obtida por gram´aticas que geram inicialmente, os s´ımbolos mais `a esquerda da senten¸ca sob an´alise; analogamente para as deriva¸c˜ao mais `a direita. Exemplo: Seja a gram´atica: E → E + E | E − E | E ∗ E | E/E | (E) | x. Pode obter a express˜ao x+x*x de duas formas, conforme a figura 4.2:

E

X

E

E

+

E

E

*

X

X

E

X

E

E

+

E

E

*

X

X

Figura 4.2: Deriva¸c˜ao `a Esquerda e `a Direita

Exerc´ıcio: Para a gram´atica G = ({S,A},{0,1},P,S) sendo P: S → 0S | A A → 1A | 1,

(27)

4.2

An´

alise Sint´

atica Ascendente - BOTTOM UP

A cria¸c˜ao da ´arvore gramatical ´e realizada no sentido folhas → raiz, ou seja, gera¸c˜ao de senten¸cas ´e feita atrav´es do processo de empilhar e reduzir. A id´eia ´e “reduzir” a senten¸ca original at´e o axioma da gram´atica atrav´es de sucessivas substitui¸c˜oes por n˜ao-terminais.

Exemplo: S → aABe A → Abc | b B → d

Verificar se a senten¸ca abbcde pode ser reduzida pela gram´atica: abbcde aAbcde aAde aABe S

4.2.1

Algoritmo “Empilhar-e-Reduzir”

Este procedimento de an´alise sint´atica ascendente consiste de dois passos: 1. Escolha de um candidato α a redu¸c˜ao (handle);

2. Redu¸c˜ao do candidato pelo n˜ao-terminal A `a esquerda da produ¸c˜ao A → α;

3. Repetir os passos 1 e 2 at´e que a senten¸ca tenha sido reduzida ao axioma da gram´atica.

Um candidato ´e uma subcadeia que reconhece o lado direito de uma produ¸c˜ao e cuja redu¸c˜ao ao n˜ao-terminal do lado esquerdo da produ¸c˜ao representa um passo ao longo do percurso de uma deriva¸c˜ao.

´

E denominado de “poda do candidato” ao processo de substitu´ı-lo pelo n˜ao-terminal `a esquerda da regra de produ¸c˜ao, obtendo desta forma, uma redu¸c˜ao na senten¸ca sob an´alise.

Uma forma conveniente de implementar um analisador sint´atico de empilhar e reduzir ´e usar uma pilha para guardar os s´ımbolos gramaticais. O analisador sint´atico opera empilhando zero ou mais s´ımbolos at´e que um candidato surja no topo da pilha. Uma poda do candidato ´e ent˜ao feita. Repete-se este processo at´e que no topo da pilha esteja o axioma da gram´atica ou um erro seja encontrado (nenhuma poda seja poss´ıvel).

Exemplo: E → E + E | E − E | E ∗ E | E/E | (E) | id. Senten¸ca sob an´alise: id + id * id

(28)

Entrada Pilha Ac¸˜ao id+id*id $ empilhar

+id*id $id reduzir E → id +id*id $E empilhar

id*id $E+ empilhar *id $E+id reduzir E → id *id $E+E reduzir E → E + E *id $E empilhar

id $E* empilhar $ $E*id reduzir E → id $ $E*E reduzir E → E ∗ E $ $E aceitar

S˜ao apenas 4 as opera¸c˜oes poss´ıveis por este m´etodo: empilhar, reduzir, aceitar ou erro.

Conflitos durante a An´alise Sint´atica de Empilhar e Reduzir

Existem gram´aticas livres de contexto para as quais o procedimento empilhar-e-reduzir n˜ao pode ser utilizado, porque, em certos casos, o analisador pode atingir um estado tal, que:

• Mesmo conhecendo toda a pilha e o pr´oximo s´ımbolo de entrada, n˜ao pode decidir

entre empilhar e reduzir. Isto ´e chamado de conflito empilhar/reduzir.

• Outro conflito poss´ıvel, o reduzir/reduzir, ocorre quando n˜ao ´e poss´ıvel optar entre

as diversas redu¸c˜oes poss´ıveis.

4.3

An´

alise Sint´

atica Descendente - TOP DOWN

A an´alise sint´atica top-down pode ser vista como uma tentativa de se encontrar uma deriva¸c˜ao mais `a esquerda para uma cadeia de entrada, ou ainda, como de se construir a ´arvore gramatical a partir da raiz em dire¸c˜ao `as folhas.

O processo de an´alise pode ser feito de forma recursiva ou n˜ao, onde a forma recur-siva pode ser realizada com ou sem retrocesso (backtracking), dependendo das regras de produ¸c˜ao gram´atica.

An´alise Sint´atica Recursiva com Retrocesso

A constru¸c˜ao da ´arvore ´e feita a partir da raiz, expandindo sempre o n˜ao-terminal mais `a esquerda primeiro. Quando existe mais de uma regra de produ¸c˜ao para o n˜ao-terminal a ser expandido, a op¸c˜ao escolhida ´e fun¸c˜ao do s´ımbolo corrente na fita de entrada (token sob an´alise). Se o token n˜ao define a produ¸c˜ao a ser usada, ent˜ao todas as alternativas v˜ao ser tentadas at´e que se obtenha sucesso (ou todas falhem).

Exemplo 1:S → cAd A → ab | a.

Verificar se a gram´atica gera a senten¸ca cad. Exemplo 2: S → cA A → aB B → D | bD D → d

(29)

S

S

c

A

d

S

c

A

d

a

b

S

c

A

d

a

falha!

sucesso!

Figura 4.3: An´alise descendente com backtracking

A an´alise sint´atica ´e dita ser uma an´alise sint´atica preditiva caso n˜ao seja necess´ario a realiza¸c˜ao de retrocesso no processo e pode ser implementada de forma recursiva ou n˜ao (atrav´es da utiliza¸c˜ao de uma pilha).

4.3.1

An´

alise Sint´

atica Preditiva

O processo de an´alise preditiva (sem retrocesso) exige modifica¸c˜oes na gram´atica original para an´alise:

• elimina¸c˜ao de recurs˜ao `a esquerda;

• fatora¸c˜ao `a esquerda das regras de produ¸c˜ao;

• os n˜ao-terminais que apresentarem mais de uma regra de produ¸c˜ao, tenham o

pri-meiro terminal deriv´avel ´unico (capaz de identificar a produ¸c˜ao a ser analisada). Ou seja, deve ser poss´ıvel determinar, para um dado s´ımbolo a, qual das produ¸c˜oes deve ser derivada.

Exemplo: No exemplo 2 visto acima a produ¸c˜ao B → D | bD D → d apresenta duas alternativas de deriva¸c˜ao. A escolha ´e feita a partir do primeiro terminal para cada regra (d ou b).

O conjunto de s´ımbolos terminais que iniciam senten¸cas deriv´aveis a partir de uma produ¸c˜ao b ´e denominado FIRST(β) ou PRIMEIRO(β).

Exemplo: FIRST(S) = {c}; FIRST(A) = {a}; FIRST(B) = {b, d}; FIRST(D) = {d}. As regras que definem o conjunto FIRST s˜ao:

(30)

• Se β → ε, ent˜ao ε ´e um elemento de FIRST.

• Se β → aδ, sendo a um s´ımbolo terminal, ent˜ao a pertence a FIRST.

• Se β → X1X2. . . XN, sendo X1X2. . . XN elementos n˜ao-terminais, ent˜ao FIRST(β)

= FIRST(X1). Se em FIRST(X1) constar o elemento ε, ent˜ao incluir FIRST(X2)

em FIRST(β) e assim por diante. Elimina¸c˜ao da Recurs˜ao `a Esquerda

´

E poss´ıvel que um analisador gramatical descendente recursivo execute indefinidamente. O problema ocorre em produ¸c˜oes recursivas `a esquerda, tais como: A → A0 | 1.

Este tipo de produ¸c˜ao gera uma ´arvore que cresce recursivamente `a esquerda at´e que um terminal 1 seja gerado `a esquerda da seq¨uˆencia de 0’s.

Para se evitar isso deve-se substituir o elemento causador da recurs˜ao `a esquerda, que ´e do tipo A → Aα | β, onde α, β representam outras seq¨uˆencias de terminais e n˜ao-terminais n˜ao iniciadas por A .

Para eliminar a recurs˜ao `a esquerda deve-se reescrever essa produ¸c˜ao, da seguinte forma: A → βA0 e A0 → αA0 | ε. A figura 4.4 apresenta as ´arvors de deriva¸c˜ao para uma

senten¸ca qualquer da forma βαααα.

4.3.2

Exerc´ıcios Propostos

• Para as gram´aticas abaixo elimine sua recurs˜ao `a esquerda.

1. G=({S,A,B},{a,b},P,S) onde P: S → Sa | Sb | A | B A → Aa | a B → bB | b 2. G=({S,A},{0,1,2},P,S) onde P: S → S0 | S1 | A | 0 A → S2

3. G=({S,A,B},{0,1},P,S) onde P: S → SA | A A → A0B | 0 B → B1 | ε 4. G=({A},{0,1},P,A) onde P: A → A0A | 1

• Apresente a cl´ausula First para as produ¸c˜oes das gram´aticas abaixo:

1. G=({S,X,Y,Z},{0,1,2,3},P,S) onde P: S → XY Z X → OXO | 1 Y → 2Y 2 | 3 Z → 0Z1 | ε

2. G=({S,A,B,C},{a,b,c},P,S) onde P: S → Sa | aA A → aA | Bb B → cB | ε 3. G=({S,X,Y,Z},{0,1},P,S) onde P: S → XY Z X → 0X | 1Y | ε Y → 1Y |

ε Z → 01Z | ε

Fatora¸c˜ao `a Esquerda

A fatora¸c˜ao `a esquerda ´e uma transforma¸c˜ao gramatical ´util para a cria¸c˜ao de uma gram´atica adequada `a an´alise sint´atica preditiva. A id´eia b´asica est´a em, quando n˜ao estiver claro qual das duas produ¸c˜oes alternativas usar para expandir um n˜ao-terminal A, estarmos capacitados a reescrever as produ¸c˜oes A e postergar a decis˜ao at´e que tenhamos visto o suficiente da entrada para realizarmos a escolha certa.

(31)

α

A

β

A

A'

α

A

α

A

α

A

A

α

β

A'

α

A'

α

A'

α

A'

ε

Figura 4.4: Exemplos de Recurs˜ao `a Esquerda e `a Direita

Ao analisarmos o token a n˜ao h´a como saber qual das duas alternativas utilizar (o comando com ou sem o “cZ”). Quando houver duas produ¸c˜oes A → αβ1 | αβ2, devemos

postergar a decis˜ao expandindo A para αA0 e ent˜ao expandir A’ para β

1 | β2. Fatorando

esta gram´atica temos: S → aXbY S0 S0 → cZ | ε.

4.4

Reconhecedor de Gram´

aticas Preditivas

Descen-dentes

Um reconhecedor preditivo descendente (orientado por tabela) compreende uma fita de entrada, uma pilha e uma tabela de an´alise, conforme ´e mostrado na figura 4.5. A fita cont´em a senten¸ca a ser analisada seguida de $. A pilha cont´em os s´ımbolos utilizados durante o processo de an´alise. A tabela de an´alise ´e uma matriz com n linhas (correspon-dendo aos s´ımbolos n˜ao-terminais) e t+1 colunas (correspon(correspon-dendo aos s´ımbolos terminais mais o s´ımbolo especial $).

Considerando X o elemento no topo da pilha e a o s´ımbolo de entrada sob an´alise, o analisador executa uma de trˆes a¸c˜oes poss´ıveis:

1. se X = a = $, o analisador p´ara, aceitando a senten¸ca;

2. se X = a 6= $, o analisador desempilha a e avan¸ca o cabe¸cote de leitura para o pr´oximo s´ımbolo na fita de entrada;

3. se X ´e um s´ımbolo n˜ao-terminal, o analisador consulta a tabela M[X,a] da tabela de an´alise. Essa entrada poder´a conter uma produ¸c˜ao da gram´atica ou ser vazia. Supondo M[X,a] = { X → XY Z }, o analisador substitui X (no topo da pilha) por ZYX (ficando X no topo). Se M[X,a] for vazio isto ´e um erro sint´atico.

Na implementa¸c˜ao de um analisador sint´atico, a maior dificuldade est´a na constru¸c˜ao da tabela de an´alise. Para construir essa tabela, ´e necess´ario computar duas fun¸c˜oes associadas `a gram´atica: FIRST e FOLLOW.

(32)

Tabela de

Análise

Parser

a + b $

X

Y

Z

Figura 4.5: Funcionamento de um Analisador Sint´atico Descendente

O algoritmo para calcular a fun¸c˜ao FIRST j´a foi visto anteriormente. O algoritmo para calcular a fun¸c˜ao FOLLOW ´e apresentado a seguir:

1. Se S ´e o s´ımbolo inicial da gram´atica e $ ´e o marcador de fim de senten¸ca, ent˜ao $ est´a em FOLLOW(S);

2. Se existe produ¸c˜ao do tipo A → αXβ, ent˜ao todos os terminais de FIRST(β), fazem parte de FOLLOW(X);

3. Se existe produ¸c˜ao do tipo A → αX, ou A → αXβ, sendo que β → ε, ent˜ao todos os terminais que estiverem em FOLLOW(A) fazem parte de FOLLOW(X).

Dada a gram´atica G = ({E, E0, T, T0, F }, {∨, ∧, ¬, id}, P, E) para express˜oes l´ogicas:

E → T E0 E0 → ∨T E0 | ε T → F T0 T0 → ∧F T0 | ε F → ¬F | id Cl´ausula First

Conv´em iniciar o processo pelos n˜ao-terminais que gerem conjuntos triviais. No exemplo, temos os n˜ao-terminais F, E’ e T’ que s´o geram elementos terminais (ou vazio):

F = {¬, id} E0 = {∧, ε}

T0 = {∨, ε}

Como T deriva apenas em FT’ e F n˜ao leva em vazio, conclui-se que FIRST(T) = FIRST(F). E ainda, FIRST(E) = FIRST(T) = FIRST(F) = {¬, id}.

(33)

Cl´ausula Follow

Pela regra 1 temos que FOLLOW(E) = {$}. Pela regra 3 tem-se que FOLLOW(E) = FOLLOW(E’). FOLLOW(T) ´e obtido a partir da uni˜ao dos conjuntos obtidos pela aplica¸c˜ao da regra 2 em (E0 → ∨T E0) e regra 3 em (E0 → ε). Sendo assim temos:

FOLLOW(T) = FIRST(E’) + FOLLOW(E’) = {∨, $}.

FOLLOW(T’) = FOLLOW(T) pela aplica¸c˜ao da regra 3 em T → F T0. E finalmente,

FOLLOW(F) = FIRST(T’) + FOLLOW(T’). Aplica¸c˜ao das regras 2 e 3 em T0 → ∧F T0 | ε, ou seja FOLLOW(F) = {∨, ∧, $}.

4.4.1

Algoritmo para Constru¸c˜

ao da Tabela de An´

alise

M´etodo:

• Para cada produ¸c˜ao X → α, execute os passos 2 e 3 (para criar a linha X da tabela

M);

• Para cada terminal a de FIRST(α), adicione a produ¸c˜ao X → α a M[X,a];

• Se FIRST(α) inclui a palavra vazia, ent˜ao adicione X → α a M[X,b] para cada b

em FOLLOW(X);

Aplicando-se o algoritmo acima `a gram´atica de express˜oes l´ogicas temos:

Para E → T E0 tem-se FIRST(TE’) = {¬, id} ent˜ao, M[E, ¬] = M[E,id] = E → T E0.

Para E0 → ∨T E0 tem-se FIRST(∨T E0) = {∨} ent˜ao, M[E’, ∨] = E0 → ∨T E0.

Para E0 → ε tem-se FOLLOW(E’) = {$} ent˜ao, M[E’, $] = E0 → ε.

Para T → F T0 tem-se FIRST(FT’) = {¬, id} ent˜ao, M[T, ¬] = M[T,id] = T → F T0.

Para T0 → ∧F T0 tem-se FIRST(∧F T0) = {∧} ent˜ao, M[T’, ∧] = T0 → ∧F T0.

Para T0 → ε tem-se FOLLOW(T’) = {∨, $} ent˜ao, M[T’, ∨] = M[T’,$] = T0 → ε.

Para F → ¬F tem-se FIRST(¬F ) = {¬} ent˜ao, M[F, ¬] = F → ¬F . Para F → id tem-se FIRST(id) = {id} ent˜ao,M[F,id] = F → id.

id ¬ $ E E → T E0 E → T E0 E’ E0 → ∨T E0 E0 → ε T T → F T0 T → F T0 T’ T0 → ε T0 → ∧F T0 T0 → ε F F → id F → ¬id

Se, em cada entrada da Tabela de An´alise, existe apenas uma produ¸c˜ao, ent˜ao a gram´atica que originou a tabela ´e dita ser do tipo LL(1), ou seja: as senten¸cas geradas pela gram´atica s˜ao pass´ıveis de serem analisadas da esquerda para a direita (Left to Right), produzindo uma deriva¸c˜ao mais `a esquerda (Leftmost Derivation), levando em conta apenas um s´ımbolo da entrada.

Exerc´ıcio: Considerando a gram´atica para a linguagem a ser reconhecida pelo

(34)

4.4.2

Projeto de uma Gram´

atica para um Analisador Sint´

atico

Preditivo Ascendente

Analisa gram´aticas do tipo LR(k), ou seja, left-to-right e rightmost derivation com k s´ımbolos lidos da entrada a cada etapa de an´alise.

Porque usar an´alise sint´atica ascendente LR?

• porque ´e poss´ıvel ser elaborados reconhecedores para todas as GLC, sem restri¸c˜ao; • porque o m´etodo de an´alise LR ´e t˜ao eficiente quanto os demais m´etodos de an´alise; • porque um analisador LR consegue encontrar um erro sint´atico o mais cedo poss´ıvel

em uma an´alise da esquerda para a direita.

As gram´aticas GLC para as quais ´e vi´avel a implementa¸c˜ao manual de reconhece-dores ascendentes (devido `a complexidade de implementa¸c˜ao) apresentam as seguintes restri¸c˜oes:

• nenhum lado direito das produ¸c˜oes seja ε

• nenhum lado direito tenha dois n˜ao-terminais adjacentes (gram´atica de operadores)

Exemplo: E → E + E | E − E | E ∗ E | E/E | (E) | −E | id

Um forma simples de se implementar um reconhecedor ascendente ´e atrav´es da an´alise de precedˆencia de operadores, por´em, justamente devido `a sua simplicidade, uma s´erie de restri¸c˜oes est˜ao associadas a estes:

• dificuldades de analisar operadores com mais de um significado semˆantico (ex.:

ope-rador un´ario e bin´ario de subtra¸c˜ao)

• somente uma pequena classe de linguagens pode ser analisada por esta alternativa,

apesar disso, j´a foram desenvolvidos analisadores de precedˆencia para linguagens inteiras.

Na an´alise de precedˆencia temos definidos as rela¸c˜oes de precedˆencia entre os opera-dores, sendo (a < • b) onde a confere precedˆencia a b; (a = b) onde a possui a mesma precedˆencia de b e (a • > b) a tem precedˆencia sobre b.

Seja o exemplo da gram´atica anterior onde a precedˆencia dos operadores ´e dada por: id + * $

id • > • > • >

+ < • • > < • • >

* < • • > • > • >

$ < • < • < •

Analisando a express˜ao: id+id*id temos as seguintes rela¸c˜oes de precedˆencia: $ < • id • > + < • id • > ∗ < • id • > $

(35)

1. Percorrer a cadeia, a partir da esquerda at´e que o primeiro • > seja encontrado. 2. Percorrer, ent˜ao, de volta (para a esquerda) por sobre quaisquer rela¸c˜oes (=) at´e

que < • seja encontrado.

3. O handle cont´em tudo `a esquerda do primeiro • > e `a direita do < •, incluindo quaisquer n˜ao-terminais presentes.

No exemplo acima, o primeiro handle ´e dado pelo primeiro id encontrado que pode ser reduzido para o n˜ao-terminal E (segundo a gram´atica vista), seguido pelos pr´oximos dois ids da senten¸ca. A seguir, a senten¸ca obtida ficaria $ E + E * E $; removendo-se os n˜ao-terminais e acrescentando-se as rela¸c˜oes de precedˆencia temos: $ < • + < • ∗ • > $, indicando que a pr´oxima redu¸c˜ao deve ser realizada sobre o operador “*” (e seus respec-tivos operandos associados “E* E”).

Devido ao fato da sua implementa¸c˜ao n˜ao ser trivial, a solu¸c˜ao de implementa¸c˜ao mais vi´avel para este tipo de gram´atica ´e fazer uso de um gerador de analisadores sint´aticos, como o YACC ou BISON.

4.4.3

Projeto de uma Gram´

atica para um Analisador Sint´

atico

Preditivo Descendente

Considerando o uso de analisadores sint´aticos descendentes preditivo algumas preocupa-¸c˜oes quanto a gram´atica a ser utilizada, devem ser tomadas: eliminar ambig¨uidade, eli-minar as recurs˜oes `a esquerda e fatorar `a esquerda a gram´atica.

Analisando um Programa Simples

A seguir, ´e apresentado um exemplo de um programa simples na linguagem P ASCALjr:

var: float N1, N2, M; int Ct; const: int Qtde = 10;

func float _Media(float a,float b) float media; { media = (a+b)/2.0; return media; } main( ) { Ct = 0; do {

print("Digite duas notas:"); scanf(N1,N2);

printl("Media = ",_Media(N1,N2)); Ct ++;

} while(Ct != Qtde); }

(36)

[ Declara¸c˜ao de Vari´aveis e Constantes ] [ Declara¸c˜ao de Sub-Rotinas ]

<Programa Principal>

onde: [ ] indica se¸c˜ao opcional e <> indica se¸c˜ao obrigat´oria.

Pode-se descrever esta estrutura na forma de uma regra de produ¸c˜ao de uma GLC da seguinte forma, onde o n˜ao-terminal Programa ser´a o axioma da gram´atica da linguagem

P ASCALjr:

Programa → AreaDecl AreaSubRot Principal Analisando a Se¸c˜ao de Declara¸c˜ao de Vari´aveis e Constantes

Esta se¸c˜ao declara todas as vari´aveis e constantes utilizadas pelo programa. ´E poss´ıvel a declara¸c˜ao de v´arias ´areas de declara¸c˜ao de vari´aveis e/ou constantes simultaneamente. Um programa pode ainda n˜ao conter esta se¸c˜ao.

AreaDecl → AreaDeclVar AreaDecl | AreaDeclConst AreaDecl | ε AreaDeclVar → prVar DoisPt DeclVars

DeclVars → Tipo ListaID PtVirg DeclVars’ DeclVars’ → Tipo ListaID PtVirg DeclVars’ | ε

Tipo → prInt | prFloat | prChar | prString | prBool ListaID → Identificador ListaID’

ListaID’ → Virg Identificador ListaID’ | ε AreaDeclConst → prConst DoisPt DeclConsts

DeclConsts → Tipo ListaIDConst PtVirg DeclConsts’ DeclConsts’ → Tipo ListaIDConst PtVirg DeclConsts’ | ε ListaIDConst → Identificador Atrib Valor ListaIDConst’

ListaIDConst’ → Virg Identificador Atrib Valor ListaIDConst’ | ε Valor → OpAritSubt Numeros | Numeros |

ConstChar | ConstString | prTrue | prFalse Numeros → NumeroInteiro | NumeroReal

Analisando a Se¸c˜ao de Declara¸c˜ao de Procedimentos e Fun¸c˜oes

A se¸c˜ao de declara¸c˜ao de procedimentos e fun¸c˜oes declara todas as sub-rotinas utilizadas pelo programa. ´E poss´ıvel a declara¸c˜ao de v´arias ´areas de declara¸c˜ao de sub-rotinas simultaneamente. Um programa pode ainda n˜ao conter esta se¸c˜ao.

(37)

AreaSubRot → AreaProc AreaSubRot | AreaFunc AreaSubRot | ε

AreaProc → prProc IdentSR AbrePar ListaParam FechaPar AreaDecl BlocoCom

ListaParam → Tipo Identificador ListaParam’ | ε ListaParam’ → Virg Tipo Identificador ListaParam’ | ε

AreaFunc → prFunc Tipo IdentSR AbrePar ListaParam FechaPar AreaDecl BlocoCom

Analisando o Programa Principal

O programa principal ´e o ponto onde inicia-se a execu¸c˜ao do c´odigo fonte. Ela ´e definida pela fun¸c˜ao “main”. Apesar da linguagem P ASCALjr n˜ao permitir a passagem de

parˆametros para esta fun¸c˜ao, ainda sim utilizar-se-˜ao os parˆenteses “(” “)” na sintaxe do comando meramente por uma quest˜ao did´atica. Esta se¸c˜ao ´e obrigat´oria em qualquer programa.

Principal → prMain AbrePar FechaPar BlocoCom Analisando um Bloco de Comandos

Um bloco de comandos pode ser entendido como um comando composto por uma lista de outros comandos simples (ou outros blocos) podendo (em alguns casos) ser separados por “;” e delimitados por “{” e “}”. Sendo assim:

BlocoCom → AbreChaves ListaCom FechaChaves ListaCom → Comando ListaCom | ε

Comando → Condicional | RepetPre | RepetPos PtVirg | RepetCont | Entrada PtVirg | Saida PtVirg | Atrib PtVirg | SubRot PtVirg | BlocoCom | Retorno PtVirg | ε

Analisando o comando Atribui¸c˜ao

Pode ser realizado atrav´es de 7 diferentes operadores: = atribui¸c˜ao simples

(38)

-= atribui¸c˜ao ap´os subtra¸c˜ao (X− = Y ⇔ X = X − Y ) *= atribui¸c˜ao ap´os multiplica¸c˜ao (X∗ = Y ⇔ X = X ∗ Y )

/= atribui¸c˜ao ap´os divis˜ao (X/ = Y ⇔ X = X/Y )obs.:N˜ao prevˆe divis˜ao por zero ++ atribui¸c˜ao incremental (X + + ⇔ X = X + 1)

- - atribui¸c˜ao decremental (X − − ⇔ X = X − 1)

Exemplo de uma gram´atica que reconhece esses comandos: Atrib → Identificador SimbAtrib Expr |

Identificador SimbAtribSoma Expr | Identificador SimbAtribSubt Expr | Identificador SimbAtribMult Expr | Identificador SimbAtribDivi Expr |

Identificador SimbIncr | Identificador SimbDecr por´em, temos problemas de fatora¸c˜ao. Fatorando `a esquerda estas produ¸c˜oes temos:

Atrib → Identificador Atrib’ Atrib’ → SimbAtrib Expr |

SimbAtribSoma Expr | SimbAtribSubt Expr | SimbAtribMult Expr | SimbAtribDivi Expr | SimbIncr | SimbDecr Analisando o comando Condicional

O comando condicional (sem fatora¸c˜ao) ficaria:

Condic → prIf AbrePar Expr FechaPar Comando | prIf AbrePar Expr FechaPar Comando prElse Comando

e ap´os fatora¸c˜ao teremos:

Condic → prIf AbrePar Expr FechaPar Comando Condic’ Condic’ → prElse Comando | ε

(39)

Analisando os comandos de Repeti¸c˜ao

Os comandos de repeti¸c˜ao podem ser reconhecidos por:

RepetPos → prDo ListaCom prWhile AbrePar Expr FechaPar RepetPre → prWhile AbrePar Expr FechaPar Comando

RepetCont → prFor AbrePar Atrib PtVirg Expr PtVirg Atrib FechaPar Comando

Analisando os comandos para chamada a Sub-Rotinas

Os comandos para chamadas a sub-rotinas incluem o comando < Retorno > que deve ser usado nas chamadas a fun¸c˜oes.

SubRot → IdentSR AbrePar ListaExpr FechaPar Retorno → prReturn Expr

Analisando os comandos de Entrada e Sa´ıda Para os comandos de entrada e sa´ıda temos:

(40)

Entrada → prScanf AbrePar ListaVar FechaPar Saida → prPrint AbrePar ListaExpr FechaPar |

prPrintl AbrePar ListaExpr FechaPar ListaExpr → Expr ListaExpr’ | ε

ListaExpr’ → Virg Expr ListaExpr’ | ε Analisando Express˜oes L´ogicas e Aritm´eticas

Para descrever senten¸cas que formam express˜oes aritm´eticas compostas das cinco opera-¸c˜oes b´asicas (adi¸c˜ao, subtra¸c˜ao, multiplica¸c˜ao, divis˜ao e potencia¸c˜ao), tendo como ope-randos: identificadores de vari´aveis e constantes, n´umeros inteiros e reais, chamadas a sub-rotinas e ainda permitir o uso de parˆenteses e do operador un´ario de sinal “-”; a representa¸c˜ao mais simples poss´ıvel seria:

ExprAr → ExprAr OpAdic ExprAr | ExprAr OpSubt ExprAr | ExprAr OpMult ExprAr | ExprAr OpDivi ExprAr | ExprAr OpPote ExprAr | AbrePar ExprAr FechaPar | OpSubt ExprAr | SubRot |

Identificador | NumeroInteiro | NumeroReal | ConstCaracter | ConstString

Exerc´ıcio: Montar a ´arvore gramatical para a express˜ao 2 ∗ (X − 5.0) + 10/B

Apesar de que, com esta gram´atica, ´e poss´ıvel gerar qualquer express˜ao aritm´etica sim-ples, esta n˜ao leva em considera¸c˜ao todas as restri¸c˜oes j´a estudadas para a implementa¸c˜ao de reconhecedores de gram´atica TOP-DOWN.

O primeiro problema que se percebe ´e o fato da gram´atica anterior n˜ao considerar a quest˜ao da precedˆencia de operadores. Para resolver este problema deve-se inserir novos elementos n˜ao-terminais `a gram´atica:

ExprAr → ExprAr OpAdic TermoAr |

ExprAr OpSubt TermoAr | TermoAr TermoAr → TermoAr OpMult FatorAr |

TermoAr OpDivi FatorAr | FatorAr

FatorAr → FatorAr OpPote ElementoAr | ElementoAr ElementoAr → AbrePar ExprAr FechaPar |

(41)

OpSubt ExprAr | SubRot |

Identificador | NumeroInteiro | NumeroReal | ConstCaracter | ConstString

Exerc´ıcio: Montar a ´arvore gramatical para a express˜ao 2 ∗ (X − 5.0) + 10/B

A id´eia ´e gerar os elementos de menor precedˆencia mais pr´oximos `a raiz da ´arvore sint´atica e os de maior precedˆencia, mais pr´oximos `as folhas.

Novamente temos problemas com a solu¸c˜ao proposta: recurs˜ao `a esquerda. A nova gram´atica ap´os realizado o processo (j´a estudado) de elimina¸c˜ao da recurs˜ao `a esquerda, temos:

ExprAr → TermoAr ExprAr’

ExprAr’ → OpAdic TermoAr ExprAr’ | OpSubt TermoAr ExprAr’ | ε TermoAr → FatorAr TermoAr’

TermoAr’ → OpMult FatorAr TermoAr’ | OpDivi FatorAr TermoAr’ | ε FatorAr → ElementoAr FatorAr’

FatorAr’ → OpPote ElementoAr FatorAr’ | ε ElementoAr → AbrePar ExprAr FechaPar |

OpSubt ExprAr | SubRot |

Identificador | NumeroInteiro | NumeroReal | ConstCaracter | ConstString

Exerc´ıcio: Montar a ´arvore sint´atica para a express˜ao: 2 ∗ (X − 5.0) + 10/B.

Analisando Express˜oes L´ogicas

Uma express˜ao l´ogica ´e, na verdade, uma compara¸c˜ao entre resultados de express˜oes aritm´eticas, ou ainda, a uni˜ao de duas express˜oes aritm´eticas atrav´es de um operador relacional.

S˜ao poss´ıveis ainda, express˜oes l´ogicas mais complexas atrav´es da uni˜ao de duas ex-press˜oes l´ogicas simples por operadores l´ogicos.

Expr → TermoLog Expr’ Ternario Ternario → Interrog Expr DoisPt Expr | ε

Expr’ → OpLogAnd TermoLog Expr’ | OpLogOr TermoLog Expr’ | OpLogXor TermoLog Expr’ | ε TermoLog → FatorLog TermoLog’

Referências

Documentos relacionados

Ganhos significativos em algumas competências socioemocionais a curto prazo (relacionamento com os pares e competência social)?. Os alunos do GI com níveis médios no

 Informamos aos senhores gestores, professores/técnicos e alunos/atletas que participarão dos XXXI JOGOS DA PRIMAVERA 2014 que as solenidades de abertura acontecerão durante

DA COMISSÃO DE ASSUNTOS ECONÔMICOS, sobre o Projeto de Lei da Câmara nº 136, de 2009, que cria a Superintendência Nacional de Previdência Complementar (PREVIC) e dispõe

Chora Peito Chora Joao Bosco e Vinicius 000 / 001.. Chão De Giz Camila e

The assets weights were calculated using the solver routine, four kinds of portfolios were created: maximum expected return, minimum variance, and maximum Sharpe (most efficient),

Assim, deixa-se de usar venenos de pesca no Chaco, devido à ausência das plantas apropriadas; no Peru ocidental, em virtude da inexistência de rios de correnteza

Nos casos de lotes de sucatas, além dos itens anteriores, deverá ser apresentado o Certificado de Credenciamento do Centro de Desmanches de Veículos Automotores, Comércio de Peças

Com base nas preocupações acima apresentadas, este trabalho se propos a estimar as perdas de nitrogênio agrícola através do estudo da dinâmica da solução no