Fase 4: Teste Completo
🎯 Objetivo
Validar que automato, lexer e main funcionam juntos corretamente.
🧪 Testes unitários
Teste 1: Automato sozinho
Arquivo: test_automato.py (na raiz)
Observacao: no nivel do AFD, palavras reservadas ainda retornam IDENTIFIER. O remapeamento para CERTIN, C_TO_PENSANU, etc. acontece no Lexer.
"""Teste de unidade do Automato."""
from pathlib import Path
from src.mineres_compilador.automato import Automato
ARQUIVO_AUTOMATO = Path(__file__).resolve().parent / "grafos" / "automato_simples.txt"
def _novo_automato() -> Automato:
"""Cria e carrega uma nova instância do AFD para cada teste."""
afd = Automato()
afd.carregar_do_arquivo(str(ARQUIVO_AUTOMATO))
return afd
def test_automato_reconhece_identifier_basico() -> None:
"""No nível do AFD, palavras reservadas ainda são IDENTIFIER."""
afd = _novo_automato()
aceito, token_type, comprimento = afd.reconhecer("certin")
assert aceito is True, "Deve aceitar 'certin'"
assert token_type == "IDENTIFIER", f"Deve ser IDENTIFIER, recebeu {token_type}"
assert comprimento == 6, f"Comprimento deve ser 6, recebeu {comprimento}"
print("test_automato_reconhece_identifier_basico passou")
def test_automato_reconhece_integer_literal() -> None:
"""Valida literal inteiro aceito pelo AFD atual."""
afd = _novo_automato()
aceito, token_type, comprimento = afd.reconhecer("2")
assert aceito is True, "Deve aceitar '2'"
assert token_type == "INTEGER_LITERAL", f"Deve ser INTEGER_LITERAL, recebeu {token_type}"
assert comprimento == 1, f"Comprimento deve ser 1, recebeu {comprimento}"
print("test_automato_reconhece_integer_literal passou")
def test_automato_reconhece_palavras_reservadas_como_identifier() -> None:
"""Palavras reservadas são classificadas como IDENTIFIER no AFD."""
afd = _novo_automato()
palavras = ["c_to_pensanu", "simbora", "cabou", "uai", "eradin"]
for palavra in palavras:
aceito, token_type, comprimento = afd.reconhecer(palavra)
assert aceito is True, f"Deve aceitar '{palavra}'"
assert token_type == "IDENTIFIER", (
f"'{palavra}' deve ser IDENTIFIER no AFD, recebeu {token_type}"
)
assert comprimento == len(palavra), (
f"'{palavra}' deve consumir {len(palavra)} caracteres, recebeu {comprimento}"
)
print("test_automato_reconhece_palavras_reservadas_como_identifier passou")
def test_automato_aplica_maximo_prefixo_valido() -> None:
"""Se houver sufixo inválido, o AFD devolve o último estado final válido."""
afd = _novo_automato()
aceito, token_type, comprimento = afd.reconhecer("certin@@")
assert aceito is True, "Deve aceitar prefixo válido de 'certin@@'"
assert token_type == "IDENTIFIER", f"Deve ser IDENTIFIER, recebeu {token_type}"
assert comprimento == 6, f"Deve consumir apenas 'certin', recebeu {comprimento}"
print("test_automato_aplica_maximo_prefixo_valido passou")
def test_automato_rejeita_inicio_invalido() -> None:
"""Se o primeiro caractere não possui transição, deve rejeitar."""
afd = _novo_automato()
aceito, token_type, comprimento = afd.reconhecer("@@@")
assert aceito is False, "Deve rejeitar '@@@'"
assert token_type is None, "Token type deve ser None em rejeição"
assert comprimento == 0, "Comprimento deve ser 0 em rejeição"
print("test_automato_rejeita_inicio_invalido passou")
def test_automato_rejeita_entrada_vazia() -> None:
"""Entrada vazia não pode formar token."""
afd = _novo_automato()
aceito, token_type, comprimento = afd.reconhecer("")
assert aceito is False, "Deve rejeitar entrada vazia"
assert token_type is None, "Token type deve ser None em rejeição"
assert comprimento == 0, "Comprimento deve ser 0 em rejeição"
print("test_automato_rejeita_entrada_vazia passou")
if __name__ == "__main__":
test_automato_reconhece_identifier_basico()
test_automato_reconhece_integer_literal()
test_automato_reconhece_palavras_reservadas_como_identifier()
test_automato_aplica_maximo_prefixo_valido()
test_automato_rejeita_inicio_invalido()
test_automato_rejeita_entrada_vazia()
print("\nTodos os testes de Automato passaram!")
Execute:
python test_automato.py
Teste 2: Lexer sozinho
Arquivo: test_lexer.py (na raiz)
"""Teste de unidade do Lexer"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent / "src"))
from mineres_compilador.lexer import Lexer
from mineres_compilador.tokentype import TokenType
def test_lexer_reconhece_string_simples():
"""Testa tokenização de string simples"""
lexer = Lexer()
lexer.carregar_string("certin")
tokens = lexer.analisar()
assert len(tokens) == 1, f"Deve haver 1 token, recebeu {len(tokens)}"
assert tokens[0].type == TokenType.CERTIN
assert tokens[0].lexeme == "certin"
assert tokens[0].line == 1
assert tokens[0].column == 1
print("✅ test_lexer_reconhece_string_simples passou")
def test_lexer_rastreia_posicao():
"""Testa rastreamento correto de linha/coluna"""
lexer = Lexer()
lexer.carregar_string("certin eradin")
tokens = lexer.analisar()
assert len(tokens) == 2
assert tokens[0].line == 1 and tokens[0].column == 1
assert tokens[1].line == 1 and tokens[1].column == 8
print("✅ test_lexer_rastreia_posicao passou")
def test_lexer_reconhece_multiplos_tokens():
"""Testa tokenização de múltiplos tokens"""
lexer = Lexer()
lexer.carregar_string("c_to_pensanu 2 uai")
tokens = lexer.analisar()
assert len(tokens) == 3
assert tokens[0].type == TokenType.C_TO_PENSANU
assert tokens[1].type == TokenType.INTEGER_LITERAL
assert tokens[2].type == TokenType.UAI
print("✅ test_lexer_reconhece_multiplos_tokens passou")
def test_lexer_arquivo():
"""Testa carregamento de arquivo"""
lexer = Lexer()
lexer.carregar_arquivo("entradas/entrada_exemplo_main.mineires.txt")
tokens = lexer.analisar()
# Deve reconhecer muitos tokens
assert len(tokens) > 0, "Deve haver tokens"
print(f"✅ test_lexer_arquivo passou ({len(tokens)} tokens reconhecidos)")
if __name__ == "__main__":
test_lexer_reconhece_string_simples()
test_lexer_rastreia_posicao()
test_lexer_reconhece_multiplos_tokens()
test_lexer_arquivo()
print("\n✅ Todos os testes de Lexer passaram!")
Execute:
python test_lexer.py
🖥️ Teste de integração (CLI)
Teste string simples
python main.py -s "certin"
Saída esperada:
Tokens reconhecidos:
------------------------------------------------------------
Tipo Lexema Linha Coluna
------------------------------------------------------------
CERTIN certin 1 1
------------------------------------------------------------
Total: 1 tokens reconhecidos
Teste string múltipla
python main.py -s "c_to_pensanu 2 uai"
Saída esperada:
Tokens reconhecidos:
------------------------------------------------------------
Tipo Lexema Linha Coluna
------------------------------------------------------------
C_TO_PENSANU c_to_pensanu 1 1
INTEGER_LITERAL 2 1 14
UAI uai 1 16
------------------------------------------------------------
Total: 3 tokens reconhecidos
Teste arquivo completo
python main.py entradas/entrada_exemplo_main.mineires.txt
Resultado esperado: ~38 tokens
Você deve ver algo como:
Tokens reconhecidos:
------------------------------------------------------------
Tipo Lexema Linha Coluna
------------------------------------------------------------
BORA_CUMPADE bora_cumpade 1 1
CABOU cabou 2 1
COMMENT_START causo 3 1
CERTIN certin 4 1
C_NAO c_nao 5 1
...
✅ Checklist de validação
Automato
- Reconhece palavras-chave corretamente (certin, eradin, c_to_pensanu, etc)
- Reconhece números (INTEGER_LITERAL)
- Reconhece identificadores genéricos
- Retorna (True, tipo, comprimento) para aceição
- Retorna (False, None, 0) para rejeição
Lexer
- Toda string vazia = 0 tokens
- String com 1 palavra = 1 token
- String com múltiplas palavras separadas por espaço = múltiplos tokens
- Rastreamento de linha começa em 1
- Rastreamento de coluna começa em 1
- Coluna incrementa com caracteres
- Coluna reseta em newline
- Identifica palavras-chave vs identificadores
- Carrega arquivo corretamente
- Carrega string corretamente
Main.py
python main.pysem argumentos mostra helppython main.py -s "texto"funcionapython main.py arquivo.txtfunciona- Exibe tokens em tabela formatada
- Exibe total de tokens
- Mostra mensagem de erro se arquivo não existe
- Exit code 0 em sucesso
- Exit code 1 em erro
📊 Resultado esperado final
Quando rodar python main.py entradas/entrada_exemplo_main.mineires.txt, você deve ver:
Tokens reconhecidos:
------------------------------------------------------------
Tipo Lexema Linha Coluna
------------------------------------------------------------
BORA_CUMPADE bora_cumpade 1 1
CABOU cabou 2 1
COMMENT_START causo 3 1
CERTIN certin 4 1
C_NAO c_nao 5 1
C_NAO_C_TO_PENSANU c_nao_c_to_pensanu 6 1
C_TO_PENSANU c_to_pensanu 7 1
DEPENDENU dependenu 8 1
DU_CASU du_casu 9 1
ENQUANTO_TIVER_TREM enquanto_tiver_trem 10 1
ERADIN eradin 11 1
FICA_ASSIM_ENTAO fica_assim_entao 12 1
COMMENT_END fim_do_causo 13 1
MEMA_COISA mema_coisa 14 1
NEH_NADA neh_nada 15 1
OIA_PROCE_VE oia_proce_ve 16 1
PARA_O_TREM para_o_trem 17 1
QUARQUE_UM quarque_um 18 1
RODA_ESSE_TREM roda_esse_trem 19 1
SIMBORA simbora 20 1
SOB sob 21 1
TA_BAO ta_bao 22 1
TAMEM tamem 23 1
TOCA_O_TREM toca_o_trem 24 1
TREM_CUM_VIRGULA trem_cum_virgula 25 1
TREM_DI_NUMERU trem_di_numeru 26 1
TREM_DISCOLHE trem_discolhe 27 1
TREM_DISCRITA trem_discrita 28 1
TROSSO trosso 29 1
UAI uai 30 1
UM_O_OTO um_o_oto 31 1
VAM_MARCA vam_marca 32 1
VEIZ veiz 33 1
XOVE xove 34 1
IDENTIFIER a 35 1
FICA_ASSIM_ENTAO fica_assim_entao 35 3
INTEGER_LITERAL 2 35 20
UAI uai 35 22
------------------------------------------------------------
Total: 38 tokens reconhecidos
🎉 Sucesso!
Se você chegar aqui com tudo funcionando, você completou com sucesso:
- ✅ Implementou um Autômato Finito
- ✅ Implementou um Analisador Léxico
- ✅ Criou uma CLI funcional
- ✅ Tokenizou código Minerês corretamente
Parabéns! Você aprendeu lexing na prática! 🚀
📚 Próximas etapas
Agora você está pronto para:
- Implementar um Parser (análise sintática)
- Criar uma Árvore de Sintaxe Abstrata (AST)
- Implementar análise semântica
- Gerar código de máquina virtual ou bytecode