Capítulo III Diagramas de Transições

Slides:



Advertisements
Apresentações semelhantes
ADT – Arvore Binária de Pesquisa
Advertisements

Java: Comandos Básicos
While While Condição // comandos End While. Exemplos While Var contador As Integer contador = 0 While (contador < 10) contador += 1 console.ConsoleWrite(contador)
Listas Encadeadas Circulares Listas Duplamente Encadeadas
Listas Encadeadas Circulares Listas Duplamente Encadeadas
Procedimentos e Funções
Grupo Paralelismo – Unesp RC
FORTRAN 90 Denise Yumi Takamura.
SQL Procedural Junho/2006.
Estruturas de Dados para projeto de SGBD. Estruturas de dados analisadas Estruturas de nível Esparso Denso Combinadas Arvore B+ Tabela de hash.
Funções de Linha 24/04/06. Funções de Linha Gerar ou construir novo valor não existente na tabela inicial. Valor construído a partir de dados de uma ou.
Sintaxe de Fortran 25/abril/2006. Comandos Fortran PROGRAM PRINT READ STOP END.
Modularização de Código. Modularizar código Construção do algoritmo em um módulo único (Início...Fim/ Program... End) Único arquivo.
Introdução ao Fortran 4/abril/2006. Pseudocódigo Compreensão do problema Elaboração da lógica de resolução Tradução da lógica para pseudocódigo.
Utilização do montador Daedalus
1. Equivalência entre portas 2. Derivação de expressões booleanas 3
Teste Estrutural de Software
Teste Funcional de Software
Análise e Solução de Problemas
(Como implementar multiplicação e divisão uma vez só :-)
Profa. Graziela Santos de Araújo Algoritmos e Programação II, 2010
Introdução A fim de preparar a geração de código, deve-se relacionar o fonte estático do programa às ações em tempo de execução. Durante a execução, o.
1 Tradução Dirigida à Sintaxe Prof. Ricardo Santos.
CE-262 Ontologia e Web Semântica Prof. José M Parente de Oliveira
CES-11 LAB 03 Bitmap Quadtree
CES-41 COMPILADORES Capítulo IV Complementos de Análise Léxica.
Capítulo VII – Tipos Enumerativos e Estruturas 7.1 – Tipos enumerativos 7.2 – A necessidade de estruturas 7.3 – Manipulação dos campos de uma estrutura.
CES-10 INTRODUÇÃO À COMPUTAÇÃO Aulas Práticas – 2013 Capítulo III Comandos de Controle.
1.3 – Interpretadores – Compiladores versus Interpretadores
CES-10 INTRODUÇÃO À COMPUTAÇÃO Capítulo III Declarações e Comandos de Atribuição.
CES-41 COMPILADORES Capítulo VII Código Intermediário.
CES-41 COMPILADORES Aulas Práticas
CES-10 INTRODUÇÃO À COMPUTAÇÃO Aulas Práticas – 2013 Capítulo VII Variáveis Indexadas Numéricas.
CES-10 INTRODUÇÃO À COMPUTAÇÃO Aulas Práticas – 2013 Capítulo XI Encadeamento de Estruturas por Ponteiros.
Capítulo IX – Ponteiros 9.1 – Introdução 9.2 – Relação entre ponteiros e variáveis indexadas 9.3 – Alocação dinâmica de memória 9.4 – Variáveis indexadas,
CES-41 COMPILADORES Aulas Práticas Capítulo III Análise Semântica no Yacc.
CES-41 COMPILADORES Aulas Práticas Capítulo III Análise Semântica no Yacc.
Exemplo: ordenação de uma lista pelo Selection-Sort:
Capítulo V Análise Sintática
1 Definição de Dicionário Dicionário é um sistema de informações: Equivalente a um conjunto de elementos não repetidos Equivalente a um conjunto de elementos.
5.5.4 – Métodos para a construção da tabela de análise LR
Capítulo I – Conceitos Primários 1.1 – Estrutura de um computador 1.2 – Informações manipuladas por um computador 1.3 – Evolução das linguagens de programação.
Curso Técnico em Informática La Salle - Canoas
Já definimos o coeficiente angular de uma curva y = f(x) no ponto onde x = x 0. Derivadas Chamamos esse limite, quando ele existia, de derivada de f em.
ALOCAÇÃO DINÂMICA DE MEMÓRIA
Teorema do Confronto Se não pudermos obter o limite diretamente, talvez possamos obtê-lo indiretamente com o teorema do confronto. O teorema se refere.
C/C++.
Laboratório de Programação Prof. Oscar Luiz Monteiro de Farias
Listas Lineares.
Prof. Giovanny Lucero Introdução Prof. Giovanny Lucero
Linguagens Formais e Tradutores Análise Sintática - 1
Linguagens Formais e Tradutores Linguagens Regulares (Revisão)
Tecnologia para Web JavaScript Enrique Pimentel Leite de Oliveira
Recursividade Estrutura de Dados.
Marco Antonio Montebello Júnior
Entendendo as definições de classe
The Data Warehouse Toolkit
Curso de Programação em C++ Universidade Federal do Ceará Departamento de Engenharia Estrutural e Construção Civil Prof: Evandro Parente Junior Monitor:
Baseado no documento do Prof. Ronaldo Martins da Costa
LINGUAGENS DE PROGRAMAÇÃO
04:27 Introdução Tipos de Fluxo de Dados e de Arquivos Manipulação de Arquivos em Java Classes FileReader e FileWriter Classes FileInputStream e FileOutputStream.
Sistemas de Informação Prof. Me. Everton C. Tetila Modelo de dados relacional Banco de Dados I.
Introdução Ciência da Computação estudo de algoritmos –ÊNFASE ao estudo de DADOS armazenamento manipulação refinamento (a partir de dados cru) estrutura.
8. Uma Função de duas Variáveis Aleatórias
Exercícios de revisão.
Análise Sintática – Parte 1
CES-11 ALGORITMOS E ESTRUTURAS DE DADOS Aulas Práticas – 2016
CES-11 ALGORITMOS E ESTRUTURAS DE DADOS Aulas Práticas
CES-11 ALGORITMOS E ESTRUTURAS DE DADOS Aulas Práticas – 2017
CES-41 COMPILADORES Aulas Práticas
Transcrição da apresentação:

