/*
    PIP - Platform Independent Primitives
    C++ code parser
    Copyright (C) 2014  Ivan Pelipenko peri4ko@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "picodeparser.h"



PIString PICodeParser::Macro::expand(const PIStringList & arg_vals, bool * ok) const {
	if (args.size() != arg_vals.size()) {
		piCout << ("Error: in expansion of macro \"" + name + "(" + args.join(", ") + ")\": expect")
				<< args.size() << "arguments but takes" << arg_vals.size() << "!";
		if (ok != 0) *ok = false;
		return PIString();
	}
	PIString ret = value;
	for (int i = 0; i < args.size_s(); ++i) {
		const PIString & an(args[i]), av(arg_vals[i]);
		int ind(-1);
		while ((ind = ret.find(an, ind + 1)) >= 0) {
			PIChar ppc(0), pc(0), nc(0);
			if (ind > 1) ppc = ret[ind - 2];
			if (ind > 0) pc = ret[ind - 1];
			if (ind + an.size_s() < ret.size_s()) nc = ret[ind + an.size_s()];
			if (ppc != '#' && pc == '#' && !_isCChar(nc)) { // to chars
				ind--;
				ret.replace(ind, an.size_s() + 1, "\"" + av + "\"");
				ind -= an.size_s() - av.size_s() - 1;
				continue;
			}
			if (_isCChar(pc) || _isCChar(nc)) continue;
			ret.replace(ind, an.size_s(), av);
			ind -= an.size_s() - av.size_s();
		}
	}
	ret.replaceAll("##", "");
	if (ok != 0) *ok = true;
	return ret;
}



PICodeParser::PICodeParser() {
	macros_iter = 32;
	clear();
	includes << "";
}


void PICodeParser::parseFile(const PIString & file) {
	clear();
	parseFileInternal(file);
	/*piCout << "\n\nDefines:";
	piForeachC (Define & m, defines)
		piCout << "define" << m.first << m.second;
	piCout << "\n\nMacros:";
	piForeachC (Macro & m, macros)
		piCout << "Macro:" << m.name << m.args << m.value;
	piCout << "\n\nClasses:";
	piForeachC (Entity * c, entities)
		piCout << "class" << c->name << c->parents;
	piCout << "\n\nEnums:";
	piForeachC (Enum & c, enums)
		piCout << "enum" << c.name << c.members;
	piCout << "\n\nTypedefs:";
	piForeachC (Typedef & c, typedefs)
		piCout << "typedef" << c;*/
}


void PICodeParser::parseFiles(const PIStringList & files) {
	clear();
	piForeachC (PIString & f, files)
		parseFileInternal(f);
	/*piCout << "\n\nDefines:";
	piForeachC (Define & m, defines)
		piCout << "define" << m.first << m.second;
	piCout << "\n\nMacros:";
	piForeachC (Macro & m, macros)
		piCout << "Macro:" << m.name << m.args << m.value;
	piCout << "\n\nClasses:";
	piForeachC (Entity * c, entities)
		piCout << "class" << c->name << c->parents;
	piCout << "\n\nEnums:";
	piForeachC (Enum & c, enums)
		piCout << "enum" << c.name << c.members;
	piCout << "\n\nTypedefs:";
	piForeachC (Typedef & c, typedefs)
		piCout << "typedef" << c;*/
}


bool PICodeParser::isEnum(const PIString & name) {
	piForeachC (Enum & e, enums)
		if (e.name == name)
			return true;
	return false;
}


bool PICodeParser::parseFileInternal(const PIString & file) {
	if (proc_files[file]) return true;
	cur_file = file;
	PIFile f(file, PIIODevice::ReadOnly);
	int ii = 0;
	while (!f.isOpened() && ii < (includes.size_s() - 1)) {
		f.setPath(includes[++ii] + "/" + file);
		//piCout << "try" << f.path();
		f.open(PIIODevice::ReadOnly);
	}
	if (!f.isOpened()) {
		//piCout << ("Error: can`t open file \"" + file + "\"!");
		return false;
	}
	//piCout << "add" << file;
	proc_files << f.path();
	PIString fc = f.readAll();
	piCout << "parsing" << f.path() << "...";
	bool is_main = isMainFile(fc);
	if (is_main) main_file = f.path();
	bool ret = parseFileContent(fc, is_main);
	piCout << "parsing" << f.path() << "done";
	return ret;
}


