Суммарный ответ на высказанные ранее мнения: "Нет ребята все не так! Все не так ребята." Я тоже когда-то грезил автопостроителем графа из кода. Всякие извращенные схемы разметки придумывал и пр.
Потом понял, что это пустое.
Граф гораздо глубже и мощнее. И провернуть фарш назад - нереальная задача.
Насчет правок - все просто :) Нужно ввести обобщенное понятие класса.
Т.е. создали Вы какую-то вершину графа. С каким-то кодом. И входными и выходными портами. И потом что-то в этой вершине поменяли. Ок - пусть живут две совмещенные вершины. В зависимотси от параметра компилится та или иная "версия вершины".
Насчет дуг, очевидно, подход аналогичный. Опять же, есть заивисмости версий - если в этой версии нет порта для дуги такой-то версии, то анализатор должен отматерить Вас.
Уверяю, так отличия в коде будут куда нагляднее.
Самое сильное, что даст правильный граф - это многомерный анализ. Т.е. вот есть набор функций. Вы знаете, что они - поток. Но компилер, и С - нет. Строим виртульный субграф - "поток №1", из которого идут связи на все, что относится к потоку №1. Причем эти связи к генерации кода отношения не умеют - это связи для Вас.
Есть объект - таймер. У него всякие поля, как-то: функция init, константа входной частоты и пр.
Есть процедура на графе - "инициализация по питанию", она вызывает все эти init'ы.
Есть конфигурационная секция, где пересчитываются все константы в зависимости от частоты кварца.
Когда Вы пишете субграф "таймер" - вым удобно видеть все сущности таймера. Когда init - все init'ы, котррые, на самом деле, являются сущностями других графов. И т.д.
А С исходник надо геренить прямо перед компиляцией. Лучше всего один большой файл.