Capítulo III Diagramas de Transições CES-41 COMPILADORES Capítulo III Diagramas de Transições

Capítulo III – Diagramas de Transições 3.1 – Considerações iniciais 3.2 – Uma linguagem ilustrativa: Mini-Pascal 3.3 – Análise léxica por diagramas de transições 3.4 – Análise sintática por diagramas de transições 3.5 – Tabela de símbolos em diagramas de transições 3.6 – Testes semânticos em diagramas de transições 3.7 – Geração de código intermediário

3.1 – Considerações Iniciais Diagramas de transições: usados na construção do front-end de um compilador Forma didática de concepção de um front-end Há outros métodos mais eficientes, porém menos didáticos A seguir, análises léxica, sintática e semântica e geração de código intermediário por diagramas de transições Diagrama de transições léxicas é um autômato finito determinístico

Exemplo: Diagrama de transições para reconhecer constantes numéricas em Fortran d é um dígito genérico

Arcos de qualquer vértice para o vértice Erro não são mostrados, mas existem d é um dígito genérico

Estado não final Estado final d é um dígito genérico

São reconhecidas constantes como: 13, +13, -13, 13. , 1. 25,. 25, - São reconhecidas constantes como: 13, +13, -13, 13., 1.25, .25, -.25, -32.43, 13E-15, 13.E-15, -13.25E+72, .75E5 d é um dígito genérico

Para as transições sintáticas usa-se vários diagramas de transições; um para cada não-terminal Existem linguagens para as quais é difícil ou até impossível a utilização de diagramas de transições sintáticas Para ilustrar será utilizada a linguagem Mini-Pascal extraída de Pascal, apresentada logo a seguir

3.2 – Uma linguagem ilustrativa: Mini-Pascal 3.2.1 – Gramática do Mini-Pascal A gramática do Mini-Pascal não é recursiva à esquerda, mas apresenta a ambiguidade dos comandos condicionais Não tem subprogramas, nem variáveis indexadas e seu único comando repetitivo é o while Não-terminais estão em itálico Terminais são átomos obtidos do analisador léxico; são apresentados com LETRAS MAIÚSCULAS, ou com os caracteres que os identificam

Produções da gramática: Prog  PROGRAM ID ; Decls CmdComp . Decls  ε  VAR ListDecl ListDecl  DeclTip  DeclTip ListDecl DeclTip  ListId : Tip ; ListId  ID  ID , ListId Tip  INTEGER  BOOLEAN CmdComp  BEGIN ListCmd END ListCmd  Cmd  Cmd ; ListCmd Cmd  CmdIf  CmdWhile  CmdRead  CmdWrite CmdAtrib  CmdComp

Produções da gramática (continuação 1) : CmdIf  IF Expr THEN Cmd | IF Expr THEN Cmd ELSE Cmd CmdWhile  WHILE Expr DO Cmd CmdRead  READ ( ListId ) CmdWrite  WRITE ( ListW ) ListW  ElemW  ElemW , ListW ElemW  Expr  CADEIA CmdAtrib  ID := Expr

Produções da gramática (continuação 2) : Expr  ExprSimpl  ExprSimpl OPREL ExprSimpl ExprSimpl  Term  Term OPAD ExprSimpl Term  Fat  Fat OPMULT Term Fat  ID  CTE  ( Expr ) TRUE  FALSE  OPNEG Fat

3.2.2 – Especificações léxicas Cada átomo tem pelo menos um atributo chamado tipo Conforme o tipo do átomo, ele poderá ter outros atributos Se o átomo é uma palavra reservada, seu tipo é a própria palavra reservada e não há outros atributos Tabela de palavras reservadas:

letra ( letra | dígito )* 3.2.2 – Especificações léxicas Átomo identificador: seu tipo é ID e seu outro atributo é a cadeia de seus caracteres; sua sintaxe é letra ( letra | dígito )* Caracteres de identificadores que excederem o número de 16 são ignorados Constantes inteiras: seu tipo é CTE e seu outro atributo é o seu valor inteiro; esse valor tem de caber em 2 bytes Cadeias de caracteres: vêm entre apóstrofos (‘ ’); têm como tipo, CADEIA, e, como outro atributo, a cadeia de seus caracteres sem os apóstrofos

