/*
    PIP - Platform Independent Primitives
    C++ code parser
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser 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 Lesser General Public License for more details.

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

#include "picodeparser.h"

#include "piliterals_string.h"


PIString PICodeParser::Macro::expand(PIString args_, bool * ok) const {
	PIStringList arg_vals;
	while (!args_.isEmpty()) {
		int ci = args_.find(','), bi = args_.find('(');
		if (ci < 0) {
			arg_vals << args_;
			break;
		}
		PIString ca;
		if (bi >= 0 && bi < ci) {
			ca = args_.left(args_.takeLeft(bi).toInt());
			ci -= ca.size_s();
			bi -= ca.size_s();
			ca += '(' + args_.takeRange('(', ')') + ')';
		} else {
			ca = args_.takeLeft(ci);
		}
		arg_vals << ca;
		args_.trim();
		args_.takeLeft(1);
		args_.trim();
	}
	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, pc, nc;
			if (ind > 1) ppc = ret[ind - 2];
			if (ind > 0) pc = ret[ind - 1];
			if (ind + an.size_s() < ret.size_s()) nc = ret.mid(ind + an.size_s(), 1)[0];
			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(PIStringAscii("##"), "");
	if (ok != 0) *ok = true;
	return ret;
}


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


void PICodeParser::parseFile(const PIString & file, bool follow_includes) {
	clear();
	parseFileInternal(file, follow_includes);
	/*piCout << "\n\n";
	for (const auto * c: entities) {
	    piCout << "";
	    piCout << c->type << c->name << c->parent_scope << c->parents << c->children << c->meta;
	    if (c->parent_scope)
	        piCout << "parent" << c->parent_scope->name;
	    piCout << "Functions:";
	    for (const auto & m: c->functions)
	        piCout << m.type << m.name << m.meta;
	    piCout << "Members:";
	    for (const auto & m: c->members)
	        piCout << m.type << m.name << m.meta;
	}
	piCout << "\n\nDefines:";
	for (const auto & m: defines)
	    piCout << PIStringAscii("define") << m.first << m.second;
	piCout << "\n\nMacros:";
	for (const auto & m: macros)
	    piCout << "Macro:" << m.name << m.args << m.value;
	piCout << "\n\nClasses:";
	piCout << "\n\nEnums:";
	for (const auto & c: enums) {
	    piCout << PIStringAscii("enum") << c.name << c.meta;
	    for (const auto & e: c.members)
	        piCout << "    " << e.name << '=' << e.value << e.meta;
	}
	piCout << "\n\nTypedefs:";
	for (const auto & c: typedefs)
	    piCout << PIStringAscii("typedef") << c;*/
}


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


void PICodeParser::parseFileContent(PIString fc) {
	parseFileContent(fc, false);
}


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


