/*
PIP - Platform Independent Primitives
Packets extractor
Copyright (C) 2013 Ivan Pelipenko peri4ko@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "picode.h"
PIString CodeParser::Macro::expand(const PIStringList & arg_vals, bool * ok) const {
if (args.size() != arg_vals.size()) {
piCout << ("Error: in expansion of macro \"" + name + "(" + args.join(", ") + ")\": expect")
<< args.size() << "arguments but takes" << arg_vals.size() << "!";
if (ok != 0) *ok = false;
return PIString();
}
PIString ret = value;
for (int i = 0; i < args.size_s(); ++i) {
const PIString & an(args[i]), av(arg_vals[i]);
int ind(-1);
while ((ind = ret.find(an, ind + 1)) >= 0) {
PIChar ppc(0), pc(0), nc(0);
if (ind > 1) ppc = ret[ind - 2];
if (ind > 0) pc = ret[ind - 1];
if (ind + an.size_s() < ret.size_s()) nc = ret[ind + an.size_s()];
if (ppc != '#' && pc == '#' && !_isCChar(nc)) { // to chars
ind--;
ret.replace(ind, an.size_s() + 1, "\"" + av + "\"");
ind -= an.size_s() - av.size_s() - 1;
continue;
}
if (_isCChar(pc) || _isCChar(nc)) continue;
ret.replace(ind, an.size_s(), av);
ind -= an.size_s() - av.size_s();
}
}
ret.replaceAll("##", "");
if (ok != 0) *ok = true;
return ret;
}
CodeParser::CodeParser() {
}
bool CodeParser::parseFile(const PIString & file) {
//piCout << "parse" << file << "...";
PIFile f(file, PIIODevice::ReadOnly);
if (!f.isOpened()) {
piCout << ("Error: can`t open file \"" + file + "\"!");
return false;
}
PIString fc = f.readAll();
return parseFileContent(fc);
}
void CodeParser::clear() {
defines.clear();
typedefs.clear();
tree.clear();
entities.clear();
}
bool CodeParser::parseFileContent(PIString & fc) {
bool mlc = false, cc = false;
int mls = 0, ole = -1, ccs = 0;
char c;
PIString pfc, line, ccmn;
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) {
c = fc[i].toAscii();
if (c == '"') {
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] != '\\') {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 = 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 + 1); --i; continue;}
}
while (!fc.isEmpty()) {
line = fc.takeLine().trimmed();
if (line.left(1) == "#") {
if (!parseDirective(line.cutLeft(1).trim()))
return false;
} else pfc << line << "\n";
}
bool replaced = true;
int replaced_cnt = 0;
while (replaced) {
if (replaced_cnt >= 64) {
piCout << "Error: recursive macros detected!";
return false;
}
replaced_cnt++;
replaced = false;
piForeachC (Define & d, defines) {
int ind(-1);
while ((ind = pfc.find(d.first, ind + 1)) >= 0) {
PIChar pc(0), nc(0);
if (ind > 0) pc = pfc[ind - 1];
if (ind + d.first.size_s() < pfc.size_s()) nc = pfc[ind + d.first.size_s()];
if (_isCChar(pc) || _isCChar(nc)) continue;
pfc.replace(ind, d.first.size_s(), d.second);
ind -= d.first.size_s() - d.second.size_s();
replaced = true;
}
}
piForeachC (Macro & m, macros) {
int ind(-1);
while ((ind = pfc.find(m.name, ind + 1)) >= 0) {
PIChar pc(0), nc(0);
if (ind > 0) pc = pfc[ind - 1];
if (ind + m.name.size_s() < pfc.size_s()) nc = pfc[ind + m.name.size_s()];
if (_isCChar(pc) || _isCChar(nc)) continue;
PIString ret, range; bool ok(false);
range = pfc.mid(ind + m.name.size_s()).takeRange("(", ")");
ret = m.expand(range.split(",").trim(), &ok);
//piCout << "range" << ret;
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 << pfc;
return true;
}
bool CodeParser::parseDirective(PIString d) {
if (d.isEmpty()) return true;
PIString dname = d.takeCWord();
if (dname == "include") {
d.replaceAll("<", "\"").replaceAll(">", "\"");
return parseFile(d.takeRange("\"", "\""));
}
if (dname == "define") {
PIString mname = d.takeCWord();
if (d.left(1) == "(") { // macro
PIStringList args = d.takeRange("(", ")").split(",").trim();
macros << Macro(mname, d.trim(), args);
} else { // define
defines << Define(mname, d.trim());
}
return true;
}
return true;
}