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:

  1. Ignorar Espaços em Branco: Pular espaços, tabs (, quebras de linha (, e comentários.
  2. Detectar Fim de Arquivo (EOF): Retornar um token especial EOF quando não houver mais caracteres.
  3. 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 StringBuilder para acumular caracteres de identificadores e números.

  • Mantenha variáveis currentLine e currentColumn atualizadas 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);
        // ...
    }
Back to top