bool PICodeParser::parseFileInternal(const PIString & file, bool follow_includes) {
	if (proc_files[file]) return true;
	with_includes = follow_includes;
	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 = PIString::fromUTF8(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() {
	for (auto * 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();
	cur_def_vis       = Global;
	anon_num          = 0;
	PIStringList defs = PIStringAscii(PICODE_DEFINES).split(",");
	for (const auto & d: defs)
		defines << Define(d, "");
	defines << Define(PIStringAscii("PICODE"), "") << custom_defines;
	macros << Macro(PIStringAscii("PIOBJECT"), "", PIStringList() << "name")
		   << Macro(PIStringAscii("PIOBJECT_PARENT"), "", PIStringList() << "parent")
		   << Macro(PIStringAscii("PIOBJECT_SUBCLASS"),
	                "",
	                PIStringList() << "name"
	                               << "parent")
		   << Macro(PIStringAscii("PIIODEVICE"), "", PIStringList() << "name")
		   << Macro(PIStringAscii("NO_COPY_CLASS"), "", PIStringList() << "name") << Macro(PIStringAscii("PRIVATE_DECLARATION"))

		   << Macro(PIStringAscii("EVENT"), "void name();", PIStringList() << "name")
		   << Macro(PIStringAscii("EVENT0"), "void name();", PIStringList() << "name")
		   << Macro(PIStringAscii("EVENT1"),
	                "void name(a0 n0);",
	                PIStringList() << "name"
	                               << "a0"
	                               << "n0")
		   << Macro(PIStringAscii("EVENT2"),
	                "void name(a0 n0, a1 n1);",
	                PIStringList() << "name"
	                               << "a0"
	                               << "n0"
	                               << "a1"
	                               << "n1")
		   << Macro(PIStringAscii("EVENT3"),
	                "void name(a0 n0, a1 n1, a2 n2);",
	                PIStringList() << "name"
	                               << "a0"
	                               << "n0"
	                               << "a1"
	                               << "n1"
	                               << "a2"
	                               << "n2")
		   << Macro(PIStringAscii("EVENT4"),
	                "void name(a0 n0, a1 n1, a2 n2, a3 n3);",
	                PIStringList() << "name"
	                               << "a0"
	                               << "n0"
	                               << "a1"
	                               << "n1"
	                               << "a2"
	                               << "n2"
	                               << "a3"
	                               << "n3")

		   << Macro(PIStringAscii("EVENT_HANDLER"),
	                "ret name()",
	                PIStringList() << "ret"
	                               << "name")
		   << Macro(PIStringAscii("EVENT_HANDLER0"),
	                "ret name()",
	                PIStringList() << "ret"
	                               << "name")
		   << Macro(PIStringAscii("EVENT_HANDLER1"),
	                "ret name(a0 n0)",
	                PIStringList() << "ret"
	                               << "name"
	                               << "a0"
	                               << "n0")
		   << Macro(PIStringAscii("EVENT_HANDLER2"),
	                "ret name(a0 n0, a1 n1)",
	                PIStringList() << "ret"
	                               << "name"
	                               << "a0"
	                               << "n0"
	                               << "a1"
	                               << "n1")
		   << Macro(PIStringAscii("EVENT_HANDLER3"),
	                "ret name(a0 n0, a1 n1, a2 n2)",
	                PIStringList() << "ret"
	                               << "name"
	                               << "a0"
	                               << "n0"
	                               << "a1"
	                               << "n1"
	                               << "a2"
	                               << "n2")
		   << Macro(PIStringAscii("EVENT_HANDLER4"),
	                "ret name(a0 n0, a1 n1, a2 n2, a3 n3)",
	                PIStringList() << "ret"
	                               << "name"
	                               << "a0"
	                               << "n0"
	                               << "a1"
	                               << "n1"
	                               << "a2"
	                               << "n2"
	                               << "a3"
	                               << "n3")

		   << Macro(PIStringAscii("EVENT_VHANDLER"),
	                "virtual ret name()",
	                PIStringList() << "ret"
	                               << "name")
		   << Macro(PIStringAscii("EVENT_VHANDLER0"),
	                "virtual ret name()",
	                PIStringList() << "ret"
	                               << "name")
		   << Macro(PIStringAscii("EVENT_VHANDLER1"),
	                "virtual ret name(a0 n0)",
	                PIStringList() << "ret"
	                               << "name"
	                               << "a0"
	                               << "n0")
		   << Macro(PIStringAscii("EVENT_VHANDLER2"),
	                "virtual ret name(a0 n0, a1 n1)",
	                PIStringList() << "ret"
	                               << "name"
	                               << "a0"
	                               << "n0"
	                               << "a1"
	                               << "n1")
		   << Macro(PIStringAscii("EVENT_VHANDLER3"),
	                "virtual ret name(a0 n0, a1 n1, a2 n2)",
	                PIStringList() << "ret"
	                               << "name"
	                               << "a0"
	                               << "n0"
	                               << "a1"
	                               << "n1"
	                               << "a2"
	                               << "n2")
		   << Macro(PIStringAscii("EVENT_VHANDLER4"),
	                "virtual ret name(a0 n0, a1 n1, a2 n2, a3 n3)",
	                PIStringList() << "ret"
	                               << "name"
	                               << "a0"
	                               << "n0"
	                               << "a1"
	                               << "n1"
	                               << "a2"
	                               << "n2"
	                               << "a3"
	                               << "n3");
}


bool PICodeParser::parseFileContent(PIString & fc, bool main) {
	static const PIString s_ns        = PIStringAscii("::");
	static const PIString s_bo        = PIStringAscii("{\n");
	static const PIString s_bc        = PIStringAscii("\n}\n");
	static const PIString s_class     = PIStringAscii("class");
	static const PIString s_struct    = PIStringAscii("struct");
	static const PIString s_union     = PIStringAscii("union");
	static const PIString s_enum      = PIStringAscii("enum");
	static const PIString s_typedef   = PIStringAscii("typedef");
	static const PIString s_namespace = PIStringAscii("namespace");
	static const PIString s_template  = PIStringAscii("template");
	static const PIString s_L         = PIStringAscii("$L");

	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 (fc[i].unicode16Code() >= 255) continue;
		if (i > 0) pc = c;
		c = fc[i].toAscii();
		if (c == '"' && !mlc && pc != '\'') {
			if (i > 0)
				if (fc[i - 1] == '\\') continue;
			cc = !cc;
			continue;
		}
		if (i > 0)
			if (c == '\\' && fc[i - 1].toAscii() != '\\') {
				fc.cutMid(i, 1);
				fc.replace(i, 1, s_L);
				++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;
		}
	}
	pfc = procMacros(fc);
	pfc.removeAll(s_L);

	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;
		for (const auto & d: defines) {
			int ind(-1);
			while ((ind = pfc.find(d.first, ind + 1)) >= 0) {
				PIChar pc, nc;
				if (ind > 0) pc = pfc[ind - 1];
				if (ind + d.first.size_s() < pfc.size_s()) nc = pfc.mid(ind + d.first.size_s(), 1)[0];
				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;
			}
		}
		for (const auto & m: macros) {
			int ind(-1);
			while ((ind = pfc.find(m.name, ind + 1)) >= 0) {
				PIChar pc, nc;
				if (ind > 0) pc = pfc[ind - 1];
				if (ind + m.name.size_s() < pfc.size_s()) nc = pfc.mid(ind + m.name.size_s(), 1)[0];
				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, &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;
			}
		}
	}

	replaceMeta(pfc);

	// piCout << PICoutManipulators::NewLine << "file" << cur_file << pfc;
	int pl      = -1;
	cur_def_vis = Global;
	while (!pfc.isEmpty()) {
		pfc.trim();
		int nl = pfc.size_s();
		if (pl == nl) break;
		pl = nl;
		if (pfc.left(9) == s_namespace) {
			pfc.cutLeft(9);
			PIString prev_namespace = cur_namespace, ccmn;
			cur_namespace += pfc.takeCWord() + s_ns;
			ccmn = pfc.takeRange('{', '}');
			// piCout << "namespace" << cur_namespace;
			parseClass(nullptr, ccmn, true);
			cur_namespace = prev_namespace;
			continue;
		}
		if (pfc.left(8) == s_template) {
			pfc.cutLeft(8);
			pfc.takeRange('<', '>');
			bool def = !isDeclaration(pfc, 0, &end);
			pfc.cutLeft(end);
			if (def)
				pfc.takeRange('{', '}');
			else
				pfc.takeSymbol();
			continue;
		}
		if (pfc.left(5) == s_class || pfc.left(6) == s_struct || pfc.left(5) == s_union) {
			int dind = pfc.find('{', 0), find = pfc.find(';', 0);
			// piCout << pfc.left(6) << dind << find;
			if (dind < 0 && find < 0) {
				pfc.cutLeft(6);
				continue;
			}
			if (dind < 0 || find < dind) {
				// piCout << "skip FC" << (find + 1) << pfc.left(find + 1);
				pfc.cutLeft(find + 1);
				// pfc.cutLeft(6);
				continue;
			}
			ccmn = pfc.left(dind) + s_bo + pfc.mid(dind).takeRange('{', '}') + s_bc;
			pfc.remove(0, ccmn.size() - 2);
			parseClass(nullptr, ccmn, false);
			continue;
		}
		if (pfc.left(4) == s_enum) {
			pfc.cutLeft(4);
			tmp = pfc.takeCWord();
			pfc.trim();
			MetaMap meta = maybeMeta(pfc);
			if (tmp == s_class || tmp == s_struct) {
				tmp = pfc.takeCWord();
				pfc.trim();
				MetaMap smeta = maybeMeta(pfc);
				meta << smeta;
			}
			// piCout << "pfc E" << cur_namespace << "," << tmp;
			parseEnum(nullptr, cur_namespace + tmp, pfc.takeRange('{', '}'), meta);
			pfc.takeSymbol();
			continue;
		}
		if (pfc.left(7) == s_typedef) {
			pfc.cutLeft(7);
			PIString typedef_type = pfc.takeCWord();
			if (typedef_type == s_class || typedef_type == s_struct || typedef_type == s_union) {
				int dind = pfc.find('{', 0), find = pfc.find(';', 0);
				if (dind < 0 && find < 0) {
					pfc.cutLeft(6);
					continue;
				}
				if (dind < 0 || find < dind) {
					pfc.cutLeft(find + 1);
					continue;
				}
				PIString cname = pfc.left(dind);
				ccmn           = cname + s_bo + pfc.mid(dind).takeRange('{', '}') + s_bc;
				pfc.remove(0, ccmn.size() - 3);
				if (cname.trimmed().isEmpty()) {
					cname = pfc.takeCWord();
					ccmn.prepend(cname);
				}
				ccmn.prepend(typedef_type + " "_a);
				parseClass(nullptr, ccmn, false);
			} else if (typedef_type == s_enum) {
				tmp = pfc.takeCWord();
				pfc.trim();
				MetaMap meta = maybeMeta(pfc);
				if (tmp == s_class || tmp == s_struct) {
					tmp = pfc.takeCWord();
					pfc.trim();
					MetaMap smeta = maybeMeta(pfc);
					meta << smeta;
				}
				ccmn = pfc.takeRange('{', '}');
				if (tmp.isEmpty()) {
					tmp = pfc.takeCWord();
				}
				// piCout << "pfc E" << cur_namespace << "," << tmp;
				parseEnum(nullptr, cur_namespace + tmp, ccmn, meta);
			} else {
				pfc.prepend(typedef_type);
			}
			PIString last = pfc.takeLeft(pfc.find(';')).trim();
			if (last.isNotEmpty()) {
				typedefs << parseTypedef(last);
				if (typedefs.back().first.isEmpty())
					typedefs.pop_back();
				else
					root_.typedefs << typedefs.back();
			}
			pfc.takeSymbol();
			continue;
		}
		int sci = pfc.find(';', 0), obi = pfc.find('{', 0);
		if (sci < 0 && obi < 0) {
			pfc.takeLeft(1);
			continue;
		}
		PIString str;
		if (sci < obi) {
			str = pfc.takeLeft(sci + 1);
		} else {
			str = pfc.takeLeft(obi);
			pfc.cutLeft(pfc.takeRange('{', '}').toInt());
		}
		parseMember(&root_, str);
	}

	return true;
}


PICodeParser::Entity * PICodeParser::parseClassDeclaration(const PIString & fc) {
	static const PIString s_ss     = PIStringAscii("  ");
	static const PIString s_M      = PIStringAscii("$M");
	static const PIString s_class  = PIStringAscii("class");
	static const PIString s_public = PIStringAscii("public");
	PIString cd                    = fc.trimmed().removeAll('\n').replaceAll('\t', ' ').replaceAll(s_ss, ' '), pn;
	MetaMap meta;
	int ind = cd.find(s_M);
	if (ind >= 0) {
		meta = tmp_meta.value(cd.takeMid(ind, 5));
		cd.replaceAll(s_ss, ' ');
	}
	PIString typename_ = cd.left(6).trim();
	bool is_class      = typename_ == s_class;
	ind                = cd.find(':');
	// piCout << "found class <****\n" << cd << "\n****>";
	PIVector<Entity *> parents;
	if (ind > 0) {
		PIStringList pl = cd.takeMid(ind + 1).trim().split(',');
		cd.cutRight(1);
		Entity * pe = 0;
		for (const auto & p: pl) {
			PIString access;
			if (p.contains(' ')) {
				access = p.left(p.find(' ')).trim();
				pn     = p.mid(p.find(' ') + 1);
			} else {
				pn = p;
			}
			bool is_public = access.isEmpty() ? !is_class : access == s_public;
			if (!is_public) continue;
			pe = findEntityByName(pn);
			if (pe == 0)
				; //{piCout << "Error: can`t find" << pn;}
			else
				parents << pe;
		}
	}
	Visibility vis    = cur_def_vis;
	cur_def_vis       = (is_class ? Private : Public);
	PIString cn       = cd.mid(6).trim();
	bool is_anonymous = cn.isEmpty();
	if (cn.isEmpty()) cn = PIStringAscii("<unnamed_") + PIString::fromNumber(anon_num++) + '>';
	// piCout << "found " << typename_ << cn;
	Entity * e      = new Entity();
	e->meta         = meta;
	e->name         = cur_namespace + cn;
	e->type         = typename_;
	e->is_anonymous = is_anonymous;
	e->parents      = parents;
	e->visibility   = vis;
	e->file         = cur_file;
	entities << e;
	return e;
}


PICodeParser::Entity * PICodeParser::parseClass(Entity * parent, PIString & fc, bool is_namespace) {
	static const PIString s_ns        = PIStringAscii("::");
	static const PIString s_public    = PIStringAscii("public");
	static const PIString s_protected = PIStringAscii("protected");
	static const PIString s_private   = PIStringAscii("private");
	static const PIString s_class     = PIStringAscii("class");
	static const PIString s_struct    = PIStringAscii("struct");
	static const PIString s_union     = PIStringAscii("union");
	static const PIString s_enum      = PIStringAscii("enum");
	static const PIString s_friend    = PIStringAscii("friend");
	static const PIString s_typedef   = PIStringAscii("typedef");
	static const PIString s_namespace = PIStringAscii("namespace");
	static const PIString s_template  = PIStringAscii("template");
	Visibility prev_vis               = cur_def_vis;
	int dind = fc.find('{'), find = fc.find(';'), end = 0;
	if (dind < 0 && find < 0) return nullptr;
	// piCout << "parse class <****\n" << fc << "\n****>";
	Entity * ce = parent;
	if (!is_namespace) {
		ce = parseClassDeclaration(fc.takeLeft(dind));
		fc.trim().cutLeft(1).cutRight(1).trim();
	}
	// piCout << "found class <****\n" << fc << "\n****>";
	if (ce) {
		ce->parent_scope = parent;
	}
	int ps                  = -1;
	bool def                = false;
	PIString prev_namespace = cur_namespace, stmp;
	if (ce) cur_namespace = ce->name + s_ns;
	// piCout << "parse class" << (ce ? ce->name : "NULL") << "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 == s_public) {
			cur_def_vis = Public;
			fc.cutLeft(1);
			continue;
		}
		if (cw == s_protected) {
			cur_def_vis = Protected;
			fc.cutLeft(1);
			continue;
		}
		if (cw == s_private) {
			cur_def_vis = Private;
			fc.cutLeft(1);
			continue;
		}
		if (cw == s_namespace) {
			PIString prev_namespace = cur_namespace, ccmn;
			cur_namespace += fc.takeCWord() + s_ns;
			ccmn = fc.takeRange('{', '}');
			parseClass(ce, ccmn, true);
			cur_namespace = prev_namespace;
			continue;
		}
		if (cw == s_class || cw == s_struct || cw == s_union) {
			// piCout << cw << isDeclaration(fc, 0, &end);
			if (isDeclaration(fc, 0, &end)) {
				fc.cutLeft(end);
				fc.takeSymbol();
				continue;
			}
			tmp             = fc.takeLeft(fc.find('{')).trim();
			stmp            = fc.takeRange('{', '}');
			stmp            = cw + ' ' + tmp + '{' + stmp + '}';
			auto new_entity = parseClass(ce, stmp, false);
			// piCout << "!!! > \"" << fc << "\"";
			PIStringList vars;
			PIString var;
			do {
				var = fc.takeCWord();
				if (var.isNotEmpty()) vars << var;
				if (fc.takeSymbol() == ";") break;
			} while (var.isNotEmpty());
			if (new_entity) {
				Member me;
				me.visibility = cur_def_vis;
				me.type       = new_entity->name;
				if (tmp.isEmpty() && vars.isEmpty()) vars = {""};
				for (const auto & v: vars) {
					me.name = v;
					ce->members << me;
				}
			}
			// piCout << "!!! <" << vars;
			continue;
		}
		if (cw == s_enum) {
			tmp = fc.takeCWord();
			fc.trim();
			MetaMap meta = maybeMeta(fc);
			if (tmp == s_class || tmp == s_struct) {
				tmp = fc.takeCWord();
				fc.trim();
				MetaMap smeta = maybeMeta(fc);
				meta << smeta;
			}
			// piCout << "pc E" << cur_namespace << "," << tmp;
			parseEnum(ce, cur_namespace + tmp, fc.takeRange('{', '}'), meta);
			fc.takeSymbol();
			continue;
		}
		if (cw == s_friend) {
			fc.cutLeft(fc.find(';') + 1);
			continue;
		}
		if (cw == s_typedef) {
			if (ce) {
				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 == s_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() && ce) parseMember(ce, tmp);
		if (def)
			fc.takeRange('{', '}');
		else
			fc.takeSymbol();
		if (ps == fc.size_s()) {
			fc.cutLeft(1);
		}
		ps = fc.size_s();
	}
	cur_def_vis   = prev_vis;
	cur_namespace = prev_namespace;
	return ce;
}


