·

Ciência da Computação ·

Compiladores

· 2023/2

Send your question to AI and receive an answer instantly

Ask Question

Preview text

Projeto de Compilador E1 de An´alise L´exica Prof. Lucas Mello Schnorr schnorr@inf.ufrgs.br 1 Introdu¸c˜ao A primeira etapa consiste em fazer um analisador l´exico utilizando a ferramenta de gerac¸˜ao de reconhecedores flex. Tu deves manter o arquivo tokens.h (fornecido) sem modificac¸ ˜oes. A func¸˜ao principal deve estar em um arquivo main.c separado do arquivo scanner.l para facilitar testes autom´aticos que utilizam uma func¸˜ao prin- cipal parecida com a fornecida em anexo. 2 Funcionalidades Necess´arias 2.1 Definir express˜oes regulares Reconhecimento dos lexemas correspondentes aos to- kens descritos na sec¸˜ao Descri¸c˜ao dos Tokens abaixo, unicamente atrav´es da definic¸˜ao de express˜oes regula- res no arquivo da ferramenta flex. Cada express˜ao re- gular deve estar associada a pelo menos um tipo de to- ken. Classificar os lexemas reconhecidos em tokens re- tornando as constantes definidas no arquivo tokens.h fornecido ou c´odigos ASCII para caracteres simples. 2.2 Contagem de linhas Controlar o n´umero de linha do arquivo de entrada. Uma func¸˜ao cujo prot´otipo ´e int get line number(void) deve ser implementada e deve retornar o n´umero da li- nha atual no processo de reconhecimento de tokens. Ela ´e utilizada nos testes autom´aticos. Lembre-se que a pri- meira linha de qualquer arquivo dado como ´e entrada ´e a linha n´umero um. 2.3 Ignorar coment´arios Ignorar coment´arios no formato C99: tudo o que segue a partir de // e tudo que est´a compreendido entre /* e */. As linhas devem ser contabilizadas mesmo dentro de coment´arios do segundo tipo. Espac¸os devem ser igual- mente ignorados. 2.4 Lan¸car erros l´exicos Lanc¸ar erros l´exicos ao encontrar caracteres inv´alidos na entrada, retornando o token de erro. 3 Descri¸c˜ao dos Tokens Existem tokens que correspondem a caracteres particu- lares, como v´ırgula, ponto-e-v´ırgula, parˆenteses, para os quais ´e mais conveniente usar seu pr´oprio c´odigo ASCII, convertido para inteiro, como valor de retorno que os identifica. Para os tokens compostos, como palavras re- servadas e identificadores, utiliza-se uma constante, con- forme o arquivo tokens.h fornecido (mais tarde utili- zaremos recursos do bison) com um c´odigo maior do que 255 para represent´a-los. Os tokens se enquadram em diferentes categorias: (1) palavras reservadas da lingua- gem; (2) caracteres especiais; (3) operadores compostos; (4) identificadores; e (5) literais. 3.1 Palavras Reservadas da Linguagem As palavras reservadas da linguagem s˜ao: int float bool char string if then else while do input output return const static foreach for switch case break continue class private public protected end default 3.2 Caracteres Especiais Os caracteres simples especiais empregados pela lingua- gem s˜ao listados abaixo separados apenas por espac¸os, e devem ser retornados com o pr´oprio c´odigo ASCII con- vertido para inteiro. S˜ao eles: , ; : ( ) [ ] { } + - | * / < > = ! & % # ˆ . $ 3.3 Operadores Compostos A linguagem possui operadores compostos, al´em dos operadores representados por alguns dos caracteres da sec¸˜ao anterior. Os operadores compostos s˜ao: <= >= == != && || >> << Onde o >> ´e o shift para a esquerda (TOK OC SL). 3.4 Identificadores Os identificadores da linguagem s˜ao formados por um caractere alfab´etico seguido de zero ou mais caracteres al- fanum´ericos, onde considera-se caractere alfab´etico como letras mai´usculas ou min´usculas ou o caractere subli- nhado e onde d´ıgitos s˜ao 0, 1, 2, ..., 9. 3.5 Literais Literais s˜ao formas de descrever constantes no c´odigo fonte. Literais do tipo int s˜ao representados como repetic¸ ˜oes de um ou mais d´ıgitos precedidos opcional- mente pelo sinal de negativo ou positivo. Literais em float s˜ao formados como um inteiro seguido de ponto decimal e uma sequˆencia de d´ıgitos. A notac¸˜ao cient´ıfica ´e poss´ıvel para n´umeros ponto flutuantes utilizando um E ou e seguindo de um n´umero positivo ou negativo in- teiro. Literais do tipo bool podem ser false ou true. Literais do tipo char s˜ao representados por um ´unico ca- ractere entre entre aspas simples como por exemplo: ’a’ ’ ’ ’+’ "meu nome" "x = 3;" 1 A Arquivo tokens.h /* Lista dos tokens, com valores constantes associados. Este arquivo ser´a posterioremente substitu´ıdo, n˜ao acrescente nada. Os valores das constantes sao arbitr´arios, mas n˜ao podem ser alterados. Cada valor deve ser distinto e fora da escala ASCII. Assim, n˜ao conflitam entre si e com os tokens representados pelo pr´oprio valor ASCII de caracteres isolados. */ #define TK_PR_INT 256 #define TK_PR_FLOAT 257 #define TK_PR_BOOL 258 #define TK_PR_CHAR 259 #define TK_PR_STRING 260 #define TK_PR_IF 261 #define TK_PR_THEN 262 #define TK_PR_ELSE 263 #define TK_PR_WHILE 264 #define TK_PR_DO 265 #define TK_PR_INPUT 266 #define TK_PR_OUTPUT 267 #define TK_PR_RETURN 268 #define TK_PR_CONST 269 #define TK_PR_STATIC 270 #define TK_PR_FOREACH 271 #define TK_PR_FOR 272 #define TK_PR_SWITCH 273 #define TK_PR_CASE 274 #define TK_PR_BREAK 275 #define TK_PR_CONTINUE 276 #define TK_PR_CLASS 277 #define TK_PR_PRIVATE 278 #define TK_PR_PUBLIC 279 #define TK_PR_PROTECTED 280 #define TK_OC_LE 281 #define TK_OC_GE 282 #define TK_OC_EQ 283 #define TK_OC_NE 284 #define TK_OC_AND 285 #define TK_OC_OR 286 #define TK_OC_SL 287 #define TK_OC_SR 288 #define TK_LIT_INT 291 #define TK_LIT_FLOAT 292 #define TK_LIT_FALSE 293 #define TK_LIT_TRUE 294 #define TK_LIT_CHAR 295 #define TK_LIT_STRING 296 #define TK_IDENTIFICADOR 297 #define TOKEN_ERRO 298 #define TK_PR_END 299 #define TK_PR_DEFAULT 300 B Arquivo main.c /* Func¸˜ao principal para impress˜ao de tokens. Este arquivo ser´a posterioremente substitu´ıdo, n˜ao acrescente nada. */ 2 #include <stdio.h> #include "tokens.h" extern int yylex(void); extern int yylex_destroy(void); extern FILE *yyin; extern char *yytext; extern int get_line_number (void); #define print_nome(TOKEN) \ printf("%d " #TOKEN " [%s]\n", get_line_number(), yytext); #define print_nome2(TOKEN) \ printf("%d TK_ESPECIAL [%c]\n", get_line_number(), TOKEN); int main (int argc, char **argv) { int token = 0; while (token = yylex()) { switch (token){ case ’,’: case ’;’: case ’:’: case ’(’: case ’)’: case ’[’: case ’]’: case ’{’: case ’}’: case ’+’: case ’-’: case ’|’: case ’*’: case ’/’: case ’<’: case ’>’: case ’=’: case ’!’: case ’&’: case ’%’: case ’#’: case ’ˆ’: case ’.’: case ’$’: print_nome2 (token); break; case TK_PR_INT: print_nome(TK_PR_INT); break; case TK_PR_FLOAT: print_nome(TK_PR_FLOAT); break; case TK_PR_BOOL: print_nome (TK_PR_BOOL); break; case TK_PR_CHAR: print_nome (TK_PR_CHAR); break; case TK_PR_STRING: print_nome (TK_PR_STRING); break; case TK_PR_IF: print_nome (TK_PR_IF); break; case TK_PR_THEN: print_nome (TK_PR_THEN); break; case TK_PR_ELSE: print_nome (TK_PR_ELSE); break; case TK_PR_WHILE: print_nome (TK_PR_WHILE); break; case TK_PR_DO: print_nome (TK_PR_DO); break; case TK_PR_INPUT: print_nome (TK_PR_INPUT); break; case TK_PR_OUTPUT: print_nome (TK_PR_OUTPUT); break; case TK_PR_RETURN: print_nome (TK_PR_RETURN); break; case TK_PR_CONST: print_nome (TK_PR_CONST); break; case TK_PR_STATIC: print_nome (TK_PR_STATIC); break; case TK_PR_FOREACH: print_nome (TK_PR_FOREACH); break; case TK_PR_FOR: print_nome (TK_PR_FOR); break; case TK_PR_SWITCH: print_nome (TK_PR_SWITCH); break; case TK_PR_CASE: print_nome (TK_PR_CASE); break; case TK_PR_BREAK: print_nome (TK_PR_BREAK); break; case TK_PR_CONTINUE: print_nome (TK_PR_CONTINUE); break; 3 case TK_PR_CLASS: print_nome (TK_PR_CLASS); break; case TK_PR_PRIVATE: print_nome (TK_PR_PRIVATE); break; case TK_PR_PUBLIC: print_nome (TK_PR_PUBLIC); break; case TK_PR_PROTECTED: print_nome (TK_PR_PROTECTED); break; case TK_PR_END: print_nome (TK_PR_END); break; case TK_PR_DEFAULT: print_nome (TK_PR_DEFAULT); break; case TK_OC_LE: print_nome (TK_OC_LE); break; case TK_OC_GE: print_nome (TK_OC_GE); break; case TK_OC_EQ: print_nome (TK_OC_EQ); break; case TK_OC_NE: print_nome (TK_OC_NE); break; case TK_OC_AND: print_nome (TK_OC_AND); break; case TK_OC_OR: print_nome (TK_OC_OR); break; case TK_OC_SL: print_nome (TK_OC_SL); break; case TK_OC_SR: print_nome (TK_OC_SR); break; case TK_LIT_INT: print_nome (TK_LIT_INT); break; case TK_LIT_FLOAT: print_nome (TK_LIT_FLOAT); break; case TK_LIT_FALSE: print_nome (TK_LIT_FALSE); break; case TK_LIT_TRUE: print_nome (TK_LIT_TRUE); break; case TK_LIT_CHAR: print_nome (TK_LIT_CHAR); break; case TK_LIT_STRING: print_nome (TK_LIT_STRING); break; case TK_IDENTIFICADOR: print_nome (TK_IDENTIFICADOR); break; case TOKEN_ERRO: print_nome (TOKEN_ERRO); break; default: printf ("<Invalid Token with code %d>\n", token); return 1; break; } } yylex_destroy(); return 0; } 4