%%{init: {"flowchart": {"useMaxWidth": false}, "sequence": {"useMaxWidth": false}, "class": {"useMaxWidth": false}, "state": {"useMaxWidth": false}, "er": {"useMaxWidth": false}}}%%
flowchart TD
A["Texto que alguém escreveu<br/>uma descrição, um programa"] --> B["Reconhecer os símbolos<br/>letras viram palavras da linguagem"]
B --> C["Reconhecer a estrutura<br/>palavras viram uma árvore"]
C --> D["Verificar o sentido<br/>nomes existem? tipos combinam?"]
D --> E["Traduzir<br/>a árvore vira algo que executa"]
E --> F["Um resultado<br/>sobre entrada que ninguém preparou"]
B -.->|"máquinas de memória finita"| G["Autômatos finitos"]
C -.->|"máquinas com pilha"| H["Gramáticas livres de contexto"]
G -.->|"o limite provado"| H
Introdução
Por onde começar
Esta é a versão de consulta, feita para quem está com o editor aberto do lado e uma pergunta específica na cabeça. Quem lê do começo ao fim encontra o mesmo conteúdo na Introdução e nos capítulos.
Cada capítulo aparece aqui em quatro peças. Uma ensina, duas recuperam o que já foi ensinado e a quarta cobra. Abrir a errada é o jeito mais rápido de perder uma tarde e concluir que o assunto é difícil. Ele é difícil, mas não por esse motivo.
O assunto tem uma propriedade cômoda: verifica-se sozinho. Um reconhecedor que aceita o que devia recusar denuncia o erro na primeira entrada torta, sem esperar por ninguém. É por isso que a recomendação que mais rende cabe numa palavra: execute. O texto é o mapa; o código é o terreno.
Três profundidades do mesmo capítulo
Uma ensina; as outras duas recuperam o que já foi ensinado.
Qual das quatro peças abrir depende de uma pergunta só: você está aprendendo isto agora, reconstruindo o que já leu ou conferindo se aprendeu mesmo? O capítulo completo é o texto principal. É também o único que responde “por que assim, e não de outro jeito”, e por isso o mais caro de trocar por outra coisa. Traz a construção inteira: os algoritmos em forma implementável, as demonstrações conduzidas quando cabem no nível do texto e as alternativas que ficaram pelo caminho. É o que se lê para entender pela primeira vez. Reservar duas horas seguidas para ele sai mais barato do que três sessões de vinte minutos.
A versão reduzida cobre o mesmo terreno numa fração do tamanho. Ela mantém o fio do argumento e corta o desenvolvimento. Serve para reconstruir um raciocínio já lido, ou para descobrir se determinado assunto é mesmo o que você procura antes de investir a leitura longa.
O resumo é mais curto ainda e tem um uso honesto só: revisão de algo já estudado, pouco antes de voltar ao código. Ele recupera; não ensina. Como primeira leitura, produz uma sensação agradável de entendimento que não sobrevive ao primeiro contato com a implementação.
A quarta peça cobra. São três exercícios em níveis crescentes, sobre a teoria daquele capítulo e nada mais, seguidos de um banco de questões conceituais. A utilidade das questões está menos em acertar do que em errar. A alternativa escolhida por engano costuma nomear com precisão o raciocínio torto que vinha sendo usado sem que ninguém notasse.
O que você constrói lendo
Nove peças de código, e cada uma consome o que a anterior produziu.
Em paralelo aos capítulos cresce um programa. É o compilador de uma pequena linguagem de padrões: alguém escreve os padrões que quer encontrar, e o compilador devolve uma máquina capaz de reconhecê-los sobre texto real, que ninguém preparou. São nove peças de código, do leitor de expressões à máquina virtual que roda o resultado. A cadeia que elas percorrem é a mesma que o texto estuda.
Dois dos quatorze capítulos não acrescentam peça. Convém saber quais, antes de estranhar a lacuna. O dos autômatos de pilha define um modelo cuja realização concreta é o analisador do capítulo seguinte. O da análise sintática ascendente forma critério de escolha entre duas famílias de analisadores e permanece fora do programa, por decisão declarada ali mesmo. Nos outros doze, cada leitura termina com alguma coisa a mais rodando na sua máquina.
Daí vem uma consequência prática. Pular um capítulo para voltar a ele depois sai caro, porque cada peça consome o que a anterior produziu. Adiar uma delas não adia o trabalho: transfere a dificuldade para um ponto três etapas adiante, onde ela reaparece fantasiada de outro problema. Reconhecê-la ali custa o dobro do que teria custado no lugar certo.
Uma rotina que funciona
Um capítulo por vez, com o editor aberto.
Comece pelo capítulo completo. Ao chegar a um algoritmo, pare de ler e escreva-o antes de seguir — a distância entre acompanhar um algoritmo bem explicado e conseguir escrevê-lo é maior do que a leitura sugere, e ela só se mede tentando. Terminada a peça, jogue nela os casos que você mesmo inventou à mão, e não os que o próprio programa gerou. Só então passe aos exercícios, que são de teoria e servem para medir se o entendimento sobreviveu à implementação.
Guarde, desde o primeiro capítulo, um registro curto das decisões que você tomou e das alternativas que descartou, com a razão de cada uma. Parece burocracia. Nos capítulos finais, alguém vai perguntar por que a árvore tem a forma que tem, ou por que a tabela de transição é representada daquele jeito. Esse registro é a diferença entre defender a própria construção e apenas descrevê-la.
Voltando depois de um intervalo longo, o caminho mais curto é o resumo daquele capítulo, seguido de meia hora mexendo na peça que ficou de pé. A memória de uma construção volta pelas mãos antes de voltar pela leitura. Repare na ordem: quem recomeça relendo teoria costuma reler três capítulos antes de reconhecer o próprio programa. O mapa continua sendo o texto. Quem anda é o código, e ele responde na hora, inclusive quando a resposta é que a teoria foi entendida pela metade.