PICodeParser::MetaMap PICodeParser::parseMeta(PIString & fc) {
	PICodeParser::MetaMap ret;
	if (fc.isEmpty()) return ret;
	PIStringList ml = fc.split(',');
	for (const auto & m: ml) {
		int i = m.find('=');
		if (i < 0) {
			ret[m.trimmed()] = PIString();
		} else {
			PIString mv = m.mid(i + 1).trim();
			if (mv.startsWith('\"')) mv.cutLeft(1);
			if (mv.endsWith('\"')) mv.cutRight(1);
			ret[m.left(i).trim()] = mv;
		}
	}
	// piCout << ms << ret;
	return ret;
}


bool PICodeParser::parseEnum(Entity * parent, const PIString & name, PIString fc, const MetaMap & meta) {
	static const PIString s_ss = PIStringAscii("  ");
	static const PIString s_M  = PIStringAscii("$M");
	// piCout << PIStringAscii("enum") << name << fc;
	Enum e(name);
	e.meta = meta;
	PIStringList vl(fc.split(','));
	PIString vn;
	int cv = -1, ind = 0;
	for (auto & v: vl) {
		MetaMap meta;
		int mi = v.find(s_M);
		if (mi >= 0) {
			meta = tmp_meta.value(v.takeMid(mi, 5));
			v.replaceAll(s_ss, ' ');
		}
		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 << EnumeratorInfo(vn.trim(), cv, meta);
	}
	if (!e.members.isEmpty())
		if (e.members.back().name.isEmpty()) e.members.pop_back();
	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) == PIChar('*')) {
			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;
}


