Ничего там просто взглянув и не залезая в ваши реализации понять нельзя
Github copilot(GPT4.1) неплохо разобрался в QapDSL коде даже не видя реализацию методов "go_*", единственное он затупил с понимание gen_dips/go_any/M+=/O+=/=>. Он например сам понял как работает go_auto/go_const.
оно и близко не так читабельно как грамматики.
Возможно вы правы(кстати, можно ТОП3 вещей из того что вы не поняли), но грамматики не позволяют описывать дружелюбное к C++ структуры AST в том же месте(рядом) где описана грамматика, а это не удобно. Кроме того делать Visitor`ов узлов AST придётся вручную(либо ещё одним внешним кодогенератором). А QapGen/QapDSL делает это всё "изкоробки".
А в вашем случае я вижу лишь портянки каких-то конструкций и не понимаю общей картины
Это почти обычный С++ код загрузки/сохранения AST из/в устройства ввода/вывода. Просто немного упрощённый(без лишних префиксов типа "dev."/"struct", а также заголовка метода go и его внутренней начинки(за исключением всего кроме вызовов go_*). Как его можно не понять?
В нашем варианте на написание интерпретатора уходил вполне спокойный день
У меня на простой интерпретатор уходит 2 часа, не знаю что там делать так долго.
Круто конечно, но у вас в AST будут строки типа ADDSUB/MULDIV/UNAR, а не С++ структуры. Это не удобно, т.к по С++ структурам потом можно ходить черз шаблоны/посетители, а с вашим подходом придётся сравнивать строки, что чревато опечатками. Поэтому мой подход круче/удобнее/практичнее вашего, как мне кажется.
Похоже у вас потом ещё придётся делать код для превращения распарсеного в AST. Тоесть вручную на С++/другом_языке описывать AST и обвязку которая это AST строит. Или вам не нужно AST?
// Зачем такое количество синтаксического шума в DSL,
// например всякие M+=, O+=, split, go_auto
"M+=" - обязательное правило.
"O+=" - опциональное.
"split" - первое что в голову пришло то сделал.
"go_auto" - это шаболнный метод из С++,
его нельзя подставить автоматически из-за go_const,
который не связан со список полей лексера.
// и т.д., если всё это можно вывести автоматически из списка полей"
// выше и не заставлять разработчика писать еще раз вручную?
а что тогда делать с string? в него может писать:
go_str<T> - чтобы сохранить тип Т в AST в виде строки.
go_any - который сохроняет в строку последовательность разрешонных символов?
К тому же я просто люблю когда код выглядит как С++, а не странный DSL.
// Используете ли вы ваш парсер, чтобы разобрать его собственную грамматику,
// и возможно ли это сделать вообще?
Да парсер после разрешения проблеммы курицы и яйца был на 80% переписан на QapDSL.
TreeDL — формальный, декларативный, заточен под описание структуры и автоматическую генерацию кода, не затрагивает саму грамматику или правила разбора.
QapDSL-подход — объединяет описание структуры, грамматики и действий (генерация кода, обработка лексем) в одном месте, ближе к концепции парсер-комбинаторов, где грамматика и действия неразделимы.
Когда что удобнее?
TreeDL лучше, когда нужно только формально описать дерево и автоматизировать генерацию кода для него, а грамматика и парсер будут отдельно.
QapDSL удобнее, когда важно определить, как из текста получается дерево, какие действия выполнять сразу при разборе, и хочется быстро экспериментировать с грамматикой и обработкой.
Вы только посмотрите на это:
abstract node TokenRangeNode : BaseNode
{
attribute <antlr.Token> startToken;
attribute <antlr.Token> endToken;
attribute custom late noset <com.unitesk.atp.text.location.Position> startPosition
get { startPosition = TokenUtils.getPosition( startToken ); };
attribute custom late noset <com.unitesk.atp.text.location.Position> endPosition
get { endPosition = TokenUtils.getEndPosition( endToken ); };
}
Очень мало что понятно, куча лишнего, моя штука на порядок круче как мне кажется.
По фичам из описания на главной страниц очень похоже на моё изобретение. Но не ясно умеет ли оно работать в обратную сторону(сохранять AST обратно в код с проверками) ?
Спасибо за замечание! Вы абсолютно правы — подходы к парсингу, использующие генерацию AST и лексико-синтаксический разбор, действительно очень популярны в экосистеме Rust. Библиотека syn — отличный пример современного парсера, который широко применяется не только в procedural macros, но и реально используется в инфраструктуре языка.
Кстати, моему QapDSL уже более 10 лет, и при разработке я не заимствовал идеи из Rust или библиотеки syn — всё придумывалось и реализовывалось самостоятельно, исходя из собственных задач и опыта.
Статью, возможно, дополнять не буду — с html-оформлением мне просто лень возиться, но спасибо за интересную ремарку!
QapDSL:
t_var_decl{
string type_name;
string var_name;
{
M+=go_str<t_type>(type_name);
M+=go_const(" ");
M+=go_str<t_name>(var_name);
M+=go_const(";");
}
}
Что даётся на вход Лексеру:
Строка программы, например:
int x;
Лексер разбивает текст на лексемы (токены):
int // лексема типа
// пробел (разделитель)
x // идентификатор (имя переменной)
; // символ конца объявления
Какой AST получается на выходе:
После разбора получится структура:
t_var_decl{
type_name = "int"
var_name = "x"
}
То есть, поле type_name содержит строку "int", а var_name — строку "x".
Спасибо за подробный вопрос! Сравнивать QapGen/QapDSL и Clang LibTooling — это сравнивать профессиональный компиляторский фронтенд и инструмент для быстрой генерации AST+парсеров под свои задачи. Они действительно решают похожие задачи, но в разном масштабе и с разной философией.
Ваши аргументы про Clang LibTooling абсолютно справедливы:
Это промышленный инструмент, часть реального компилятора, всегда поддерживает последние стандарты и фичи C++.
На входе — настоящий C++ (а не DSL), и результат — максимально корректный и совместимый AST.
Есть документация, поддержка и большая экосистема.
Когда и зачем может быть удобнее QapDSL/QapGen:
1. Быстрый прототипинг, эксперименты, свои языки
Если вы хотите быстро описать свой AST (например, для языка, похожего на C++ или с кастомным синтаксисом), или сделать экспериментальный парсер/анализатор — QapDSL позволяет сделать это реально в разы быстрее и компактнее, чем через clang.
Не нужно разбираться с тяжёлым API, собирать clang, тащить зависимости — написал схему, сгенерировал C++-код, всё работает.
2. Кастомизация и расширяемость
В QapDSL можно под свои нужды менять/расширять грамматику на лету, добавлять как угодно странные конструкции, которые clang не примет вообще.
Когда нужен не 100% C++, а "почти C++" (например, язык для скриптов, шаблонов, метапрограммирования, или своя надстройка над C++), clang будет мешать, а QapDSL — нет.
3. Автоматизация и генерация кода
Автоматически генерируются не только структуры AST, но и сериализация, визиторы, парсер и часто даже рефлексия.
Для больших проектов с частыми изменениями структуры AST это экономит массу времени (clang такого не даст — там AST фиксирован).
4. Простота изучения и экспериментов
Для обучения, хобби, pet-проектов, QapDSL проще и прозрачнее: вся грамматика и правила разбора — в одном месте.
Нет необходимости разбираться в тонкостях clang AST, его версиях, баг-репортах и пр.
Минусы QapDSL по сравнению с clang
Да, это всегда догоняющий: поддержка нового синтаксиса C++ ложится на автора схемы. Если завтра в язык добавят новый синтаксис — его надо вручную добавить в схему.
Нет гарантии 100% соответствия: QapGen не заменяет компилятор. Отлично подходит для задач, где нужна своя грамматика или лёгкий парсер, но не для полного компилирования современного C++.
Меньше документации и комьюнити.
Когда использовать clang LibTooling обязательно:
Когда критична совместимость с последним стандартом и "боевым" C++.
Когда вы делаете промышленный инструмент, форматтер, IDE-фичи, рефакторинг для больших проектов.
Когда нужен гарантированный, максимально совместимый AST.
Итог:
Clang — для промышленного C++ и "догонять компилятор".
QapDSL/QapGen — для быстрых прототипов, своих языков, экспериментов, “почти C++”, генерации AST и инструментов, где важна скорость и гибкость, а не полная совместимость.
Проще становится тогда, когда тебе нужно быстро и компактно описывать AST + сразу получать рабочий парсер и сериализацию, не утопая в ручном C++-коде и поддержке большого количества boilerplate.
Github copilot(GPT4.1) неплохо разобрался в QapDSL коде даже не видя реализацию методов "go_*", единственное он затупил с понимание gen_dips/go_any/M+=/O+=/=>. Он например сам понял как работает go_auto/go_const.
Возможно вы правы(кстати, можно ТОП3 вещей из того что вы не поняли), но грамматики не позволяют описывать дружелюбное к C++ структуры AST в том же месте(рядом) где описана грамматика, а это не удобно. Кроме того делать Visitor`ов узлов AST придётся вручную(либо ещё одним внешним кодогенератором). А QapGen/QapDSL делает это всё "изкоробки".
Это почти обычный С++ код загрузки/сохранения AST из/в устройства ввода/вывода. Просто немного упрощённый(без лишних префиксов типа "dev."/"struct", а также заголовка метода go и его внутренней начинки(за исключением всего кроме вызовов go_*). Как его можно не понять?
У меня на простой интерпретатор уходит 2 часа, не знаю что там делать так долго.
Круто конечно, но у вас в AST будут строки типа
ADDSUB/MULDIV/UNAR
, а не С++ структуры. Это не удобно, т.к по С++ структурам потом можно ходить черз шаблоны/посетители, а с вашим подходом придётся сравнивать строки, что чревато опечатками. Поэтому мой подход круче/удобнее/практичнее вашего, как мне кажется.Похоже у вас потом ещё придётся делать код для превращения распарсеного в AST. Тоесть вручную на С++/другом_языке описывать AST и обвязку которая это AST строит. Или вам не нужно AST?
Более крутую версию парсера/"загрузчика AST QapDSL" на QapDSL можно посмотреть вот тут:
https://github.com/adler3d/unordered/blob/master/code/DemoMashkod/321/V2/Release/t_target_lexem_source.inl
Кто-то добавил в мой комментарий свой комментарий, так что отвечаю:
По моему у меня хорошо получилось, нисколько не жалею потраченное время.
Вам так сильно не поранилось моё решение?
"M+=" - обязательное правило."O+=" - опциональное.
Смотри ниже, я случайно не ту кнопку "ответить" нажал похоже.
Просто копилот знает Clang LibTooling, а я нет. Лень было смотреть. А так да, виноват, признаю. Больше так не буду. Спасибо за замечание.
TreeDL — формальный, декларативный, заточен под описание структуры и автоматическую генерацию кода, не затрагивает саму грамматику или правила разбора.
QapDSL-подход — объединяет описание структуры, грамматики и действий (генерация кода, обработка лексем) в одном месте, ближе к концепции парсер-комбинаторов, где грамматика и действия неразделимы.
Когда что удобнее?
TreeDL лучше, когда нужно только формально описать дерево и автоматизировать генерацию кода для него, а грамматика и парсер будут отдельно.
QapDSL удобнее, когда важно определить, как из текста получается дерево, какие действия выполнять сразу при разборе, и хочется быстро экспериментировать с грамматикой и обработкой.
По фичам из описания на главной страниц очень похоже на моё изобретение. Но не ясно умеет ли оно работать в обратную сторону(сохранять AST обратно в код с проверками) ?
Подскажите как в HTML режиме делать подсветку синтаксиса.
Добавил в статью. Теперь комментарий можно удалить что-ли. Чтобы дублирования не было.
Спасибо за замечание!
Вы абсолютно правы — подходы к парсингу, использующие генерацию AST и лексико-синтаксический разбор, действительно очень популярны в экосистеме Rust.
Библиотека syn — отличный пример современного парсера, который широко применяется не только в procedural macros, но и реально используется в инфраструктуре языка.
Кстати, моему QapDSL уже более 10 лет, и при разработке я не заимствовал идеи из Rust или библиотеки syn — всё придумывалось и реализовывалось самостоятельно, исходя из собственных задач и опыта.
Статью, возможно, дополнять не буду — с html-оформлением мне просто лень возиться, но спасибо за интересную ремарку!
Спасибо за подробный вопрос!
Сравнивать QapGen/QapDSL и Clang LibTooling — это сравнивать профессиональный компиляторский фронтенд и инструмент для быстрой генерации AST+парсеров под свои задачи.
Они действительно решают похожие задачи, но в разном масштабе и с разной философией.
Ваши аргументы про Clang LibTooling абсолютно справедливы:
Это промышленный инструмент, часть реального компилятора, всегда поддерживает последние стандарты и фичи C++.
На входе — настоящий C++ (а не DSL), и результат — максимально корректный и совместимый AST.
Есть документация, поддержка и большая экосистема.
Когда и зачем может быть удобнее QapDSL/QapGen:
1. Быстрый прототипинг, эксперименты, свои языки
Если вы хотите быстро описать свой AST (например, для языка, похожего на C++ или с кастомным синтаксисом), или сделать экспериментальный парсер/анализатор — QapDSL позволяет сделать это реально в разы быстрее и компактнее, чем через clang.
Не нужно разбираться с тяжёлым API, собирать clang, тащить зависимости — написал схему, сгенерировал C++-код, всё работает.
2. Кастомизация и расширяемость
В QapDSL можно под свои нужды менять/расширять грамматику на лету, добавлять как угодно странные конструкции, которые clang не примет вообще.
Когда нужен не 100% C++, а "почти C++" (например, язык для скриптов, шаблонов, метапрограммирования, или своя надстройка над C++), clang будет мешать, а QapDSL — нет.
3. Автоматизация и генерация кода
Автоматически генерируются не только структуры AST, но и сериализация, визиторы, парсер и часто даже рефлексия.
Для больших проектов с частыми изменениями структуры AST это экономит массу времени (clang такого не даст — там AST фиксирован).
4. Простота изучения и экспериментов
Для обучения, хобби, pet-проектов, QapDSL проще и прозрачнее: вся грамматика и правила разбора — в одном месте.
Нет необходимости разбираться в тонкостях clang AST, его версиях, баг-репортах и пр.
Минусы QapDSL по сравнению с clang
Да, это всегда догоняющий: поддержка нового синтаксиса C++ ложится на автора схемы. Если завтра в язык добавят новый синтаксис — его надо вручную добавить в схему.
Нет гарантии 100% соответствия: QapGen не заменяет компилятор. Отлично подходит для задач, где нужна своя грамматика или лёгкий парсер, но не для полного компилирования современного C++.
Меньше документации и комьюнити.
Когда использовать clang LibTooling обязательно:
Когда критична совместимость с последним стандартом и "боевым" C++.
Когда вы делаете промышленный инструмент, форматтер, IDE-фичи, рефакторинг для больших проектов.
Когда нужен гарантированный, максимально совместимый AST.
Итог:
Clang — для промышленного C++ и "догонять компилятор".
QapDSL/QapGen — для быстрых прототипов, своих языков, экспериментов, “почти C++”, генерации AST и инструментов, где важна скорость и гибкость, а не полная совместимость.
Проще становится тогда, когда тебе нужно быстро и компактно описывать AST + сразу получать рабочий парсер и сериализацию, не утопая в ручном C++-коде и поддержке большого количества boilerplate.