Цезиум дает мне кучу анти-уроков по архитектуре компиляторов. Это явно не самый эффективный способ построения компилятора, так как вечный рефакторинг деморализует даже оптимистов вроде меня. Потому попробую рассказать как выглядит лень и не желание учить теорию.
Есть такая маленькая особенность в С, это то что в main последний return не обязателен и компилятор может его сам вставить за программиста. Мы как большие прагматики, сперва сделали какой-то костыль, чтобы тестовые програмки которые мы писали проходили. Просто смотрели на наличие return в теле функции и если его не было, компилятор возмущался. у нас тогда не было даже нормальных циклов в свитчей, потому это была победа. Но пришли циклы и с ними новые вызовы, добрый человек взял и начал строить граф зависимости между стейтментами, и пытался найти все ли пути ведут к выходу. Зашибись, прогресс идет. Но кто же знал что бывают бесконечные циклы, в которых есть return. Новый алгоритм с графами сломался :(. Сломался он потому что дерево кода у нас в этом моменте все еще достаточно высокоуровнево, у нас есть блоки {} со своей областью видимости(CompoundStatement), все переменные живут каждая в своей области видимости, это все делает нашу структуру кода древовидной и где-то что-то мы делаем не так.
Наверное можно починить алгоритм построения графа прохождения по коду, но это все выглядит слишком сложно, для такой простой вещи как компилятор С. Я решил что пора нам в компилятор засунуть создаение базовых блоков (basic blocks), но оказалось (как я выше упоминал) что у нас слишком высокооуровневое дерево. Потом я подумал что может быть нам стоит линеаризировать все что находится внутри тела функции чтобы у нас не было никаких CompoundStatement. В целом это было легко сделать, но оказалось что у нас привязаны к области видимости внутренних блоков, и если мы убираем их, то переменные надо переносить в область видимости функции. Тоесть нужен еще один этап когда мы переназываем переменные.
Как можно заметить, вместо одного подхаченого способа, нужно иметь три допольнительных механизма в определенной последовательность.
- переименование переменных и перенос их под уникальными именами в область видимости функции
- линеаризация кода
- построение базовых блоков из линеаризованного кода
Я больше чем уверен что это не конец, и наше ковбойское отношение будет нам и дальше аукаться. Так что учите матчасть.