LLVM API Documentation
00001 //===- StringMatcher.cpp - Generate a matcher for input strings -----------===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file implements the StringMatcher class. 00011 // 00012 //===----------------------------------------------------------------------===// 00013 00014 #include "llvm/TableGen/StringMatcher.h" 00015 #include "llvm/Support/raw_ostream.h" 00016 #include <map> 00017 using namespace llvm; 00018 00019 /// FindFirstNonCommonLetter - Find the first character in the keys of the 00020 /// string pairs that is not shared across the whole set of strings. All 00021 /// strings are assumed to have the same length. 00022 static unsigned 00023 FindFirstNonCommonLetter(const std::vector<const 00024 StringMatcher::StringPair*> &Matches) { 00025 assert(!Matches.empty()); 00026 for (unsigned i = 0, e = Matches[0]->first.size(); i != e; ++i) { 00027 // Check to see if letter i is the same across the set. 00028 char Letter = Matches[0]->first[i]; 00029 00030 for (unsigned str = 0, e = Matches.size(); str != e; ++str) 00031 if (Matches[str]->first[i] != Letter) 00032 return i; 00033 } 00034 00035 return Matches[0]->first.size(); 00036 } 00037 00038 /// EmitStringMatcherForChar - Given a set of strings that are known to be the 00039 /// same length and whose characters leading up to CharNo are the same, emit 00040 /// code to verify that CharNo and later are the same. 00041 /// 00042 /// \return - True if control can leave the emitted code fragment. 00043 bool StringMatcher:: 00044 EmitStringMatcherForChar(const std::vector<const StringPair*> &Matches, 00045 unsigned CharNo, unsigned IndentCount) const { 00046 assert(!Matches.empty() && "Must have at least one string to match!"); 00047 std::string Indent(IndentCount*2+4, ' '); 00048 00049 // If we have verified that the entire string matches, we're done: output the 00050 // matching code. 00051 if (CharNo == Matches[0]->first.size()) { 00052 assert(Matches.size() == 1 && "Had duplicate keys to match on"); 00053 00054 // If the to-execute code has \n's in it, indent each subsequent line. 00055 StringRef Code = Matches[0]->second; 00056 00057 std::pair<StringRef, StringRef> Split = Code.split('\n'); 00058 OS << Indent << Split.first << "\t // \"" << Matches[0]->first << "\"\n"; 00059 00060 Code = Split.second; 00061 while (!Code.empty()) { 00062 Split = Code.split('\n'); 00063 OS << Indent << Split.first << "\n"; 00064 Code = Split.second; 00065 } 00066 return false; 00067 } 00068 00069 // Bucket the matches by the character we are comparing. 00070 std::map<char, std::vector<const StringPair*> > MatchesByLetter; 00071 00072 for (unsigned i = 0, e = Matches.size(); i != e; ++i) 00073 MatchesByLetter[Matches[i]->first[CharNo]].push_back(Matches[i]); 00074 00075 00076 // If we have exactly one bucket to match, see how many characters are common 00077 // across the whole set and match all of them at once. 00078 if (MatchesByLetter.size() == 1) { 00079 unsigned FirstNonCommonLetter = FindFirstNonCommonLetter(Matches); 00080 unsigned NumChars = FirstNonCommonLetter-CharNo; 00081 00082 // Emit code to break out if the prefix doesn't match. 00083 if (NumChars == 1) { 00084 // Do the comparison with if (Str[1] != 'f') 00085 // FIXME: Need to escape general characters. 00086 OS << Indent << "if (" << StrVariableName << "[" << CharNo << "] != '" 00087 << Matches[0]->first[CharNo] << "')\n"; 00088 OS << Indent << " break;\n"; 00089 } else { 00090 // Do the comparison with if memcmp(Str.data()+1, "foo", 3). 00091 // FIXME: Need to escape general strings. 00092 OS << Indent << "if (memcmp(" << StrVariableName << ".data()+" << CharNo 00093 << ", \"" << Matches[0]->first.substr(CharNo, NumChars) << "\", " 00094 << NumChars << "))\n"; 00095 OS << Indent << " break;\n"; 00096 } 00097 00098 return EmitStringMatcherForChar(Matches, FirstNonCommonLetter, IndentCount); 00099 } 00100 00101 // Otherwise, we have multiple possible things, emit a switch on the 00102 // character. 00103 OS << Indent << "switch (" << StrVariableName << "[" << CharNo << "]) {\n"; 00104 OS << Indent << "default: break;\n"; 00105 00106 for (std::map<char, std::vector<const StringPair*> >::iterator LI = 00107 MatchesByLetter.begin(), E = MatchesByLetter.end(); LI != E; ++LI) { 00108 // TODO: escape hard stuff (like \n) if we ever care about it. 00109 OS << Indent << "case '" << LI->first << "':\t // " 00110 << LI->second.size() << " string"; 00111 if (LI->second.size() != 1) OS << 's'; 00112 OS << " to match.\n"; 00113 if (EmitStringMatcherForChar(LI->second, CharNo+1, IndentCount+1)) 00114 OS << Indent << " break;\n"; 00115 } 00116 00117 OS << Indent << "}\n"; 00118 return true; 00119 } 00120 00121 00122 /// Emit - Top level entry point. 00123 /// 00124 void StringMatcher::Emit(unsigned Indent) const { 00125 // If nothing to match, just fall through. 00126 if (Matches.empty()) return; 00127 00128 // First level categorization: group strings by length. 00129 std::map<unsigned, std::vector<const StringPair*> > MatchesByLength; 00130 00131 for (unsigned i = 0, e = Matches.size(); i != e; ++i) 00132 MatchesByLength[Matches[i].first.size()].push_back(&Matches[i]); 00133 00134 // Output a switch statement on length and categorize the elements within each 00135 // bin. 00136 OS.indent(Indent*2+2) << "switch (" << StrVariableName << ".size()) {\n"; 00137 OS.indent(Indent*2+2) << "default: break;\n"; 00138 00139 for (std::map<unsigned, std::vector<const StringPair*> >::iterator LI = 00140 MatchesByLength.begin(), E = MatchesByLength.end(); LI != E; ++LI) { 00141 OS.indent(Indent*2+2) << "case " << LI->first << ":\t // " 00142 << LI->second.size() 00143 << " string" << (LI->second.size() == 1 ? "" : "s") << " to match.\n"; 00144 if (EmitStringMatcherForChar(LI->second, 0, Indent)) 00145 OS.indent(Indent*2+4) << "break;\n"; 00146 } 00147 00148 OS.indent(Indent*2+2) << "}\n"; 00149 }