Nesta parte do projeto, você irá implementar um analisador léxico para a linguagem C-
O analisador léxico do trabalho T1 deve ler um programa fonte e produzir uma lista de tokens, sequencialmente, um token de cada vez, anotado com as seguintes informações:
- localização (número da linha) no código fonte
- classe (identificador, constante inteira, símbolo, etc)
- lexema (cadeia de caracteres correspondente).
Se uma entrada incorreta for encontrada, o analisador léxico deve reportá-lo e prosseguir com a análise.
Antes de iniciar a sua implementação, recomendamos que leia com atenção:
- O capítulo 3 do livro "Introduction to Compilers and Language Design" de Douglas Thain. Apesar da sintaxe de C- ser um pouco diferente da usada no livro do Thain, os exemplos de código e o material podem ser extremamente úteis.
- O capítulo 2 do livro "Flex & Bison" de John Levine.
Em sua implementação do analisador léxico, considere as seguintes classes de tokens da linguagem:
ID Identificador
NUM Literal decimal (inteiro)
KEY Keyword (palavra-chave)
SYM Símbolo especial
ERROR Erro léxico
Comentários e caracteres de espacejamento (white space) devem ser descartados durante a fase de análise léxica do compilador.
O formato da saída do analisador léxico, um token por linha, é:
(line_num,token_class,"lexeme")
(lexeme entre aspas)
Você deve imprimir o número da linha na qual o token foi encontrado, inclusive no caso de erro. No caso de erro léxico, retornar o token ERROR. O analisador léxico deve reportar todos os erros léxicos existentes no programa fonte analisado, prosseguindo até o final do arquivo de entrada.
void main(void)
{
int a;
a = 4 + 5;
}
O analisador léxico para C- deverá ler um arquivo de entrada contendo um programa fonte em C- (com extensão .cm) e gerar um arquivo de saída (com extensão .out).
Por exemplo, para o programa fonte em main.cm (acima), a saída gerada no arquivo main.out deverá ser:
(1,KEY,"void")
(1,ID,"main")
(1,SYM,"(")
(1,KEY,"void")
(1,SYM,")")
(2,SYM,"{")
(3,KEY,"int")
(3,ID,"a")
(3,SYM,";")
(4,ID,"a")
(4,SYM,"=")
(4,NUM,"4")
(4,SYM,"+")
(4,NUM,"5")
(4,SYM,";")
(5,SYM,"}")
O nome do analisador léxico (executável) deve ser lexer. O programa lexer deve ler a entrada a partir de um arquivo com extensão .cm e escrever a saída em um arquivo com extensão .out. O nome do arquivo de saída .out deve ser igual ao nome do arquivo de entrada .cm.
O exemplo abaixo mostra o comando para executar o analisador léxico com dois argumentos (nomes dos arquivos de entrada e de saída):
$ ./lexer main.cm main.out
Disponibilizamos scripts para compilação do analisador léxico para C- (compile.sh) e para execução do analisador léxico (run.sh) para a linguagem C-.
- compile.sh (sem argumentos)
#!/bin/bash
# compile.sh (no parameters)
# edit the template file scr/lexer.l and add your Flex code to get rid of the warning message:
# "src/lexer.l:18: warning, -s option given but default rule can be matched"
flex src/lexer.l
cc -o lexer lex.yy.c -ll # "lexer" is the lexical analyzer for C-
rm lex.yy.c
- run.sh (recebe dois argumentos que deverão ser os nomes dos arquivos de entrada e de saída)
#!/bin/bash
# run.sh
# parameters: $1 is the input file name that contains C- code (.cm)
# $2 is the output file name
./lexer $1 $2
Após escrever o seu analisador léxico em src/lexer.l, rodar ./compile para gerar o executável "lexer". Em seguida, rodar ./run inputfile outputfile. No diretório examples há vários programas fonte (extensão .cm) e a saída esperada para cada um deles (extensão .out).
Observação importante: Arquivos de texto devem ser criados com Unix (apenas "\n" no final de linha).
Adaptação do material cedido pelo Prof. Vinicius Petrucci.