/* 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, 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"; 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 << PIStringAscii("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 << PIStringAscii("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 << PIStringAscii("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 << PIStringAscii("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 << PIStringAscii("class") << c->name << c->parents; piCout << "\n\nEnums:"; piForeachC (Enum & c, enums) piCout << PIStringAscii("enum") << c.name << c.members; piCout << "\n\nTypedefs:"; piForeachC (Typedef & c, typedefs) piCout << PIStringAscii("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; PIStringList defs = PIStringAscii(PICODE_DEFINES).split(","); piForeachC (PIString & 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"); 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; 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;} } 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, 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; } } piForeachC (Macro & 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; 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('{', '}'); parseClass(0, 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); if (dind < 0 && find < 0) {pfc.cutLeft(6); continue;} if (dind < 0 || find < dind) {pfc.cutLeft(6); continue;} ccmn = pfc.left(dind) + s_bo + pfc.mid(dind).takeRange('{', '}') + s_bc; pfc.remove(0, ccmn.size()); parseClass(0, ccmn, false); continue; } if (pfc.left(4) == s_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) == s_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) { static const PIString s_ss = PIStringAscii(" "); static const PIString s_M = PIStringAscii("$M"); static const PIString s_class = PIStringAscii("class"); 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, ' '); } //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_ == s_class; cur_def_vis = (is_class ? Private : Public); PIString cn = cd.mid(6).trim(); bool has_name = !cn.isEmpty(); if (cn.isEmpty()) cn = PIStringAscii("'; //piCout << "found " << typename_ << cn; if (cn.isEmpty()) return 0; Entity * e = new Entity(); e->meta = meta; e->name = cur_namespace + cn; e->type = typename_; e->has_name = has_name; e->parents = parents; e->file = cur_file; entities << e; return e; } void 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; if (dind < 0 || find < dind) { fc.left(find); return; } //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) return PIString(); if (ce) { if (parent) parent->children << 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->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 == 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) { 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, false); continue; } if (cw == s_enum) { tmp = fc.takeCWord(); fc.trim(); MetaMap meta = maybeMeta(fc); 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; } 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) { 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; piForeach (PIString & 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 << "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 = 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('('); //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(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; 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] == s_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); //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" << fc << tl; //piCout << "member after eb" << fc << ", bits =" << bits; if (tl.isEmpty()) return true; piForeach (PIString & v, tl) removeAssignment(v); bool vn = true; ctemp = tl.front().trim(); PIString meta_t; if (ctemp.contains(s_M)) { meta_t = ctemp.takeMid(ctemp.find(s_M), 5); ctemp.trim(); } 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 = s_s5 + me.type; if (type.find(s_s_const_s) >= 0) { me.attributes |= Const; type.replaceAll(s_s_const_s, ' '); } if (type.find(s_s_static_s) >= 0) { me.attributes |= Static; type.replaceAll(s_s_static_s, ' '); } if (type.find(s_s_mutable_s) >= 0) { me.attributes |= Mutable; type.replaceAll(s_s_mutable_s, ' '); } if (type.find(s_s_volatile_s) >= 0) { me.attributes |= Volatile; type.replaceAll(s_s_volatile_s, ' '); } if (type.find(s_s_extern_s) >= 0) { me.attributes |= Extern; type.replaceAll(s_s_extern_s, ' '); } type.trim(); normalizeEntityNamespace(type); tl[0] = ctemp.trim(); //piCout << "vars" << tl; 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() == 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; 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; piForeachC (Entity * 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; } } piForeachC (Enum & 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; } } piForeachC (Typedef & 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; piForeach (PIString & a, e->arguments_full) { while ((i = a.find(s_T)) >= 0) a.replace(i, 5, tmp_temp[a.mid(i, 5)]); } piForeach (PIString & 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) { 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) { 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) { 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(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"); 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"); if (d.isEmpty()) return true; 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; }