Ага, наконец пора. И это работает… вроде. Ладно не совсем.

Вы правильно читаете! Я выпускаю все, над чем я работал до этого момента, с ByFrost. Ссылка на репозиторий будет в конце статьи, и приготовьтесь, это беспорядок.
В этой статье я расскажу, как использовать то, что уже есть, уроки и подводные камни каждого и, как вы уже догадались, как я собираюсь сделать это лучше с моей (не второй, а) третьей попытки.
Откровенно говоря, я сильно недооценил, насколько сложным будет то, что я пытаюсь сделать.
Я решил выпустить первую из двух, поскольку она более готова к прайм-тайму, чем вторая попытка, и является отличной «демонстрацией» того, что возможно. Что касается секретного третьего, я проведу следующие пару месяцев, работая над знаниями, которые я получил здесь. ByFrost изначально задумывался как первый компонент в проекте, я действительно хотел, чтобы он был, но я полагал, что то, как идут дела, никогда не сработает. Особенно если в первую очередь ориентироваться на Голанг.
Эта статья будет состоять из трех частей; описание V1 и того, как работает V2, и что я буду делать в третьей попытке. Я не стал выпускать V2, потому что состояние, в котором она находится, просто грустное… и так точное.
Итак, давайте углубимся в это.
ByFrost V1 — Задиристый аутсайдер
Эта версия была самой первой итерацией и была основана на релизном видео, которое я сделал в начальном посте Представляем Byfrost.
Общая архитектура
Эта версия была невероятно наивной, расширяя только библиотеки функций, игнорируя такие вещи, как struct члены и методы.
Это было примерно так; рассмотрите этот код ниже.

V1 будет сканировать main и вслепую искать шаблон ID (в данном случае fmt ), а затем DOT ( . ), а затем на следующем токене, который он находит (в данном случае идентификатор с именем LocalPrintln ), он будет сканировать все пакеты обнаружен в этом каталоге, на который указывает fmt. Используя абсолютный путь к проекту, в котором мы сейчас находимся, будет установлен путь к этому пакету; если ваш абсолютный путь к корню вашего текущего проекта Go равен /Users/alyson/newproject , то путь к fmt теперь будет /Users/alyson/newproject/localprint/fmt . Этот каталог будет просканирован на наличие func LocalPrintln, а затем будет создано представление для него.
Довольно простой, но этот подход был довольно глупым.
Тупой, но очень-очень быстрый. Для запоминания структурной информации не требуется, поэтому синтаксический анализ выполняется так же просто, как вызов чтения с начальным и конечным индексами. Нам не нужно ничего знать; ни его возвращаемый тип, ни его параметры, ничего.
Недостатки
Это может быть очевидным для наблюдательного глаза, но самым большим из них является отсутствие знаний об отношениях между структурами. Этот метод не совсем полезен для таких вещей, как типы данных, которые вызывают методы, потому что тогда нам пришлось бы отслеживать объявление и присвоение переменных и понимать, когда они были созданы и/или уничтожены, и понимать их область действия и так далее.
Действительно очень сложная проблема.
Почетным упоминанием этого недостатка, я думаю, будет тот факт, что вы даже не можете думать о расширении… я думаю… сложенных вызовов методов. Распаковка чего-то вроде value.Call().To().Me() была бы невозможна без полной переделки. Не говоря уже о таких вещах, как когда вызов функции передается в функцию в качестве параметра.
Таким образом, это вызвало первую острую потребность в переписывании, поскольку вскоре я обнаружил, что многие программы Go используют вызовы методов почти как свой хлеб с маслом.
So, on to V2
Byfrost V2 — Формализация этого Bad Boi
Вывод сделан? Состав! Структура является основой интеллектуального индексирования.
Ушли с хламом прославленных текстовых файлов и вперед с полномасштабным (не совсем) компилятором! Ага! Теперь я разработчик компилятора!!
Но шутки в сторону, мне действительно пришлось покопаться в тонкостях компиляции и структурирования токенов и абстрактных синтаксических деревьев таким образом, чтобы сохранить информацию о взаимосвязях.
Итак, давайте посмотрим, как это выглядит, и поговорим о нескольких моих новых структурах.
Окружение

