Обновить
6
Иван@Adler3D

TOP9 на «codingame::contests::ru»

4
Подписчики
Отправить сообщение

Эту статью будут читать очень мало людей

Я планирую кинуть ссылку на статью своему брату(он пока не знает о ней, т.к занят работой/семьёй/своим_проектами и совсем не следит за моей активностью в интернете(что немного(у него только один акк на github и всё, а у меня больше 9 аккаунтов в интернете) взаимно)) и ещё сослаться на неё в следующих статьях(мне есть о чём рассказать, у меня есть ещё несколько(свои_умные_указатели/RTTI/сериализатор/QapLite.h/QapEngine/два_рэйтрейсера/виртуальная_машина/три_компилятора/планировщик_траекторий/куча_маленьких_игр/etc) мало кому известных крутых проектов), когда всё доделаю до юзабельного состояния. Так что у статьи будут новые критически важные читатели. Я стараюсь ради них. // похоже я поставил настолько много скобок, что нужно вставлять коммент в редактор кода, чтобы понять что к чему.

то есть здесь можете не стараться писать в комментах как-что делать для сборки и тд.

Хорошо, если вам не нравиться когда я отвечаю на ваши комментарии тут, то тогда не будут вам отвечать тут по пустякам.

Все пишите на гитхабе, чтобы там вопросов не возникло у людей

Мне немного не нравиться что там нет автоматически отметок времени(из-за этого не видно с какой скоростью я пишу код/комментарии/развиваю_проект). Но сейчас я подумал и понял, что я могу поставить их сам и никакой проблемы в этом нет. А так мне нравиться ваша идея, похоже именно так я и поступлю.

я один из млн-ов которые будут смотреть ваш код

