Цель данного поста на данный момент прежде всего в том чтобы собрать фидбэк и интересные идеи, если таковые появятся в комментариях.
Готового к публикации компилятора пока нет, только прототип, загляните сюда позже, если вас интересует именно готовый продукт.

Немного Теории

Для начала немного теоретических рассуждений. Прежде всего, что же из себя представляет "компилятор" в реалиях варкрафта? Можно было бы обратиться к формальному определению компилятора и долго ломать копья об уместности этого слова в текущем контексте, но об этом в другой раз - сейчас важно определиться со смыслом который я вкладываю в это слово здесь и сейчас.
Компилятор - в текущем контексте это утилита, которая берет исходный код карты и преобразует его к виду пригодному для запуска в игре.
Именно так, ни больше ни меньше - только обработка исходного кода. Все остальные действия над картой - задача других утилит.
Поскольку компилятор только трансформирует исходный код, но не трогает карту руками, значит нам нужна еще одна утилита, которая затолкает переработанный код в карту и подготовит её к запуску. Назовем эту утилиту "билдер" или "сборщик".
Билдер/Сборщик - в текущем контексте это утилита, которая перепаковывает карту после обработки кода компилятором, а также запускает другие утилиты, если таковые предусмотрены процессом сборки.
И финальный недостающий для комфортной работы элемент - интеграция с внешней средой разработки. На данный момент для работы с Lua я пользуюсь VSCode, поэтому интеграция будет в виде плагина к VSCode.

Требования

Компилятор: общие требования

  • Должен уметь собирать исходный код из файлов вне архива карты
  • Должен уметь получать код карты, сгенерированный редактором карт и объединять его с кодом из внешних файлов
  • Должен уметь кешировать скомпилированный код для ускорения повторной компиляции
  • Не должен своевольно добавлять что-то в код карты, все действия с кодом должны иметь видимую причину
  • Не должен слепо имитировать работу require

Билдер: общие требования

  • Должен уметь запускать компилятор, передавая ему пути к исходному коду
  • Должен уметь собирать пригодную к запуску карту из папки и исходного кода
  • Должен уметь запускать дополнительные утилиты при условии их наличия
  • Должен уметь игнорировать файлы "только для редактора" при сборке релизной версии карты

Интеграция: общие требования

  • Простота настройки и начала работы
  • Все входящие в базовый комплект утилиты должны скачиваться автоматически, без необходимости ручной установки и тем более компиляции
  • Обязательные настройки перед началом работы должны сводиться к указанию путей к игре и редактору, все остальные настройки должны быть опциональными и иметь приемлемые значения по умолчанию
  • Наличие пресета пустого проекта со стандартными настройками и готовой к работе файловой структурой
  • Возможность работать в редакторе карт параллельно с редактированием кода в VSCode

Проект: общие требования

  • Представлен в виде папки
  • Одна карта - один проект
  • Содержит воркспейс VSCode
  • Содержит файлы конфигурации для конкретного проекта
  • Содержит фиксированную структуру папок, диктующую расположение элементов проекта
  • Содержит карту представленную в виде папки
  • Содержит файлы с исходным кодом
  • Содержит папку, в которую складываются промежуточные результаты и временные файлы
  • Содержит папку, в которую складывается карта после сборки
  • Все файлы находящиеся вне фиксированной структуры не влияют напрямую на происходящие в проекте процессы и формально не являются его частью

Предполагаемые фичи

Интеграция

  • Возможность отдельного запуска компилятора из VSCode
  • Возможность запуска процесса сборки карты из VSCode
  • Возможность запуска карты на тестирование из VSCode
  • Возможность открытия карты в редакторе карт по команде из VSCode
  • Возможность хранить настройки самой интеграции и всех утилит в конфигурационных файлах конкретного проекта
  • Возможность выбирать пресет по которому работают билдер и компилятор
  • Возможность применять набор твиков к исходной карте, например исправленый abilitydata.slk для отображения скрытых способностей