Tabela dos tipos e atributos dos operadores: OPAD : operador aditivo OPMULT : operador multiplicativo OPNEG : operador negador OPREL : operador relacional

Tabela dos tipos dos separadores (não possuem outros atributos): Os programas em Mini-Pascal não têm comentários Espaços em branco entre átomos são opcionais, com a exceção das palavras reservadas Essas não podem estar concatenadas com outras e com identificadores e constantes inteiras

3.3 – Análise Léxica por Diagramas de Transições 3.3.1 – Objetivos da análise léxica Os caracteres do programa são grupados em átomos Os átomos têm sua validade verificada Os átomos válidos são classificados e recebem seus atributos

Tabela de átomos e seus atributos Exemplo: Programa para o cálculo do fatorial de um numero lido: PROGRAM fatorial; VAR n, fat, i: INTEGER; BEGIN READ (n); fat := 1; i := 1; WHILE i <= n DO BEGIN fat := fat * i; i := i + 1 END; WRITE (‘O fatorial de’, n, ‘ eh ’, fat) END. Tabela de átomos e seus atributos

3.3.2 – Diagrama de transições A estrutura de um analisador léxico pode ser montada sobre um diagrama de transições de estados O diagrama é uma só entidade, mas, por razões didáticas, está apresentado em várias figuras Uma figura para cada transição que parte do estado inicial Supõe-se que, no estado inicial, o analisador já tenha em mãos um caractere

a) Uma letra é o primeiro caractere Em cada transição: Caractere(s) responsável(eis) Ações antes da transição

Forma Cadeia: introduz caractere numa cadeia Pega Caractere: lê novo caractere do programa; retorna ‘\0’ para end-of-file Class Cadeia: classifica cadeia formada Forma Átomo: forma novo átomo com o tipo obtido da classificação; se for ID, o atributo será a cadeia formada Possíveis átomos: Palavra reservada OPMULT - AND OPAD - OR OPNEG - NOT ID - cadeia

b) Um dígito é o primeiro caractere Forma número: obtém o número inteiro correspondente à cadeia formada Forma Átomo: forma novo átomo com o número formado e com o tipo CTE Átomo formado: CTE - número inteiro

c) Um apóstrofo é o primeiro caractere Se o fecha-apóstrofo for esquecido, o resto do programa será guardado em cadeia Átomo formado: CADEIA - cadeia

d) Um dos caracteres + - * / ~ = é o primeiro Class Caractere: classifica caractere lido Possíveis átomos: OPMULT - VEZES OPMULT - DIV OPAD - MAIS OPAD - MENOS OPNEG - NEG OPREL - IGUAL

e) O caractere < é o primeiro Possíveis átomos: OPREL - MENIG OPREL - DIFER OPREL - MENOR

f) O caractere > é o primeiro Possíveis átomos: OPREL - MAIG OPREL - MAIOR

g) O caractere : é o primeiro Possíveis átomos: ATRIB DPONTS

h) Um dos caracteres ; . , ( ) é o primeiro Possíveis átomos: PVIRG PONTO VIRG ABPAR FPAR

i) Um dos caracteres ‘\0’, ‘ ’, ‘\n’, ‘\t’, ‘\r’, ou qualquer outro é o primeiro Átomo de tipo FINAL: artificial Possíveis átomos: FINAL INVAL

3.3.3 – Implementação de um diagrama de transições léxicas Primeiramente, será apresentada uma função main para gerenciar a classificação de todos os átomos de um programa em Mini-Pascal Essa função tem a simples finalidade de testar o analisador léxico Ela não será usada quando esse analisador estiver integrado ao analisador sintático