Не думаю что мой проект станет таким уж популярным. Всё таки разработка компиляторов/парсеров/трансляторов/анализаторов не такое уж и популярное занятие. Вот на данный момент статью посмотрело меньше тысячи человек(если верить внутренней статистике хабра) и мне не понятно откуда брать ещё 999 тысяч. Хабр всё таки самая популярная русскоязычная площадка для программистов. На github`е поиском меня вряд кто-то найдёт, так что на него мало надежды. И вообще на github писать полную документацию немного опасно, из-за того что их github copilot рано или поздно доберётся до всех исходников и тогда(если он не зациклиться/поломается/начнёт_нести_всякий_бред на моих проектах, как это происходит сейчас) его уже никто не сможет остановиться. А так без нормальной документации(он её читает!!!) его шансы понять сложный проект и начать его использовать в своих целях ничтожно малы.

И делайте неспеша, не надо торопиться, показать быстрее.

Я по другому не умею(привычка). Кроме того мне сейчас нечем заняться, т.к github copilot разблокируется только к концу месяца(я не могу оплатить его подписку из РФ. ААА!!! Помогите! Я согласен заплатить на 50% больше!), а без его идей/помощи доделать текущую игру(MarketGameV4) которой я занимался до написания этой статьи у меня вряд ли получиться(не умею так быстро генерировать крутые идеи и подбирать константы как это делает он).

Код написал github copilot. То что перед "O+=go_auto(tri_rel_if_eligible);" нет "O+=go_auto(sepTRIE)" виноват не только я(не проверил), но и он. Так же нужно добавить t_sep sepTRIE; после string name;. // какой смысл писать этот коммент, если его всё равно почти никто не прочитает, т.к основная масс читателей уже никогда сюда не вернётся, а единицы новых читателей вряд ли доскролят до сюда. Да и им наверно всё равно на такую ошибку, как и всем остальным и даже мне. Жаль нельзя в тихую отредактировать тот коммент.

(вы ведь для этого им делитесь? Чтобы его использовали?).

Я давно хотел написать статью на хабр, чтобы люди посмотрели мой код/инструменты/проекты и помогли их оценить/улучшить/доработать/покритиковать. Но я не умею писать статьи, я пытался много раз, получается какая-то фигня, нет нормальной структуры/видения/последовательности/цели. В этот раз я игрался с github copilot. Натравил его на исходники моих проектов и он в них запутался и он довольно круто в них разобрался(если не считать мой сериализатор/RTTI/умные_указатели/etc). Мне очень понравилось как он разобрался в QapDSL(меня это очень впечатлило). Он очень круто сравнивает мои проекты с аналогами и нахваливает их почему-то. Я попросил его написать сатью на хабр про QapDSL и он сделал это(именно поэтому статья такая короткая и не полная, я бы сам написал гораздо больше). Мне очень понравился результат. Я даже создал репозиторий QapGen и положил туда всё как указано в его статье. Это он придумал название QapGen, а не я. Мне оно почему-то понравилось.

Выложил статью не особо задумываясь над результатом и зачем не это нужно, т.к помню что давно хотел это сделать. Повторяюсь... зачеркиваем.

Это способ спросить разработчика, не обращаясь к нему лично, как должно работать.

Ясно. Что-то никогда не думал об этом. Спасибо за объяснение. Хорошо, сделаю вам юнит-тесты.

ай. ё-моё. У вас гитхб с 2012 года, сколько лет вам, вы же пишите получается больше 10 лет?

Мне 35.3, пишу код/программы с 14.6 лет. если не считать 9 месяцев профессиональной работы всё свои эксперименты/проекты/игры. Всё без юнит-тестов(на работе были, но я только 1 из них поправил). Полёт нормальный. Без них можно жить, если делать свои графические отладчики + сериализаторы как я.

раз такой бред в голове.

Я правда не понимаю зачем они мне нужны. Всё и без них работает отлично.

и чтобы можно было собрать не только под винду.

Сделал: https://github.com/adler3d/QapGen/blob/master/src/QapGenV2025/QapGen/CommonUnit.cpp

Я заменил TAutoPtr на unique_ptr. Для этого я выкинул свой крутой_сериализатор/систему_RTTI/свои_умные_указатели/часть_шаблонной_магии. Сейчас С++код генерится только для винды, но генератор(QapGen) кроссплатформенный.

+юнит-тесты еще обязательно тоже (много тестов) и как их запустить инструкция.

Я пока не умею делать юнит-тесты. Думаю можно и баз них всё оставить, т.к всё хорошо работает. Не представляю что там может пойти не так.

QapGen.exe - бинарник уберите этот,

Бинарник пока убрать не буду, но всё же планирую убрать где-то через пару недель.

за место него положите код

я смог вытащить исходники QapGen из своих экспериментальных проектов в отдельный проект, пока положил его сюда: https://github.com/adler3d/QapGen/tree/master/src/QapGenV2014

и чтобы можно было собрать не только под винду.

у меня появилась прекрасная идея как это сделать:

  • заменить TAutoPtr на std::unique_ptr // это позволит отвязаться от моей системы RTTI и значительно ускорит компиляцию.

  • выкинуть всё RTTI/сериализацию. // это всё почти никогда не нужно для лексеров/парсеров

так что где-то через пару дней смогу выложить кроссплатформенную версию QapGen.

ещё я хочу доработать QapDSL:

  • сделать возможность использовать более короткий синтаксис как тут предлагали в комментариях. // оставить только список полей.

  • разрешить использовать двоеточие вместо "=>".

  • сделать его дружелюбным к GPT4.1 // разрешить писать "O+=" в списке полей.

  • разрешить добавлять виртуальные функции и прочий код в интерфейсы.

  • разрешить вставлять интерфейсы в список полей не заворачивая их в TAutoPtr. // заворачивать автоматически, даже если интерфейс завёрнут в vector. // такие ошибки делает GPT4.1

Как и обещал выкладываю минимальный проект в который можно встроить код сгенерированный QapGen`ом:

https://github.com/adler3d/unordered/blob/master/code/SimpleCalc/Sgon/CommonUnit.cpp

