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 }