nome nomearq; FILE *program, *result; void main () { printf ("A N A L I S E L E X I C A\n\n"); printf ("Nome do arquivo: "); fflush (stdin); gets (nomearq); program = fopen (nomearq, "r"); result = fopen ("atomosmp", "w"); carac = NovoCarac (); while (carac) { NovoAtomo (); ImprimeAtomo (); } printf ("\nAnalise do arquivo '%s' encerrada", nomearq); printf ("\n\nVer atomos no arquivo 'atomosmp'"); getch (); typedef struct atomo atomo; struct atomo { int tipo; atribatomo atrib; }; typedef union atribatomo atribatomo; union atribatomo { char *cadeia; long valor; int atr; char carac; }; Variáveis globais: nome nomearq; FILE *program, *result; char carac; atomo atom; char *cadeia; NovoAtomo: coloca em atom o tipo e o atributo do próximo átomo do programa; é o centro do analisador léxico NovoCarac: retorna o próximo caractere lido do programa; retorna ‘\0’ caso seja encontrado end-of-file

A função NovoAtomo: Coloca na estrutura atom o tipo e o atributo do próximo átomo encontrado Implementa o caminhamento pelo diagrama de transições léxicas A seguir o esquema geral de NovoAtomo

if (atom.tipo == ID || atom.tipo == CADEIA) free (atom.atrib.cadeia); void NovoAtomo () { int estado = 1; if (atom.tipo == ID || atom.tipo == CADEIA) free (atom.atrib.cadeia); cadeia = malloc(MAXCADEIA*sizeof(char)); *cadeia = 0; while (estado != 3) switch (estado) { case 1: - - - - - - - - - - ; break; case 2: - - - - - - - - - - ; break; case 4: - - - - - - - - - - ; break; case 5: - - - - - - - - - - ; break; case 6: - - - - - - - - - - ; break; case 7: - - - - - - - - - - ; break; case 8: - - - - - - - - - - ; break; } free (cadeia); Preparação para uma eventual formação de cadeia Caso o átomo anterior tenha como atributo uma cadeia, ela deve ser desalocada Aqui ocorrem as transições de estados e o preparo do átomo Depois de formado o átomo, a cadeia é desalocada

Transições a partir do Estado 1

case 1: switch (carac) { case '\'': - - - - -; estado = 5; break; case '+': case '-': case '*': case '/': case '~': case '=': case ';': case '.': case ',': case '(': case ')': - - - - -; estado = 3; break; case '<': - - - - -; estado = 6; break; case '>': - - - - -; estado = 7; break; case ':': - - - - -; estado = 8; break; case '\0': - - - - -; estado = 3; break; default: if (isalpha (carac)) {- - - - -; estado = 2;} else if (isdigit (carac)) {- - - - -; estado = 4;} else if ((isspace(carac) || iscntrl(carac)) && (carac != 0)) { - - - - -; estado = 1; } else {- - - - -; estado = 3; } break;

case 1: switch (carac) { default: if (isalpha (carac)) { FormaCadeia (); carac = NovoCarac(); estado = 2;} else if (isdigit (carac)) { estado = 4;}

case 1: switch (carac) { case '\'': carac = NovoCarac( ); estado = 5; break;

case 1: switch (carac) { case '+': case '-': case '*': case '/': case '~': case '=': case ';': case '.': case ',': case '(': case ')': atom = Classifica (); carac = NovoCarac(); estado = 3; break;

case 1: switch (carac) { case '<': carac = NovoCarac(); estado = 6; break; case '>': carac = NovoCarac(); estado = 7; break; case ':': carac = NovoCarac(); estado = 8; break;

case 1: switch (carac) { case '\0': atom.tipo = FINAL; estado = 3; break; default: if ----- else if ((isspace(carac) || iscntrl(carac)) && (carac != 0)) { carac = NovoCarac(); estado = 1;} else {atom.tipo = INVAL; atom.atrib.carac = carac; carac = NovoCarac(); estado = 3; }

case 2: if (isalnum (carac)) { FormaCadeia (); carac = NovoCarac(); estado = 2;} else { atom = ClassificaCadeia (); estado = 3;} break; case 4: if (isdigit (carac)) { FormaCadeia (); carac = NovoCarac();estado = 4;} else {atom = FormaNumero (); estado = 3;}

case 6: if (carac == '=') { atom.tipo = OPREL; atom.atrib.atr = MENIG; carac = NovoCarac();} else if (carac == '>'){ atom.tipo = OPREL; atom.atrib.atr = DIFER; else { atom.tipo = OPREL; atom.atrib.atr = MENOR; } estado = 3; break; Outros estados ficam como exercícios

void FormaCadeia (void): Funções auxiliares: void FormaCadeia (void): Armazena o novo caractere lido no final da variável cadeia É chamada para formar a cadeia de um identificador, palavra reservada, número ou constante cadeia de caracteres, ou um dos operadores and, or ou not atomo ClassificaCadeia (void): Classifica uma cadeia de caracteres Possíveis classes: Identificador palavra reservada operadores and, or e not; Retorna o átomo formado (tipo e atributo)

atomo FormaNumero (void): Funções auxiliares: atomo FormaNumero (void): Converte uma cadeia de dígitos decimais em seu valor numérico Retorna o átomo formado (tipo e atributo) atomo Classifica (void): Classifica átomos formados por apenas um caractere, exceto os inválidos Retorna o átomo formado (tipo e atributo se for o caso) int PalavraReserv (void): Verifica se uma cadeia é uma palavra reservada Retorna seu tipo, em caso positivo

Definição de constantes simbólicas para as palavras reservadas, para os outros tipos de átomos e para os atributos dos operadores (#defines):

3.4 – Análise Sintática por Diagramas de Transições 3.4.1 – Objetivos da análise sintática Verificar a estrutura sintática de um programa Servir de esqueleto para: Construção da tabela de símbolos Análise semântica Geração do código intermediário Exemplo: árvore sintática do programa do fatorial (várias figuras)

3.4.2 – Diagramas de transições Análise sintática por diagramas de transições é um método top-down preditor: Top-down: Partindo do símbolo inicial, através de derivações diretas, vai simulando a construção do programa analisado Preditor: Um só átomo é suficiente para decidir qual produção usar para fazer uma derivação direta

Características de um método preditor: Os átomos são analisados um por um, do início para o final do programa Para decidir qual produção usar numa derivação direta, não é necessário checar um átomo já analisado Não é necessário olhar para algum átomo que vai aparecer mais adiante A gramática não pode ser recursiva à esquerda A gramática deve estar fatorada à esquerda (assunto a ser estudado no capítulo sobre análise sintática)

A gramática do Mini-Pascal admite análise sintática preditora, tal como o método dos diagramas de transições Muitas gramáticas não admitem métodos preditores Requerem a análise de átomos futuros e/ou passados

Percorrer diagrama de X Usa-se um diagrama para cada não-terminal Nas transições, usa-se terminais ou não-terminais (na análise léxica: caracteres) Exemplo para não-terminais: sejam as produções: Y → . . . . . . X . . . . . X → . . . . . . X Percorrer diagrama de X Diagrama de Y Diagrama de X

A seguir, diagramas de transições sintáticas de cada não-terminal da gramática do Mini-Pascal Incluídas transições para tratamento de erros sintáticos Tal tratamento é bem simples, só a título de ilustração

a) Prog  PROGRAM ID PVIRG Decls CmdComp PONTO FINAL

ExecTransic (X): Percorre o diagrama de X, antes de mudar de estado Estados 9 e 10: Tratamento de erro Incondicional: Mudança incondicional de estado

b) Decls  ε  VAR ListDecl

ListDecl  ( DeclTipo )+ c) ListDecl  DeclTipo  DeclTipo ListDecl Para facilitar a construção do diagrama, pode-se escrever suas produções de outra maneira: ListDecl  ( DeclTipo )+

d) DeclTipo  ListId DPONTS Tip PVIRG

e) ListId  ID  ID VIRG ListId Pode-se escrever suas produções de outra maneira: ListId  ID ( VIRG ID )*

f) Tip  INTEGER  BOOLEAN

g) CmdComp  BEGIN ListCmd END

ListCmd  Cmd ( PVIRG Cmd )* h) ListCmd  Cmd  Cmd PVIRG ListCmd Pode-se escrever suas produções de outra maneira: ListCmd  Cmd ( PVIRG Cmd )*

i) Cmd  CmdIf  CmdWhile  CmdRead  CmdWrite | CmdAtrib  CmdComp

IF Expr THEN IF Expr THEN Cmd ELSE Cmd j) CmdIf  IF Expr THEN Cmd | IF Expr THEN Cmd ELSE Cmd Ambiguidade: a sub-forma sentencial IF Expr THEN IF Expr THEN Cmd ELSE Cmd tem duas árvores sintáticas Regra de solução: o ELSE corresponde ao último THEN encontrado O diagrama de transições resolve o problema