void PICodeParser::clear() {
	piForeach (Entity * i, entities) delete i;
	defines.clear();
	macros.clear();
	enums.clear();
	typedefs.clear();
	entities.clear();
	proc_files.clear();
	cur_namespace.clear();
	main_file.clear();
	evaluator.clearCustomVariables();
	defines << Define("PICODE", "") << custom_defines;
}


bool PICodeParser::parseFileContent(PIString & fc, bool main) {
	bool mlc = false, cc = false;
	int mls = 0, ole = -1, /*ccs = 0,*/ end = 0;
	char c = 0, pc = 0;
	PIString pfc, line, ccmn, tmp;
	PIMap<PIString, PIString> cchars;
	
	/// Remove comments, join multiline "*" and replace "*" to $n (cchars)
	fc.replaceAll("\r\n", "\n");
	fc.replaceAll("\r", "\n");
	for (int i = 0; i < fc.size_s() - 1; ++i) {
		if (i > 0) pc = c;
		c = fc[i].toAscii();
		if (c == '"' && !mlc && pc != '\'') {
			if (i > 0) if (fc[i - 1] == '\\') continue;
			cc = !cc;
			/*if (cc) ccs = i;
			if (!cc) {
				ccmn = "$" + PIString::fromNumber(cchars.size());
				cchars[ccmn] = fc.mid(ccs, i - ccs + 1);
				fc.replace(ccs, i - ccs + 1, ccmn);
				i = ccs - 1 + ccmn.size_s();
			}*/
			continue;
		}
		if (i > 0)
			if (c == '\\' && fc[i - 1].toAscii() != '\\') {
				fc.cutMid(i, 2);
				--i;
				continue;
			}
		if (cc) continue;
		if (fc.mid(i, 2) == "/*") {mlc = true; mls = i; ++i; continue;}
		if (fc.mid(i, 2) == "*/" && mlc) {mlc = false; fc.cutMid(mls, i - mls + 2); i = mls - 1; continue;}
		if (fc.mid(i, 2) == "//" && !mlc) {ole = fc.find('\n', i); fc.cutMid(i, ole < 0 ? -1 : ole - i); --i; continue;}
	}
	//piCout << fc;
	pfc = procMacros(fc);
	
	if (main) return true;
	
	bool replaced = true;
	int replaced_cnt = 0;
	while (replaced) {
		//piCout << "MACRO iter" << replaced_cnt;
		if (replaced_cnt >= macros_iter) {
			piCout << "Error: recursive macros detected!";
			break;//return false;
		}
		replaced_cnt++;
		replaced = false;
		piForeachC (Define & d, defines) {
			int ind(-1);
			while ((ind = pfc.find(d.first, ind + 1)) >= 0) {
				PIChar pc(0), nc(0);
				if (ind > 0) pc = pfc[ind - 1];
				if (ind + d.first.size_s() < pfc.size_s()) nc = pfc[ind + d.first.size_s()];
				if (_isCChar(pc) || _isCChar(nc) || nc.isDigit()) continue;
				pfc.replace(ind, d.first.size_s(), d.second);
				ind -= d.first.size_s() - d.second.size_s();
				replaced = true;
			}
		}
		piForeachC (Macro & m, macros) {
			int ind(-1);
			while ((ind = pfc.find(m.name, ind + 1)) >= 0) {
				PIChar pc(0), nc(0);
				if (ind > 0) pc = pfc[ind - 1];
				if (ind + m.name.size_s() < pfc.size_s()) nc = pfc[ind + m.name.size_s()];
				if (_isCChar(pc) || _isCChar(nc) || nc.isDigit()) continue;
				PIString ret, range; bool ok(false);
				range = pfc.mid(ind + m.name.size_s()).takeRange("(", ")");
				ret = m.expand(range.split(",").trim(), &ok);
				if (!ok) return false;
				int rlen = pfc.find(range, ind + m.name.size_s()) + range.size_s() + 1 - ind;
				pfc.replace(ind, rlen, ret);
				ind -= rlen - ret.size_s();
				replaced = true;
			}
		}
	}
	
	//piCout << NewLine << "file" << cur_file << pfc;
	for (int i = 0; i < pfc.size_s() - 5; ++i) {
		if (pfc.mid(i, 8) == "template") {
			pfc.cutLeft(i + 8);
			pfc.takeRange("<", ">");
			bool def = !isDeclaration(pfc, 0, &end);
			pfc.cutLeft(end);
			if (def) pfc.takeRange("{", "}");
			else pfc.takeSymbol();
			i = 0;
			continue;
		}
		if (pfc.mid(i, 5) == "class" || pfc.mid(i, 6) == "struct") {
			int dind = pfc.find("{", i), find = pfc.find(";", i);
			if (dind < 0 && find < 0) {pfc.cutLeft(i + 6); i = 0; continue;}
			if (dind < 0 || find < dind) {pfc.cutLeft(i + 6); i = 0; continue;}
			ccmn = pfc.mid(i, dind - i) + "{\n" + pfc.mid(dind).takeRange('{', '}') + "\n}\n";
			pfc.remove(i, ccmn.size());
			parseClass(ccmn);
			i = 0;
			continue;
		}
		if (pfc.mid(i, 4) == "enum") {
			pfc.cutLeft(i + 4);
			tmp = pfc.takeCWord();
			parseEnum(cur_namespace + tmp, pfc.takeRange("{", "}"));
			pfc.takeSymbol();
			i = 0;
			continue;
		}
		if (pfc.mid(i, 7) == "typedef") {
			pfc.cutLeft(i + 7);
			typedefs << parseTypedef(pfc.takeLeft(pfc.find(";")));
			if (typedefs.back().first.isEmpty()) typedefs.pop_back();
			pfc.takeSymbol();
			i = 0;
			continue;
		}
	}
	
	return true;
}


