Etapa 2: Analisador Léxico Manual
Objetivos
- Entender o funcionamento interno de um Scanner.
- Implementar a transformação de um fluxo de caracteres (CharStream) em um fluxo de tokens (TokenStream).
- Gerenciar autômatos finitos determinísticos (DFA) via código.
Fundamentação Teórica
O Scanner (ou Lexer) é a primeira fase do compilador. Ele lê o arquivo fonte caractere por caractere e agrupa esses caracteres em Tokens. - Lexema: A sequência bruta de caracteres (ex: var, x, 123). - Token: Um par <Tipo, Valor> (ex: <KEYWORD, "var">, <ID, "x">, <NUMBER, "123">).
Atividades Práticas
1. Implementação do Scanner
Implemente a classe Scanner que recebe o código fonte (String ou File). O método principal é public Token nextToken(). A cada chamada, ele deve:
- Ignorar Espaços em Branco: Pular espaços, tabs (, quebras de linha (, e comentários.
- Detectar Fim de Arquivo (EOF): Retornar um token especial
EOFquando não houver mais caracteres. - Identificar o próximo Token:
- Se começar com letra: Pode ser um Identificador ou Palavra Reservada. (Dica: Leia até não ser mais letra/dígito, depois consulte um Map de palavras reservadas).
- Se começar com dígito: É um Número Inteiro (leia enquanto for dígito).
- Se for um operador (
+,-,*,/,(…): Retorne o token correspondente. - Atenção: Operadores compostos (
:=,<=,>=) exigem olhar um caractere à frente (Lookahead).
2. Tratamento de Erros Léxicos
Se o Scanner encontrar um caractere que não inicia nenhum token válido (ex: @, $ ou _ isolado, dependendo da linguagem), ele deve lançar uma LexicalException. A exceção deve conter: - A mensagem de erro (“Caractere inválido”). - O caractere problemático. - A linha e coluna onde o erro ocorreu.
3. Loop de Teste
Crie um programa MainScanner que: 1. Lê um arquivo .pas. 2. Chama nextToken() num laço while (token.type != EOF). 3. Imprime cada token encontrado no formato: [Tipo, "Lexema"].
Exemplo de Entrada e Saída
Entrada (teste.pas):
var x : integer;
x := 10;Saída Esperada:
[KEYWORD, "var"]
[IDENTIFIER, "x"]
[DELIMITER, ":"]
[KEYWORD, "integer"]
[DELIMITER, ";"]
[IDENTIFIER, "x"]
[OPERATOR, ":="]
[NUMBER, "10"]
[DELIMITER, ";"]
[EOF, ""]
Dicas de Implementação
Use um
StringBuilderpara acumular caracteres de identificadores e números.Mantenha variáveis
currentLineecurrentColumnatualizadas a cada caractere lido.Crie um método
peek()para ver o próximo caractere sem consumi-lo (útil para:=).Crie um mapa estático para palavras reservadas:
static final Map<String, TokenType> keywords = new HashMap<>(); static { keywords.put("program", TokenType.PROGRAM); keywords.put("var", TokenType.VAR); // ... }