j) CmdIf  IF Expr THEN Cmd | IF Expr THEN Cmd ELSE Cmd

k) CmdWhile  WHILE Expr DO Cmd

l) CmdRead  READ ABPAR ListId FPAR

m) CmdWrite  WRITE ABPAR ListW FPAR

ListW  ElemW ( VIRG ElemW )* n) ListW  ElemW  ElemW VIRG ListW Pode-se escrever suas produções de outra maneira: ListW  ElemW ( VIRG ElemW )*

o) ElemW  CADEIA  Expr

p) CmdAtrib  ID ATRIB Expr

Expr  ExprSimpl ( OPREL ExprSimpl ) ? q) Expr  ExprSimpl  ExprSimpl OPREL ExprSimpl Pode-se escrever suas produções de outra maneira: Expr  ExprSimpl ( OPREL ExprSimpl ) ?

ExprSimpl  Term ( OPAD Term )* r) ExprSimpl  Term  Term OPAD ExprSimpl Pode-se escrever suas produções de outra maneira: ExprSimpl  Term ( OPAD Term )*

Term  Fat ( OPMULT Fat )* s) Term  Fat  Fat OPMULT Term Pode-se escrever suas produções de outra maneira: Term  Fat ( OPMULT Fat )*

t) Fat  ID  CTE  TRUE  FALSE  ABPAR Expr FPAR  OPNEG Fat

3.4.3 – Implementação dos diagramas de transições sintáticas Primeiramente, uma função main para acionar o analisador sintático

Variáveis globais: logic erro nome nomearq; FILE *program, *result; void main () { printf ("A N A L I S E S I N T A T I C A\n\n"); printf ("Nome do arquivo: "); fflush (stdin); gets (nomearq); program = fopen (nomearq, "r"); result = fopen ("atomosmp", "w"); erro = FALSE; carac = NovoCarac (); NovoAtomo (); ExecProg (); printf ("\nAnalise do arquivo '%s' encerrada", nomearq); if (erro) printf ("\n\nPrograma com erros!!!"); printf ("\n\nVer analise no arquivo 'atomosmp'"); getch (); } Variáveis globais: logic erro nome nomearq; FILE *program, *result; char carac; atomo atom; char *cadeia; ExecProg: implementa o diagrama de transições do não-terminal Prog ; é o centro do analisador sintático

a)A função ExecProg:

