flowchart TB
subgraph distancia["Eixo 1 — distância semântica entre fonte e alvo"]
direction LR
M["montador<br/>correspondência quase um para um"] --> T["tradutor entre linguagens<br/>de alto nível"] --> C["compilador para<br/>código de máquina"]
end
subgraph tempo["Eixo 2 — quando a execução acontece"]
direction LR
AN["análise antes,<br/>execução depois"] --> CO["compilação"]
AF["análise e execução<br/>no mesmo momento"] --> IN["interpretação"]
end
distancia --> HIB["sistemas híbridos<br/>compilam para máquina virtual<br/>e interpretam o resultado"]
tempo --> HIB
Panorama da Compilação e Linguagens Formais
O mapa que o percurso inteiro vai usar para se localizar
Roteiro da aula
- Três programas defeituosos
- O contrato de correção
- Compilar, interpretar e os parentes
- As fases pelas interfaces
- O mapa teórico das gramáticas
- O que nenhum compilador pode fazer · demonstração
Ao final, você será capaz de
- distinguir compilador, interpretador, montador e tradutor entre linguagens de alto nível;
- nomear as fases e dizer o que cada uma recebe e produz;
- localizar as quatro classes da hierarquia e a máquina de cada uma.
Bloco de abertura. Não anuncie o roteiro antes dos três programas: chegue com eles já projetados e sem título. Este slide entra depois, para nomear o que a turma acabou de tentar explicar. Escrever o roteiro no quadro e deixá-lo visível nas duas aulas.
Anunciar que a segunda aula termina com código digitado ao vivo — quem só assiste não aprende aquele bloco.
Três programas defeituosos
Por que o compilador aceita o terceiro?
Sem ponto e vírgula
Reclamação imediata, apontando linha e coluna.
Texto somado a número
Reclamação mais tarde, apontando a operação inteira.
Laço que nunca termina
Nenhuma reclamação. O executável é gerado.
Forma · sentido · indecidibilidade — as três palavras ficam no canto do quadro até o fim da segunda aula.
Projete os três programas lado a lado, sem título e sem explicação, e faça só esta pergunta. Deixe a sala responder em voz alta e anote as hipóteses no quadro sem julgá-las.
A hipótese que quase sempre aparece é “porque o terceiro é mais difícil de detectar”. É o gancho: responda que não é difícil, é impossível, que existe demonstração desde 1936 — e não diga mais nada até o bloco de indecidibilidade da segunda aula. A pendência declarada segura a atenção melhor do que a resposta imediata.
Feche escrevendo as três palavras no canto do quadro e não as apague.
O contrato de correção
Traduzir é preservar o significado
\llbracket T(p) \rrbracket_{L_a}(x) = \llbracket p \rrbracket_{L_f}(x)
T é uma função parcial: nem todo texto é programa, e o tradutor tem obrigação de dizer isso em vez de produzir código sem sentido.
Escreva a igualdade no quadro antes de projetar. Dois pontos merecem verbalização explícita, porque a turma passa por eles sem notar: a parcialidade, e o plural dos diagnósticos — quem reporta só o primeiro problema é insuportável de usar. Essa observação prosaica é o que justifica, mais adiante, a infraestrutura de erro que os grupos vão construir.
Pergunta que rende: um compilador que aceitasse todo texto e sempre produzisse algum executável seria melhor ou pior?
Otimizar é trocar o programa mantendo a igualdade
O que é otimização
Substituir um programa por outro mais rápido com a igualdade intacta em todas as entradas.
O que não é
Uma transformação que muda o resultado num único caso de fronteira. Isso não é otimização agressiva: é defeito.
Amarrar a igualdade à otimização já aqui é o que a torna memorável — sem isso ela vira uma linha decorada. Adiante, quando o otimizador entrar em cena, esta é a frase que o restringe.
Se alguém perguntar por que não se busca o programa ótimo, responda em uma frase — encontrá-lo é indecidível — e volte ao roteiro; o assunto tem bloco próprio na segunda aula.
Compilar, interpretar e os parentes
A diferença cabe numa palavra: quando
Compilar
A análise é paga uma vez e amortizada. Em troca, o código gerado precisa servir a todos os valores possíveis.
Interpretar
A análise é paga a cada execução, às vezes a cada iteração. Em troca, sabe-se o valor de cada variável.
Conduza pela pergunta “quando”, nunca por uma lista de diferenças. Compilador e interpretador fazem a mesma análise; o que muda é o momento em que ela é paga. Do “quando” as duas consequências saem sozinhas, sem decorar.
Aqui aparece quase sempre a digressão “por que a linguagem X é mais rápida que a Y”. É legítima: responda em duas frases, reconduzindo ao custo da análise, e proponha que a comparação concreta volte quando houver vocabulário de otimização para sustentá-la.
Os parentes, em duas frases
Montador
Formalmente um compilador cuja fonte já corresponde um a um às instruções da máquina. O nome separado existe porque nenhuma dificuldade interessante sobra.
Tradutor entre linguagens de alto nível
Precisa preservar estrutura, nomes e formatação — o que um compilador para código de máquina pode dissolver sem culpa.
Seja econômico neste slide: ele existe para fechar o vocabulário, não para render discussão. O que interessa é a razão de o montador ter nome próprio — não há instrução a escolher nem registrador a alocar, porque quem escreveu o fonte já fez as duas coisas.
Dois eixos independentes, não duas gavetas
Não peça que decorem fronteiras. O ponto do diagrama é que as duas dimensões são independentes, e por isso um sistema pode ocupar posição nas duas ao mesmo tempo.
Deixe o diagrama no ar ao passar para a votação: quem votar por eliminação vai usá-lo.
Primeira votação
Traduz para uma máquina virtual e compila em execução os trechos frequentes. Esse sistema é:
- um compilador;
- um interpretador;
- os dois, porque as definições falam de objetos diferentes;
- nenhum dos dois, por ser híbrido sem classificação.
Procedimento inteiro: voto individual primeiro, sem nenhum comentário seu; discussão em duplas; segundo voto. Revelar a resposta antes da discussão anula a técnica.
Resposta: (c). Quem vota (a) ou (b) tenta encaixar o sistema numa gaveta única. Quem vota (d) percebeu o problema da gaveta e concluiu que não há classificação — é o erro mais interessante, e é dele que se fecha o conceito: a pergunta produtiva não é “isso é compilador ou interpretador”, e sim “o que foi decidido antes da execução e o que foi adiado”.
Votação anônima na projeção. O valor do primeiro voto está na dispersão.
As fases pelas interfaces
Fase não é nome: é uma tripla
Recebe
A representação que a fase anterior produziu.
Produz
A representação de que a fase seguinte precisa.
Detecta
Os erros que só ela tem condições de perceber.
Em dúvida sobre onde colocar uma verificação: qual é a primeira fase que dispõe da informação necessária?
Este é o erro de condução mais comum do módulo: fazer a turma decorar seis nomes. A terceira componente da tripla é a que sobrevive ao percurso inteiro, e o critério da nota é o que se leva para casa.
Construa a tabela do próximo slide no quadro, uma linha por vez, perguntando antes de escrever. A tabela projetada é conferência, não exposição.
Do texto ao código
flowchart LR
A["texto-fonte<br/>sequência de caracteres"] --> B["análise léxica"]
B --> C["sequência de tokens"]
C --> D["análise sintática"]
D --> E["árvore sintática"]
E --> F["análise semântica"]
F --> G["árvore anotada<br/>e tabela de símbolos"]
G --> H["geração intermediária"]
H --> I["representação intermediária"]
I --> J["otimização"]
J --> I
I --> K["geração de código"]
K --> L["código da máquina-alvo"]
subgraph analise["Análise (frente)"]
B
C
D
E
F
G
end
subgraph sintese["Síntese (retaguarda)"]
H
I
J
K
end
Percorra o diagrama pelas setas, nomeando o que trafega em cada uma — é o que impede a leitura como lista de caixas.
Chame a atenção para o laço da otimização sobre a representação intermediária: é a única fase cuja entrada e saída têm o mesmo formato, e isso não é detalhe de desenho.
As interfaces, linha a linha
| Fase | Recebe | Produz | Detecta |
|---|---|---|---|
| Léxica | caracteres | tokens | símbolo desconhecido |
| Sintática | tokens | árvore sintática | estrutura inválida |
| Semântica | árvore | árvore anotada e tabela de símbolos | nome não declarado |
| Geração intermediária | árvore anotada | representação intermediária | — |
| Otimização | representação intermediária | representação intermediária | — |
| Geração de código | representação intermediária | código da máquina-alvo | recurso insuficiente |
Confira contra o que a sala ditou no quadro. As duas linhas com travessão costumam incomodar, e o incômodo é produtivo: fase que não detecta erro nenhum ainda é fase, porque a definição é pela interface.
Distinga aqui fase de passagem, em uma frase: o léxico costuma entregar um token por vez sob demanda do sintático, o que dá duas fases numa travessia só.
Por que existe uma representação intermediária
12 4 origens x 3 destinos, sem camada intermediária
7 4 + 3 peças, com a camada no meio
100 10 x 10, sem a camada
20 10 + 10, com a camada
O ganho maior é de manutenção: um defeito na análise de uma linguagem se corrige num lugar só e beneficia todas as máquinas.
Faça a conta na frente deles, no quadro, antes de projetar os números — o argumento pede aritmética, não retórica. Amplie de quatro por três para dez por dez e deixe a sala ver a diferença crescer.
Complete com o argumento que vale mais e é menos citado, o da nota. Ele é o que explica por que uma linguagem nova nasce já com bom código para dezenas de arquiteturas.
A assimetria que quase nunca é dita
Análise
Reconhecimento. Teoria madura, algoritmos ótimos, ferramentas que geram código a partir de especificação declarativa.
Síntese
Otimização combinatória. Subproblemas difíceis no caso geral, heurísticas onde não há ótimo alcançável.
Avise que o tom da segunda metade do percurso muda por causa disso, e que a mudança é do objeto, não de quem conduz. Estudantes que não ouvem esse aviso interpretam a mudança como falha didática.
Segunda votação
Programa bem pontuado usa, numa expressão, um nome nunca declarado. Qual é a primeira fase capaz de detectar isso?
- análise léxica;
- análise sintática;
- análise semântica;
- geração de código.
Resposta: (c). Quem vota (a) supõe que o analisador léxico conhece os nomes do programa — confunde reconhecer a categoria de um lexema com saber o que ele denota. Quem vota (b) confunde “estar de acordo com as regras da linguagem” com “derivar da gramática”: a sequência de tokens deriva perfeitamente, e é por isso que a fase seguinte existe.
O erro (b) costuma ser majoritário no primeiro voto e cair muito no segundo. Comente esse movimento com a turma quando o resultado aparecer: é o melhor argumento a favor da própria técnica.
Turma que converge para a alternativa errada precisa que você refaça a definição antes da discussão em duplas — a discussão só corrige quando há quem tenha entendido.
O mapa teórico
Retomada: qual das três palavras ainda não foi tratada?
Forma
Tratada: análise sintática.
Sentido
Tratada: análise semântica.
Indecidibilidade
Pendente.
Antes da impossibilidade vem o mapa em que ela mora.
Abertura da segunda aula. Não recapitule em forma de resumo: retome pelas três palavras que ficaram no canto do quadro e faça a pergunta. A turma responde sozinha.
Faça a inversão de ordem explicitamente, em voz alta: “para explicar por que aquilo é impossível, preciso primeiro mostrar o mapa em que a impossibilidade mora”. Sem isso, o bloco do mapa parece digressão.
Uma linguagem é qualquer subconjunto de \Sigma^*
L \subseteq \Sigma^*
Os programas corretos
Formam uma linguagem nesse sentido.
Os programas que terminam
Também formam. A diferença está na dificuldade de decidir a pertinência.
Passe rápido pelos objetos elementares — alfabeto finito e não vazio, cadeia como sequência finita, \varepsilon de comprimento zero, \Sigma^* infinito enumerável — e pare aqui, porque a generalidade da definição é o que a turma não vê sozinha.
Os dois cartões são o pré-anúncio da indecidibilidade: o mesmo aparato descreve os dois conjuntos, e um deles não é decidível. Não abra o assunto agora.
Duas gramáticas quase iguais
S \to a\,S\,b \mid \varepsilon
S \Rightarrow aSb \Rightarrow aaSbb \Rightarrow aabb
Gera \{a^n b^n \mid n \ge 0\}: é preciso contar.
A \to a\,A \mid b
A \Rightarrow aA \Rightarrow aaA \Rightarrow aab
Gera a^n b: não há nada a lembrar.
Derive as duas no quadro, passo a passo, antes de projetar. Faça a turma notar que, na primeira, a única produção que introduz um a também introduz um b — é daí que sai a conclusão sobre a linguagem gerada, e não de afirmação sua.
Pergunte o que mudou. A resposta que você quer ouvir é exatamente a das duas últimas linhas dos cartões.
Alguém sempre pergunta como se demonstra que a^n b^n não é regular. Responda que a demonstração vem adiante, dê a frase intuitiva e siga: ceder aqui consome o bloco de indecidibilidade.
Quatro andares
flowchart TB
subgraph T0["Tipo 0 — irrestritas · máquina de Turing"]
subgraph T1["Tipo 1 — sensíveis ao contexto · autômato linearmente limitado"]
subgraph T2["Tipo 2 — livres de contexto · autômato de pilha"]
subgraph T3["Tipo 3 — regulares · autômato finito"]
LEX["análise léxica<br/>categorias de tokens"]
end
SIN["análise sintática<br/>estrutura aninhada"]
end
SEM["condições dependentes de contexto<br/>verificadas por código, não por gramática"]
end
IND["território indecidível<br/>terminação e propriedades do comportamento"]
end
Apresente a ideia de Chomsky pela restrição progressiva da forma das produções, nunca pela lista dos quatro tipos: a lista é o que a turma copia e não usa.
Registre a inclusão própria no quadro: \mathcal{L}_3 \subsetneq \mathcal{L}_2 \subsetneq \mathcal{L}_1 \subsetneq \mathcal{L}_0. Em cada nível existe linguagem que a classe abaixo não alcança.
A hierarquia lida como escala de memória
| Tipo | Gramática | Reconhecedor | Memória |
|---|---|---|---|
| 3 | regular | autômato finito | nenhuma além do estado atual |
| 2 | livre de contexto | autômato de pilha | ilimitada, com disciplina de topo |
| 1 | sensível ao contexto | autômato linearmente limitado | acesso livre, limitada pela entrada |
| 0 | irrestrita | máquina de Turing | acesso livre, ilimitada |
É esta coluna, e não os números dos tipos, que explica por que existe um segundo modelo de máquina adiante no percurso.
Este é o slide que o percurso inteiro vai cobrar. Estudante que sai daqui sem a escala de memória chega ao bloco dos autômatos de pilha sem entender por que ele existe.
Amarre à coluna de memória o que já foi visto: casar delimitadores aninhados é exatamente ler e escrever no topo, e por isso a estrutura de blocos e expressões mora no segundo andar.
Dois avisos antes de seguir
Não determinismo
Para autômatos finitos não aumenta o poder de reconhecimento. Para autômatos de pilha, aumenta.
O mapa é elegante demais
Classifica por poder de descrição, não por custo de reconhecimento — e não captura ambiguidade.
O primeiro aviso é a origem de metade das dificuldades da análise sintática: o compilador precisa de um analisador determinístico e, portanto, não trabalha com todas as linguagens livres de contexto.
O segundo evita que a turma saia achando que a hierarquia responde às perguntas de engenharia. Ambiguidade é propriedade da gramática, não da linguagem, e para um compilador é intolerável.
Terceira votação
Sobre o alfabeto \{a, b\}, qual linguagem não é regular?
- cadeias com número par de a;
- cadeias da forma a^n b^n;
- cadeias que começam por a e terminam por b;
- cadeias que contêm abba como subcadeia.
Resposta: (b). Quem vota (a) supõe que contar é sempre proibido a uma máquina de estados finitos — mas contar módulo dois exige apenas dois estados, e desfazer essa confusão é o principal ganho da questão. Quem vota (d) acha que reconhecer uma subcadeia exige memória do que passou, quando basta lembrar quanto do padrão já foi casado.
Feche com a frase que os módulos seguintes vão formalizar: reconhecer a^n b^n exige lembrar um número ilimitado, e nenhum conjunto fixado de estados guarda um número ilimitado.
O que nenhum compilador pode fazer
A dívida da abertura, paga
Turing, 1936
Não existe procedimento mecânico que decida corretamente, para todo programa e toda entrada, se a execução termina.
O argumento é de autorreferência: supondo o procedimento, constrói-se com ele um programa que termina exatamente quando o procedimento afirma que não termina.
Volte ao terceiro programa da abertura antes de projetar. Esboce o argumento no quadro em três linhas e não vá além: a demonstração completa não é conteúdo desta disciplina, e forçá-la aqui consome o bloco seguinte, que é o de digitar código.
Diga que a impossibilidade é lógica, não tecnológica. É a confusão que sobra quando o esboço é curto demais.
Forma é decidível; comportamento não
Perguntas sobre a forma do texto
Decidíveis. São todas as que o compilador responde.
Perguntas sobre o comportamento
Indecidíveis, e não por limitação de engenharia.
A saída intermediária
Responder “certamente não” ou “talvez sim”, errando sempre para o mesmo lado.
Escreva o critério prático no quadro: ele é o que sobrevive ao percurso. Antes de tentar fazer o compilador detectar alguma coisa, pergunte se ela é propriedade do texto ou do comportamento.
O terceiro cartão é a ponte para a análise de fluxo, adiante. Anuncie-o como pendência, sem abrir.
Demonstração
O que se escreve quando nenhuma fase existe ainda
A decisão contraintuitiva
Não se começa pelo analisador léxico, e sim pela camada que todas as fases usam.
As duas peças
Saber onde as coisas estão no arquivo de entrada, e saber reclamar de forma útil quando algo dá errado.
Quem enfia o tratamento de posição dentro do analisador léxico está criando dívida garantida.
Bloco de construção ao vivo, e ele não é demonstração: avise a sala para abrir o editor e digitar junto. Pause ao fim de cada peça e espere as duplas alcançarem o mesmo ponto — cheque circulando, não perguntando “todo mundo conseguiu?”, que sempre recebe silêncio afirmativo.
Defenda a decisão antes de digitar a primeira linha. Se metade da turma estiver olhando em vez de digitando, pare e espere: quem não digitar esta infraestrutura chega sem projeto onde escrever o autômato.
Deslocamento em linha e coluna, por busca binária
Position SourceFile::positionAt(std::size_t offset) const {
const std::size_t clamped = std::min(offset, text_.size());
// upper_bound devolve o primeiro início de linha estritamente maior que o
// deslocamento; a linha que contém o deslocamento é a anterior. Como
// lineStarts_[0] é sempre 0 e clamped nunca é negativo, o iterador nunca
// é begin(), e o decremento abaixo é seguro.
const auto it = std::upper_bound(lineStarts_.begin(), lineStarts_.end(), clamped);
const auto distance = std::distance(lineStarts_.begin(), it);
const std::size_t index = static_cast<std::size_t>(distance) - 1;
return Position{clamped, index + 1, clamped - lineStarts_[index] + 1};
}Faça a conta com números concretos no quadro antes de projetar o código. Suponha linhas começando nos deslocamentos 0, 18, 45, 72 e 96; para o deslocamento 60 a busca encontra a maior entrada não superior a ele, que é 45, na terceira linha, e a coluna é 60 - 45 + 1 = 16.
Faça a conta ingênua ao lado, para mostrar o que se evita: varrer o texto contando quebras de linha custa tempo proporcional ao arquivo a cada erro reportado — cinquenta erros num arquivo de vinte mil caracteres percorrem um milhão de caracteres; com o índice de mil linhas, cada conversão custa cerca de dez comparações, e as cinquenta somam quinhentas.
Mencione, ao digitar a leitura do arquivo, a armadilha do ambiente: o fim de linha usa dois caracteres, e lido em modo texto o par pode virar um só, fazendo os deslocamentos deixarem de corresponder aos bytes. Ninguém descobre isso por raciocínio; todo mundo descobre depurando.
O diagnóstico é coletado, não lançado
// Coleta os diagnósticos de todas as fases em vez de abortar no primeiro.
// A partir do analisador léxico, cada fase reporta o que encontrar e segue;
// quem decide parar é o programa principal, olhando hasErrors().
class DiagnosticBag {
public:
void report(Severity severity, Position position, std::string message);
void error(Position position, std::string message);
void warning(Position position, std::string message);
bool hasErrors() const noexcept;
std::size_t errorCount() const noexcept;
std::size_t size() const noexcept;
const std::vector<Diagnostic>& all() const noexcept;
// Imprime no formato "arquivo:linha:coluna: severidade: mensagem",
// seguido da linha ofensora e de um cursor sob a coluna.
void printAll(const SourceFile& source, std::ostream& out) const;A política é o conteúdo do slide, não a estrutura: cada fase registra o que encontra e segue, e quem decide parar é o programa principal.
Alguém vai objetar que isso é exagero num projeto sem fase alguma. Responda com o benefício datado, não com princípio geral: é o que torna possível, adiante, um analisador sintático que reporta três erros de uma vez.
Encerre a construção ligando o modo estrito antes de qualquer outra coisa, e diga em voz alta por quê: ligar as verificações rigorosas sobre uma base já escrita produz dezenas de erros de uma vez, e a reação natural diante desse muro é desligá-las de novo.
A mesma árvore, submetida aos dois tratamentos
// Emite em pós-ordem: primeiro os dois operandos, depois a operação. É a ordem
// natural para uma máquina de pilha, porque quando a instrução de operação
// executa os dois valores já estão empilhados.
void emitir(const No& no, std::vector<Instrucao>& saida) {
if (no.tipo == TipoNo::Literal) {
saida.push_back(Instrucao{OpCode::PushConst, no.valor});
return;
}
emitir(*no.esquerda, saida);
emitir(*no.direita, saida);
switch (no.operador) {
case Operador::Somar:
saida.push_back(Instrucao{OpCode::Add, 0.0});
break;
case Operador::Subtrair:
saida.push_back(Instrucao{OpCode::Sub, 0.0});
break;
case Operador::Multiplicar:
saida.push_back(Instrucao{OpCode::Mul, 0.0});
break;
}
}Desenhe no quadro a árvore de (2 + 3) \times 4 - 5 antes de projetar. O interpretador percorre a árvore em profundidade e devolve 15 agora; o compilador percorre a mesma árvore e devolve instruções que uma máquina de pilha executa depois.
Chame a atenção para a ordem da emissão: pós-ordem, primeiro os operandos e depois a operação. É a única que funciona numa máquina de pilha, porque no momento em que a operação executa os dois valores já precisam estar empilhados. É a mesma ordem que reaparece na geração de código, adiante.
Os dois caminhos, executando
$ peneira --demo
expressao: (2 + 3) * 4 - 5
caminho do interpretador
percorre a arvore e produz o resultado agora
resultado: 15
caminho do compilador
percorre a arvore e produz instrucoes para depois
0: PUSH_CONST 2
1: PUSH_CONST 3
2: ADD
3: PUSH_CONST 4
4: MUL
5: PUSH_CONST 5
6: SUB
resultado da execucao: 15Os dois caminhos chegam ao mesmo número, e essa igualdade é exatamente o contrato escrito na primeira aula. Retome a fórmula no quadro apontando para a tela.
Registre a armadilha, porque ela apanha quase todo mundo uma vez na vida: ao desempilhar, o segundo operando sai primeiro, por ter entrado por último. Trocar a ordem não altera nada na soma nem na multiplicação, e produz resultado errado na subtração. A expressão termina em subtração de propósito — é uma lição sobre escolher o caso de teste que revela o defeito.
A infraestrutura funciona antes de existir fase
A única verificação que este ponto do projeto consegue fazer é reclamar de arquivo vazio. Ela existe menos pela utilidade e mais como prova de que a infraestrutura de diagnóstico funciona ponta a ponta antes de haver qualquer fase que a use.
Chame a atenção para o formato — arquivo, linha, coluna, severidade, mensagem — e diga por que ele é esse: editores sabem interpretá-lo e transformam a saída em navegação clicável. E para o código de saída diferente de zero, que é o que permite ao build automatizado saber que houve erro.
O que a demonstração deixou provado
A evidência
Os dois caminhos chegam ao mesmo número. É a igualdade escrita na primeira aula, executando.
A confirmação do desenho
A infraestrutura de posição e diagnóstico funciona ponta a ponta antes de existir qualquer fase que a use.
O preço
Custo imediato, benefício remoto: nada aqui compila linguagem nenhuma, e é por isso que essa camada é sistematicamente adiada.
O terceiro cartão é o que separa demonstração de propaganda, e precisa ser dito em voz alta: quem escreve essa camada no primeiro dia paga sem receber nada visível em troca. O retorno aparece quando o analisador sintático reportar três erros de uma vez em vez de obrigar a corrigir e recompilar três vezes.
Deixe explícito à turma que a entrega deste módulo não compila linguagem nenhuma e que isso está certo — o que se entrega é decisão de projeto e infraestrutura. Grupos que tentam antecipar o analisador léxico terão de refazê-lo sobre uma teoria que ainda não conheciam.
Três defeitos, três destinos
Sem ponto e vírgula
Morre na análise sintática: a sequência de tokens não deriva da gramática.
Texto somado a número
Morre na análise semântica: a condição violada depende de contexto.
Laço que nunca termina
Não morre em lugar nenhum: pergunta sobre comportamento.
Bloco de fechamento. Volte ao quadro da abertura, às três palavras que ficaram no canto, e só então projete. A turma consegue preencher os três destinos sozinha — peça que preencham antes de você revelar.
Síntese
Um compilador é um tradutor sujeito a uma igualdade entre significados; distingue-se de um interpretador pelo quando, decompõe-se em fases definidas por interfaces, e vive nos dois andares inferiores de um mapa em que a forma das produções determina a memória necessária ao reconhecimento.
Contrato igualdade entre significados
Quando análise antes ou durante
Fases recebe, produz, detecta
Memória a escala da hierarquia
Retome os três objetivos do primeiro slide, um a um, citando o que da aula serve de evidência para cada um.
Anuncie o que a tutoria vai cobrar: formação dos grupos, escolha e defesa do domínio da linguagem, e o veredito sobre o tamanho do escopo ainda dentro deste módulo — depois fica caro trocar.
Se o tempo apertar, o que se corta é o slide dos dois avisos sobre o mapa; a escala de memória, não.
Referências
- LOUDEN, Kenneth C. Compiladores: princípios e práticas. São Paulo: Cengage Learning, 2004.
- AHO, Alfred V. Compiladores: princípios, técnicas e ferramentas. 2. ed. São Paulo: Pearson Education do Brasil, 2008.
- MENEZES, Paulo B. Linguagens formais e autômatos. v. 3, 6. ed. Porto Alegre: Grupo A, 2011.
- TURING, A. M. On Computable Numbers, with an Application to the Entscheidungsproblem. Proceedings of the London Mathematical Society, s2-42, p. 230-265, 1937.