1 module qte5prs; 2 3 import asc1251 : fromUtf8to1251; 4 import std..string : translate, split, strip, indexOf, toLower, replace; 5 import std.file : exists; 6 import std.path: dirSeparator, pathSeparator; 7 import std.process : environment; 8 private import std.stdio : File, writeln, readln; 9 10 // Должен быть объект, получающий на вход строку. Строка раскладываается 11 // на состовные слова и запоминается в поисковике. Список слов может 12 // быть найден (выдан) по входной последовательности 13 14 // ================================================================== 15 // CFinder - поисковик 16 // ================================================================== 17 // __________________________________________________________________ 18 19 struct s2 { 20 string c; // class 21 string p; // parent 22 } 23 24 class CFinder { //=> Поисковик. Помнит все слова в файле 25 // ______________________________________________________________ 26 this() { 27 } 28 // ______________________________________________________________ 29 ~this() { 30 } 31 // ______________________________________________________________ 32 private int[string] listForParserBefore; // Словарь файлов, которые должны быть распарсены 33 private int[string] listForParserAfter; // Словарь файлов, которые уже распарсены 34 35 private 36 struct fNode { //-> Узел списка гирлянды 37 string str; // Строка (слово) 38 //----------------- 39 un link; // Указатель на следующий или null 40 } 41 alias fNode* un; // Ссылка на узел цепочки 42 43 private 44 struct fClass { //-> Узел списка гирлянды для класса 45 string name; // Имя самого класса 46 string rawStr; // Исходная строка описания 47 uc parent; // Указатель на родителя или null 48 um metod; // указатель на цепочку методов 49 //----------------- 50 uc link; // Указатель на следующий или null 51 } 52 alias fClass* uc; // Ссылка на узел цепочки класса 53 54 private 55 struct fMetod { //-> Узел списка гирлянды для метода 56 string name; // Имя самого метода 57 string rawStr; // Исходная строка описания метода 58 uc parent; // Указатель на родителя или null 59 //----------------- 60 um link; // Указатель на следующий или null 61 um allLink; // Общий список методов 62 } 63 alias fMetod* um; // Ссылка на узел цепочки метода 64 // ______________________________________________________________ 65 // Методы, для работы со списком файлов для парсинга 66 // ______________________________________________________________ 67 void addParserBefore(string nameFile) { //-> Добавить имя файла в список, но не задваивать 68 int *p; 69 p = (nameFile in listForParserBefore); 70 if(p is null) { 71 listForParserBefore[nameFile] = 1; 72 } 73 } 74 // ______________________________________________________________ 75 string[] listParserBefore() { //-> выдать обыкновенный массив 76 string[] rez; foreach(el; listForParserBefore.byKey) rez ~= el; 77 return rez; 78 } 79 // ______________________________________________________________ 80 void addParserAfter(string nameFile) { //-> Добавить имя файла в список, но не задваивать 81 int *p; 82 p = (nameFile in listForParserAfter); 83 if(p is null) { 84 listForParserAfter[nameFile] = 1; 85 } 86 } 87 // ______________________________________________________________ 88 string[] listParserAfter() { //-> выдать обыкновенный массив 89 string[] rez; foreach(el; listForParserAfter.byKey) rez ~= el; 90 return rez; 91 } 92 // ______________________________________________________________ 93 bool isFileInParserAfter(string nameFile) { //-> Есть файл в списке распарсенных файлов? 94 int *p; 95 bool rez; 96 p = (nameFile in listForParserAfter); 97 if(p is null) { 98 rez = false; 99 } else { 100 rez = true; 101 } 102 return rez; 103 } 104 // ______________________________________________________________ 105 void addImpPrs(string[] mMod, string[5] PathForSrcDmd) { //-> Добавить список файлов импорта для парсинга 106 writeln("--1--", PathForSrcDmd); 107 return; 108 string pathDmd2 = getPathDmd2(PathForSrcDmd); 109 writeln(pathDmd2); 110 return; 111 foreach(el; mMod) { 112 string[] rawMod = split(el, ":"); 113 string pathFile = rawMod[0] ~ ".d"; 114 if(exists(pathFile)) { 115 addParserBefore(pathFile); 116 } else { 117 // Проверим на std. 118 if(indexOf(pathFile, "std.") >= 0) { 119 pathFile = pathFile.replace("std.", "std" ~ dirSeparator); 120 } else { 121 pathFile = pathFile.replace("etc.", "etc" ~ dirSeparator); 122 } 123 // Проверим на наличие 124 string fullPath = pathDmd2 ~ pathFile; 125 try { 126 if(!exists(fullPath)) continue; 127 } catch(Throwable) { 128 continue; 129 } 130 // Надо проверить, есть ли такое в списке, если нет, то добавить 131 addParserBefore(fullPath); 132 } 133 } 134 // writeln("--1--> ", listParserBefore()); 135 } 136 // ______________________________________________________________ 137 string getPathDmd2(string[5] getPathDmd) { //-> // Выдать путь до библиотеки src из dmd2 138 // writeln("---1---", getPathDmd); 139 string rez; 140 version (Windows) { 141 string myPath = environment["PATH"]; 142 string[] masPath = split(myPath, pathSeparator); 143 string pathDmd2; 144 foreach(el; masPath) { if(indexOf(el, "dmd2") > 0) { pathDmd2 = el; break; } } 145 version (X86) { // ... 32 bit code ... 146 if(getPathDmd[0] != "") { // Есть явное указание в INI 147 rez = getPathDmd[0] ~ dirSeparator; 148 } else { 149 if(pathDmd2 == "") return ""; 150 // Путь до Dmd2 найден и он не пустой 151 int begNom = cast(int)(indexOf(pathDmd2, "windows" )); 152 if(begNom > 0) { // Windows 153 rez = pathDmd2[0 .. begNom] ~ "src" ~ dirSeparator ~ "phobos" ~ dirSeparator; 154 } 155 } 156 } 157 version (X86_64) { // ... 64 bit code 158 if(getPathDmd[1] != "") { // Есть явное указание в INI 159 rez = getPathDmd[1] ~ dirSeparator; 160 } else { 161 if(pathDmd2 == "") return ""; 162 // Путь до Dmd2 найден и он не пустой 163 int begNom = cast(int)(indexOf(pathDmd2, "windows" )); 164 if(begNom > 0) { // Windows 165 rez = pathDmd2[0 .. begNom] ~ "src" ~ dirSeparator ~ "phobos" ~ dirSeparator; 166 } 167 } 168 } 169 } 170 version (linux) { 171 version (X86) { // ... 32 bit code ... 172 if(getPathDmd[2] != "") { // Есть явное указание в INI 173 rez = getPathDmd[2] ~ dirSeparator; 174 } 175 } 176 version (X86_64) { // ... 64 bit code 177 if(getPathDmd[3] != "") { // Есть явное указание в INI 178 rez = getPathDmd[3] ~ dirSeparator; 179 } 180 } 181 } 182 version (OSX) { 183 if(getPathDmd[4] != "") { // Есть явное указание в INI 184 rez = getPathDmd[4] ~ dirSeparator; 185 } 186 } 187 return rez; 188 } 189 // ______________________________________________________________ 190 // Методы, для работы с деревьями 191 // ______________________________________________________________ 192 um findMethod(uc klass, string metod) { //-> Найти или добавить метод 193 if(klass is null) return null; 194 um nod = klass.metod; // Начало цепочки 195 m1: if(nod is null) { // Цепочка пуста, вставка 1-го элемента 196 nod = new fMetod; nod.name = metod; 197 nod.link = klass.metod; nod.parent = klass; 198 klass.metod = nod; 199 nod.allLink = trapMetod; trapMetod = nod; 200 } else { // Цепочка не пуста, ищем ... 201 while(nod !is null) { 202 // writeln("compare: ", nameClass, " == ", nod.name); 203 if(nod.name == metod) { return nod; } 204 else { nod = nod.link; } 205 } 206 } 207 if(nod is null) goto m1; 208 return nod; 209 } 210 // ______________________________________________________________ 211 string[] getEqMet1(string w) { //-> Выдать массив похожих слов из методов 212 string[] rez; size_t dlw, dln; 213 if(w.length == 0) return rez; 214 um nod = trapMetod; 215 while(nod !is null) { 216 dlw = w.length; dln = nod.name.length; 217 if(dln >= dlw) { if(nod.name[0 .. dlw] == w) rez ~= nod.name; } 218 nod = nod.allLink; 219 } 220 return rez; 221 } 222 // ______________________________________________________________ 223 void printMet() { //-> Распечатать список всех методов 224 um nod = trapMetod; 225 while(nod !is null) { 226 writeln("[", nod.name, "] --> ", nod.rawStr); 227 nod = nod.allLink; 228 } 229 } 230 // ______________________________________________________________ 231 string getRawMet(string met) { //-> Вернуть сырое описание первого метода 232 um nod = trapMetod; 233 while(nod !is null) { 234 if(met == nod.name) { 235 return nod.rawStr; 236 } else { 237 nod = nod.allLink; 238 } 239 } 240 return ""; 241 } 242 // ______________________________________________________________ 243 void printUc() { //-> Распечатать список всех классов 244 uc nod = trapClass; 245 while(nod !is null) { 246 writeln(nod, " --> [", nod.name, "][", (nod.parent is null) ? "" : nod.parent.name, "] - ", nod.rawStr); 247 um nodm = nod.metod; 248 while(nodm !is null) { 249 writeln("\t", nodm.name, " --> ", nodm.rawStr); 250 nodm = nodm.link; 251 } 252 nod = nod.link; 253 } 254 // nod = findClass("QFrame"); 255 // writeln("QFrame.Parent = ", nod.parent.name); 256 // writeln(nod.rawStr); 257 } 258 // ______________________________________________________________ 259 uc findClassOnly(string nameClass) { //-> Найти класс 260 uc nod = trapClass; 261 while(nod !is null) { 262 if(nod.name == nameClass) { return nod; } 263 else { nod = nod.link; } 264 } 265 return nod; 266 } 267 // ______________________________________________________________ 268 uc findClass(string nameClass) { //-> Найти или добавить класс 269 uc nod = trapClass; 270 m1: if(nod is null) { 271 nod = new fClass; nod.name = nameClass; 272 nod.link = trapClass; trapClass = nod; 273 // writeln("add: ", nameClass); 274 return nod; 275 } else { 276 while(nod !is null) { 277 // writeln("compare: ", nameClass, " == ", nod.name); 278 if(nod.name == nameClass) { return nod; } 279 else { nod = nod.link; } 280 } 281 } 282 if(nod is null) goto m1; 283 return nod; 284 } 285 // ______________________________________________________________ 286 // Получает на вход Класс:Родитель и ИсходнаяСтрокаКласса и сохраняет в цепочке классов 287 uc insertClassParent(s2 cp, string rewStr) { //-> Вставить в цепочку классов Класс:Родитель+ИсхСтрока 288 // 1 - Разобраться с Parent 289 uc uparent, uclass; 290 if(cp.p != "") uparent = findClass(cp.p); 291 if(cp.c == "") return null; 292 uclass = findClass(cp.c); 293 uclass.name = cp.c; uclass.rawStr = rewStr; uclass.parent = uparent; 294 lastClass = uclass; 295 return uclass; 296 } 297 // ______________________________________________________________ 298 private un[256] harrow; //-> гребенка, для 256 списков слов 299 dchar[dchar] transTable1; 300 un[] masAllWords; // Список указателей на все слова 301 uc trapClass; // Базовый якорь для цепочки Классов 302 um trapMetod; // Базовый якорь для цепочки всех Методов 303 uc lastClass; // Активный в данный момент класс 304 // ______________________________________________________________ 305 ubyte getC0(string s) { //-> Выдать индекс в гребенке 306 import std.utf: stride; 307 if(s.length == 0) return 0; 308 309 // Это защита от 3 и более байтовых последовательностей 310 if(stride(s, 0) > 2) return 0; 311 312 char[] w1251 = fromUtf8to1251(cast(char[])s[0..stride(s, 0)]); 313 return w1251[0]; 314 } 315 // ______________________________________________________________ 316 void addWord(string w) { //-> Добавить слово в список, если его нет 317 if(w.length == 0) return; 318 ubyte c0; 319 if(!isWordMono(w)) { 320 c0 = getC0(w); // Первая буква слова, как индекс цепочки в harrow 321 // Создадим узел цепочки (списка) 322 un nod = new fNode; nod.str = w; 323 masAllWords ~= nod; // Запомним это слово в полном списке слов 324 nod.link = harrow[c0]; // Вставим новый узел в цепочку 325 harrow[getC0(w)] = nod; // Подвесим обновленную цепочку 326 /* 327 // Надо идти по цепочке и удалять все производные слова 328 int dlw = w.length, dln; 329 un ukaz = nod, ukaz0 = ukaz; 330 while(!(ukaz is null)) { 331 dln = ukaz.str.length; 332 if(dln < dlw) { 333 // Найденное слово короче вставленного слова 334 if(w[0 .. dln] == ukaz.str) { 335 // Удаляем этот элемент 336 ukaz0.link = ukaz.link; delete ukaz; 337 if( !(ukaz0.link is null) ) { ukaz = ukaz0.link; } 338 else { break; } 339 } 340 } 341 ukaz0 = ukaz; ukaz = ukaz.link; 342 } 343 344 */ 345 346 } 347 } 348 // ______________________________________________________________ 349 bool isWordMono(string w) { //-> Есть целое слово в списке? 350 size_t dlw, dln; 351 bool rez; 352 ubyte ind = getC0(w); 353 un ukaz = harrow[ind]; 354 dlw = w.length; 355 while(!(ukaz is null)) { 356 dln = ukaz.str.length; 357 if(dln == dlw) { 358 if(ukaz.str == w) { 359 rez = true; break; 360 } 361 } 362 ukaz = ukaz.link; 363 } 364 return rez; 365 } 366 // ______________________________________________________________ 367 bool isWord(string w) { //-> Есть целое слово или производные в списке? 368 size_t dlw, dln; 369 bool rez; ubyte ind = getC0(w); un ukaz = harrow[ind]; 370 dlw = w.length; 371 while(!(ukaz is null)) { 372 dln = ukaz.str.length; 373 if(dln >= dlw) { 374 if(ukaz.str[0 .. dlw] == w) { 375 rez = true; break; 376 } 377 } 378 ukaz = ukaz.link; 379 } 380 return rez; 381 } 382 // ______________________________________________________________ 383 string[] getSubFromAll(string w) { //-> Выдать массив похожих слов из общего хранилища 384 string[] rez; 385 string sh = toLower(w); 386 foreach(el; masAllWords) { 387 string wrd = toLower(el.str); 388 if(indexOf(wrd, sh) >= 0) rez ~= el.str; 389 } 390 return rez; 391 } 392 // ______________________________________________________________ 393 string[] getEq(string w) { //-> Выдать массив похожих слов из хранилища 394 string[] rez; size_t dlw, dln; 395 if(w.length == 0) return rez; 396 ubyte ind = getC0(w); un ukaz = harrow[ind]; 397 while(!(ukaz is null)) { 398 dlw = w.length; dln = ukaz.str.length; 399 if(dln >= dlw) { if(ukaz.str[0 .. dlw] == w) rez ~= ukaz.str; } 400 ukaz = ukaz.link; 401 } 402 return rez; 403 } 404 // ______________________________________________________________ 405 void addLine(string line) { //-> Добавить строку в хранилище 406 // import std.stdio; 407 immutable string clearLine = strip(line); 408 if(clearLine == "") return; 409 dchar[dchar] transTable = [ 410 '(':' ', 411 ')':' ', 412 9:' ', 413 '*':' ', 414 ';':' ', 415 '.':' ', 416 '[':' ', 417 ']':' ', 418 ',':' ', 419 '"':' ', 420 '!':' ', 421 '/':' ', 422 '=':' ', 423 '\\':' ', 424 ':':' ', 425 '@':' ' 426 ]; 427 static import asc1251; 428 string zish = translate(clearLine, transTable); 429 auto msRaw = split(zish, ' '); 430 string[] ms; 431 foreach(el; msRaw) { if(el == "") continue; ms ~= el; } 432 // Нужно удалить пустышки 433 try { 434 foreach(i, string el; ms) { 435 if(el == "") continue; 436 // string z = cast(string)strip(el); 437 if(el.length > 2) addWord(el); 438 // Всё добавлено в список поиска, можно проверить на нужные 439 // мне строки 440 if((el == "class") && (i == 0)) { 441 insertClassParent(nameClass(zish), clearLine.dup); 442 continue; 443 } 444 if(el == "->") { 445 // writeln(lastClass.name, " --> [", nameMethod(zish), "] -- ", clearLine); 446 um met = findMethod(lastClass, nameMethod(zish)); 447 if(met !is null) { 448 met.rawStr = clearLine.dup; 449 } 450 continue; 451 } 452 /* 453 mar 454 if(i == ms.length - 1) continue; 455 uc fnod = findClassOnly(el); 456 if(fnod is null) continue; 457 writeln(fnod.name, " = ", ms[i+1], " --> ", clearLine.dup); 458 if(el == "new") { 459 if(i == 0) continue; 460 if(i == ms.length - 1) continue; 461 462 writeln("var=[", ms[i-1], "] class = [", ms[i+1],"] = ", ms); 463 continue; 464 // Нужна функция, которая выдаёт s2 = Переменная|Тип или пусто 465 // - Взять предыдущее и следующие за new слово. Если нет, то null 466 } 467 */ 468 } 469 } catch(Throwable) { 470 // writeln("catch: ", line); 471 // writeln("catch: ", ms); 472 } 473 474 475 // if( indexOf(line, "//->") > 0 ) { writeln(zish); 476 // } 477 // Есть: 478 // Class : Parent 479 // Method : Функция(арг, ...) { //-> Описание функции 480 } 481 // ______________________________________________________________ 482 s2 nameClass(string s) { //-> Промежуточная. Выдать имя класса и родителя из строки 483 s2 rez; 484 auto ms = split(s, ' '); 485 string[] arg; 486 foreach(i, string el; ms) { 487 if(el == "") continue; 488 arg ~= el; 489 } 490 // arg --> очищенный массив строк 491 if(arg[0] == "class") { 492 if(arg.length == 1) return rez; 493 if(arg.length == 2) { rez.c = arg[1]; rez.p = ""; return rez; } 494 if(arg.length == 3) { rez.c = arg[1]; 495 if(arg[2] == "{") { 496 rez.p = ""; 497 } else { 498 rez.p = arg[2]; 499 } 500 return rez; 501 } 502 if(arg[3] == "{") { // class Name: Parent { 503 rez.c = arg[1]; rez.p = arg[2]; 504 } else { 505 if(arg[2] == "{") { // class Name { 506 rez.c = arg[1]; rez.p = ""; 507 } 508 } 509 } 510 return rez; 511 } 512 // ______________________________________________________________ 513 string nameMethod(string s) { //-> Промежуточная. Выдать имя метода из строки 514 string rez; 515 auto ms = split(s, ' '); 516 string[] arg; 517 foreach(i, string el; ms) { 518 if(el == "") continue; 519 arg ~= el; 520 } 521 rez = arg[1]; 522 return rez; 523 } 524 // ______________________________________________________________ 525 void addFile(string nameFile) { //-> Добавить файл в хранилище 526 File fileSrc = File(nameFile, "r"); 527 int ks; 528 try { 529 foreach(line; fileSrc.byLine()) { 530 try { 531 // Проверка на BOM 532 ks++; 533 if(ks == 0) if(line.length>2 && line[0]==239 && line[1]==187 && line[2]==191) line = line[3 .. $].dup; 534 addLine(cast(string)line); 535 } catch(Throwable) { 536 writeln("Warning! Error parsing string: [", cast(string)strip(line), "]"); 537 } 538 } 539 } catch(Throwable) { 540 writeln("Error read file: ", nameFile); 541 readln(); 542 } 543 } 544 } 545 546 unittest { 547 CFinder finder1 = new CFinder(); 548 bool b1; 549 550 // Проверка работы поиска слов 551 finder1.addWord("Gena"); 552 b1 = finder1.isWordMono("Gena"); assert(b1 == true); 553 b1 = finder1.isWordMono("gena"); assert(b1 == false); 554 b1 = finder1.isWord("Gen"); assert(b1 == true); 555 b1 = finder1.isWord("gen"); assert(b1 == false); 556 557 string[] m; 558 m = finder1.getEq("Gen"); assert(m == ["Gena"]); 559 m = finder1.getEq("gen"); assert(m == []); 560 m = finder1.getSubFromAll("Gen"); assert(m == ["Gena"]); 561 m = finder1.getSubFromAll("gen"); assert(m == ["Gena"]); 562 m = finder1.getSubFromAll("len"); assert(m == []); 563 564 // Проверяем работу с Классами 565 CFinder.uc adr; 566 adr = finder1.findClass("CTest1"); assert(adr.name == "CTest1"); 567 }