Autor
Afiliações

Moacyr Francischetti Corrêa, Bacharel em Ciência da Computação, Licenciado em Computação, Especialista em Ciência de Dados e Inteligência Artificial, PhD em Biotecnologia in Silico

Análise Léxica — Plano de Aula

Documento exclusivo do professor. Este é o guia operacional das seis aulas deste módulo: roteiro por blocos das duas aulas teóricas, plano das quatro aulas de tutoria, entregáveis do marco de consolidação e riscos antecipados. Não distribua à turma — as questões de discussão e a leitura pedagógica dos erros perdem função assim que o estudante as lê antes da aula.

Visão Geral do Módulo

Onde este módulo fica — é onde a teoria vira produto, e onde o semestre cobra a primeira consolidação.

flowchart LR
    M05["Módulo 5<br/>Determinização e<br/>minimização"]
    M06["Módulo 6<br/>Limites das<br/>linguagens regulares"]
    M07["Módulo 7<br/>Análise léxica<br/>MARCO DE CONSOLIDAÇÃO"]
    M08["Módulo 8<br/>Gramáticas livres<br/>de contexto"]
    M10["Módulo 10<br/>Análise sintática<br/>descendente"]

    M05 --> M06 --> M07 --> M08 --> M10

    subgraph FECHA["O que o módulo 7 fecha"]
        F1["Motor de autômatos<br/>vira peça de software<br/>com interface"]
        F2["Decisões que a teoria<br/>não menciona: desempate,<br/>descarte, erro, retrocesso"]
        F3["Geradores automáticos<br/>reconhecidos como<br/>o percurso já construído"]
    end

    subgraph ENTREGA["O que o módulo 7 entrega ao resto do curso"]
        D1["Interface sob demanda<br/>que a análise sintática<br/>vai consumir"]
        D2["Posição em todo símbolo,<br/>base de toda mensagem<br/>de erro posterior"]
        D3["Padrão de qualidade do<br/>relato de erro, critério<br/>permanente a partir daqui"]
    end

    M07 --- FECHA
    M07 --- ENTREGA
    D1 -.->|"cliente da fase"| M10
    D2 -.->|"toda fase adiante"| M08
Figura 1: O módulo 7 fecha o eixo regular e entrega a interface que a análise sintática vai consumir.

Este módulo é diferente de todos os anteriores por um motivo que convém anunciar à turma na primeira frase da primeira aula: nenhum algoritmo novo aparece aqui. Cinco módulos construíram um motor completo — expressão regular entra, autômato mínimo sai — e este módulo o embrulha numa peça de software com nome próprio, interface declarada e comportamento previsível diante de entrada malformada.

Isso muda a natureza da condução. Nos módulos anteriores havia teorema a demonstrar; aqui o conteúdo é um conjunto de decisões de projeto com alternativas defensáveis. O risco correspondente é a aula degenerar em opinião — “eu prefiro assim” —, e o antídoto é ancorar cada decisão num caso concreto que dá errado quando a decisão é a outra. Este plano fornece esses casos, e eles são o esqueleto do roteiro. Há também uma inversão de valores a corrigir na turma, porque a formação em computação teórica a produz sozinha: o que é elegante e cabe num teorema não é o que decide se o compilador funciona sobre arquivos reais.

O módulo é ainda o primeiro marco de consolidação, o que dobra o peso da tutoria: além do incremento, revisa-se tudo o que foi produzido desde o primeiro módulo, e é aqui que ocorre a primeira avaliação por pares.

Objetivos, Competências e Habilidades

Objetivos de aprendizagem. Consolidar todo o eixo regular em uma fase de compilador funcionando. Introduzir as decisões práticas que a teoria não menciona e que constituem a maior parte do esforço de um analisador léxico real. Estabelecer a qualidade do relato de erro como critério permanente.

Competências a desenvolver. Capacidade de transformar um componente teórico em uma peça de software com interface definida, tratamento de casos excepcionais e comportamento previsível diante de entrada malformada. Capacidade de avaliar uma ferramenta automática a partir do conhecimento do que ela implementa.

Habilidades a adquirir. Distinguir padrão, lexema, símbolo léxico e atributo. Aplicar as regras de casamento mais longo e de prioridade entre padrões. Tratar espaços, comentários e fim de entrada. Detectar erros léxicos e produzir mensagens que indiquem posição e causa provável. Explicar o que um gerador automático de analisadores léxicos faz e quando usá-lo.

Estrutura das Aulas

Aulas 1 e 2 — Aula Teórica

Roteiro por blocos — cada bloco resolve uma ambiguidade que o anterior deixou visível no quadro.

