Executando verificação de segurança...
9

Cara, concordo plenamente que C é "simples": só 32 palavras reservadas, umas 6 regras de precedência claras e uma gramática que cabia num livrinho de 200 páginas em 1978. Agora, implementar isso corretamente é outra historia.

Caiu na armadilha clássica: confundir "linguagem pequena" com "linguagem fácil de parsear". C é context-sensitive, sua gramática formal nem é LR(1) de verdade. T * x; pode ser multiplicação ou declaração de ponteiro dependendo se T é typedef ou variável naquele escopo específico. Você precisa de symbol table funcionando durante o parsing pra decidir qual ramo da gramática seguir.

Isso é bruxaria? Não, é só engenharia de parser que 90% dos cursos de compiladores fingem que não existe.

E as declarações? C tem uma gramática de declarações que é quase Turing-complete. Consegue parsear isso aqui de primeira?

static int (*(*foo[])(int (*)(int, int)))(int, int);

Pois é, isso é um array estático de ponteiros para funções que recebem ponteiro para função que recebe dois ints e retorna int, e retornam ponteiro para função que recebe dois ints e retorna int. Você começa no foo e sai espiralando no sentido horário [] pra direita (array), * pra esquerda (ponteiro), () pra direita (função), * pra esquerda (ponteiro), () pra direita (função), int pra esquerda Nenhum gerador de parser tipo yacc ou antlr consegue lidar com isso. Então vai lá implementar isso: contexto sendo resolvido no meio do parsing, e tabelas de símbolos atualizando entre um lookahead e outro. Mas calma nem chegamos na parte mais difícil...

E o static aí? Ah, meu caro, ele decide storage duration. Mas se eu mover esse static pra fora:

static int foo(void) { static int x = 0; return ++x; }

Agora o primeiro static muda o linkage para internal (símbolo não exportado), e o segundo muda storage duration para static initialization. Mas espera, tem mais: static em parâmetro de array em C99?

void bar(int arr[static 10]);

Agora é uma promise de otimização pro compilador que o array tem pelo menos 10 elementos, permitindo vetorização agressiva. Mesma palavra, três universos semânticos completamente diferentes dependendo da posição.

E é por isso que quando Rust, Zig, Swift ou até Python precisam fazer FFI com C, ninguém escreve um parser de C do zero. Usam o parser do LLVM. Porque parsear C de verdade, o C que compila o kernel Linux, é uma das coisas mais dificeis que existem na computação.

Escrever um compilador que aceita int main(){printf("oi");} é projeto de fim de semana. Escrever um que engole mm/memory.c do kernel é bruxaria.

O amigo que "escreveu um compilador em C" provavelmente implementou uma recursive descent pra subset C89 e chamou de compilador de C. E sim, isso explica porque ele não pegou a vaga de pleno.

Carregando publicação patrocinada...
1
1

O amigo que "escreveu um compilador em C" provavelmente implementou uma recursive descent pra subset C89 e chamou de compilador de C.

Pode até ser, não cheguei a testar a fundo pra validar a ideia.

E sim, isso explica porque ele não pegou a vaga de pleno.

Ele só não tinha os requisitos para a vaga, fazer um compilador não é um diferencial.