Pull to refresh

QapDSL — декларативное описание AST и парсеров для C++

Level of difficultyMedium
Reading time4 min
Views1.5K



QapDSL — декларативное описание AST и парсеров для C++


QapDSL — это специализированный язык (DSL), который позволяет описывать абстрактные синтаксические деревья (AST) и правила их разбора для языков программирования, прежде всего C++. Такая формализация помогает автоматизировать построение парсеров, генерацию кода, анализ исходников и даже рефакторинг.



Зачем нужен QapDSL?


  • Компактно и наглядно описывать структуру и грамматику языка.
  • Автоматически генерировать C++-структуры, парсеры, сериализаторы и визиторы.
  • Ускорять эксперименты с языками, создавая прототипы компиляторов и анализаторов.
  • Упрощать анализ и рефакторинг сложных языков, в т.ч. C++.


Пример QapDSL-описания


Рассмотрим, как описывается объявление класса C++ на QapDSL:


t_class{
  string keyword;
  t_sep sep0;
  string name;
  t_sep sep1;
  TAutoPtr<t_parents> parents;
  t_sep sep2;
  TAutoPtr<t_class_body> body;
  t_sep sep3;
  {
    M+=go_any_str_from_vec(keyword,split("struct,class,union",","));
    O+=go_auto(sep0);
    M+=go_str<t_name>(name);
    O+=go_auto(sep1);
    O+=go_auto(parents);
    O+=go_auto(sep2);
    O+=go_auto(body);
    O+=go_auto(sep3);
    M+=go_const(";");
  }
}


  • Поле keyword — ключевое слово (struct, class, union).
  • name — имя класса, parents — список базовых классов.
  • В фигурных скобках — правила разбора для каждого поля («M+=» — обязательное правило. «O+=» — опциональное, делее тип парсинга).


Пример сгенерированного C++-кода


Вот пример C++-структуры, которую может сгенерировать QapGen по приведённому выше QapDSL-описанию:


class t_class{
  #define DEF_PRO_STRUCT_INFO(NAME,PARENT,OWNER)NAME(t_class)OWNER(t_inl_file)
  #define DEF_PRO_VARIABLE(ADDBEG,ADDVAR,ADDEND)\
  ADDBEG()\
  ADDVAR(string,keyword,DEF,$,$)\
  ADDVAR(t_sep,sep0,DEF,$,$)\
  ADDVAR(string,name,DEF,$,$)\
  ADDVAR(t_sep,sep1,DEF,$,$)\
  ADDVAR(TAutoPtr<t_parents>,parents,DEF,$,$)\
  ADDVAR(t_sep,sep2,DEF,$,$)\
  ADDVAR(TAutoPtr<t_class_body>,body,DEF,$,$)\
  ADDVAR(t_sep,sep3,DEF,$,$)\
  ADDEND()
  //=====+>>>>>t_class
  #include "QapGenStructNoTemplate.inl"
  //<<<<<+=====t_class
  public:
    bool go(i_dev&dev){
      t_fallback scope(dev,__FUNCTION__);
      auto&ok=scope.ok;
      auto&D=scope.mandatory;
      auto&M=scope.mandatory;
      auto&O=scope.optional;
      static const auto g_static_var_0=QapStrFinder::fromArr(split("struct,class,union",","));
      M+=dev.go_any_str_from_vec(keyword,g_static_var_0);
      if(!ok)return ok;
      O+=dev.go_auto(sep0);
      if(!ok)return ok;
      M+=dev.go_str<t_name>(name);
      if(!ok)return ok;
      O+=dev.go_auto(sep1);
      if(!ok)return ok;
      O+=dev.go_auto(parents);
      if(!ok)return ok;
      O+=dev.go_auto(sep2);
      if(!ok)return ok;
      O+=dev.go_auto(body);
      if(!ok)return ok;
      O+=dev.go_auto(sep3);
      if(!ok)return ok;
      M+=dev.go_const(";");
      if(!ok)return ok;
      return ok;
    }
  };

  • Структура полностью повторяет схему из QapDSL и содержит макросы для генерации кода и сериализации.
  • Метод go реализует правила разбора для каждого поля — как и в QapDSL.
  • Включение QapGenStructNoTemplate.inl добавляет автогенерированные методы для RTTI/визиторов/сериализации.


QapDSL в действии: как это работает?


  1. Пишем QapDSL-описание грамматики.
  2. Автогенератор создает C++-структуры и код парсера.
  3. Получаем AST, по которому можно строить анализаторы, рефактореры, сериализаторы и т.д.

В проекте Sgon можно встретить QapDSL-описание, закодированное в base64 или encodeURIComponent внутри комментария — это целостная схема AST и грамматики.
Пример живого проекта: cpp_ast_scheme.cpp в QapGen и репозиторий QapGen.



Сравнение с аналогами


QapDSL ANTLR Yacc/Bison protobuf
Тип DSL для AST+грамматики Генератор парсеров Генератор парсеров DSL для сериализации
AST Автоматически Через actions Ручное Только структуры данных
Язык генерации C++ Java, C++, Python и др. C/C++ C++, Python и др.
Поддержка C++-синтаксиса Глубокая Возможно Возможно Нет
Порог вхождения Средний Средний/Высокий Средний/Высокий Низкий


QapDSL: плюсы и минусы


  • + Одна схема — и AST, и парсер.
  • + Просто расширять и поддерживать новые конструкции C++ (шаблоны, пространства имён, препроцессор).
  • + Автоматическая генерация кода, сериализация, визиторы.
  • Меньше документации и сообщества, чем у ANTLR/Yacc.
  • Ориентация прежде всего на C++ и AST-heavy задачи.


Где посмотреть/попробовать?




Заключение


QapDSL — мощный инструмент для тех, кто работает с AST, парсерами и анализом кода C++. Он позволяет компактно описывать самые сложные конструкции C++ и автоматизировать рутинные задачи, связанные с синтаксисом. Если вы любите декларативные подходы и часто пишете компиляторы или анализаторы — обязательно попробуйте QapDSL!




upd:
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».

Автор: Adler3d. Статья подготовлена при поддержке GitHub Copilot.



Only registered users can participate in poll. Log in, please.
Понятно?
22.22% Да2
44.44% Нет4
33.33% Посчитайте как полчеловека3
9 users voted. 1 user abstained.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+4
Comments58

Articles