Компилятор

  • Преобразование равкодов в числа на этапе компиляции. Предварительно реализовано макросом RAW('A000')
  • Инлайн переменных и функций. Предварительно частично реализовано макросом --#INLINE для переменных
  • Генерация отладочной информации, позволяющей найти строку в файлах исходного кода по номеру строки в итоговом коде
  • Режим компиляции release. Из кода исключаются фрагменты не нужные в релизной версии карты, а также код карты подвергается минификации
  • Режим компиляции dev. Предназначен для быстрого запуска карты в близком к релизному виде, но с сохранением структуры кода.
  • Режим компиляции debug, в код карты добавляются дополнительные отладочные элементы. Предназначен для поиска причин возникновения ошибок ценой потенциальной потери производительности.
  • Макросы, позволяющие отмечать фрагменты кода соответствующие различным режимам компиляции

Биллдер

  • Несколько режимов работы, отличающихся передаваемыми другим утилитам настройками
  • Режим release - карта максимально очищается от лишних и не обязательных файлов, код компилируется в релизном режиме,возможен запуск внешних оптимизаторов.
  • Режим dev - карта собирается в близком к релизному виде, но с применением только минимального набора оптимизаций.
  • Режим debug - в карту инжектится отладочный модуль, позволяющий выводить сообщения об ошибках в отдельный фрейм.
-Игнор-лист позволяющий указать список файлов которые не будут включены в архив карты при сборке

Запланированные макросы

  • --#INLINE для маркировки функций подлежащих инлайну в пределах текущего файла
  • --#INLINE GLOBAL для маркировки функций и переменных подлежащих инлайну во всех файлах
  • --#IF для начала блока условной компиляции, предварительно без серьезных вычислений, только на основе сравнения констант
  • --#ELSE для начала блока else при условной компиляции
  • --#END для завершения блоков у любых блочных макросов

Реализованные макросы

  • RAW('A000') для конвертации равкодов в число при компиляции.
  • --#INLINE для маркировки переменных подлежащих инлайну в пределах текущего файла. Локальные переменные инлайнятся с удалением объявления/присвоения переменной из кода, а для глобальных объявление/присвоение переменной сохраняется.
  • --#ALIAS для назначения метки файлу. Взаимозаменяем с макросом #NAME.
  • --#AFTER для указания списка меток, которые должны быть включены в код карты прежде чем можно будет включить туда текущий файл. При отсутствии этого макроса файлы включаются в порядке обхода папок и только после кода сгенерированного WE.

На рассмотрении

  • Управление порядком включения файлов кода в итоговый код карты через макрос --#IMPORT вставляющий код из указанного файла в место расположения макроса. При такой реализации импортированный файл включается в код как есть, столько раз, сколько использован макрос, защита от конфликтов имен полностью на совести разработчика.
  • Управление порядком включения файлов кода в итоговый код карты через макрос --#REQUIRE вставляющий код из указанного файла в место расположения макроса. При такой реализации импортированный файл включается в код путем оборачивания в функцию и возврата объекта из этой функции. Также возможно наличие проверки, позволяющей избежать повторного включения одного и того же модуля. В целом, предполагается что этот макрос будет работать аналогично стандартному require, за исключением невозможности динамически формировать пути к включаемым файлам.
  • Фреймворк, который будет поставляться вместе с плагином и который можно будет подключить для получения каких-то дополнительных возможностей в рантайме. Например, система модулей или библиотек, каст-система, различные универсальные утилиты и так далее.
  • Расширенная отладочная система, позволяющая по клику в отладочном фрейме внутри игры получить переход на место в коде, в котором возникла показанная в отладочном фрейме ошибка, если это место возможно обнаружить. Причем переход не к финальному монолитному коду карты, а конкретно в тот файл, из которого эта строка попала в итоговый код. Возможно будет работать в два этапа для упрощения реализации, сперва клик в отладочном фрейме экспортирует данные об ошибке в файл, а потом клик в VSCode читает информацию из файла и выполняет переход.
  • Режим сборки карты, при котором код из внешних файлов переносится в триггеры в таком виде чтобы его мог исправно сохранять редактор. Предназначен для выкладывания карты на сайт, чтобы люди без настроенной среды разработки тоже могли воспользоваться. Поскольку редактор не сможет обработать макросы - их необходимо обработать до импорта в триггеры.
  • Встроенный slk-оптимизатор, конвертирующий данные РО в slk таблицы при запаковке карты.
  • Подсказки и автокомплит для макросов.
  • Доступ к данным карты из среды разработки, в частности подсказки и автокомплит для равкодов.
  • Поддержка линукса, а также других IDE кроме VSCode. Несомненно нужно, но очень затратно по времени.