void removeAssignment(PIString & s) {
	if (s.size() <= 2) return;
	int ei = s.find('=');
	if (ei < 0) return;
	s.remove(ei, s.size_s() - ei);
	s.trim();
}

bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
	static const PIString s_operator     = PIStringAscii("operator");
	static const PIString s_ss           = PIStringAscii("  ");
	static const PIString s_cs           = PIStringAscii(", ");
	static const PIString s_sb           = PIStringAscii(" (");
	static const PIString s_sM           = PIStringAscii(" $M");
	static const PIString s_M            = PIStringAscii("$M");
	static const PIString s_T            = PIStringAscii("$T");
	static const PIString s_inline_s     = PIStringAscii("inline ");
	static const PIString s_static_s     = PIStringAscii("static ");
	static const PIString s_virtual_s    = PIStringAscii("virtual ");
	static const PIString s_void         = PIStringAscii("void");
	static const PIString s_using        = PIStringAscii("using");
	static const PIString s_s5           = PIStringAscii("     ");
	static const PIString s_s_const_s    = PIStringAscii(" const ");
	static const PIString s_s_static_s   = PIStringAscii(" static ");
	static const PIString s_s_mutable_s  = PIStringAscii(" mutable ");
	static const PIString s_s_volatile_s = PIStringAscii(" volatile ");
	static const PIString s_s_extern_s   = PIStringAscii(" extern ");

	if (fc.trim().isEmpty()) return true;
	if (fc.find(s_operator) >= 0) return true;
	tmp_temp.clear();
	// piCout << "\nparse 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 = s_T + 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(s_ss, ' ').replaceAll(s_cs, ',').replaceAll(s_sb, '(').replaceAll(s_sM, s_M);
	// piCout << "parse member" << fc;
	PIStringList tl, al;
	Member me;
	// piCout << fc;
	if (fc.contains('(')) {
		MetaMap meta;
		int ind = fc.find(s_M);
		if (ind >= 0) {
			meta = tmp_meta.value(fc.takeMid(ind, 5));
			fc.replaceAll(s_ss, ' ').replaceAll(s_sb, '(');
		}
		fc.cutRight(fc.size_s() - fc.findLast(')') - 1);
		te = fc.find('(');
		for (ts = te - 1; ts >= 0; --ts)
			if (!_isCChar(fc[ts]) && !(fc[ts].isDigit())) break;
		me.meta = meta;
		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(s_inline_s) >= 0) {
			me.attributes |= Inline;
			me.type.removeAll(s_inline_s);
		}
		if (me.type.find(s_static_s) >= 0) {
			me.attributes |= Static;
			me.type.removeAll(s_static_s);
		}
		if (me.type.find(s_virtual_s) >= 0) {
			me.attributes |= Virtual;
			me.type.removeAll(s_virtual_s);
		}
		normalizeEntityNamespace(me.type);
		int i = 0;
		// piCout << me.arguments_full;
		for (auto & a: me.arguments_full)
			if ((i = a.find('=')) > 0) a.cutRight(a.size_s() - i).trim();
		for (int j = 0; j < me.arguments_full.size_s(); ++j)
			if (me.arguments_full[j] == s_void) {
				me.arguments_full.remove(j);
				--j;
			}
		me.arguments_type = me.arguments_full;
		for (auto & 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 {
		if (fc.endsWith(';')) fc.cutRight(1);
		// piCout << "member" << fc;
		if (fc.startsWith(s_using) || !(fc.contains(' ') || fc.contains('\t') || fc.contains('\n'))) return true;
		int bits = extractMemberBits(fc);
		tl       = fc.split(',');
		// piCout << "member after eb" << fc << ", bits =" << bits;
		if (tl.isEmpty()) return true;
		for (auto & v: tl)
			removeAssignment(v);
		// piCout << "member" << tl;
		ctemp = s_s5 + tl.front().trim();
		PIString meta_t;
		if (ctemp.contains(s_M)) {
			meta_t = ctemp.takeMid(ctemp.find(s_M), 5);
			ctemp.trim();
		}
		if (ctemp.find(s_s_const_s) >= 0) {
			me.attributes |= Const;
			ctemp.replaceAll(s_s_const_s, ' ');
		}
		if (ctemp.find(s_s_static_s) >= 0) {
			me.attributes |= Static;
			ctemp.replaceAll(s_s_static_s, ' ');
		}
		if (ctemp.find(s_s_mutable_s) >= 0) {
			me.attributes |= Mutable;
			ctemp.replaceAll(s_s_mutable_s, ' ');
		}
		if (ctemp.find(s_s_volatile_s) >= 0) {
			me.attributes |= Volatile;
			ctemp.replaceAll(s_s_volatile_s, ' ');
		}
		if (ctemp.find(s_s_extern_s) >= 0) {
			me.attributes |= Extern;
			ctemp.replaceAll(s_s_extern_s, ' ');
		}
		PIString type = ctemp.trim().takeWord();
		ctemp.trim();
		me.visibility = cur_def_vis;
		ctemp += meta_t;
		// me.type = s_s5 + me.type;
		type.trim();
		normalizeEntityNamespace(type);
		tl[0] = ctemp.trim();
		// piCout << "type" << type;
		// piCout << "vars" << tl;
		for (const auto & v: tl) {
			crepl.clear();

			me.name = v.trimmed();
			me.type = type;
			restoreTmpMeta(&me);
			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() == PIChar('*') || me.name.front() == PIChar('&')) {
					me.type += me.name.takeLeft(1);
					me.name.trim();
				} else
					break;
			}
			me.is_type_ptr = (me.type.right(1) == PIChar(']') || me.type.right(1) == PIChar('*'));
			me.type += crepl;
			me.bits = bits;
			while (!crepl.isEmpty()) {
				PIString cdim = crepl.takeRange('[', ']').trim();
				if (cdim.isEmpty()) break;
				me.dims << cdim;
			}
			// PICout(PICoutManipulators::AddAll) << "var" << me.type << me.name << me.bits;
			// piCout << "var" << v << me.type << me.name << me.bits;
			restoreTmpTemp(&me);
			parent->members << me;
		}
	}
	// piCout << "parse member" << fc;
	return true;
}