Bloco de abertura — uma linha de texto e três leituras

Chegue com uma única linha escrita no quadro, sem título e sem explicação:

\texttt{sef>=12.}

Pergunte só isto: quantos símbolos há aí, e quais? Deixe a sala responder em voz alta e anote todas as leituras, sem julgar nenhuma. Três sempre aparecem: a leitura em pedaços mínimos, se, f, >, =, 12, ., com seis unidades; a leitura que aglutina tudo numa coisa só; e a correta, sef, >=, 12, sobrando um ponto.

Agora faça a pergunta que abre o módulo: os autômatos construídos nos últimos cinco módulos não decidem entre essas leituras. Cada um responde “sim” ou “não” para uma cadeia inteira que alguém já delimitou. Quem delimita? A pergunta fica pendente, e é o gancho do módulo inteiro.

Feche o bloco nomeando os três fenômenos da linha e escreva as três palavras no canto do quadro, para não apagar durante as duas aulas: comprimento (por que >= é um símbolo e não dois), empate (por que se sozinho seria palavra reservada e dentro de sef não é) e sobra (o que fazer com o ponto). Você voltará às três no fechamento.

Bloco seguinte — por que a fase existe separada

Enfrente a pergunta que quase todo livro responde de passagem: por que não deixar a gramática descer até o caractere? Conduza por três argumentos que se acumulam — nenhum sozinho seria decisivo. O primeiro é de adequação de formalismo: a estrutura interna de um identificador é regular, a de um programa não é, e usar o formalismo caro para o que o barato resolve é desperdício. O segundo é de legibilidade: uma gramática ao nível do caractere teria de admitir um não terminal de espaço opcional entre cada par de símbolos de cada produção. O terceiro, o menos citado e o mais sentido em projeto grande, é a concentração de dependências: codificação do texto, convenção de fim de linha e repertório de caracteres ficam confinados a um componente, e o resto do compilador trabalha sobre símbolos, que não têm codificação.

Termine com a honestidade que a turma valoriza: a fronteira vaza. Onde um nome designa um tipo ou uma variável conforme tenha sido declarado antes, o analisador léxico consulta informação que só a análise semântica possui — realimentação conhecida, largamente usada e reconhecidamente feia.

Bloco seguinte — a interface, e o erro que não é de algoritmo

Este é o bloco mais consequente da primeira aula, e não tem conta nem teorema; tem uma decisão. Apresente as duas opções com neutralidade aparente — entregar a lista completa de símbolos, ou entregar um símbolo por solicitação mantendo o estado da varredura — e deixe a turma defender a primeira, que parece mais simples de testar. É mesmo. Depois derrube-a com três argumentos em ordem crescente de força. Memória é o mais citado e o mais fraco: para arquivos de programa é irrelevante. Latência do diagnóstico é melhor: com a lista completa, um erro sintático na terceira linha só aparece depois de o arquivo inteiro ser varrido. E acoplamento temporal é o decisivo — materializar a lista fixa que a análise léxica termina antes de a análise sintática começar, e nenhuma decisão léxica poderá depender de informação produzida depois. Diga em voz alta que esse é erro de arquitetura, não de algoritmo: o de algoritmo aparece no teste, o de acoplamento aparece meses depois, quando corrigi-lo custa a reescrita da interface inteira.

Primeira questão de discussão em duplas. “Um analisador léxico produz a lista completa de símbolos antes de a análise sintática começar. Qual é o problema mais grave dessa escolha? (a) o consumo de memória, proporcional ao tamanho do arquivo; (b) o atraso na exibição do primeiro erro sintático; (c) a impossibilidade de qualquer decisão léxica depender de informação produzida por fase posterior; (d) nenhum — a escolha é equivalente à alternativa e mais simples de testar.”

Aplique o procedimento inteiro: voto individual sem comentário seu, discussão em duplas, segundo voto. A resposta é (c). Quem vota (a) ou (b) escolheu consequências reais mas circunstanciais, que somem quando o arquivo é pequeno ou a compilação não é interativa; (c) não some nunca. Quem vota (d) dá o voto mais útil: peça a essa dupla que descreva como acrescentaria a realimentação depois, e a descrição produz a resposta.

Bloco seguinte — quatro noções que a conversa informal funde