PICodeParser::Entity * PICodeParser::parseClassDeclaration(const PIString & fc) {
	PIString cd = fc.trimmed().removeAll('\n').replaceAll("\t", " ").replaceAll("  ", " "), pn;
	//piCout << "found class <****\n" << cd << "\n****>";
	int ind = cd.find(":");
	PIVector<Entity * > parents;
	if (ind > 0) {
		PIStringList pl = cd.takeMid(ind + 1).trim().split(",");
		cd.cutRight(1);
		Entity * pe = 0;
		piForeachC (PIString & p, pl) {
			if (p.contains(" ")) pn = p.mid(p.find(" ") + 1);
			else pn = p;
			pe = findEntityByName(pn);
			if (pe == 0) ;//{piCout << "Error: can`t find" << pn;}
			else parents << pe;
		}
	}
	bool is_class = cd.left(5) == "class";
	cur_def_vis = (is_class ? Private : Public);
	PIString cn = cd.mid(6).trim();
	if (cn.isEmpty()) return 0;
	Entity * e = new Entity();
	e->name = cur_namespace + cn;
	e->type = (is_class ? "class" : "struct");
	e->parents = parents;
	e->file = cur_file;
	entities << e;
	return e;
}


PIString PICodeParser::parseClass(PIString & fc) {
	Visibility prev_vis = cur_def_vis;
	int dind = fc.find("{"), find = fc.find(";"), end = 0;
	if (dind < 0 && find < 0) return PIString();
	if (dind < 0 || find < dind) return fc.left(find);
	Entity * ce = parseClassDeclaration(fc.takeLeft(dind));
	fc.trim().cutLeft(1).cutRight(1).trim();
	//piCout << "found class <****\n" << fc << "\n****>";
	if (!ce) return PIString();
	int ps = -1;
	bool def = false;
	PIString prev_namespace = cur_namespace, stmp;
	cur_namespace = ce->name + "::";
	//piCout << "parse class" << ce->name << "namespace" << cur_namespace;
	//piCout << "\nparse class" << ce->name << "namespace" << cur_namespace;
	while (!fc.isEmpty()) {
		PIString cw = fc.takeCWord(), tmp;
		//piCout << "\ntaked word" << cw;
		if (cw == "public") {cur_def_vis = Public; fc.cutLeft(1); continue;}
		if (cw == "protected") {cur_def_vis = Protected; fc.cutLeft(1); continue;}
		if (cw == "private") {cur_def_vis = Private; fc.cutLeft(1); continue;}
		if (cw == "class") {if (isDeclaration(fc, 0, &end)) {fc.cutLeft(end); fc.takeSymbol(); continue;} tmp = fc.takeLeft(fc.find("{")); stmp = fc.takeRange("{", "}"); fc.takeSymbol(); stmp = "class " + tmp + "{" + stmp + "}"; parseClass(stmp); continue;}
		if (cw == "struct") {if (isDeclaration(fc, 0, &end)) {fc.cutLeft(end); fc.takeSymbol(); continue;} tmp = fc.takeLeft(fc.find("{")); stmp = fc.takeRange("{", "}"); fc.takeSymbol(); stmp = "struct " + tmp + "{" + stmp + "}"; parseClass(stmp); continue;}
		if (cw == "enum") {tmp = fc.takeCWord(); parseEnum(cur_namespace + tmp, fc.takeRange("{", "}")); fc.takeSymbol(); continue;}
		if (cw == "friend") {fc.cutLeft(fc.find(";") + 1); continue;}
		if (cw == "typedef") {ce->typedefs << parseTypedef(fc.takeLeft(fc.find(";"))); typedefs << ce->typedefs.back(); typedefs.back().first.insert(0, cur_namespace); if (ce->typedefs.back().first.isEmpty()) ce->typedefs.pop_back(); fc.takeSymbol(); continue;}
		if (cw == "template") {
			fc.takeRange("<", ">");
			def = !isDeclaration(fc, 0, &end);
			fc.cutLeft(end);
			if (def) fc.takeRange("{", "}");
			else fc.takeSymbol();
			continue;
		}
		def = !isDeclaration(fc, 0, &end);
		tmp = (cw + fc.takeLeft(end)).trim();
		if (!tmp.isEmpty())
			parseMember(ce, tmp);
		if (def) fc.takeRange("{", "}");
		else fc.takeSymbol();
		if (ps == fc.size_s()) {/*cur_namespace = prev_namespace;*/ fc.cutLeft(1);/*return false*/;}
		ps = fc.size_s();
	}
	cur_def_vis = prev_vis;
	cur_namespace = prev_namespace;
	return ce->name;
}