int PICodeParser::extractMemberBits(PIString & fc) {
	int i = fc.findLast(':');
	if (i <= 0) return -1;
	if (fc[i - 1].toAscii() == ':') return -1;
	PIString bs = fc.takeMid(i).mid(1).trim();
	if (bs.isEmpty()) return -1;
	return bs.toInt();
}


void PICodeParser::normalizeEntityNamespace(PIString & n) {
	static const PIString s_const_s      = PIStringAscii("const ");
	static const PIString s_static_s     = PIStringAscii("static ");
	static const PIString s_mutable_s    = PIStringAscii("mutable ");
	static const PIString s_volatile_s   = PIStringAscii("volatile ");
	static const PIString s_s_const_s    = PIStringAscii(" const ");
	static const PIString s_s_static_s   = PIStringAscii(" static ");
	static const PIString s_s_mutable_s  = PIStringAscii(" mutable ");
	static const PIString s_s_volatile_s = PIStringAscii(" volatile ");
	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(s_s_const_s) >= 0) {
		n.replaceAll(s_s_const_s, "");
		pref += s_const_s;
	}
	if (n.find(s_s_static_s) >= 0) {
		n.replaceAll(s_s_static_s, "");
		pref += s_static_s;
	}
	if (n.find(s_s_mutable_s) >= 0) {
		n.replaceAll(s_s_mutable_s, "");
		pref += s_mutable_s;
	}
	if (n.find(s_s_volatile_s) >= 0) {
		n.replaceAll(s_s_volatile_s, "");
		pref += s_volatile_s;
	}
	n.trim();
	int f = 0;
	for (const auto * e: entities) {
		if (e->name == n) {
			n = (pref + n + suff).trim();
			return;
		}
		if ((f = e->name.find(n)) >= 0)
			if (e->name.at(f - 1) == PIChar(':'))
				if (e->name.find(cur_namespace) >= 0) {
					n = pref + e->name + suff;
					return;
				}
	}
	for (const auto & e: enums) {
		if ((f = e.name.find(n)) >= 0)
			if (e.name.at(f - 1) == PIChar(':'))
				if (e.name.find(cur_namespace) >= 0) {
					// piCout << "change" << n << "to" << e.name + suff;
					n = pref + e.name + suff;
					return;
				}
	}
	for (const auto & e: typedefs) {
		if ((f = e.first.find(n)) >= 0)
			if (e.first.at(f - 1) == PIChar(':'))
				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) {
	static const PIString s_T = PIStringAscii("$T");
	int i                     = 0;
	for (auto & a: e->arguments_full) {
		while ((i = a.find(s_T)) >= 0)
			a.replace(i, 5, tmp_temp[a.mid(i, 5)]);
	}
	for (auto & a: e->arguments_type) {
		while ((i = a.find(s_T)) >= 0)
			a.replace(i, 5, tmp_temp[a.mid(i, 5)]);
	}
	while ((i = e->type.find(s_T)) >= 0)
		e->type.replace(i, 5, tmp_temp[e->type.mid(i, 5)]);
}


