180 likes | 280 Views
CES-41 COMPILADORES. Capítulo IV Complementos de Análise Léxica. Capítulo IV – Complementos de Análise Léxica. 4.1 – Interface entre programa-fonte e compilador 4.2 – Bufferização do programa 4.3 – Observação de átomos seguintes 4.4 – Lex para observar átomos seguintes.
E N D
CES-41 COMPILADORES Capítulo IV Complementos de Análise Léxica
Capítulo IV – Complementos de Análise Léxica 4.1 – Interface entre programa-fonte e compilador 4.2 – Bufferizaçãodo programa 4.3 – Observação de átomos seguintes 4.4 – Lex para observar átomos seguintes
4.1 – Interface entre Programa-Fonte e Compilador • Os principais conceitos sobre análise léxica foram introduzidos no capítulo anterior: • Formação e classificação de átomos • Diagrama de transições léxicas • Ferramenta do analisador sintático
Sendo a interface entre programa-fonte e compilador, pode executar algumas tarefas secundárias: • Pular comentários, espaços em branco, tabulações, new-lines, etc (vistos em diagramas de transições e em Flex) • Contar o número de linhas lidas, possibilitando a inclusão do número da linha em mensagens de erro • Gerenciar o controle das linhas e sua continuação, nos programas escritos em linguagens em que a divisão em linhas é relevante (Fortran, Cobol)
Exemplo: contagem de linhas no diagrama de transições léxicas – para a análise sintática
Exemplo: uma linha de programa em Fortran • Caractere na coluna 6: a linha é continuação da anterior
4.2 – Bufferização do Programa • A complexidade do analisador léxico é bem menor, conceitualmente falando, do que a dos outros componentes de um compilador • No entanto, é nele que o front-end de um compilador consome a maior porção de seu tempo • É o componente que lê caractere por caractere do arquivo do programa
A velocidade do analisador léxico é crucial no projeto de um compilador • Os caracteres podem ser lidos em blocos (1 ou 4 Kbytes), evitando-se a taxa de um acesso ao disco por caractere lido • Tais blocos podem ser armazenados um por vez num buffer (string) e a análise de cada caractere pode ser feita nele • Essa bufferização pode ficar encapsulada numa função; no caso do Mini-Pascal, isso poderia acontecer na função NovoCarac
4.3 – Observação de Átomos Seguintes • Algumas linguagens requerem, para a formação e classificação de um átomo, a observação de átomos que o seguem, dentro do programa • Em Fortran, espaços em branco são eliminados durante o pré-processamento, dificultando a classificação dos átomos
DO 5 I = 1.25 DO 5 I = 1,25 são transformados em DO5I=1.25 DO5I=1,25 Exemplo: Em Fortran, os comandos Os átomos de cada comando são: 1º) DO5I=1.25: (ID, DO5I), (ATRIB), (CTREAL, 1.25) 2º) DO5I=1,25: (DO), (ROTULO, 5), (ID, I), (ATRIB), (CTINT, 1), (VIRG), (CTINT, 25) • Nos dois comandos, até antes da virgula ou do ponto, nada se pode decidir sobre a formação e classificação de todos esses átomos
1º) DO5I=1.25: (ID, DO5I), (ATRIB), (CTREAL, 1.25) 2º) DO5I=1,25: (DO), (ROTULO, 5), (ID, I), (ATRIB), (CTINT, 1), (VIRG), (CTINT, 25) • Os átomos formados na tentativa de definição de outro deles podem ser colocados numa fila pelo analisador léxico e entregues um por um, quando solicitados pelo analisador sintático • A função NovoAtomo do Mini-Pascal só deve procurar novos caracteres, quando a fila de átomos estiver vazia
4.4 – Lexpara Observar Átomos Seguintes • A linguagem Lex admite uma expressão regular do tipo r1 /r2 onde r1 e r2 são expressões regulares • Significado: uma cadeia se enquadra em r1 somente se for seguida de outra cadeia que se enquadre em r2
Exemplo: Em Fortran, para o caso dos comandos DO5I=1.25 e DO5I=1,25 • A palavra reservada do pode ser reconhecida pela expressão: do/({letra}|{digito})*=({letra}|{digito})*, • Seja o programa em Lex a seguir:
%{ #define DOLAR 0 #define ID 1 #define NUM 2 #define DO 3 #define VIRG 4 #define PONTO 5 #define EQ 6 union { char *cadeia; int valor;} yylval; %} delim [ \t\n] ws {delim}+ dig [0-9] letr [A-Za-z] num {dig}+ id {letr}({letr}|{dig})*
%% {ws} { ;} do/(({letr}|{dig})*=({letr}|{dig})*,)|{ws} {return DO;} {id} {yylval.cadeia = yytext; return ID;} {num} {yylval.valor = atoi (yytext); return NUM;} "=" {return EQ;} "," {return VIRG;} "." {return PONTO;} "$" {return DOLAR;} %%
main () { int i; while (i = yylex ()) { printf ("tipo = %d; atributo = ", i); switch (i) { case EQ: case PONTO: case VIRG: case DO: printf ("%s\n", yytext); break; case ID: printf ("%s\n", yylval.cadeia); break; case NUM: printf ("%d\n", yylval.valor); break; } } }
Rodando para a seguinte entrada: xxx do 123 do5i=1,25 do5i=1.25 $ Resultados: Obs.: o programa não reconhece constantes reais
Observações finais: • Fortran é uma linguagem de difícil análise léxica, devido ao controle das linhas e das colunas dos programas e outras coisas mais • É mais fácil fazer um analisador manual do que usar um gerador automático