bool PICodeParser::parseEnum(const PIString & name, PIString fc) {
	//piCout << "enum" << name << fc;
	Enum e(name);
	PIStringList vl(fc.split(","));
	PIString vn;
	int cv = -1, ind = 0;
	piForeachC (PIString & v, vl) {
		vn = v; ind = v.find("=");
		if (ind > 0) {cv = v.right(v.size_s() - ind - 1).toInt(); vn = v.left(ind);}
		if (ind < 0) ++cv;
		e.members << Enumerator(vn.trim(), cv);
	}
	enums << e;
	return true;
}


PICodeParser::Typedef PICodeParser::parseTypedef(PIString fc) {
	//piCout << "parse typedef" << fc;
	Typedef td;
	fc.replaceAll("\t", " ");
	
	if (fc.contains("(")) {
		int start = fc.find("("), end = fc.find(")");
		td.first = fc.takeMid(start + 1, end - start - 1).trim();
		if (td.first.left(1) == "*") {td.first.cutLeft(1).trim(); fc.insert(start + 1, "*");}
		td.second = fc.trim();
	} else {
		td.first = fc.takeMid(fc.findLast(" ")).trim();
		td.second = fc.trim();
	}
	//piCout << "found typedef" << td;
	return td;
}


bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
	if (fc.trim().isEmpty()) return true;
	if (fc.find("operator") >= 0) return true;
	tmp_temp.clear();
	//piCout << "parse member" << fc;
	int ts = fc.find("<"), te = 0;
	PIString ctemp, crepl;
	while (ts >= 0) {
		ctemp = fc.mid(ts).takeRange("<", ">");
		if (ctemp.isEmpty()) {te = ts + 1; ts = fc.find("<", te); continue;}
		crepl = "$" + PIString::fromNumber(tmp_temp.size_s()).expandLeftTo(3, "0");
		fc.replace(ts, ctemp.size_s() + 2, crepl);
		tmp_temp[crepl] = "<" + ctemp + ">";
		ts = fc.find("<", te);
	}
	fc.replaceAll("\n", " ").replaceAll("\t", " ").replaceAll("  ", " ").replaceAll(", ", ",");
	PIStringList tl, al;
	Member me;
	//piCout << fc;
	if (fc.contains("(")) {
		fc.cutRight(fc.size_s() - fc.findLast(")") - 1);
		te = fc.find("(");
		//piCout << fc;
		for (ts = te - 1; ts >= 0; --ts)
			if (!_isCChar(fc[ts]) && !(fc[ts].isDigit())) break;
		//piCout << "takeMid" << ts + 1 << te - ts - 1;
		me.name = fc.takeMid(ts + 1, te - ts - 1);
		if (me.name == parent->name) return true;
		me.arguments_full = fc.takeMid(ts + 2).cutRight(1).split(",");
		me.type = fc.cutRight(1).trim();
		me.visibility = cur_def_vis;
		if (me.type.find("inline ") >= 0) {
			me.attributes |= Inline;
			me.type.removeAll("inline ");
		}
		if (me.type.find("static ") >= 0) {
			me.attributes |= Static;
			me.type.removeAll("static ");
		}
		if (me.type.find("virtual ") >= 0) {
			me.attributes |= Virtual;
			me.type.removeAll("virtual ");
		}
		normalizeEntityNamespace(me.type);
		int i = 0;
		piForeach (PIString & a, me.arguments_full)
			if ((i = a.find("=")) > 0)
				a.cutRight(a.size_s() - i).trim();
		me.arguments_type = me.arguments_full;
		piForeach (PIString & a, me.arguments_type) {
			crepl.clear();
			if (a.contains("["))
				crepl = a.takeMid(a.find("["), a.findLast("]") - a.find("[") + 1);
			for (ts = a.size_s() - 1; ts >= 0; --ts)
				if (!_isCChar(a[ts]) && !(a[ts].isDigit())) break;
			a.cutRight(a.size_s() - ts - 1);
			normalizeEntityNamespace(a);
			a += crepl;
			a.trim();
		}
		restoreTmpTemp(&me);
		//piCout << "func" << me.type << me.name << me.arguments_full << me.arguments_type;
		parent->functions << me;
	} else {
		tl = fc.split(",");
		bool vn = true;
		ctemp = tl.front();
		for (ts = ctemp.size_s() - 1; ts > 0; --ts) {
			if (vn) {if (!_isCChar(ctemp[ts]) && !ctemp[ts].isDigit() && ctemp[ts] != '[' && ctemp[ts] != ']') vn = false;}
			else {if (_isCChar(ctemp[ts]) || ctemp[ts].isDigit()) break;}
		}
		me.type = ctemp.takeLeft(ts + 1);
		me.visibility = cur_def_vis;
		restoreTmpTemp(&me);
		PIString type = "    " + me.type;
		if (type.find(" const ") >= 0) {
			me.attributes |= Const;
			type.replaceAll(" const ", " ");
		}
		if (type.find(" static ") >= 0) {
			me.attributes |= Static;
			type.replaceAll(" static ", " ");
		}
		if (type.find(" mutable ") >= 0) {
			me.attributes |= Mutable;
			type.replaceAll(" mutable ", " ");
		}
		if (type.find(" volatile ") >= 0) {
			me.attributes |= Volatile;
			type.replaceAll(" volatile ", " ");
		}
		type.trim();
		normalizeEntityNamespace(type);
		tl[0] = ctemp.trim();
		piForeachC (PIString & v, tl) {
			crepl.clear();
			me.name = v.trimmed();
			me.type = type;
			if (me.name.isEmpty()) continue;
			if (me.name.contains("["))
				crepl = me.name.takeMid(me.name.find("["), me.name.findLast("]") - me.name.find("[") + 1);
			while (!me.name.isEmpty()) {
				if (me.name.front() == "*" || me.name.front() == "&") {
					me.type += me.name.takeLeft(1);
					me.name.trim();
				} else break;
			}
			me.is_type_ptr = (me.type.right(1) == "]" || me.type.right(1) == "*");
			me.type += crepl;
			//piCout << "var" << me.type << me.name << me.is_const << me.is_static;
			parent->members << me;
		}
	}
	//piCout << "parse member" << fc;
	return true;
}


