blob: 62be6751b646bd8a50a9513438ecee9d62f99bee [file] [log] [blame] [edit]
#include "pptable.h"
#include <wx/tokenzr.h>
#include <set>
bool IsWordChar(const wxString &s, int strSize)
{
if(strSize) {
return s.find_first_of(wxT("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_")) != wxString::npos;
} else {
return s.find_first_of(wxT("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_")) != wxString::npos;
}
}
bool IsWordCharA(char c, int strSize)
{
if(strSize) {
return ((c >= 97 && c <= 122) ||// a-z
(c >= 65 && c <= 90) ||// A-Z
(c >= 48 && c <= 57) ||// 0-9
(c == '_'));
} else {
return ((c >= 97 && c <= 122) ||// a-z
(c >= 65 && c <= 90) ||// A-Z
(c == '_'));
}
}
std::string ReplaceWordA(const std::string &str, const std::string &word, const std::string &replaceWith)
{
char currChar;
char nextChar;
std::string currentWord;
std::string output;
output.reserve( str.length() * 2 );
for(size_t i=0; i<str.length(); i++) {
// Look ahead
if( str.length() > i + 1 ) {
nextChar = str[i+1];
} else {
// we are at the end of buffer
nextChar = '\0';
}
currChar = str[i];
if(!IsWordCharA( currChar, currentWord.length() )) {
output += str[i];
currentWord.clear();
} else {
currentWord += currChar;
if(IsWordCharA(nextChar, currentWord.length())) {
// do nothing
} else if( !IsWordCharA(nextChar, currentWord.length()) && currentWord == word ) {
output += replaceWith;
currentWord.clear();
} else {
output += currentWord;
currentWord.clear();
}
}
}
return output;
}
// Helper string find metho
wxString ReplaceWord(const wxString &str, const wxString &word, const wxString &replaceWith)
{
wxString currChar;
wxString nextChar;
wxString currentWord;
wxString output;
for(size_t i=0; i<str.Length(); i++) {
// Look ahead
if( str.Length() > i + 1 ) {
nextChar = str[i+1];
} else {
// we are at the end of buffer
nextChar = wxT('\0');
}
currChar = str[i];
if(!IsWordChar( currChar, currentWord.Length() )) {
output << str[i];
currentWord.Clear();
} else {
currentWord << currChar;
if(IsWordChar(nextChar, currentWord.Length())) {
// do nothing
} else if( !IsWordChar(nextChar, currentWord.Length()) && currentWord == word ) {
output << replaceWith;
currentWord.Clear();
} else {
output << currentWord;
currentWord.Clear();
}
}
}
return output;
}
wxArrayString TokenizeWords(const wxString &str)
{
wxString currChar;
wxString nextChar;
wxString currentWord;
wxArrayString outputArr;
wxString::const_iterator iter = str.begin();
for(; iter != str.end(); iter++) {
// Look ahead
if( (iter + 1) != str.end() ) {
nextChar = *(iter+1);
} else {
// we are at the end of buffer
nextChar = wxT('\0');
}
currChar = *iter;
if(!IsWordChar( currChar, currentWord.Length() )) {
currentWord.Clear();
} else {
currentWord << currChar;
if(IsWordChar(nextChar, currentWord.Length())) {
// do nothing
} else {
outputArr.Add(currentWord);
currentWord.Clear();
}
}
}
return outputArr;
}
static PPTable* ms_instance = NULL;
void PPToken::processArgs(const wxString &argsList)
{
args = wxStringTokenize(argsList, wxT(","), wxTOKEN_STRTOK);
// replace all occurrences of 'arg' with %1, %2 etc
for(size_t i=0; i<args.GetCount(); i++) {
wxString replaceWith = wxString::Format(wxT("%%%d"), (int)i);
#if wxVERSION_NUMBER < 2900
replacement = ReplaceWord(replacement, args.Item(i), replaceWith);
#else
std::string res = ReplaceWordA(replacement.To8BitData().data(), args.Item(i).To8BitData().data(), replaceWith.To8BitData().data());
if(res.empty()) {
replacement.clear();
} else {
replacement = wxString::From8BitData(res.c_str());
}
#endif
}
}
void PPToken::print(wxFFile &fp)
{
#if 0
wxString buff;
buff << wxT("Name : ") << name << wxT("\n");
buff << wxT("replacement : ") << replacement << wxT("\n");
buff << wxT("isFunctionLike: ") << isFunctionLike << wxT("\n");
if(isFunctionLike) {
for(size_t i=0; i<args.size(); i++) {
buff << wxT("Arg: ") << args.Item(i) << wxT("\n");
}
}
buff << wxT(" ---- \n");
fp.Write(buff);
#else
wxString buff;
buff << name << wxT("(") << (flags & IsFunctionLike) << wxT(")") << wxT("=") << replacement << wxT("\n");
fp.Write(buff);
#endif
}
wxString PPToken::fullname() const
{
wxString fullname;
fullname << name;
if(flags & IsFunctionLike) {
fullname << wxT("(");
for(size_t i=0; i<args.size(); i++) {
fullname << wxT("%") << i << wxT(",");
}
if(args.size()) {
fullname.RemoveLast();
}
fullname << wxT(")");
}
return fullname;
}
void PPToken::squeeze()
{
std::set<wxString> alreadyReplacedMacros;
// perform the squeeze 5 times max
for(size_t count=0; count < 5; count++) {
bool modified(false);
// get list of possible macros in the replacement
wxArrayString tmpWords = TokenizeWords(replacement);
wxArrayString words;
// make sure that a word is not been replaced more than once
// this will avoid recursion
// an example (taken from qglobal.h of the Qt library):
//
// #define qDebug QT_NO_QDEBUG_MACRO
// #define QT_NO_QDEBUG_MACRO if(1); else qDebug
//
for(size_t i=0; i<tmpWords.size(); i++) {
if(alreadyReplacedMacros.find(tmpWords.Item(i)) == alreadyReplacedMacros.end()){
alreadyReplacedMacros.insert(tmpWords[i]);
words.Add(tmpWords[i]);
}
}
for(size_t i=0; i<words.size(); i++) {
PPToken tok = PPTable::Instance()->Token(words.Item(i));
if(tok.flags & IsValid) {
if(tok.flags & IsFunctionLike) {
int where = replacement.Find(words.Item(i));
if(where != wxNOT_FOUND) {
wxString initList;
wxArrayString initListArr;
if(readInitList( replacement, where + words.Item(i).Length(), initList, initListArr )) {
tok.expandOnce(initListArr);
replacement.Remove(where, words.Item(i).Length() + initList.Length());
tok.replacement.Replace(wxT("##"), wxT(""));
replacement.insert(where, tok.replacement);
modified = true;
}
}
} else {
if(replacement.Replace(words.Item(i), tok.replacement)) {
modified = true;
}
}
}
}
if(!modified)
break;
}
replacement.Replace(wxT("##"), wxT(""));
}
bool PPToken::readInitList(const std::string& in, size_t from, std::string& initList, std::vector<std::string>& initListArr)
{
if(in.length() < from) {
return false;
}
std::string tmpString = in.substr(from);
size_t start = tmpString.find('(');
if(start == std::string::npos ) {
return false;
}
// skip the open brace
tmpString = tmpString.substr(start+1);
for(size_t i=0; i<start; i++) {
initList += " ";
}
initList += "(";
std::string word;
int depth(1);
for(size_t i=0; i<tmpString.length(); i++) {
char ch = tmpString[i];
initList += ch;
switch(ch) {
case ')':
depth --;
if(depth == 0) {
initListArr.push_back(word);
return true;
} else {
word += ch;
}
break;
case '(':
depth++;
word += ch;
break;
case ',':
if(depth == 1) {
initListArr.push_back(word);
word.clear();
} else {
word += ch;
}
break;
default:
word += ch;
break;
}
}
return false;
}
bool PPToken::readInitList(const wxString &in, int from, wxString& initList, wxArrayString& initListArr)
{
if((int)in.Length() < from) {
return false;
}
wxString tmpString = in.Mid(from);
int start = tmpString.Find(wxT("("));
if(start == wxNOT_FOUND ) {
return false;
}
tmpString = tmpString.Mid(start+1);
for(size_t i=0; i<(size_t)start; i++) {
initList << wxT(" ");
}
initList << wxT("(");
wxString word;
int depth(1);
for(size_t i=0; i<tmpString.Length(); i++) {
wxChar ch = tmpString[i];
initList << ch;
switch(ch) {
case wxT(')'):
depth --;
if(depth == 0) {
initListArr.Add(word);
return true;
} else {
word << ch;
}
break;
case wxT('('):
depth++;
word << ch;
break;
case wxT(','):
if(depth == 1) {
initListArr.Add(word);
word.Clear();
} else {
word << ch;
}
break;
default:
word << ch;
break;
}
}
return false;
}
void PPToken::expandOnce(const wxArrayString& initList)
{
if(initList.size() != args.size())
return;
for(size_t i=0; i<args.size(); i++) {
wxString placeHolder;
placeHolder << wxT("%") << i;
wxString replaceWith = initList.Item(i);
replaceWith.Trim().Trim(false);
if(replaceWith.Contains(placeHolder))
continue;
replacement.Replace(placeHolder, initList.Item(i));
}
}
wxString PPToken::signature() const
{
wxString sig;
if(flags & IsFunctionLike) {
sig << wxT("(");
for(size_t i=0; i<args.size(); i++) {
sig << wxT("%") << i << wxT(",");
}
if(args.size()) {
sig.RemoveLast();
}
sig << wxT(")");
}
return sig;
}
///////////////////////////////////////////////////
PPTable::PPTable()
{
}
PPTable::~PPTable()
{
}
PPTable* PPTable::Instance()
{
if(ms_instance == NULL) {
ms_instance = new PPTable();
}
return ms_instance;
}
void PPTable::Release()
{
if(ms_instance) {
delete ms_instance;
}
ms_instance = NULL;
}
PPToken PPTable::Token(const wxString& name)
{
std::map<wxString, PPToken>::iterator iter = m_table.find(name);
if(iter == m_table.end()) {
return PPToken();
}
return iter->second;
}
void PPTable::Add(const PPToken& token)
{
if(token.name.IsEmpty())
return;
wxString name = token.name;
name.Trim().Trim(false);
std::map<wxString, PPToken>::iterator iter = m_table.find(name);
if(iter == m_table.end())
m_table[name] = token;
else {
// if the new token's replacement is empty and the current one is NOT empty,
// replace the two (we prefer empty replacements)
if(iter->second.flags & PPToken::IsOverridable && !iter->second.replacement.IsEmpty() && token.replacement.IsEmpty()) {
m_table[name] = token;
}
}
}
void PPTable::AddUsed(const wxString& name)
{
if(name.IsEmpty()) {
return;
}
m_namesUsed.insert(name);
}
void PPTable::Print(wxFFile &fp)
{
std::map<wxString, PPToken>::iterator iter = m_table.begin();
for(; iter != m_table.end(); iter++) {
iter->second.print(fp);
}
}
bool PPTable::Contains(const wxString& name)
{
std::map<wxString, PPToken>::iterator iter = m_table.find(name);
return iter != m_table.end();
}
wxString PPTable::Export()
{
wxString table;
std::map<wxString, PPToken>::iterator iter = m_table.begin();
for(; iter != m_table.end(); iter++) {
iter->second.squeeze();
wxString replacement = iter->second.replacement;
replacement.Trim().Trim(false);
// remove extra whitespaces
while(replacement.Replace(wxT(" "), wxT(" "))){}
if(replacement.IsEmpty()) {
table << iter->second.fullname() << wxT("\n");
} else if(iter->second.flags & PPToken::IsFunctionLike) {
table << iter->second.fullname() << wxT("=") << replacement << wxT("\n");
} else {
// macros with replacement but they are not in a form of a function
// we take only macros that their replacement is not a number
long v(-1);
if(!replacement.ToLong(&v) && !replacement.ToLong(&v, 8) && !replacement.ToLong(&v, 16) && replacement.find(wxT('"')) == wxString::npos && !replacement.StartsWith(wxT("0x"))) {
table << iter->second.fullname() << wxT("=") << replacement << wxT("\n");
}
}
}
return table;
}
void PPTable::Squeeze()
{
std::map<wxString, PPToken>::iterator iter = m_table.begin();
for(; iter != m_table.end(); iter++) {
m_table[iter->first].squeeze();
}
}
void PPTable::Clear()
{
m_table.clear();
}
void PPTable::ClearNamesUsed()
{
m_namesUsed.clear();
}
bool CLReplacePattern(const wxString& in, const wxString& pattern, const wxString& replaceWith, wxString &outStr)
{
int where = pattern.Find(wxT("%0"));
if(where != wxNOT_FOUND) {
wxString replacement(replaceWith);
// a patterened expression
wxString searchFor = pattern.BeforeFirst(wxT('('));
where = in.Find(searchFor);
if(where == wxNOT_FOUND) {
return false;
}
wxString initList;
wxArrayString initListArr;
if(PPToken::readInitList(in, searchFor.Length() + where, initList, initListArr) == false)
return false;
outStr = in;
// update the 'replacement' with the actual values ( replace %0..%n)
for(size_t i=0; i<initListArr.size(); i++) {
wxString placeHolder;
placeHolder << wxT("%") << i;
replacement.Replace(placeHolder, initListArr.Item(i));
}
outStr.Remove(where, searchFor.Length() + initList.Length());
outStr.insert(where, replacement);
return true;
} else {
if(in.Find(pattern) == wxNOT_FOUND) {
return false;
}
// simple replacement
outStr = ReplaceWord(in, pattern, replaceWith);
return outStr != in;
}
}
std::string replacement;
bool CLReplacePatternA(const std::string& in, const CLReplacement& repl, std::string& outStr)
{
if(repl.is_compound) {
size_t where = in.find(repl.searchFor);
if(where == std::string::npos)
return false;
std::string initList;
std::vector<std::string> initListArr;
if(PPToken::readInitList(in, repl.searchFor.length() + where, initList, initListArr) == false)
return false;
// update the 'replacement' with the actual values ( replace %0..%n)
replacement = repl.replaceWith;
char placeHolder[4];
for(size_t i=0; i<initListArr.size(); i++) {
memset(placeHolder, 0, sizeof(placeHolder));
sprintf(placeHolder, "%%%d", (int)i);
size_t pos = replacement.find(placeHolder);
const std::string& init = initListArr[i];
while( pos != std::string::npos ) {
replacement.replace(pos, strlen(placeHolder), init.c_str());
// search for the next match
pos = replacement.find(placeHolder, pos + 1);
}
}
outStr = in;
where = outStr.find(repl.searchFor);
if(where == std::string::npos)
return false;
outStr.replace(where, repl.searchFor.length() + initList.length(), replacement);
return true;
} else {
size_t where = in.find(repl.searchFor);
if(where == std::string::npos) {
return false;
}
outStr = ReplaceWordA(in, repl.searchFor, repl.replaceWith);
// outStr = in;
// outStr.replace(where, repl.searchFor.length(), repl.replaceWith);
// simple replacement
return outStr != in;
}
}
void CLReplacement::construct(const std::string& pattern, const std::string& replacement)
{
is_ok = true;
full_pattern = pattern;
is_compound = full_pattern.find("%0") != std::string::npos;
if(is_compound) {
// a patterened expression
replaceWith = replacement;
size_t where = pattern.find('(');
if(where == std::string::npos) {
is_ok = false;
return;
}
searchFor = pattern.substr(0, where);
if(searchFor.empty()){
is_ok = false;
return;
}
} else {
// simple Key=Value pair
replaceWith = replacement;
searchFor = full_pattern;
}
}