1 module qte5prs;
2 
3 import asc1251 : fromUtf8to1251;
4 import std..string : translate, split, strip, indexOf, toLower;
5 private import std.stdio : File, writeln, readln;
6 
7 // Должен быть объект, получающий на вход строку. Строка раскладываается
8 // на состовные слова и запоминается в поисковике. Список слов может
9 // быть найден (выдан) по входной последовательности
10 
11 // ==================================================================
12 // CFinder - поисковик
13 // ==================================================================
14 // __________________________________________________________________
15 
16 struct s2 {
17 	string c;  // class
18 	string p;  // parent
19 }
20 
21 class CFinder { //=> Поисковик. Помнит все слова в файле
22 	// ______________________________________________________________
23 	this() {
24 	}
25 	// ______________________________________________________________
26 	~this() {
27 	}
28 	// ______________________________________________________________
29 	private
30 	struct fNode { //-> Узел списка гирлянды
31 		string 		str;		// Строка (слово)
32 		//-----------------
33 		un	 		link;		// Указатель на следующий или null
34 	}
35 	alias fNode* un; // Ссылка на узел цепочки
36 
37 	private
38 	struct fClass { //-> Узел списка гирлянды для класса
39 		string name;			// Имя самого класса
40 		string rawStr;			// Исходная строка описания
41 		uc		parent;			// Указатель на родителя или null
42 		um		metod;				// указатель на цепочку методов
43 		//-----------------
44 		uc	 	link;			// Указатель на следующий или null
45 	}
46 	alias fClass* uc; // Ссылка на узел цепочки класса
47 
48 	private
49 	struct fMetod { //-> Узел списка гирлянды для метода
50 		string name;			// Имя самого метода
51 		string rawStr;			// Исходная строка описания метода
52 		uc		parent;			// Указатель на родителя или null
53 		//-----------------
54 		um	 	link;			// Указатель на следующий или null
55 		um		allLink;		// Общий список методов
56 	}
57 	alias fMetod* um; // Ссылка на узел цепочки метода
58 	// ______________________________________________________________
59 	um findMethod(uc klass, string metod) { //-> Найти или добавить метод
60 		if(klass is null) return null;
61 		um nod = klass.metod;	// Начало цепочки
62 m1:		if(nod is null) {		// Цепочка пуста, вставка 1-го элемента
63 			nod = new fMetod; nod.name = metod;
64 			nod.link = klass.metod; nod.parent = klass;
65 			klass.metod = nod;
66 			nod.allLink = trapMetod; trapMetod = nod;
67 		} else {							// Цепочка не пуста, ищем ...
68 			while(nod !is null) {
69 				// writeln("compare: ", nameClass, " == ", nod.name);
70 				if(nod.name == metod) { return nod; }
71 				else { nod = nod.link; }
72 			}
73 		}
74 		if(nod is null) goto m1;
75 		return nod;
76 	}
77 	// ______________________________________________________________
78 	string[] getEqMet1(string w) { //-> Выдать массив похожих слов из методов
79 		string[] rez; size_t dlw, dln;
80 		if(w.length == 0) return rez;
81 		um nod = trapMetod;
82 		while(nod !is null) {
83 			dlw = w.length; dln = nod.name.length;
84 			if(dln >= dlw) { if(nod.name[0 .. dlw] == w) rez ~= nod.name; }
85 			nod = nod.allLink;
86 		}
87 		return rez;
88 	}
89 	// ______________________________________________________________
90 	void printMet() { //-> Распечатать список всех методов
91 		um nod = trapMetod;
92 		while(nod !is null) {
93 			writeln("[", nod.name, "] --> ", nod.rawStr);
94 			nod = nod.allLink;
95 		}
96 	}
97 	// ______________________________________________________________
98 	void printUc() { //-> Распечатать список всех классов
99 		uc nod = trapClass;
100 		while(nod !is null) {
101 			writeln(nod, " --> [", nod.name, "][", (nod.parent is null) ? "" : nod.parent.name, "] - ", nod.rawStr);
102 			um nodm = nod.metod;
103 			while(nodm !is null) {
104 				writeln("\t", nodm.name, " --> ", nodm.rawStr);
105 				nodm = nodm.link;
106 			}
107 			nod = nod.link;
108 		}
109 //		nod = findClass("QFrame");
110 // 		writeln("QFrame.Parent = ", nod.parent.name);
111 // 		writeln(nod.rawStr);
112 	}
113 	// ______________________________________________________________
114 	uc findClassOnly(string nameClass) { //-> Найти класс
115 		uc nod = trapClass;
116 		while(nod !is null) {
117 			if(nod.name == nameClass) { return nod; }
118 			else { nod = nod.link; }
119 		}
120 		return nod;
121 	}
122 	// ______________________________________________________________
123 	uc findClass(string nameClass) { //-> Найти или добавить класс
124 		uc nod = trapClass;
125 m1:		if(nod is null) {
126 			nod = new fClass;  nod.name = nameClass;
127 			nod.link = trapClass; trapClass = nod;
128 			// writeln("add: ", nameClass);
129 			return nod;
130 		} else {
131 			while(nod !is null) {
132 				// writeln("compare: ", nameClass, " == ", nod.name);
133 				if(nod.name == nameClass) { return nod; }
134 				else { nod = nod.link; }
135 			}
136 		}
137 		if(nod is null) goto m1;
138 		return nod;
139 	}
140 	// ______________________________________________________________
141 	// Получает на вход Класс:Родитель и ИсходнаяСтрокаКласса и сохраняет в цепочке классов
142 	uc insertClassParent(s2 cp, string rewStr) { //-> Вставить в цепочку классов Класс:Родитель+ИсхСтрока
143 		// 1 - Разобраться с Parent
144 		uc uparent, uclass;
145 		if(cp.p != "") uparent = findClass(cp.p);
146 		if(cp.c == "") return null;
147 		uclass = findClass(cp.c);
148 		uclass.name = cp.c; uclass.rawStr = rewStr; uclass.parent = uparent;
149 		lastClass = uclass;
150 		return uclass;
151 	}
152 	// ______________________________________________________________
153 	private un[256] harrow; 	//-> гребенка, для 256 списков слов
154 	dchar[dchar] transTable1;
155 	un[]	masAllWords;			// Список указателей на все слова
156 	uc		trapClass;				// Базовый якорь для цепочки Классов
157 	um		trapMetod;				// Базовый якорь для цепочки всех Методов
158 	uc		lastClass;				// Активный в данный момент класс
159 	// ______________________________________________________________
160 	ubyte getC0(string s) { //-> Выдать индекс в гребенке
161 		import std.utf: stride;
162 		if(s.length == 0) return 0;
163 		char[] w1251 = fromUtf8to1251(cast(char[])s[0..stride(s, 0)]);
164 		return w1251[0];
165 	}
166 	// ______________________________________________________________
167 	void addWord(string w) { //-> Добавить слово в список, если его нет
168 		if(w.length == 0) return;
169 		ubyte c0;
170 		if(!isWordMono(w)) {
171 			c0 = getC0(w);	// Первая буква слова, как индекс цепочки в harrow
172 			// Создадим узел цепочки (списка)
173 			un nod = new fNode;  nod.str = w;
174 			masAllWords ~= nod;		// Запомним это слово в полном списке слов
175 			nod.link = harrow[c0];	// Вставим новый узел в цепочку
176 			harrow[getC0(w)] = nod;	// Подвесим обновленную цепочку
177 /*
178 			// Надо идти по цепочке и удалять все производные слова
179 			int dlw = w.length, dln;
180 			un ukaz  = nod, ukaz0 = ukaz;
181 			while(!(ukaz is null)) {
182 				dln = ukaz.str.length;
183 				if(dln < dlw) {
184 					// Найденное слово короче вставленного слова
185 					if(w[0 .. dln] == ukaz.str) {
186 						// Удаляем этот элемент
187 						ukaz0.link = ukaz.link; delete ukaz;
188 						if( !(ukaz0.link is null) ) { ukaz = ukaz0.link; }
189 						else { break; }
190 					}
191 				}
192 				ukaz0 = ukaz; ukaz = ukaz.link;
193 			}
194 
195  */
196 
197 		}
198 	}
199 	// ______________________________________________________________
200 	bool isWordMono(string w) { //-> Есть целое слово в списке?
201 		size_t dlw, dln;
202 		bool rez; ubyte ind = getC0(w); un ukaz = harrow[ind];
203 		dlw = w.length;
204 		while(!(ukaz is null)) {
205 			dln = ukaz.str.length;
206 			if(dln == dlw) {
207 				if(ukaz.str == w) {
208 					rez = true; break;
209 				}
210 			}
211 			ukaz = ukaz.link;
212 		}
213 		return rez;
214 	}
215 	// ______________________________________________________________
216 	bool isWord(string w) { //-> Есть целое слово или производные в списке?
217 		size_t dlw, dln;
218 		bool rez; ubyte ind = getC0(w); un ukaz = harrow[ind];
219 		dlw = w.length;
220 		while(!(ukaz is null)) {
221 			dln = ukaz.str.length;
222 			if(dln >= dlw) {
223 				if(ukaz.str[0 .. dlw] == w) {
224 					rez = true; break;
225 				}
226 			}
227 			ukaz = ukaz.link;
228 		}
229 		return rez;
230 	}
231 	// ______________________________________________________________
232 	string[] getSubFromAll(string w) { //-> Выдать массив похожих слов из общего хранилища
233 		string[] rez;
234 		string sh = toLower(w);
235 		foreach(el; masAllWords) {
236 			string wrd = toLower(el.str);
237 			if(indexOf(wrd, sh) >= 0) rez ~= el.str;
238 		}
239 		return rez;
240 	}
241 	// ______________________________________________________________
242 	string[] getEq(string w) { //-> Выдать массив похожих слов из хранилища
243 		string[] rez; size_t dlw, dln;
244 		if(w.length == 0) return rez;
245 		ubyte ind = getC0(w); un ukaz = harrow[ind];
246 		while(!(ukaz is null)) {
247 			dlw = w.length; dln = ukaz.str.length;
248 			if(dln >= dlw) { if(ukaz.str[0 .. dlw] == w) rez ~= ukaz.str; }
249 			ukaz = ukaz.link;
250 		}
251 		return rez;
252 	}
253 	// ______________________________________________________________
254 	void addLine(string line) { //-> Добавить строку в хранилище
255 		// import std.stdio;
256 		immutable string clearLine = strip(line);
257 		if(clearLine == "") return;
258 		dchar[dchar] transTable = [
259 			'(':' ',
260 			')':' ',
261 			9:' ',
262 			'*':' ',
263 			';':' ',
264 			'.':' ',
265 			'[':' ',
266 			']':' ',
267 			',':' ',
268 			'"':' ',
269 			'!':' ',
270 			'/':' ',
271 			'=':' ',
272 			'\\':' ',
273 			':':' ',
274 			'@':' '
275 		];
276 		static import asc1251;
277 		string zish = translate(clearLine, transTable);
278 		auto msRaw = split(zish, ' ');
279 		string[] ms;
280 		foreach(el; msRaw) {	if(el == "") continue; ms ~= el;	}
281 		// Нужно удалить пустышки
282 	try {
283 		foreach(i, string el; ms) {
284 			if(el == "") continue;
285 			// string z = cast(string)strip(el);
286 			if(el.length > 2) 	addWord(el);
287 			// Всё добавлено в список поиска, можно проверить на нужные
288 			// мне строки
289 			if((el == "class") && (i == 0)) {
290 				insertClassParent(nameClass(zish), clearLine.dup);
291 				continue;
292 			}
293 			if(el == "->") {
294 				// writeln(lastClass.name, " --> [", nameMethod(zish), "] -- ", clearLine);
295 				um met = findMethod(lastClass, nameMethod(zish));
296 				if(met !is null) {
297 					met.rawStr = clearLine.dup;
298 				}
299 				continue;
300 			}
301 /*
302 			mar
303 			if(i == ms.length - 1) continue;
304 			uc fnod = findClassOnly(el);
305 			if(fnod is null) continue;
306 			writeln(fnod.name, " = ", ms[i+1], " --> ", clearLine.dup);
307 			if(el == "new") {
308 				if(i == 0) continue;
309 				if(i == ms.length - 1) continue;
310 
311 				writeln("var=[", ms[i-1], "]     class = [", ms[i+1],"] = ", ms);
312 				continue;
313 				// Нужна функция, которая выдаёт s2 = Переменная|Тип или пусто
314 				// - Взять предыдущее и следующие за new слово. Если нет, то null
315 			}
316 */
317 		}
318 	} catch {
319 		// writeln("catch: ", line);
320 		// writeln("catch: ", ms);
321 	}
322 
323 
324 		// if( indexOf(line, "//->") > 0 ) { writeln(zish);
325 		// }
326 		// Есть:
327 		// Class : Parent
328 		// Method : Функция(арг, ...) { //-> Описание функции
329 	}
330 	// ______________________________________________________________
331 	s2 nameClass(string s) { //-> Промежуточная. Выдать имя класса и родителя из строки
332 		s2 rez;
333 		auto ms = split(s, ' ');
334 		string[] arg;
335 		foreach(i, string el; ms) {
336 			if(el == "") continue;
337 			arg ~= el;
338 		}
339 		// arg --> очищенный массив строк
340 		if(arg[0] == "class") {
341 			if(arg.length == 1) return rez;
342 			if(arg.length == 2) { rez.c = arg[1]; rez.p = ""; return rez; }
343 			if(arg.length == 3) { rez.c = arg[1];
344 				if(arg[2] == "{") {
345 					rez.p = "";
346 				} else {
347 					rez.p = arg[2];
348 				}
349 				return rez;
350 			}
351 			if(arg[3] == "{") { 	// class Name: Parent {
352 				rez.c = arg[1]; rez.p = arg[2];
353 			} else {
354 				if(arg[2] == "{") {	// class Name {
355 					rez.c = arg[1]; rez.p = "";
356 				}
357 			}
358 		}
359 		return rez;
360 	}
361 	// ______________________________________________________________
362 	string nameMethod(string s) { //-> Промежуточная. Выдать имя метода из строки
363 		string rez;
364 		auto ms = split(s, ' ');
365 		string[] arg;
366 		foreach(i, string el; ms) {
367 			if(el == "") continue;
368 			arg ~= el;
369 		}
370 		rez = arg[1];
371 		return rez;
372 	}
373 	// ______________________________________________________________
374 	void addFile(string nameFile) { //-> Добавить файл в хранилище
375 		// writeln("parsing: ", nameFile);
376 		File fileSrc = File(nameFile, "r");
377 		int ks;
378 		try {
379 			foreach(line; fileSrc.byLine()) {
380 				try {
381 					// Проверка на BOM
382 					ks++;
383 					// if(ks++ == 0) if(line.length>2 && line[0]==239 && line[1]==187 && line[2]==191) line = line[3 .. $].dup;
384 					addLine(cast(string)line);
385 				} catch {
386 					writeln("Warning! Error parsing string: [", cast(string)strip(line), "]");
387 				}
388 			}
389 		} catch {
390 			writeln("Error read file: ", nameFile);
391 			readln();
392 		}
393 	}
394 }
395 
396 unittest {
397 	CFinder finder1 = new CFinder();
398 	bool b1;
399 
400 	// Проверка работы поиска слов
401 	finder1.addWord("Gena");
402 	b1 = finder1.isWordMono("Gena");	assert(b1 == true);
403 	b1 = finder1.isWordMono("gena");	assert(b1 == false);
404 	b1 = finder1.isWord("Gen");			assert(b1 == true);
405 	b1 = finder1.isWord("gen");			assert(b1 == false);
406 
407 	string[] m;
408 	m = finder1.getEq("Gen");	assert(m == ["Gena"]);
409 	m = finder1.getEq("gen");	assert(m == []);
410 	m = finder1.getSubFromAll("Gen");	assert(m == ["Gena"]);
411 	m = finder1.getSubFromAll("gen");	assert(m == ["Gena"]);
412 	m = finder1.getSubFromAll("len");	assert(m == []);
413 
414 	// Проверяем работу с Классами
415 	CFinder.uc adr;
416 	adr = finder1.findClass("CTest1");	assert(adr.name == "CTest1");
417 }