void PICodeParser::normalizeEntityNamespace(PIString & n) {
	PIString suff, pref;
	for (int i = n.size_s() - 1; i > 0; --i)
		if (_isCChar(n[i]) || n[i].isDigit()) {
			suff = n.right(n.size_s() - i - 1);
			n.cutRight(suff.size_s());
			break;
		}
	n.push_front(" ");
	if (n.find(" static ") >= 0) {n.replaceAll(" static ", ""); pref += "static ";}
	if (n.find(" const ") >= 0) {n.replaceAll(" const ", ""); pref += "const ";}
	if (n.find(" mutable ") >= 0) {n.replaceAll(" mutable ", ""); pref += "mutable ";}
	if (n.find(" volatile ") >= 0) {n.replaceAll(" volatile ", ""); pref += "volatile ";}
	n.trim();
	int f = 0;
	piForeachC (Entity * e, entities) {
		if (e->name == n) {
			n = (pref + n + suff).trim();
			return;
		}
		if ((f = e->name.find(n)) >= 0)
			if (e->name.mid(f - 1, 1) == ":")
				if (e->name.find(cur_namespace) >= 0) {
					n = pref + e->name + suff;
					return;
				}
	}
	piForeachC (Enum & e, enums)
		if ((f = e.name.find(n)) >= 0)
			if (e.name.mid(f - 1, 1) == ":")
				if (e.name.find(cur_namespace) >= 0) {
					//piCout << "change" << n << "to" << e.name + suff;
					n = pref + e.name + suff;
					return;
				}
	piForeachC (Typedef & e, typedefs)
		if ((f = e.first.find(n)) >= 0)
			if (e.first.mid(f - 1, 1) == ":")
				if (e.first.find(cur_namespace) >= 0) {
					//piCout << "change" << n << "to" << e.name + suff;
					n = pref + e.first + suff;
					return;
				}
	n = (pref + n + suff).trim();
}


