Доброго времени суток!
Ситуация следующая: В наличии есть GraphQL SDL (Schema Definition Language). Это декларативный предметноориентированный ЯП под строго заточенную платформу (встраиваемый в другой язык). Примерно как "XML" + "парсер XML".
Всё уже есть, всё готовое. Как выглядит всё сейчас в целом:
1) EBNF-Like грамматика.
2) Самопальный лексер: Делит исходники по этой грамматике на токены.
3) Самопальный LL(k) парсер: Выстраивает AST.
4) Объектная модель и Reflection: Далее происходит выстраивание базовой объектной модели типов (классов рефлексии, как в Java или PHP). Например, мы находим тип X, и создаём в "словаре", помечая, что он ещё не собран до конца и отправляемся дальше.
5) Линкер: После предварительного билда проходит линковка, находим все типы и выстраиваем отношения между ними. Например, берём те типы, которые ещё не были собраны и начинаем их файтюнить. Как только находим неизвестную связь с другим - ищем его в системе и пытаемся дособрать, если он так же не собран до конца. В противном случае вылетают ошибки, вида "Undefined type X".
6) Coercion: Допустимые приведения типов и исправляторы кода. Например, если аргумент помечен как "Nullable" (т.е. может содержать NULL), то к нему принудительно добавляется NULL в качестве значения по-умолчанию, если программа не содержит обратного.
7) Валидатор: Проверка типов, правил наследования, аргументов и прочего. Например, у нас есть тип-объект "X", который содержит два поля с одинаковыми названиями. Надо проверить это и сказать человеку, мол, нельзя так.
8) Препроцессинг: Выполняются константные скалярные выражения, перегрузки. Например, мы указываем какую-то директиву (аннотация или аттрибут в другом языке), это invocation инструкция, т.е. вызов существующей задекларированной директивы.
9) Билдер (или кеш): Результат собирается в набор файлов, которые потом умеют грузиться в память в будущем, минуя все эти этапы с проверками и линковкой.
В итоге мы на выходе получаем АПИ (например, на псевдоджаве):
Compiler compiler = new Compiler();
// Собирает или берёт уже скомпиленный файл и возвращает результат - класс рефлексии этого файла (документа)
ReflectionDocument document = compiler.compile('interface Example {}');
// Массив типов в документе (в нашем случае он один)
document.getTypes();
// Массив полей у типа (интерфейса) Example
document.getType('Example').getFields();
А теперь к вопросам:
1) Насколько правильна такая модель работы? У меня есть подозрения, что превращаение из AST в классы рефлексии не совсем перспективно в т.ч. для поддержки. Но т.к. это декларативный язык - хз. Если есть какой-то опыт запиливания подобных штук - хорошо бы услышать советы.
2) Может имеет смысл вначале сборки в опкод (или байткод), для переносимости между платформами (т.е. возможности напрямую считывать императивные команды, вроде "
создай тип Х", "
добавь ему поле Y", уже выстроенные в нужном порядке и отвалидированные) и уже потом из этих инструкций генерировать и Reflection API, и какие-нибудь XML схемки?