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 }