Разработка

Стартовое состояние

Имеется плагин warcraft-vscode, выполняющий большую часть функций интеграции и билдера. К сожалению, автором этого плагина было принято несколько сомнительных решений, в частности код оборачивается в громоздкую систему имитирующую работу require и это поведение невозможно отключить. Требует серьезной доработки. В составе содержит кастомный сборщик карты, умеющий собрать mpq архив по списку файлов в конфиге.
Для подсветки синтаксиса и поиска синтаксических ошибок используется плагин sumneko.lua, включащий в себя языковой сервер Lua, обработку аннотаций EmmyLua и другие полезные фичи. Не содержит из коробки списка нативок, поэтому их приходится подавать ему из внешних файлов. Мог бы работать лучше, но самостоятельная доработка того не стоит, тем более плагин находится в активной разработке автором и постепенно становится только лучше.

Первая итерация

  • Скачаны исходники warcraft-vscode, проведена настройка окружения для компиляции
  • Безжалостно удалены лишние фичи из кода плагина, такие как обработка кода шаблонами, имитация require и нерабочие макросы условной компиляции, загрузка библиотек с гитхаба и так далее
  • В тестовых целях временно добавлена обработка простых и рабочих макросов условной компиляции
  • Начат процесс приведения плагина к виду, в котором его можно было бы опубликовать не опасаясь проблем с авторским правом, фактически он переписывается с нуля с минимальным использованием оригинального кода

Вторая итерация

  • Начата разработка внешнего компилятора
  • Реализован парсинг кода из файла путем токенизации
  • Реализованы макросы #INLINE и RAW
  • Реализован вывод кода в файл путем обратной конвертации токенов
  • На текущем этапе обрабатывается только один файл - нет ни поиска других внешних файлов ни указания пути по которому должны сохраняться результирующие файлы