void PICodeParser::restoreTmpTemp(Member * e) {
	int i = 0;
	piForeach (PIString & a, e->arguments_full) {
		while ((i = a.find("$")) >= 0)
			a.replace(i, 4, tmp_temp[a.mid(i, 4)]);
	}
	piForeach (PIString & a, e->arguments_type) {
		while ((i = a.find("$")) >= 0)
			a.replace(i, 4, tmp_temp[a.mid(i, 4)]);
	}
	while ((i = e->type.find("$")) >= 0)
		e->type.replace(i, 4, tmp_temp[e->type.mid(i, 4)]);
}


bool PICodeParser::macroCondition(const PIString & mif, PIString mifcond) {
	//piCout << "macroCondition" << mif << mifcond;
	if (mif == "ifdef") return isDefineExists(mifcond);
	if (mif == "ifndef") return !isDefineExists(mifcond);
	if (mif == "if" || mif == "elif") {
		mifcond.removeAll(" ").removeAll("\t");
		return procMacrosCond(mifcond) > 0.;
	}
	return false;
}


double PICodeParser::procMacrosCond(PIString fc) {
	bool neg = false, first = true, br = false;
	double ret = 0., brv = 0.;
	int oper = 0, ps = -1;
	char cc, nc;
	PIString ce;
	fc.removeAll("defined");
	//piCout << "procMacrosCond" << fc;
	while (!fc.isEmpty()) {
		cc = fc[0].toAscii();
		nc = (fc.size() > 1 ? fc[1].toAscii() : 0);
		if (cc == '!') {neg = true; fc.pop_front(); continue;}
		if (cc == '(') {br = true; brv = procMacrosCond(fc.takeRange('(', ')'));}
		if (cc == '&' && nc == '&') {fc.remove(0, 2); oper = 1; continue;}
		if (cc == '|' && nc == '|') {fc.remove(0, 2); oper = 2; continue;}
		if (!br) {
			ce = fc.takeCWord();
			if (ce.isEmpty()) ce = fc.takeNumber();
		}
		if (first) {
			first = false;
			ret = br ? brv : defineValue(ce);
			if (neg) ret = -ret;
		} else {
			//piCout << "oper" << oper << "with" << ce;
			if (!br) brv = defineValue(ce);
			switch (oper) {
			case 1: ret = ret && (neg ? -brv : brv); break;
			case 2: ret = ret || (neg ? -brv : brv); break;
			}
		}
		if (ps == fc.size_s()) fc.cutLeft(1);
		ps = fc.size_s();
		br = neg = false;
	}
	//piCout << "return" << ret;
	return ret;
}


bool PICodeParser::isDefineExists(const PIString & dn) {
	piForeachC (Define & d, defines) {
		if (d.first == dn)
			return true;
	}
	return false;
}


double PICodeParser::defineValue(const PIString & dn) {
	piForeachC (Define & d, defines) {
		if (d.first == dn)
			return d.second.isEmpty() ? 1. : d.second.toDouble();
	}
	return dn.toDouble();
}


PICodeParser::Entity * PICodeParser::findEntityByName(const PIString & en) {
	piForeach (Entity * e, entities)
		if (e->name == en)
			return e;
	return 0;
}