Passe ao vocabulário e não corra: esta é a confusão terminológica mais persistente do assunto. Monte a tabela no quadro com quatro colunas — padrão, lexema, símbolo, atributo — e preencha-a com um exemplo único, dizendo em voz alta onde cada um vive. Tome o padrão de número, sinal opcional seguido de dígitos com parte fracionária opcional, e o trecho -3.14. O padrão vive na especificação: foi escrito uma vez, quando a linguagem foi projetada, e não aparece durante a varredura — o que aparece é o autômato que dele derivou. O lexema vive no texto: é a cadeia de cinco caracteres. O símbolo vive na comunicação entre fases: é a categoria “número” que atravessa a interface. E o atributo vive pendurado no símbolo: o valor -3{,}14, obtido por conversão, e a posição de origem.

Sobre a posição, seja enfático, porque é a decisão de melhor relação custo-benefício do projeto inteiro. Ela existe naturalmente num único lugar do compilador — aqui, com o índice do caractere na mão. Faça a sala percorrer o cenário alternativo: a análise semântica descobre um nome não declarado, olha para o nó da árvore e encontra o nome e nada mais; sobram procurar o nome no texto original, que dá a primeira ocorrência e não a certa, reportar sem posição, ou reescrever tudo depois.

Segunda questão de discussão em duplas. “No texto-fonte aparece o trecho -3.14, reconhecido pela categoria de números. O valor numérico -3{,}14 armazenado junto ao símbolo é: (a) o padrão; (b) o lexema; (c) o símbolo; (d) um atributo.”

A resposta é (d). Quem vota (b) confunde a cadeia de caracteres com o valor obtido dela por conversão — é o erro mais frequente e o ganho principal da questão. Quem vota (c) fundiu símbolo com a estrutura que o carrega. Feche com a consequência prática: quem confunde lexema com símbolo faz a fase seguinte comparar cadeias de texto em vez de categorias.

Bloco de abertura da segunda aula — retomada pela linha pendente

Não recapitule em forma de resumo. Aponte para a linha sef>=12. e para as três palavras no canto, e pergunte qual delas a primeira aula resolveu. Nenhuma: ela estabeleceu o que a fase é, com quem conversa e o que entrega; a segunda decide onde cortar o texto.

Enuncie então a afirmação que organiza a aula inteira: a especificação léxica é ambígua por construção, e não por descuido de quem a escreveu. Categorias léxicas se sobrepõem, e nenhuma escolha melhor de expressões regulares elimina a sobreposição. São necessárias duas convenções externas, e elas resolvem conflitos de naturezas diferentes.

Bloco seguinte — as duas regras, e o mecanismo do último aceite

Escreva as duas em contraste, uma sob a outra, porque confundi-las é o erro conceitual do módulo. O casamento mais longo resolve conflito entre candidatos de comprimentos diferentes: vence quem chegar mais longe. A prioridade resolve conflito entre candidatos de comprimento igual: vence quem estiver primeiro na especificação.

Formalize o suficiente para que a definição não pareça arbitrária. Para cada regra j e posição i, defina

m_j(i) = \max\{\, \ell \ge 1 \;\mid\; w[i \ldots i+\ell-1] \in L(r_j) \,\}, \qquad M(i) = \max_j m_j(i),

e demonstre em três linhas que o máximo existe: os comprimentos candidatos são inteiros positivos limitados superiormente por |w| - i, e conjunto não vazio de inteiros positivos limitado superiormente tem máximo único. A demonstração vale pela hipótese que explicita — o texto é finito. Sobre um fluxo que não termina ela falha, e o casamento mais longo passa a exigir um limite artificial de comprimento.

Agora o exemplo numérico do bloco, com índices visíveis. Tome o trecho 12. na posição i = 0, com o autômato de número que exige dígito e admite ponto seguido de mais dígitos. Após ler 1, estado final: último aceite em comprimento 1. Após ler 2, final de novo: último aceite em comprimento 2. Após ler ., o estado deixa de ser final e não há próximo caractere: o autômato para em comprimento 3. Pergunte qual é o lexema. A resposta majoritária é 12., e é errada: o candidato é o último aceite, comprimento 2, lexema 12; o ponto sobra e será relido a partir da posição 2. Registre a conta ao lado, porque reaparece no bloco do retrocesso: três caracteres examinados, dois consumidos. A distinção entre “onde parou” e “onde aceitou pela última vez” é o que mais gente implementa errado, e o defeito só se manifesta em entradas em que o autômato avança além do último aceite — exatamente as que ninguém escreve nos primeiros testes.

Volte então à linha da abertura. Em sef>=12. a partir da posição 0: a regra de palavra reservada aceita comprimento 2 (se); a de identificador aceita 1, 2 e 3, sendo 3 o seu máximo. Logo M(0) = 3, só uma regra atinge o máximo, e a categoria é identificador. A prioridade nem chega a ser consultada. Depois faça o caso do texto contendo se isolado: as duas regras atingem comprimento 2, há empate, e aí sim a prioridade decide pela palavra reservada.