Изображение намного больше, чем я думал, но это должно дать хорошее представление о том, что я собирался сделать. Каждый пакет в V2 имеет следующую структуру:
- Пакет — это имя пакета
- Путь к пакету — это его абсолютный путь от корня
- IDRegistry — это просто более быстрый поиск символов в этом пакете, где ключ — это идентификатор, а значение — его тип для ускорения поиска. Так, например,
mainбудет иметь значениеFunction. - ImportList — это список всего импорта в этом пакете.
- Функции — это все функции в этом пакете.
- Структуры — это все структуры в этом пакете (структура Struct имеет члены Function для поддержки своих методов).
- GlobalVars — это все глобальные переменные в этой функции.
Это основная идея V2.
Недостаток
Это была лучшая попытка на данный момент и, вероятно, наиболее близкая к «правильной» версии этого инструмента. Он индексирует все, от импорта функций до методов. Теперь я предполагаю, что большинство моих проблем проистекают из моего непонимания того, как компилируется Go.
В отличие от C, который компилируется сверху вниз (вы не можете использовать что-либо, если оно не объявлено выше места его физического использования в файле из соображений оптимизации старой школы), Go и многие современные языки имеют политика «открытого сезона», когда вещи могут быть объявлены где угодно и использоваться где угодно, если они где-то существуют. Все, Везде, Все сразу / Я не мог удержаться.
Это привело к большому количеству циклов и чтению исходного файла, чтобы сначала прочитать все type , а затем все functions, а затем все global variables, чтобы гарантировать, что все, что нужно, всегда было доступно, когда это необходимо.
Побочным эффектом этого является то, что в относительно небольших проектах ByFrost может показать вам все, но в больших проектах сканирование кодовой базы занимает вечность, и этот инструмент становится совершенно непригодным для использования, потому что он только что остановился.
В этом подходе есть очевидные оптимизации, о которых я подумал только постфактум, может быть, сканировать в одном направлении, а когда вещи объявлены, кэшировать их до тех пор, пока не будет найдено их определение, и так далее, но это просто исправление фундаментального непонимания компиляции Go, которое я подозреваемый в конечном итоге вернул бы меня к аналогичной проблеме каким-то другим способом или в другой форме.
Что дальше? ByFrost V3 — (повторно) представляем By(C?)Frost
Итак, первые две версии. Код представляет собой кучу мусора, за которым может быть сложно следить, но я хотел показать вам всем, как далеко я продвинулся (и, конечно же, доказать, что это не пустая программа).
Есть инструкции по компиляции, так что вы можете попробовать.
Итак, что я делаю дальше? Ну, я все еще работаю над ByFrost. Я мог бы изменить его имя. Вместо этого я решил заставить его работать на C и, возможно, также написать его на C.
Настоящая цель, к которой я стремился с ByFrost, заключалась в возможном «вечном опыте отладки» в визуальном пользовательском интерфейсе. Идея заключается в бесшовном обходе и проверке выполнения кода по мере его возникновения. Хотя похоже, я не стремлюсь к инструменту бинарного анализа, это будет строго для разработки, так как вам нужно будет иметь доступ к исходному коду, который вы проверяете.
Большие проблемы с запуском в Go
Основная причина, по которой я отказываюсь от Go, заключается в том, что это действительно сложный язык. Для такого типа анализа, которого я хочу достичь, прямое сопоставление исполняемого машинного кода с исходным кодом должно быть точным и относительно быстрым для экстраполяции.
C находится в том прекрасном царстве, где существуют прямые конструкции, эквивалентные ассемблеру, для структур и концепций C; Короче говоря, хороший программист C/Assembly может посмотреть на C и иметь очень хорошее представление о том, как будет выглядеть его вывод ASM (несмотря на оптимизацию) и наоборот.
Ознакомьтесь с кодовой базой
Так вот чем я буду заниматься. Начну с C. Немного отстой, но теперь я знаю, что мне нужно делать.
Что касается того, как запустить код; давайте углубимся в это.
Как бежать от Frost
Вам понадобятся два терминала.
Запуск бэкенда
Это довольно просто. При отсутствии аргументов функции вы получите следующий вывод:

Пример выполнения будет выглядеть так

Будет запущен текущий проект.
ПРИМЕЧАНИЕ: имя вашего проекта в файле go.mod должно совпадать с именем каталога, в котором он сейчас находится. Я не стал делать это динамическим, потому что мне лень.
Затем вы увидите кучу распечаток, которые я забыл удалить и которые я когда-то использовал для отладки; игнорировать их.
Затем перейдите в каталог web и под byfrost-frontend выполните следующее

Наконец, посетите localhost:4200/indexer, и вы должны увидеть экран, похожий на этот:

Обратите внимание, что только имена, выделенные желтым и голубым цветом, можно щелкнуть и развернуть. Импорт собственных пакетов нельзя расширить, только локальные пакеты.
После расширения нажмите на метки под Visible справа, когда они появятся, и посмотрите, как это выглядит.
Вы можете попробовать это здесь: https://github.com/AlysonBee/ByfrostV1
Пока ByFrost не вернется!
Элисон