| #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; | |
| } | |
| } | |