Bloco seguinte — a ordem entre as regras, e as duas estratégias

Escreva no quadro, em letras grandes: o casamento mais longo vem primeiro, a prioridade vem depois. Nessa ordem, e não na inversa.

Mostre a inversão em ação com a mesma linha. Se a implementação consultar a lista de palavras reservadas assim que os caracteres lidos formarem uma palavra da lista, ela encontra se nos dois primeiros caracteres, emite a palavra reservada e recomeça em f: uma palavra reservada seguida de lixo, num programa perfeitamente válido. Faça a turma notar por que o defeito sobrevive aos testes — ele passa em todos os casos com a palavra reservada isolada, que são exatamente os que alguém escreve ao testar palavras reservadas.

Apresente então as duas estratégias, sem esconder que a sua conclusão é preferência e não teorema. Dar a cada palavra reservada seu próprio padrão, antes do identificador na especificação, usa apenas o mecanismo existente e é o que os geradores incentivam; custa um autômato por palavra reservada e faz a correção do analisador depender da ordem das linhas — mover uma linha para baixo do identificador quebra a linguagem em silêncio. Reconhecer tudo como identificador e reclassificar por consulta a uma lista custa uma busca por identificador e concentra as reservadas num lugar declarado. Escolha a segunda, pelo motivo de acoplamento; quem usar gerador adotará a primeira e deve comentar na especificação que a ordem daquelas linhas é semanticamente significativa.

Bloco seguinte — o que a teoria não menciona

Trate os três casos que a teoria ignora e que respondem por boa parte do código real. Espaço em branco: estabeleça a distinção entre descartar e ignorar. Descartar é reconhecer o trecho, consumi-lo e não produzir símbolo; ignorar seria não reconhecê-lo, e é errado. O espaço precisa estar na especificação porque é ele que define onde um identificador termina. Acrescente o detalhe que os grupos erram: o descarte é em laço, não em uma passada, porque depois de espaço pode vir comentário e depois de comentário pode vir mais espaço.

Comentários: este é o momento de maior rendimento teórico do módulo. Comentário de linha é regular — a marca inicial seguida de qualquer sequência sem quebra de linha. Comentário de bloco não aninhável também é. Comentário de bloco aninhável não é, e a demonstração é a do módulo anterior: n aberturas seguidas de n fechamentos pertence à linguagem, com m \ne n não pertence, reconhecer isso exige contar um número ilimitado, e pelo lema do bombeamento nenhum autômato finito o faz. Diga à turma o que acabou de acontecer: um resultado de impossibilidade decidiu uma característica visível da linguagem, pela primeira vez no percurso. Não deixe a conclusão pender para um lado só — aninhar é conveniência real; o ponto é que custa um contador escrito à mão, fora do modelo.

Fim de entrada: recomende o símbolo explícito, com categoria própria e posição igual ao fim do texto, em vez de ausência, valor especial ou exceção. A razão é uniformidade na fase seguinte — a mensagem “esperava-se tal construção e encontrou-se o fim do arquivo” sai do mesmo código que produz todas as outras. E depois de emitido, chamadas subsequentes continuam devolvendo o símbolo de fim, porque a recuperação de erro sintático pede o próximo símbolo mais de uma vez.

Terceira questão de discussão em duplas. “Uma linguagem quer comentários de bloco que possam conter outros comentários completos, com o fechamento encerrando apenas o nível correspondente. Sobre reconhecer esses comentários no analisador léxico: (a) basta uma expressão regular mais elaborada; (b) basta um autômato determinístico com mais estados; (c) é impossível com autômato finito, e exige um contador fora do modelo; (d) é impossível em qualquer modelo, e a linguagem não pode ter esse recurso.”

A resposta é (c). Quem vota (a) ou (b) acha que o obstáculo é de engenho e não de poder de reconhecimento — confronte essa dupla pedindo o número de estados para profundidade arbitrária. Quem vota (d) confunde “fora do alcance deste modelo” com “impossível”, e é o erro mais interessante: linguagens sérias oferecem comentários aninháveis. Feche apontando para o próximo módulo.

Bloco de construção ao vivo — o laço de varredura

Avise a sala para abrir o editor e digitar junto, e circule para verificar em vez de perguntar “todo mundo conseguiu?”, que sempre recebe silêncio afirmativo. Comece declarando o que não vai ser escrito aqui: nenhuma construção de autômato. Os autômatos vêm da especificação, uma vez na inicialização, pelo percurso já construído. Se o analisador léxico contiver código de construção de autômato, a fase virou uma segunda implementação do motor em vez de cliente dele.