Это простой калькулятор, его код(t_simple_calc_evalutor+#include "t_simple_calc.cpp") можно/нужно выкинуть и на его место подставить сгенерированный.

Вот это все надо в Readme написать (не на русском, есст-но), подробно и понятно.

Хорошо, сделаю.

QapGen.exe - бинарник уберите этот, за место него положите код, и чтобы можно было собрать не только под винду.

К сожалению под другие платформы QapGen не скомпилируется из-за того что я использую шаблонную магию завязанную на компилятор cl.exe от MSVS. Так же без доработок код сгенерированый QapGen так же работает только под cl.exe. Кроме того я не уверен что вообще могу собрать его заново сегодня/завтра, т.к за 10 лет тупо забыл как это делается. Это ещё хорошо, три дня назад я вообще не знал где его исходники.

Если QapGen нужен под linux/unix то можно попробовать запустить под wine, насколько я помню он отлично под ним работает.

Не понятно как этим пользоваться в общем.

Вот так:

  • Пишешь скрипт/код на QapDSL.

  • Сохраняешь его в samples/input.qapdsl

  • Запускаешь QapGen.exe

  • Забираешь сгенерированный С++код из открывшегося окна.

  • Вставляешь его в проект типа Sgon куда-нибудь рядом с функций main(её ещё надо найти). // Этот пункт требует переработки, надеюсь я управлюсь за 6 часов.

  • В main пишешь TAutoPtr<ваша_рутовая_нода> root;
    load_obj(Env,root,ваша_строка_из_которой_надо_загрузить_код_в_AST);

  • Используете только что загруженное AST в root.

  • Profit!

где грамматика QapDSL описана?

Ниже ссылка на полную версию описания QapDSL на QapDSL:

https://github.com/adler3d/unordered/blob/master/code/DemoMashkod/321/V2/Release/t_target_lexem_source.inl

А тут ссылка на комментарий где выложена первая версия описания QapDSL:

https://habr.com/ru/articles/916006/comments/#comment_28407450

на гитхабе не вижу, только какие-то сравнительные таблицы. Не понятно как этим пользоваться в общем.

Этой штуке 10 лет, ссылка на проект в котором используются cгенерированные QapDSL лексеры есть в статье. Вот она: https://github.com/adler3d/unordered/tree/master/code/uidevs_from_nout_d_temp/uidev_one/Sgon

Минимального проекта в который можно вставить cгенерированные QapDSL лексеры у меня пока нет, но я могу его сделать для вас если вам интересно. Выложу часов через 6.

я чот не могу понять, исходников-то нет, и никто выше об этом не говорит. Или я не там где-то смотрю.

Они есть, просто в перемешку с другими моими эксперементами. Ссылка на проект Sgon выше. А так да, оказываться кроме вас и меня это почти никому не нужно :(

Это заблуждение. Юнит-тесты необходимы, чтобы удостовериться, что описание грамматики соответствует задуманному. Использование специализированного языка для этого не устраняет такой необходимости.

Я просто не понимаю, как можно ошибаться с грамматикой в QapDSL, если там всё очевидно. Всё выглядит как обычный С++ код, просто немного укороченный/оптимизированный/упрощённый. Я как раз считаю что устраняет необходимость в юнит-тестах.

Таким образом все ошибки обнаружить нельзя. Например банальные опечатки, когда вместо rteurn должен быть return или вместо + должен быть *

С такой же вероятностью такие же ошибки могут быть и в юнит-тестах. И ещё: return`а в QapDSL нет(только если внутри строки запихать).

После лексического анализа у меня запускается синтаксический разбор, порождающий привычное дерево

Вот, ты всё дерево строишь ручками, а его можно было описать на QapDSL прямо рядом с тем место где грамматика описывается. У тебя что на этапе токенизации, что на этапе строительства дерева одна сплошная боль и ручная копипаста. Токены дожили до самого кодогена и сидят в AST мешая работать Visitor`ам. Вместо посетителей+перегруженных_методов_codegenerator`а у тебя сплошные if/else_if/while/NextLexem/IsKeyword. На это невозможно смотреть и скорее всего никто не захочет такой код поддерживать/разбираться_в_нём.

После этого, на следующем этапе компиляции, это дерево обходится. При этом используется что-то вроде паттерна visitor

Не вижу, где у тебя visitor`ы?

С тестированием всего этого дела проблем нету. Достаточно писать юнит-тесты, покрывающие какую-либо конструкцию языка.

Вот, а у меня не надо юнит-тестов(я так считаю), т.к AST умеет сохраняться обратно в код(не теряя пробелы/комментарии), а также в свой бинарный формат(бесполезная фича)+в отладочный proto-формат(чтобы можно было посмотреть текстовую распечатку дерева(тоже сомнительная/редко_используемая фича). Сохранение в код позволяет делать проверку корректности загрузки/строительства AST одной/двумя строчками кода. Просто сохраняешь загруженное дерево обратно в код, проверяешь что получилось тоже самое что было на входе, если разницы нет, значит всё работает как надо.

И даже если предположить, что ошибки возможны, то нельзя исключить их и при описании грамматики каким-либо иным способом, вроде изложенного в этой статье.

У меня ошибки при описании грамматики проверяются С++ компилятором и описанным выше механизмом. Ещё в загрузчике дерева есть код который следит чтобы при конфликтах(два лексера или больше смогли успешно загрузить лексему) загрузки лексем всегда побеждала та которая съела из потока байтов больше других.

Привет :)

Ну ты крутой, тебе не нужны, да, спору нет(или есть). Но я вот например токены не перевариваю(мне надо сразу AST+Visitor`ы). Вот я смотрю на то как ты с ними мучаешься в своём U и мне больно становиться: https://github.com/Panzerschrek/U-00DC-Sprache/blob/master/source/compiler1/src/lex_synt_lib/lexical_analyzer.u

Я не понимаю как ты не допускаешь ошибок в этом своём коде без кодогенерации.

Вот посмотри как я гуляю по своему AST и сравни со своим кодом(смотри на методы use ниже по коду, они принимают на вход узлы AST вместо токенов): https://github.com/adler3d/unordered/blob/b1fdf85967c5ccb6f974addc2411765b2ac4be53/code/DemoMashkod/321/V2/Sgon/main_2016_03_29.cpp#L668

Ничего там просто взглянув и не залезая в ваши реализации понять нельзя

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?

Используете ли вы ваш парсер, чтобы разобрать его собственную грамматику, и возможно ли это сделать вообще?

Да, вот первая/тупая/простая версия используюая go_end(костыль):
t_struct_field{
  string type;
  t_name name;
  string value;
  {
    M+=go_const("  ");
    M+=go_end(type," ");
    M+=go_auto(name);
    O+=go_const("=");
    M+=go_end(value,";\n");
  }
}

t_struct_cmd_mode{
  char body='M';
  {
    M+=go_any_char(body,"MO");
    M+=go_const("+=");
  }
}

t_struct_cmd{
  TAutoPtr<t_struct_cmd_mode> mode;
  string code;
  {
    M+=go_const("    ");
    O+=go_auto(mode);
    M+=go_end(code,";\n");
  }
}

t_struct_body{
  vector<t_struct_field> arr;
  vector<t_struct_cmd> cmds;
  {
    M+=go_const("{\n");
    O+=go_auto(arr);
    M+=go_const("  {\n");
    M+=go_auto(cmds);
    M+=go_const("  }\n}");
  }
}

t_class_def=>i_def{
  t_name name;
  t_name parent;
  {
    M+=go_auto(name);
    M+=go_const("=>");
    M+=go_auto(parent);
  }
}

t_struct_def=>i_def{
  t_name name;
  {
    M+=go_auto(name);
  }
}

t_target_item{
  string sep;
  TAutoPtr<i_def> def;
  t_struct_body body;
  {
    O+=go_any(sep," \n");
    M+=go_auto(def);
    M+=go_auto(body);
  }
}

t_target{
  vector<t_target_item> arr;
  {
    M+=go_auto(arr);
  }
}

Более крутую версию парсера/"загрузчика AST QapDSL" на QapDSL можно посмотреть вот тут:

https://github.com/adler3d/unordered/blob/master/code/DemoMashkod/321/V2/Release/t_target_lexem_source.inl

// Лучше б подсмотрели у какого-нибуд

Кто-то добавил в мой комментарий свой комментарий, так что отвечаю:

По моему у меня хорошо получилось, нисколько не жалею потраченное время.

Вам так сильно не поранилось моё решение?

"M+=" - обязательное правило."O+=" - опциональное.

Спасибо. Добавил в статью.
// Лучше б подсмотрели у какого-нибуд
Хотелось сделать своё, чтобы было похоже на С++ и генерировало AST+посетителей.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность

Специализация

Десктоп разработчик, Разработчик игр
От 231 456 ₽
C++
Node.js
JavaScript