void PICodeParser::restoreTmpMeta(PICodeParser::Member * e) {
	static const PIString s_M = PIStringAscii("$M");
	int i                     = e->name.find(s_M);
	if (i < 0) return;
	e->meta = tmp_meta[e->name.takeMid(i, 5)];
}


PICodeParser::MetaMap PICodeParser::maybeMeta(PIString & fc) {
	static const PIString s_M = PIStringAscii("$M");
	PICodeParser::MetaMap ret;
	if (fc.left(2) == s_M) {
		ret = tmp_meta.value(fc.takeLeft(5));
		fc.trim();
	}
	return ret;
}


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


double PICodeParser::procMacrosCond(PIString fc) {
	static const PIString s_defined = PIStringAscii("defined");
	bool neg = false, first = true, br = false;
	double ret = 0., brv = 0.;
	int oper = 0, ps = -1;
	char cc, nc;
	PIString ce;
	fc.removeAll(s_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) {
	for (const auto & d: defines) {
		if (d.first == dn) return true;
	}
	return false;
}


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


void PICodeParser::replaceMeta(PIString & dn) {
	static const PIString s_PIMETA = PIStringAscii("PIMETA");
	static const PIString s_M      = PIStringAscii("$M");
	tmp_meta.clear();
	if (dn.isEmpty()) return;
	int s = dn.find(s_PIMETA);
	while (s >= 0) {
		int ms = 0, ml = 0;
		ms = dn.findRange('(', ')', '\\', s + 6, &ml);
		if (ms < 0) return;
		PIString meta = dn.mid(ms, ml).trim();
		PIString rm   = s_M + PIString::fromNumber(tmp_meta.size_s()).expandLeftTo(3, '0');
		dn.replace(s, ms + ml + 1 - s, rm);
		// piCout << "FOUND META \"" << meta << '\"';
		tmp_meta[rm] = parseMeta(meta);
		s            = dn.find(s_PIMETA);
	}
}


PICodeParser::Entity * PICodeParser::findEntityByName(const PIString & en) {
	for (auto * e: entities)
		if (e->name == en) return e;
	return nullptr;
}


bool PICodeParser::isDeclaration(const PIString & fc, int start, int * end) {
	int dind = fc.find('{', start), find = fc.find(';', start);
	// piCout << "isDeclaration" << dind << find << fc.left(10);
	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(PIStringAscii(" main"), si);
		if (csi < 0) csi = fc.find(PIStringAscii("\tmain"), si);
		if (csi < 0) csi = fc.find(PIStringAscii("\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 == PIStringAscii("main(")) return true;
		}
		si += 5;
	}
	return false;
}


PIString PICodeParser::procMacros(PIString fc) {
	static const PIString s_ifdef  = PIStringAscii("ifdef");
	static const PIString s_ifndef = PIStringAscii("ifndef");
	static const PIString s_if     = PIStringAscii("if");
	static const PIString s_elif   = PIStringAscii("elif");
	static const PIString s_else   = PIStringAscii("else");
	static const PIString s_endif  = PIStringAscii("endif");
	static const PIString s_L      = PIStringAscii("$L");
	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) == PIChar('#')) {
			mifcond = line.mid(1);
			mif     = mifcond.takeCWord();
			// piCout << mif;
			// piCout << "mif mifcond" << mif << mifcond << ifcnt;
			if (skip || grab) {
				if (mif.left(2) == s_if) ifcnt++;
				if (mif.left(5) == s_endif) {
					if (ifcnt > 0)
						ifcnt--;
					else {
						// piCout << "main endif" << skip << grab;
						if (grab) pfc += procMacros(nfc);
						skip = grab = false;
						continue;
					}
				}
				if (mif.left(4) == s_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) == s_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) == s_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 {
				parseDirective(line.cutLeft(1).trim());
				// return false; /// WARNING: now skip errors
			}
		} else {
			if (grab)
				nfc += line + '\n';
			else if (!skip)
				pfc += line + '\n';
		}
	}
	return pfc;
}