Construa o laço na ordem em que as decisões foram tomadas na aula: descarte em laço dos trechos irrelevantes; para cada regra, avanço com registro do último aceite; escolha do comprimento máximo; desempate por prioridade entre os candidatos de comprimento máximo; construção do símbolo com categoria, lexema, atributo e posição; avanço da posição de início. Verbalize e nomeie cada decisão enquanto digita — é o que transforma o code-along em revisão da aula. Encerre acrescentando os dois contadores, caracteres examinados e consumidos: deixados para o fim, viram tarefa adiada; feitos aqui, custam duas linhas.

Bloco seguinte — erro léxico, mensagem e recuperação

Delimite primeiro, porque a expressão “erro léxico” é usada de forma generosa demais: ocorre erro léxico na posição i quando nenhuma regra admite casamento não vazio a partir de i. Erro de estrutura é da fase seguinte; erro de nome é da fase depois dela. Volte à linha da abertura e pague a terceira palavra do canto do quadro: o ponto que sobrou em sef>=12. só é erro léxico se a especificação não tiver categoria alguma que o inicie — havendo pontuação com ponto, ele é símbolo válido e quem reclama é a análise sintática. A mesma sobra tem dois destinos conforme a especificação, e isso demonstra que a fronteira entre as fases é decisão de projeto.

Substitua “boa mensagem de erro” por propriedades verificáveis: localização precisa, com linha e coluna contadas a partir de um; formato consagrado com arquivo, linha, coluna, severidade e mensagem separados por dois-pontos, que os editores transformam em navegação clicável de graça; linha ofensora com cursor, reproduzindo as tabulações originais em vez de trocá-las por espaços; causa provável quando puder ser inferida com segurança; e ausência de ruído.

O último item merece a conta. Recuperar avançando um caractere por vez faz a análise prosseguir, e um trecho de dez caracteres inválidos colados por engano produz dez mensagens idênticas. Coalescer a corrida inválida — consumir a sequência inteira de caracteres que não podem iniciar símbolo algum e reportar uma vez, citando o trecho — produz uma, com diferença de poucas linhas de código. Enuncie o critério que valerá para todas as fases seguintes: um erro real vale mais que dez erros derivados. E registre a armadilha da condição de parada: a corrida precisa parar também em caractere descartável e em início de comentário, senão um caractere inválido seguido de espaço engole o identificador seguinte.

Bloco seguinte — o retrocesso tem preço, e o preço é medível

Separe as duas questões que costumam vir juntas. Buffer é assunto de entrada e saída: a técnica clássica dos dois buffers alternados com caractere sentinela resolve um problema que você provavelmente não tem, porque se o arquivo-fonte couber na memória o retrocesso vira subtração de índices. Vale conhecê-la porque reaparece quando a entrada é um fluxo verdadeiro.

O retrocesso, esse existe sempre. Defina o fator de releitura como o quociente E/C entre caracteres examinados e consumidos, e separe as duas causas do seu crescimento: o retrocesso propriamente dito, inerente ao último aceite e presente mesmo com um único autômato, e o paralelismo entre regras, que é decisão de implementação e desaparece se as regras forem combinadas num autômato único.

Faça a cota no quadro. Com k regras, t símbolos produzidos e L o maior prefixo que alguma regra percorre antes de travar,

E \le k \cdot L \cdot t,

porque cada símbolo corresponde a uma posição de início e, em cada uma, cada regra executa no máximo L transições. Instancie: com k = 5, t = 100, L = 4 e C = 300, a cota dá E \le 5 \times 4 \times 100 = 2000 e o fator fica limitado por 2000/300 \approx 6{,}7.

Depois mostre a degeneração, que é a parte interessante. Uma categoria de literal textual cujo autômato avança até o delimitador de fechamento, num arquivo em que a abertura foi esquecida, percorre todo o resto do arquivo a partir de cada posição de início. Com L da ordem de |w| a varredura vira quadrática: num arquivo de 20\,000 caracteres com 2\,000 posições de início, a cota sobe para a ordem de 4 \times 10^{7} transições — por causa de um único caractere errado. É a patologia que não aparece em teste e aparece na primeira vez que alguém compila um arquivo gerado por máquina.

Bloco de fechamento — os geradores, e a volta ao quadro da abertura

Deixe os geradores automáticos para o fim e diga à turma por que. Apresentados logo depois das expressões regulares, ninguém teria motivo para estudar Thompson, determinização ou minimização — os três teriam virado curiosidade teórica sem consequência, que é o que parecem ser para quem só usa a ferramenta.

