1.56k likes | 1.71k Views
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
E N D
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, -.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 emitá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: ProgPROGRAM ID;DeclsCmdComp. DeclsεVARListDecl ListDeclDeclTipDeclTipListDecl DeclTipListId:Tip; ListIdIDID,ListId Tip INTEGERBOOLEAN CmdCompBEGINListCmdEND ListCmdCmdCmd;ListCmd CmdCmdIfCmdWhileCmdReadCmdWrite CmdAtribCmdComp
Produções da gramática (continuação 1) : CmdIfIFExprTHENCmd | IFExprTHENCmdELSECmd CmdWhileWHILEExprDOCmd CmdReadREAD( ListId) CmdWriteWRITE(ListW) ListWElemWElemW,ListW ElemWExprCADEIA CmdAtribID :=Expr
Produções da gramática (continuação 2) : ExprExprSimplExprSimplOPRELExprSimpl ExprSimpl Term Term OPADExprSimpl Term Fat Fat OPMULT Term Fat IDCTE(Expr)TRUEFALSE OPNEGFat
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:
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 )* • Constantes inteiras: seu tipo é CTE e seu outro atributo é o seu valor inteiro; esse valor tem de caber em 4 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
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
Possíveis átomos: OPREL - MENIG OPREL - DIFER OPREL - MENOR e) O caractere < é o primeiro
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
Átomo de tipo FINAL: artificial i) Um dos caracteres ‘\0’, ‘ ’, ‘\n’, ‘\t’, ‘\r’, ou qualquer outro é o primeiro 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
typedef struct atomo atomo; struct atomo { int tipo; atribatomo atrib; }; 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"); atom.tipo = INVAL; carac = NovoCarac (); while(atom.tipo != FINAL){ NovoAtomo (); ImprimeAtomo ();} printf ("\nAnalise do arquivo '%s' encerrada", nomearq); printf ("\n\nVeratomos no arquivo 'atomosmp'"); getch (); } 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
voidNovoAtomo () { 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
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;} elseif (isdigit (carac)) {- - - - -; estado = 4;} elseif ((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;} elseif (isdigit (carac)) { FormaCadeia (); carac = NovoCarac(); 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 ----- elseif ((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;} break;
case 6: if (carac == '=') { atom.tipo = OPREL; atom.atrib.atr = MENIG; carac = NovoCarac();} elseif (carac == '>'){ atom.tipo = OPREL; atom.atrib.atr = DIFER; carac = NovoCarac();} else { atom.tipo = OPREL; atom.atrib.atr = MENOR; } estado = 3; break; Outros estados ficam como exercícios
Funções auxiliares: • voidFormaCadeia (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 • atomoClassificaCadeia (void): • Classifica uma cadeia de caracteres • Possíveis classes: • Identificador • Uma das palavras reservadas • Um dos operadores and, or e not; • Retorna o átomo formado (tipo e atributo)
Funções auxiliares: • atomoFormaNumero (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)