/*
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 .
*/
#include "picodeparser.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(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.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("##", "");
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";
piForeachC (Entity * 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:";
piForeachC (Member & m, c->functions)
piCout << m.type << m.name << m.meta;
piCout << "Members:";
piForeachC (Member & m, c->members)
piCout << m.type << m.name << m.meta;
}
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:";
piCout << "\n\nEnums:";
piForeachC (Enum & c, enums) {
piCout << "enum" << c.name << c.meta;
piForeachC (EnumeratorInfo & e, c.members)
piCout << " " << e.name << "=" << e.value << e.meta;
}
piCout << "\n\nTypedefs:";
piForeachC (Typedef & c, typedefs)
piCout << "typedef" << c;*/
}
void PICodeParser::parseFiles(const PIStringList & files, bool follow_includes) {
clear();
piForeachC (PIString & f, files)
parseFileInternal(f, follow_includes);
/*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, 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() {
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();
cur_def_vis = Global;
anon_num = 0;
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 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;
/*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(PICoutManipulators::DefaultControls) << fc;
pfc = procMacros(fc);
replaceMeta(pfc);
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.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;
}
}
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.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;
}
}
}
//piCout << NewLine << "file" << cur_file << pfc;
int pl = -1;
while (!pfc.isEmpty()) {
pfc.trim();
int nl = pfc.size_s();
if (pl == nl) break;
pl = nl;
if (pfc.left(9) == "namespace") {
pfc.cutLeft(pfc.find("{") + 1);
continue;
}
if (pfc.left(8) == "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) == "class" || pfc.left(6) == "struct" || pfc.left(5) == "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(6); continue;}
ccmn = pfc.left(dind) + "{\n" + pfc.mid(dind).takeRange('{', '}') + "\n}\n";
pfc.remove(0, ccmn.size());
parseClass(0, ccmn);
continue;
}
if (pfc.left(4) == "enum") {
pfc.cutLeft(4);
tmp = pfc.takeCWord();
pfc.trim();
MetaMap meta = maybeMeta(pfc);
parseEnum(0, cur_namespace + tmp, pfc.takeRange("{", "}"), meta);
pfc.takeSymbol();
continue;
}
if (pfc.left(7) == "typedef") {
pfc.cutLeft(7);
typedefs << parseTypedef(pfc.takeLeft(pfc.find(";")));
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) {
PIString cd = fc.trimmed().removeAll('\n').replaceAll("\t", " ").replaceAll(" ", " "), pn;
MetaMap meta;
int ind = cd.find("$M");
if (ind >= 0) {
meta = tmp_meta.value(cd.takeMid(ind, 5));
cd.replaceAll(" ", " ");
}
//piCout << "found class <****\n" << cd << "\n****>";
ind = cd.find(":");
PIVector 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;
}
}
PIString typename_ = cd.left(6).trim();
bool is_class = typename_ == "class";
cur_def_vis = (is_class ? Private : Public);
PIString cn = cd.mid(6).trim();
bool has_name = !cn.isEmpty();
if (cn.isEmpty()) cn = "";
//piCout << "found " << typename_ << cn;
if (cn.isEmpty()) return 0;
Entity * e = new Entity();
e->meta = meta;
e->name = cur_namespace + cn;
e->type = typename_;//(is_class ? "class" : "struct");
e->has_name = has_name;
e->parents = parents;
e->file = cur_file;
entities << e;
return e;
}
PIString PICodeParser::parseClass(Entity * parent, 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);
//piCout << "parse class <****\n" << fc.left(20) << "\n****>";
Entity * ce = parseClassDeclaration(fc.takeLeft(dind));
fc.trim().cutLeft(1).cutRight(1).trim();
//piCout << "found class <****\n" << fc << "\n****>";
if (!ce) return PIString();
if (parent) parent->children << ce;
ce->parent_scope = parent;
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" || cw == "struct" || cw == "union") {
if (isDeclaration(fc, 0, &end)) {
fc.cutLeft(end);
fc.takeSymbol();
continue;
}
tmp = fc.takeLeft(fc.find("{"));
stmp = fc.takeRange("{", "}");
fc.takeSymbol();
stmp = cw + " " + tmp + "{" + stmp + "}";
parseClass(ce, stmp);
continue;
}
if (cw == "enum") {
tmp = fc.takeCWord();
fc.trim();
MetaMap meta = maybeMeta(fc);
parseEnum(ce, cur_namespace + tmp, fc.takeRange("{", "}"), meta);
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;
}
PICodeParser::MetaMap PICodeParser::parseMeta(PIString & fc) {
PICodeParser::MetaMap ret;
if (fc.isEmpty()) return ret;
PIStringList ml = fc.split(",");
piForeachC (PIString & m, ml) {
int i = m.find("=");
if (i < 0) continue;
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) {
//piCout << "enum" << name << fc;
Enum e(name);
e.meta = meta;
PIStringList vl(fc.split(","));
PIString vn;
int cv = -1, ind = 0;
piForeach (PIString & v, vl) {
MetaMap meta;
int mi = v.find("$M");
if (mi >= 0) {
meta = tmp_meta.value(v.takeMid(mi, 5));
v.replaceAll(" ", " ");
}
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) == "*") {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 = "$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(" ", " ").replaceAll(", ", ",").replaceAll(" (", "(").replaceAll(" $M", "$M");
//piCout << "parse member" << fc;
PIStringList tl, al;
Member me;
//piCout << fc;
if (fc.contains("(")) {
MetaMap meta;
int ind = fc.find("$M");
if (ind >= 0) {
meta = tmp_meta.value(fc.takeMid(ind, 5));
fc.replaceAll(" ", " ").replaceAll(" (", "(");
}
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.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("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;
//piCout << me.arguments_full;
piForeach (PIString & 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] == "void") {
me.arguments_full.remove(j);
--j;
}
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 {
if (fc.endsWith(";")) fc.cutRight(1);
if (fc.startsWith("using") || !(fc.contains(' ') || fc.contains('\t') || fc.contains('\n'))) return true;
int bits = extractMemberBits(fc);
tl = fc.split(",");
//piCout << "member" << fc << tl;
//piCout << "member after eb" << fc << ", bits =" << bits;
if (tl.isEmpty()) return true;
bool vn = true;
ctemp = tl.front().trim();
PIString meta_t;
if (ctemp.contains("$M"))
meta_t = ctemp.takeMid(ctemp.find("$M"));
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;
ctemp += meta_t;
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 ", " ");
}
if (type.find(" extern ") >= 0) {
me.attributes |= Extern;
type.replaceAll(" extern ", " ");
}
type.trim();
normalizeEntityNamespace(type);
tl[0] = ctemp.trim();
piForeachC (PIString & 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() == "*" || 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;
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;
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) {
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("$T")) >= 0)
a.replace(i, 5, tmp_temp[a.mid(i, 5)]);
}
piForeach (PIString & a, e->arguments_type) {
while ((i = a.find("$T")) >= 0)
a.replace(i, 5, tmp_temp[a.mid(i, 5)]);
}
while ((i = e->type.find("$T")) >= 0)
e->type.replace(i, 5, tmp_temp[e->type.mid(i, 5)]);
}
void PICodeParser::restoreTmpMeta(PICodeParser::Member * e) {
int i = e->name.find("$M");
if (i < 0) return;
e->meta = tmp_meta[e->name.takeMid(i, 5)];
}
PICodeParser::MetaMap PICodeParser::maybeMeta(PIString & fc) {
PICodeParser::MetaMap ret;
if (fc.left(2) == "$M") {
ret = tmp_meta.value(fc.takeLeft(5));
fc.trim();
}
return ret;
}
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();
}
void PICodeParser::replaceMeta(PIString & dn) {
tmp_meta.clear();
if (dn.isEmpty()) return;
int s = dn.find("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 = "$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("PIMETA");
}
}
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 << 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(" 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;
//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: now skip errors
}
} 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, ifc = d.takeRange("\"", "\"");
if (with_includes) {
bool ret = parseFileInternal(ifc, with_includes);
cur_file = cf;
return ret;
}
}
if (dname == "define") {
PIString mname = d.takeCWord();
//piCout << mname;
if (mname == "PIMETA") return true;
if (d.left(1) == "(") { // macro
PIStringList args = d.takeRange("(", ")").split(",").trim();
macros << Macro(mname, d.trim(), args);
} else { // define
d.trim();
//if (mname == d) d.clear();
defines << Define(mname, d);
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;
}