Descreva o que o gerador faz por dentro e faça a turma reconhecer cada item: analisa cada padrão numa árvore de expressão regular, converte por Thompson, une sob um estado inicial comum, determiniza propagando a rotulação de categoria aos estados finais, minimiza, e emite a tabela de transição com o laço de casamento mais longo. Diga a frase inteira: não há nada num gerador de analisadores léxicos que vocês não tenham construído. Acrescente os recursos que nomeiam problemas reais — ação associada ao padrão, condições de início e antecipação — e observe que as condições de início são mecanismo declaradamente fora do modelo.

Feche com a pergunta que decide o uso: não é “qual é melhor”, é “o que muda mais neste projeto” — se muda a especificação, o gerador transforma alteração de código em alteração de dado; se muda o comportamento diante de entrada defeituosa, escreva à mão, porque refinar mensagem dentro de analisador gerado é lutar contra o mecanismo.

Termine voltando à linha do quadro e às três palavras do canto. sef é identificador porque o comprimento máximo é três e só uma regra o atinge; >= é um símbolo só pela mesma regra; 12 para no último aceite, e o ponto é sobra, com destino decidido pela especificação e não pelo analisador; e o empate, que a linha não chegou a produzir, é o que decide se isolado. Quatro cortes, três convenções, e nenhum algoritmo novo — exatamente o que foi anunciado na primeira frase da primeira aula.

Aulas 3 a 6 — Tutoria do Projeto Integrador

Quatro aulas de tutoria — marco de consolidação: revisão executada do acumulado, analisador léxico completo, casos de teste, documentação em dia e a primeira avaliação por pares.

A carga desta tutoria é maior que a de um módulo comum e a diferença não está no incremento de código, que é modesto, e sim na revisão. Anuncie isso na abertura da primeira sessão, com a frase que evita a reclamação da última: “o analisador léxico vocês terminam em duas sessões; a revisão dos seis módulos anteriores é que vai consumir as outras duas”.

Tenha o seu próprio projeto aberto e projetado nas quatro sessões, e projete em especial a tabela de verificação da entrega consolidada — item, como conferir, estado. Grupos que veem a tabela pronta entendem o formato do marco imediatamente; grupos que só ouvem “entreguem tudo revisado” entregam um arquivo de texto com boas intenções.

Primeira sessão — a revisão que executa, não a que confere

Dedique a sessão inteira ao levantamento do pendente, e imponha o método, porque o método é o conteúdo desta sessão. A revisão de um marco não é ler documentação: é executar o que nunca foi executado.

Peça a cada grupo que rode todos os autômatos de todas as categorias léxicas contra os corpora de aceitação e rejeição escritos lá no módulo 2, e que produza uma tabela com categoria, número de estados do mínimo, casos aceitos, casos rejeitados e veredito. Insista no confronto com os corpora, e não em inspeção visual do diagrama. O argumento que convence a turma é o meu próprio erro, e vale contá-lo em voz alta: no meu projeto, a categoria de pontuação carregava desde o módulo 2 uma expressão escrita em prosa legível em vez da mini-notação — espaços que eu lia como separadores eram símbolos literais, e parênteses que eu queria como caracteres eram operadores de agrupamento. A expressão foi analisada sem erro, gerou um autômato bem formado de cento e doze estados, atravessou a determinização e a minimização e apareceu em duas tabelas que eu já havia publicado, reconhecendo a linguagem errada o tempo inteiro. Depois da correção, aquela categoria caiu de 112 para 64 estados após a construção de Thompson, de 29 para 16 após a determinização e de 7 para 5 após a minimização; os totais das seis categorias passaram de 1344 para 1296, de 345 para 332 e de 22 para 20. Feche o relato com a frase que os grupos precisam levar: medir não é verificar — os módulos 4 e 5 mediram um artefato que não fazia o que dizia fazer, e nenhuma das duas medições tinha como perceber.

Circule perguntando a cada grupo qual categoria eles nunca executaram. Sempre há uma. Num projeto cumulativo, cada módulo usa apenas o que precisa, e o que não foi usado não foi verificado. É essa categoria que a sessão tem de exercitar primeiro.

Antes de liberar a implementação, exija de cada grupo a interface escrita e justificada — que operação a fase seguinte vai chamar, o que ela devolve, o que acontece no fim da entrada, o que acontece diante de erro. Recuse começar a implementação sem isso, e diga o motivo com data: a decisão de materializar a lista inteira de símbolos parece mais simples de testar e fixa o momento em que a análise léxica termina; quem a tomar vai descobrir o problema no módulo 10, com o analisador sintático escrito, e não aqui.