bool PICodeParser::parseDirective(PIString d) {
	static const PIString s_include = PIStringAscii("include");
	static const PIString s_define  = PIStringAscii("define");
	static const PIString s_undef   = PIStringAscii("undef");
	static const PIString s_PIMETA  = PIStringAscii("PIMETA");
	static const PIString s_L       = PIStringAscii("$L");
	if (d.isEmpty()) return true;
	d.replaceAll(s_L, '\n');
	PIString dname = d.takeCWord();
	// piCout << "parseDirective" << d;
	if (dname == s_include) {
		d.replaceAll('<', '\"').replaceAll('>', '\"');
		PIString cf = cur_file, ifc = d.takeRange('\"', '\"');
		if (with_includes) {
			bool ret = parseFileInternal(ifc, with_includes);
			cur_file = cf;
			return ret;
		}
	}
	if (dname == s_define) {
		PIString mname = d.takeCWord();
		// piCout << mname;
		if (mname == s_PIMETA) return true;
		if (d.left(1) == PIChar('(')) { // macro
			PIStringList args = d.takeRange('(', ')').split(',').trim();
			for (int i = 0; i < macros.size_s(); ++i)
				if (macros[i].name == mname) {
					macros.remove(i);
					break;
				}
			macros << Macro(mname, d.trim(), args);
		} else { // define
			d.trim();
			for (int i = 0; i < defines.size_s(); ++i)
				if (defines[i].first == mname) {
					defines.remove(i);
					break;
				}
			defines << Define(mname, d);
			evaluator.setVariable(mname, complexd_1);
		}
		return true;
	}
	if (dname == s_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;
}