void ExecProg () { int estado = 1; while (estado != 8) switch (estado) { case 1: if (atom.tipo == PROGRAM) {NovoAtomo (); estado = 2;} else {Esperado ("PROGRAM"); estado = 9;} break; case 2: if (atom.tipo == ID) {NovoAtomo (); estado = 3;} else {Esperado ("IDENTIFICADOR"); estado = 9;} case 3: if (atom.tipo == PVIRG) {NovoAtomo (); estado = 4;} else {Esperado ("PONTO E VIRGULA"); estado = 9; }

case 4: ExecDecls (); estado = 5; break; case 5: ExecCmdComp (); estado = 6; break; case 6: if (atom.tipo == PONTO) {NovoAtomo (); estado = 7;} else {Esperado ("PONTO"); estado = 10;} break; case 7: if (atom.tipo == FINAL) estado = 8; else {Esperado ("END OF FILE"); estado = 10;} case 9: if (atom.tipo == PVIRG) {NovoAtomo (); estado = 4;} else if (atom.tipo == FINAL) estado = 8; else {NovoAtomo (); estado = 9;} case 10: else {NovoAtomo (); estado = 10;} }

b) A função ExecDecls: void ExecDecls () { int estado = 11; while (estado != 13) switch (estado) { case 11: if (atom.tipo == VAR) {NovoAtomo (); estado = 12;} else estado = 13; break; case 12: ExecListDecl (); estado = 13; }

c) A função ExecListDecl: void ExecListDecl () { int estado = 14; while (estado != 16) switch (estado) { case 14: ExecDeclTipo (); estado = 15; break; case 15: if (atom.tipo == ID) estado = 14; else estado = 16; }

d) A função DeclTipo:

Funções para outros não-terminais ficam como exercícios void ExecDeclTipo () { int estado = 17; while (estado != 21) switch (estado) { case 17: ExecListId (); estado = 18; break; case 18: if (atom.tipo == DPONTS) {NovoAtomo (); estado = 19;} else {Esperado ("DOIS PONTOS"); estado = 22;} break; case 19: ExecTip (); estado = 20; break; case 20: if (atom.tipo == PVIRG) {NovoAtomo (); estado = 21;} else {Esperado ("PONTO E VIRGULA"); estado = 22;} break; case 22: if (atom.tipo == PVIRG) else if (atom.tipo == FINAL) estado = 21; else {NovoAtomo (); estado = 22;} break; } Funções para outros não-terminais ficam como exercícios

3.5 – Tabela de Símbolos em Diagramas de Transições 3.5.1 – Objetivos da tabela de símbolos Agregar informações sobre todos os identificadores do programa Disponibilizar essas informações para: Análise semântica Geração do código intermediário

Para o Mini-Pascal, considerando só a fase de análise, as informações são as seguintes: Cadeia de caracteres do identificador Tipo do identificador: Nome de programa Nome de variável Nome de função (para o Pascal padrão) Nome de procedimento (para o Pascal padrão)

Se for variável: Tipo da variável (inteira, booleana) Se é indexada (para o Pascal padrão) Se tem inicialização Se é referenciada Para a fase de síntese, outras informações: Endereço de memória Número de bytes ocupados, etc.

3.5.2 – Estrutura de dados para a tabela de símbolos Pode-se usar estrutura de hashing aberto Função para o hashing: Onde: NCLASSHASH é o número de classes. n é o número de caracteres de x (sem o ‘\0’)

Exemplo: Tabela de símbolos (hashing) do programa do fatorial (NCLASSHASH = 23)

Neste exemplo, as classes têm somente 0 e 1 símbolo; Em outros, as listas das classes podem ser maiores.

Declarações para a tabela de símbolos: typedef struct celsimb celsimb; typedef celsimb *simbolo; struct celsimb { char *cadeia; int tid, tvar; logic inic, ref; simbolo prox; }; Variáveis globais: simbolo simb; simbolo tabsimb[NCLASSHASH];

3.5.3 – Implementação da tabela de símbolos em diagramas de transições A geração da tabela de símbolos é feita nas ações dos diagramas de transições sintáticas O símbolo com o nome do programa é inserido na produção do não-terminal Prog

Ampliado a seguir:

InsereSimb (atom.atrib.cadeia, IDPROG); Insere na tabela de símbolos o atributo cadeia do átomo recém-formado, como sendo do tipo identificador de programa Retorna um ponteiro para a célula inserida na tabela de símbolos Sua programação consiste em inserir numa tabela hashing

Os nomes de variáveis são inseridos nas transições do não terminal ListId

O diagrama de ListId é chamado: No diagrama de DeclTip, na região das declarações No diagrama de CmdRead, na região dos comandos Diagrama de CmdRead Diagrama de DeclTip

A inserção deve ocorrer na região das declarações e não na dos comandos aqui sim aqui não Diagrama de CmdRead Diagrama de DeclTip

Pode-se usar uma variável-flag de nome declarando (global): Na região de declarações: declarando = VERDADE; Nas outras regiões: declarando = FALSO; Em ListId , só se deve inserir quando declarando == VERDADE No início da função ExecProg (início da análise sintática): void ExecProg () { int estado = 1; while (estado != 8) switch (estado) { case 1: - - - - - - - - - - - - -

No diagrama de Decls : No início, declarando = VERDADE; No final, declarando = FALSO;

No diagrama de ListId , tratamento distinto para declarando == VERDADE declarando == FALSO

ProcuraSimb (atom . atrib . cadeia): Procura na tabela de símbolos a célula que guarda o atributo cadeia do átomo recém-formado

ProcuraSimb (atom . atrib . cadeia): Quando encontra, retorna um ponteiro para a célula procurada; quando não, retorna NULL Sua programação consiste em procurar numa tabela hashing

ProcuraSimb (atom . atrib . cadeia): Nas declarações, em caso positivo, é uma dupla declaração No comando READ, em caso negativo, é identificador usado mas não declarado

Nas declarações, o tipo da variável só aparece mais adiante, fora do diagrama de ListId Podem ser várias variáveis inseridas sem o tipo

Solução: colocar os símbolos de uma declaração numa lista linear de símbolos (lista de ponteiros para células - global) Mais adiante, quando aparecer o tipo na declaração, percorre-se essa lista, adicionando-o em cada célula

Exemplo: i, fat, n: INTEGER; listsimb: variável global

Para adicionar, nas células das variáveis declaradas, a informação sobre o tipo: No diagrama de DeclTip : A lista global de símbolos é anulada e inicializada vazia, antes da chamada do diagrama de ListId Depois da chamada do diagrama de Tip , adiciona-se o tipo às referidas células, usando o valor de outra variável global: tipocorrente

Para adicionar, nas células das variáveis declaradas, a informação sobre o tipo: E no diagrama de Tip :

AdicTipoVar adiciona, em cada símbolo da lista linear, a informação contida na variável global tipocorrente i, fat, n: INTEGER;

No comando READ, a célula com o nome da variável é marcada como inicializada e referenciada

No diagrama de CmdAtrib: Se encontrado na tabela de símbolos, o identificador do lado esquerdo deve ser marcado como inicializado e como referenciado Se não encontrado, é identificador usado mas não declarado

No diagrama de Fat : Se encontrado na tabela de símbolos, o identificador deve ser marcado como referenciado Se não encontrado, é identificador usado mas não declarado

3.6 – Testes Semânticos em Diagramas de Transições 3.6.1 – Especificações semânticas do Mini-Pascal Qualquer identificador de variável usado nos comandos do programa deve estar declarado Nenhum identificador pode ser declarado mais de uma vez no programa Toda variável deve ser inicializada e referenciada no programa O nome do programa não pode ser usado como variável

Especificações relacionadas com expressões: Num comando de atribuição, a expressão e a variável que recebe seu valor devem ser do mesmo tipo Os operadores +, -, ~, *, / e os relacionais <, <=, >, >= só admitem operandos inteiros Os operandos dos operadores = e <> devem ser de mesmo tipo Os operadores AND, OR e NOT só admitem operandos booleanos As expressões nos cabeçalhos dos comandos IF e WHILE devem ser booleanas

3.6.2 – Detecção de identificadores de variáveis não-declaradas Ao ser usado num comando, um identificador deve estar na tabela de símbolos O tipo do identificador deve ser IDVAR Um identificador é usado em: Comandos READ Lado esquerdo de comandos de atribuição Fatores de expressões

a) Detecção em Comandos READ: no diagrama do ListId

b) Detecção no lado esquerdo de um comando de atribuição:

c) Detecção no fator de uma expressão :