Segunda e terceira sessões — construir o analisador e os casos de teste

Estas são as sessões de construção, com programação em pares e revezamento verificado. O analisador reutiliza o motor de autômatos já pronto: se aparecer código de construção de autômato dentro do analisador léxico, alguma coisa está no lugar errado, e vale dizer isso à sala antes que aconteça.

Circule com uma bateria fixa de três entradas na mão e aplique a mesma a todos os grupos, na mesma ordem. A primeira é um identificador que tem uma palavra reservada como prefixo próprio — no meu projeto, onibus contra a palavra reservada on, e patterns contra pattern. É a entrada que revela a inversão da ordem das duas regras, e ela passa despercebida em todo conjunto de testes escrito por quem só testou palavras reservadas isoladas. A segunda é um operador de dois caracteres cujo primeiro caractere é um operador válido sozinho, como >= contra >, e um número negativo colado, como -5: sem casamento mais longo, a condição de comparação vira comparação estrita seguida de um sinal de igual solto, e a mensagem de erro sai três símbolos adiante, sem apontar a causa. A terceira é uma corrida de caracteres inválidos, cinco ou seis seguidos, que precisa produzir um diagnóstico e não seis.

Sobre palavras reservadas, deixe o grupo escolher entre as duas estratégias e exija a justificativa. Eu escolhi reconhecer como identificador e reclassificar por consulta a uma lista, e o argumento não é de velocidade: dar um padrão próprio a cada palavra reservada faz a correção do analisador depender da ordem das linhas da especificação, e já vi essa ordem ser alterada por alguém organizando o arquivo em ordem alfabética. Se o grupo escolher a outra estratégia, aceite, e exija um comentário na especificação avisando que a ordem daquelas linhas é semanticamente significativa.

Peça, ainda nesta etapa, os dois contadores do fator de releitura — caracteres examinados e caracteres consumidos. São duas linhas de código e transformam a discussão sobre autômatos separados contra autômato combinado numa comparação entre um número e um requisito. No meu projeto, cinco regras em paralelo sobre um programa de duzentos e cinquenta e sete caracteres produzem quarenta e sete símbolos, cento e sessenta e um caracteres consumidos, trezentos e noventa e um examinados, fator de 2,43 vezes. Projete esse número e faça a pergunta certa: duas vezes e meia quase nada continua sendo quase nada, e a decisão seria outra se o alvo fossem arquivos de megabytes.

Quarta sessão — fechamento, avaliação por pares e diário

Reserve a última sessão para o fechamento da entrega consolidada e conduza-a pela tabela de verificação, item a item, com o grupo demonstrando cada linha na máquina em vez de afirmando. A demonstração que mais rende é a das mensagens de erro: peça que rodem um arquivo com dois trechos inválidos em linhas diferentes e mostrem a saída, com arquivo, linha, coluna, linha ofensora, cursor alinhado e a contagem de símbolos reconhecidos ao fim — que precisa ser maior que zero, provando que a análise continuou depois do erro.

A primeira avaliação por pares acontece aqui, e o modo de apresentá-la determina o que você vai receber. Explique o propósito antes de distribuir o instrumento, com estas palavras ou equivalentes: o objetivo é registrar a distribuição real do trabalho e a compreensão de cada integrante sobre o conjunto, não denunciar quem trabalhou menos. Diga também, sem rodeio, que a sua observação direta em tutoria prevalece em caso de divergência — o que desarma tanto o grupo que combina notas iguais quanto o integrante que se autoavalia acima do que você viu.

Aproveite a anotação que você fez no módulo 1 sobre quem mais falava em cada grupo. É aqui que ela se paga: a compreensão precisa ser demonstrável por qualquer integrante, e o teste é simples de aplicar — escolha quem falou menos e peça que explique por que a consulta à lista de palavras reservadas acontece depois do casamento mais longo e não durante. Se a resposta não vier, o problema não é da avaliação por pares, é da divisão de trabalho, e ainda há oito módulos para corrigi-la.

Feche mandando registrar no diário três coisas: o defeito antigo que a revisão encontrou (todo grupo encontra um; grupo que diz não ter encontrado nada não executou), a decisão sobre palavras reservadas com a justificativa, e o fator de releitura medido.

Entregáveis e Avaliação

