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 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, newlines, 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 Exemplo: Em Fortran, os comandos DO DO 5 5 I I = = 1.25 1,25 são transformados em DO5I=1.25 DO5I=1,25 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 – Lex para 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