3.6.3 – Detecção de dupla declaração de identificadores No diagrama de ListId , ao ser declarado, um identificador não deve estar na tabela de símbolos

3.6.4 – Detecção de identificadores não referenciados e não inicializados Percorrer todas as classes de TabSimb Visitar célula por célula em cada classe Reportar aquelas marcadas como não referenciadas e/ou não inicializadas TabSimb

3.6.5 – Determinação do tipo das expressões Os testes semânticos relacionados com expressões requerem a determinação do tipo de expressões e sub-expressões: Os operadores +, -, ~, *, / e os relacionais <, <=, >, >= só admitem operandos inteiros Os operandos dos operadores =, <> devem ser de mesmo tipo Os operadores AND, OR e NOT só admitem operandos booleanos Num comando de atribuição, a expressão e a variável que recebe seu valor devem ser do mesmo tipo As expressões nos cabeçalhos dos comandos IF e WHILE devem ser booleanas

Sejam as seguintes produções contendo operadores do Mini-Pascal: Expr  ExprSimpl ( OPREL ExprSimpl )? ExprSimpl  Term ( OPAD Term )* Term  Fat ( OPMULT Fat )* Fat  OPNEG Fat Cada OPREL é cercado por duas ExprSimpl’s Cada OPAD é cercado por dois Term’s Cada OPMULT é cercado por dois Fat’s Cada OPNEG é seguido por um Fat Esses não-terminais representam os operandos de tais operadores

Expr  ExprSimpl ( OPREL ExprSimpl )? ExprSimpl  Term ( OPAD Term )* Term  Fat ( OPMULT Fat )* Fat  OPNEG Fat Os tipos dos operandos devem obedecer às especificações de compatibilidade com tais operadores Idéia: a execução dos diagramas de Expr, ExprSimpl, Term e Fat pode retornar o tipo da sub-expressão que representam