bool PICodeParser::isDeclaration(const PIString & fc, int start, int * end) {
	int dind = fc.find("{", start), find = fc.find(";", start);
	//piCout << "isDeclaration" << dind << find;
	if (dind < 0 && find < 0) {if (end) *end = -1; return true;}
	if (dind < 0 || find < dind) {if (end) *end = find; return true;}
	if (end) *end = dind;
	return false;
}


bool PICodeParser::isMainFile(const PIString & fc) {
	int si = 0;
	while (si >= 0) {
		int csi = fc.find(" main", si);
		if (csi < 0) csi = fc.find("\tmain", si);
		if (csi < 0) csi = fc.find("\nmain", si);
		if (csi < 0) return false;
		si = csi;
		int fi = fc.find("(", si + 5);
		if (fi < 0) return false;
		if (fi - si < 10) {
			PIString ms(fc.mid(si, fi - si + 1));
			ms.removeAll(" ").removeAll("\t").removeAll("\n");
			if (ms == "main(") return true;
		}
		si += 5;
	}
	return false;
}


PIString PICodeParser::procMacros(PIString fc) {
	if (fc.isEmpty()) return PIString();
	int ifcnt = 0;
	bool grab = false, skip = false, cond_ok = false;
	PIString pfc, nfc, line, mif, mifcond;
	//piCout << "procMacros\n<******" << fc << "\n******>";
	fc += "\n";
	while (!fc.isEmpty()) {
		line = fc.takeLine().trimmed();
		if (line.left(1) == "#") {
			mifcond = line.mid(1);
			mif = mifcond.takeCWord();
			//piCout << "mif mifcond" << mif << mifcond << ifcnt;
			if (skip || grab) {
				if (mif.left(2) == "if") ifcnt++;
				if (mif.left(5) == "endif") {
					if (ifcnt > 0) ifcnt--;
					else {
						//piCout << "main endif" << skip << grab;
						if (grab) pfc << procMacros(nfc);
						skip = grab = false;
						continue;
					}
				}
				if (mif.left(4) == "elif" && ifcnt == 0) {
					//piCout << "main elif" << skip << grab << cond_ok;
					if (cond_ok) {
						if (grab) {
							pfc << procMacros(nfc);
							skip = true; grab = false;
						}
						continue;
					}
					if (skip) {
						//piCout << "check elif" << skip << grab << cond_ok;
						if (!macroCondition(mif, mifcond.trimmed())) continue;
						//piCout << "check elif ok";
						skip = false; grab = cond_ok = true;
						continue;
					}
					continue;
				}
				if (mif.left(4) == "else" && ifcnt == 0) {
					//piCout << "main else" << skip << grab;
					if (grab) pfc << procMacros(nfc);
					if (skip && !cond_ok) {skip = false; grab = true;}
					else {skip = true; grab = false;}
					continue;
				}
				if (grab) nfc << line << "\n";
				continue;
			}
			if (mif.left(2) == "if") {
				//piCout << "main if";
				skip = grab = cond_ok = false;
				if (macroCondition(mif, mifcond.trimmed())) grab = cond_ok = true;
				else skip = true;
				ifcnt = 0;
				nfc.clear();
			} else {
				if (!parseDirective(line.cutLeft(1).trim()))
					;//return false; /// WARNING
			}
		} else {
			if (grab) nfc << line << "\n";
			else if (!skip) pfc << line << "\n";
		}
	}
	return pfc;
}


bool PICodeParser::parseDirective(PIString d) {
	if (d.isEmpty()) return true;
	PIString dname = d.takeCWord();
	//piCout << "parseDirective" << d;
	if (dname == "include") {
		d.replaceAll("<", "\"").replaceAll(">", "\"");
		PIString cf = cur_file;
		bool ret = parseFileInternal(d.takeRange("\"", "\""));
		cur_file = cf;
		return ret;
	}
	if (dname == "define") {
		PIString mname = d.takeCWord();
		if (d.left(1) == "(") { // macro
			PIStringList args = d.takeRange("(", ")").split(",").trim();
			macros << Macro(mname, d.trim(), args);
		} else { // define
			defines << Define(mname, d.trim());
			evaluator.setVariable(mname, complexd_1);
		}
		return true;
	}
	if (dname == "undef") {
		PIString mname = d.takeCWord();
		for (int i = 0; i < defines.size_s(); ++i)
			if (defines[i].first == mname) {defines.remove(i); --i;}
		return true;
	}
	return true;
}