A entrega deste módulo é consolidada e avaliada com o mesmo rigor da entrega final — diga isso à turma antes de começar, não depois. Cada grupo entrega o analisador léxico completo da própria linguagem funcionando sobre programas reais, casos de teste cobrindo todas as categorias e os casos de erro, documentação revisada de tudo o que foi produzido desde o primeiro módulo, diário em dia e avaliação por pares preenchida por cada integrante.

Confira cada entrega contra a mesma lista, na mesma ordem para todos os grupos, e exija demonstração na máquina em vez de afirmação no documento: categorias verificadas contra os corpora escritos no módulo de expressões regulares; programa de exemplo reconhecido de ponta a ponta, com a contagem de símbolos; interface sob demanda, com a operação de lista declarada como conveniência secundária; casamento mais longo demonstrado num operador de dois caracteres e num número com sinal colado; prioridade demonstrada por uma palavra reservada reclassificada e por um identificador que a contém como prefixo e não é reclassificado; espaço e comentário descartados; erro com arquivo, linha, coluna, linha ofensora e cursor alinhado; recuperação sem cascata; fator de releitura medido; e compilação limpa sob o modo estrito.

A partir deste módulo, a qualidade do relato de erro é critério explícito de avaliação, e permanece até a entrega final: não é a quantidade de verificações, é a utilidade da mensagem para quem escreveu o programa errado. Registre no componente contínuo a pontualidade, a contribuição nas discussões em duplas e o engajamento nas atividades colaborativas. Sobre escopo, mantenha o critério do semestre: o que se penaliza é a redução silenciosa, descoberta pelo professor em vez de declarada pelo grupo.

Orientações Sobre o Aplicativo

Use o aplicativo da disciplina para as três votações das aulas teóricas, com a votação anônima na projeção, e guarde os dois histogramas de cada questão. O sinal específico deste módulo está na questão dos quatro conceitos: turma que converge no primeiro voto permite acelerar o bloco de desempate; turma que se divide entre lexema e atributo exige que você refaça a tabela das quatro noções antes de seguir, porque todo o bloco seguinte assume essa distinção.

Aplique também a primeira avaliação por pares pelo aplicativo, e não em papel — o registro precisa ficar arquivado e comparável com a segunda, no fim do semestre. E acompanhe o engajamento no estudo do material comparando-o com o dos módulos anteriores: como não há algoritmo novo, quem decide “isso eu já sei” e não lê chega à tutoria sem as decisões de projeto, e a queda aqui é o indicador mais antecipado desse problema.

Pontos de Atenção Específicos

“Não tem algoritmo novo” é lido como “é fácil”. É o risco número um do módulo, e atinge a turma inteira, não os grupos fracos. Corte na abertura da primeira aula: o algoritmo de subconjuntos cabe em quarenta linhas e o erro nele morre no primeiro teste; a regra de desempate não cabe em teorema nenhum e o erro nela atravessa o projeto inteiro escondido.

A ordem entre as duas regras é o defeito mais caro e o mais silencioso. Consultar a lista de palavras reservadas durante o casamento, e não depois dele, passa em todo conjunto de testes escrito por quem só testou palavras reservadas isoladas. Exija, na aula e na tutoria, um caso de teste com identificador prefixado por palavra reservada, e exija-o desde o início — descobri-lo depois custa a reescrita do laço.

Os geradores no fim, e a razão dita em voz alta. Alguém vai perguntar, já na primeira aula, por que não se usa uma ferramenta pronta. Responda em duas frases — a ferramenta faz exatamente o percurso dos últimos cinco módulos, e conhecê-la depois é o que permite prever o que ela produz — e marque o assunto para o bloco de fechamento. Antecipá-lo esvazia os cinco módulos anteriores.

A interface mal definida não cobra o preço aqui. Grupos que materializam a lista completa de símbolos terminam o módulo sem sintoma algum e descobrem o problema na análise sintática descendente, com o analisador já escrito. Por isso a interface é exigida por escrito e justificada antes de a implementação começar.

Revisar não é conferir documentação. No marco, a parte que encontra defeito é a que executa o que nunca foi executado — e todo projeto cumulativo tem algum artefato nessa condição. Grupo que relata “revisamos tudo e estava certo” quase sempre releu em vez de rodar.

A avaliação por pares pode ser lida como delação. Explique o propósito antes de aplicar: registrar a distribuição real do trabalho e a compreensão de cada integrante, com a sua observação direta prevalecendo em caso de divergência. Sem essa conversa, o instrumento produz notas iguais combinadas e não informa nada.

Cuidado com a digressão sobre codificação de caracteres. A pergunta sobre acentuação e repertórios aparece no bloco das três razões da separação. Responda pelo argumento que interessa — é essa a classe de dependência que a fase concentra num lugar só — e siga.