Третья итерация

  • В этой итерации планируется допилить компилятор до состояния когда он будет способен самостоятельно найти все внешние файлы с кодом по указанному пути, скомпилировать их и вывести результирующий файл в указанное место. (DONE)
  • Поскольку способ подключения внешних файлов пока под вопросом - принятие этого решения тоже входит в эту итерацию. (DONE, для начала реализованы макросы #NAME/#ALIAS и #AFTER, сойдет пока)
  • Также на эту итерацию запланирована интеграция компилятора с плагином к VSCode, чтобы в плагине остались только функции билдера и интеграции, а вся компиляция выполнялась внешним компилятором.(DONE, в предварительной версии)

Четвертая итерация

  • Мы сейчас здесь
  • Нужно запилить игнор-лист для сборщика карты, чтобы можно было вручную указать какие файлы не надо паковать (DONE)
  • На эту итерацию запланирован первый релиз, пока тестовый, с ручной установкой онли
  • До релиза еще необходимо дочистить код от всего лишнего, завести репозиторий на гитхабе и всякое такое

FAQ

Q: Зачем козе баян? Что не так с Lua без дополнительного компилятора?
A: Прежде всего, ради поддержки импорта кода из внешних файлов и возможности работать с кодом карты вне редактора.
Q: Поддержка JASS?
A: Нет и не планируется. Но вместе с релизом будут выложены исходники - все желающие смогут попробовать самостоятельно адаптировать тулсет под JASS.
Q: Когда релиз? Где скачать?
A: Как только так сразу. Скачать можно будет сразу после релиза, из маркетплейса плагинов к VSCode или с XGM.

Вместо послесловия

Разработка ведется по мере наличия свободного времени, с периодической фиксацией результатов.
Когда плагин и компилятор будут пригодны к использованию - они появятся в маркетплейсе VSCode и здесь.
Идеи и предложения приветствуются.
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
0
24
5 лет назад
Отредактирован prog
0
PT153, напоминаю, что этот компилятор рассчитан на работу с кодом в VSCode, а WE остается только GUI и все что с кодом не связано. Поэтому, например, не имеет смысла что-то чудить с пробелами перед синтакс чеком т.к. синтакс чек из WE просто не будет вызываться в 99% случаев.

Что касается синтаксиса макросов - я не случайно пошел путем аннотаций к переменным вместо кода в комментах - в моем варианте с этим более-менее прилично работает обычный синтакс-чекер для Lua и мне не нужно писать кастомный.
За счет этого, например, наведя мышку на константу я могу увидеть её значение.
А в результирующем коде от скрина выше останется только print("CONST_VAL")

Аналогично с макросом RAW - он принимает только значения в одинарных кавычках и имена констант чтобы проходить стандартный синтакс-чек. Более того, используется еще и объявление глобальной переменной RAW=FourCC, чтобы если что-то пошло не так с макросом, был шанс на срабатывание стандартного конвертера, ну и строгий синтакс-чек без этого не пройти.
Загруженные файлы
0
28
5 лет назад
Отредактирован PT153
0
Поэтому, например, не имеет смысла что-то чудить с пробелами перед синтакс чеком т.к. синтакс чек из WE просто не будет вызываться в 99% случаев.
Я про syntax check в самом VSCode. Я сказал "ПЕРЕД" для того, чтобы в алгоритме по удалению пробелов можно было засечь ошибку, да и всяко лучше запускать чекер после всех препроцессорных махинаций.
Что касается синтаксиса макросов - я не случайно пошел путем аннотаций к переменным вместо кода в комментах - в моем варианте с этим более-менее прилично работает обычный синтакс-чекер для Lua и мне не нужно писать кастомный.
В целом, так тоже неплохо.
PT153:
Ещё бы я хотел, чтобы выражения в константах считались препроцессором.
На самом деле было бы хорошо делать вычисления для всех выражений без переменных, как это делает python.
Аналогично с макросом RAW - он принимает только значения в одинарных кавычках и имена констант чтобы проходить стандартный синтакс-чек. Более того, используется еще и объявление глобальной переменной RAW=FourCC, чтобы если что-то пошло не так с макросом, был шанс на срабатывание стандартного конвертера, ну и строгий синтакс-чек без этого не пройти.
Мудро.
0
24
5 лет назад
0
Я про syntax check в самом VSCode. Я сказал "ПЕРЕД" для того, чтобы в алгоритме по удалению пробелов можно было засечь ошибку, да и всяко лучше запускать чекер после всех препроцессорных махинаций.
У меня немного другая идеология, которая заключается в том, что валидный код до препроцессора всегда должен выдавать валидный код после препроцессора и наоборот, не валидный код до препроцессора может выдать только не валидный код после препроцессора. Это немного сужает допустимый функционал препроцессора, но избавляет от огромного количества головной боли и хорошо интегрируется с существующими инструментами для работы с Lua.
0
28
5 лет назад
0
Планируешь ли добавить inject main и config?
0
24
5 лет назад
Отредактирован prog
0
PT153, в этом нет большого смысла на уровне компилятора, ведь это можно реализовать силами самого Lua
Например у меня есть такой файл, main.lua
function InjectMain()
    local alpha_main = main
    return function()
        local alpha_init = RunInitializationTriggers
        RunInitializationTriggers = function() end
        alpha_main()
        InitLibraries()
        alpha_init()
    end
end
main = InjectMain()
Работает за счет того, что в отличии от WE, мой компилятор добавляет код из файлов после кода сгенерированого WE.
Единственный недостаток - если нужно полностью заменить main или config, а не дополнить их, то старый код этих функций останется в коде карты мертвым грузом.
0
29
5 лет назад
0
Единственный недостаток - если нужно полностью заменить main или config, а не дополнить их, то старый код этих функций останется в коде карты мертвым грузом.
Для этого по идее надо использовать отдельный шаг после компиляции - оптимизация кода
0
24
5 лет назад
0
alexprey, это надо чтобы оптимизатор умел понимать что можно оптимизировать. Имхо, этот конкретный случай того не стоит. Банальная минификация сторонней либой и то больше профита даст при куда меньших затратах времени. Но как задача на когда совсем нечем будет заняться сойдет.
0
28
5 лет назад
Отредактирован PT153
0
prog, я думаю, что просто возможность создать отдельные файлы для config и main, если их нужно полностью заменить.

Как в принципе собирается код карты? Мне вот сгенерированный код от WE вообще не нужен. Было бы неплохо иметь возможность полностью заменять файл кода.
0
24
5 лет назад
Отредактирован prog
0
PT153, я изначально хотел сохранить возможность расставлять юнитов и ректы в WE и писать гуи триггеры для настроек, поэтому вариант с полной заменой всего оригинального кода WE никогда не рассматривал. Если я когда-нибудь дойду до того чтобы запилить билдфайл по которому будет работать компилятор вместо автоматического режима - такая возможность появится, а раньше я врядли стану тратить на это время.
Автоматический режим сборки кода достаточно прост:
  • файл оригинального кода от WE компилируется, скомпилированная версия падает в папку .build, получает идентификатор MAP и добавляется в список на сборку
  • в указаной директории рекурсивно перебираются все LUA файлы, компилируются, складываются в .build, получают идентификаторы и требования из директив компилятора и добавляются в список на сборку
  • перед компиляцией каждого файла выполняется проверка на наличие его скомпилированной версии и сравнение дат, если компилировать файл заново не нужно, то просто читается его заголовок с директивами компилятора, а компиляция файла пропускается, файл сразу добавляется в список сборки
  • все файлы получают в требования MAP, чтобы оригинальный код от WE всегда шел первым в списке сборки
  • список сборки сортируется таким образом чтобы выполнялись все требования (если требования выполнить невозможно из-за рекурсии или несуществующих индикаторов файлов, то сборка кода фейлится, но пока это только в логе видно - код в любом случае собирается по списку сборки, просто не гарантируется выполнение всех требований )
  • все файлы из списка сборки мержатся в единое целое согласно списку и падают в фиксированное место одним файлом
  • при сборке карты в mpq сборщик подхватывает готовый код карты из известного места и копирует его в архив собираемой карты
Что касается магических файлов для main и config - я не очень хочу плодить магические файлы, работающие определенным образом только потому что они так названы без более явного действия со стороны пользователя.
Поэтому куда более вероятен вариант макроса --#REPLACE или INJECT, который заставит компилятор найти оригинал функции и стереть его, но пока парсер не готов к таким макросам т.к. индивидуально проходит все файлы по очереди, только один раз и в алфавитном порядке продиктованном системой.
0
28
5 лет назад
0
Я думаю, что можно просто добавить опцию "Not include original code" (с пометкой, что это для advanced пользователей), из-за которой пункты сборки 1 и 4 будут просто проигнорированы.
0
24
5 лет назад
0
PT153, дело в том, что компилятор у меня это exe файл, запиленый на C++, а всякие параметры и настройки получает плагин к vscode - это мне нужно будет начинать передавать компилятору дополнительные параметры, чего я не очень хочу делать, отложил так уже несколько фич которые тоже требуют дополнительных флагов - вот когда будет билд-файл, то компилятор будет получать путь к нему и читать оттуда все дополнительные параметры, сколько бы их ни было.
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.