ret: variável de retorno Idéia: a execução dos diagramas de Expr, ExprSimpl, Term e Fat pode retornar o tipo da sub-expressão que representam int ExecExpr () { int ret; - - - - - - - return ret; } int ExecExprSimpl () { int ret; - - - - - - - return ret; } int ExecTerm () { int ret; - - - - - - - return ret; } int ExecFat () { int ret; - - - - - - - return ret; } ret: variável de retorno Deve guardar o tipo do não-terminal do diagrama executado O valor de ret deve ser calculado durante a execução do diagrama

Diagrama de Fat (função ExecFat): Seja cada um dos caminhos que levam, sem erros, ao estado 81

Na produção Fat  ID: O tipo de Fat é o tipo da variável representada por ID

Nas produções Fat  CTE | TRUE | FALSE O tipos de Fat são respectivamente inteiro, booleano e booleano: FALSE

Na produção Fat  ( Expr ): O tipo de Fat é o tipo de Expr a ser apresentado adiante

Produção Fat  OPNEG Fat : O tipo de Fat do lado esquerdo deve ser o tipo admitido pelo atributo de OPNEG O atributo de OPNEG pode ser: NOT, que só admite operando booleano NEG, que só admite operando inteiro É necessário um teste de compatibilidade entre o atributo de OPNEG e o tipo de Fat do lado direito

Produção Fat  OPNEG Fat : Da análise sintática oper: variável local inteira, para guardar o atributo de um operador Em caso de erro, retorna-se o tipo admitido pelo atributo de OPNEG

Diagrama de Term: Term  Fat ( OPMULT Fat )* Ao transitar de 75 para 76 pela primeira vez, não há teste de compatibilidade Ao voltar de 76 para 75, deve haver teste entre o OPMULT e o Fat anterior Ao transitar de 75 para 76 depois da primeira vez, deve haver teste entre o Fat e o OPMULT anterior Função ExecTerm

oper = 0, no início de ExecTerm Diagrama de Term: Term  Fat ( OPMULT Fat )* Para não haver teste de compatibilidade, ao passar pela primeira vez: oper = 0, no início de ExecTerm

Diagrama de Term: Term  Fat ( OPMULT Fat )* O valor de ret veio de Fat na última transição de 75 para 76

ExprSimpl  Term ( OPAD Term )* Diagrama de ExprSimpl : ExprSimpl  Term ( OPAD Term )* Estratégia análoga ao diagrama de Term

ExprSimpl  Term ( OPAD Term )* Diagrama de ExprSimpl : ExprSimpl  Term ( OPAD Term )* oper = 0, no início de ExecExprSimpl

ExprSimpl  Term ( OPAD Term )* Diagrama de ExprSimpl : ExprSimpl  Term ( OPAD Term )*

Expr  ExprSimpl ( OPREL ExprSimpl )? Diagrama de Expr: Expr  ExprSimpl ( OPREL ExprSimpl )? Ao transitar de 68 para 69 não há teste de compatibilidade Ao transitar de 69 para 70, deve haver teste entre o OPREL e o ExprSimpl anterior Ao transitar de 70 para 71, deve haver teste entre o novo ExprSimpl e o OPREL anterior

Expr  ExprSimpl ( OPREL ExprSimpl )? Diagrama de Expr: Expr  ExprSimpl ( OPREL ExprSimpl )?

Expr  ExprSimpl ( OPREL ExprSimpl )? Diagrama de Expr: Expr  ExprSimpl ( OPREL ExprSimpl )?

Expr  ExprSimpl ( OPREL ExprSimpl )? Diagrama de Expr: Expr  ExprSimpl ( OPREL ExprSimpl )? O tipo de uma expressão relacional é booleano

CmdAtrib  ID ATRIB Expr Diagrama de CmdAtrib: CmdAtrib  ID ATRIB Expr O tipo de Expr deve ser o mesmo da variável correspondente ao ID

CmdAtrib  ID ATRIB Expr Diagrama de CmdAtrib: CmdAtrib  ID ATRIB Expr tvar: variável local destinada a guardar o tipo da variável

CmdAtrib  ID ATRIB Expr Diagrama de CmdAtrib: CmdAtrib  ID ATRIB Expr texpr: variável local destinada a guardar o tipo da expressão

CmdIf  IF Expr THEN Cmd Diagrama de CmdIf: | IF Expr THEN Cmd ELSE Cmd O tipo de Expr deve booleano

CmdIf  IF Expr THEN Cmd Diagrama de CmdIf: | IF Expr THEN Cmd ELSE Cmd

CmdWhile  WHILE Expr DO Cmd Diagrama de CmdWhile: CmdWhile  WHILE Expr DO Cmd O tipo de Expr deve booleano

CmdWhile  WHILE Expr DO Cmd Diagrama de CmdWhile: CmdWhile  WHILE Expr DO Cmd

3.7 – Geração de Código Intermediário Supor que serão utilizadas quádruplas A geração das quádruplas deve estar contida nas ações dos diagramas de transições sintáticas Exemplo: Código intermediário não otimizado para o programa fatorial:

openmod: aloca memória para as variáveis locais e parâmetros de um módulo A estrutura de dados para essas quádruplas será vista em capítulo específico. O código intermediário deve ser otimizado e traduzido para Assembly