00001 //---------------------------------------------------------------------------- 00002 /** @file SgGameReader.cpp */ 00003 //---------------------------------------------------------------------------- 00004 00005 #include "SgSystem.h" 00006 #include "SgGameReader.h" 00007 00008 #include <cstdio> // Defines EOF 00009 #include <iostream> 00010 #include <map> 00011 #include <vector> 00012 #include "SgDebug.h" 00013 #include "SgException.h" 00014 #include "SgNode.h" 00015 00016 using namespace std; 00017 00018 //---------------------------------------------------------------------------- 00019 00020 namespace { 00021 00022 /** Print warning and reset flag in temporary copy of m_warnings. */ 00023 void PrintWarning(ostream& out, SgGameReader::Warnings& warnings, int index, 00024 const char* text) 00025 { 00026 if (! warnings.test(index)) 00027 return; 00028 out << text << '\n'; 00029 warnings.reset(index); 00030 } 00031 00032 } // namespace 00033 00034 //---------------------------------------------------------------------------- 00035 00036 SgGameReader::SgGameReader(istream& in, int defaultSize) 00037 : m_in(in), 00038 m_defaultSize(defaultSize), 00039 m_fileFormat(4) 00040 { 00041 } 00042 00043 bool SgGameReader::GetIntProp(const SgGameReader::RawProperties& properties, 00044 const string& label, int& value) 00045 { 00046 RawProperties::const_iterator it = properties.find(label); 00047 if (it == properties.end() || it->second.size() == 0) 00048 return false; 00049 istringstream in(it->second[0]); 00050 in >> value; 00051 return in; 00052 } 00053 00054 /** Create SgProp instances and add them to node. 00055 The only properties that are interpreted by the reader are SZ (board size) 00056 and GM (point format, because they must be handled before all other root 00057 node properties to parse points correctly. 00058 */ 00059 void SgGameReader::HandleProperties(SgNode* node, 00060 const RawProperties& properties, 00061 int& boardSize, SgPropPointFmt& fmt) 00062 { 00063 int value; 00064 if (GetIntProp(properties, "SZ", value)) 00065 { 00066 if (value < SG_MIN_SIZE || value > SG_MAX_SIZE) 00067 m_warnings.set(INVALID_BOARDSIZE); 00068 else 00069 boardSize = value; 00070 } 00071 if (GetIntProp(properties, "GM", value)) 00072 fmt = SgPropUtil::GetPointFmt(value); 00073 for (RawProperties::const_iterator it = properties.begin(); 00074 it != properties.end(); ++it) 00075 { 00076 const string& label = it->first; 00077 const vector<string>& values = it->second; 00078 if (values.size() == 0) 00079 m_warnings.set(PROPERTY_WITHOUT_VALUE); 00080 SgProp* prop; 00081 SgPropID id = SgProp::GetIDOfLabel(label); 00082 if (id != SG_PROP_NONE) 00083 prop = SgProp::CreateProperty(id); 00084 else 00085 prop = 00086 new SgPropUnknown(SG_PROP_UNKNOWN, label, vector<string>()); 00087 if (prop->FromString(values, boardSize, fmt)) 00088 node->Add(prop); 00089 } 00090 } 00091 00092 void SgGameReader::PrintWarnings(ostream& out) const 00093 { 00094 Warnings warnings = m_warnings; 00095 // Print more severe warnings first, less severe warnings later 00096 PrintWarning(out, warnings, INVALID_BOARDSIZE, "Invalid board size"); 00097 PrintWarning(out, warnings, PROPERTY_WITHOUT_VALUE, 00098 "Property withour value"); 00099 SG_ASSERT(warnings.none()); 00100 } 00101 00102 SgNode* SgGameReader::ReadGame(bool resetWarnings) 00103 { 00104 if (resetWarnings) 00105 m_warnings.reset(); 00106 SgNode* root = 0; 00107 int c; 00108 while ((c = m_in.get()) != EOF) 00109 { 00110 while (c != '(' && c != EOF) 00111 c = m_in.get(); 00112 if (c == EOF) 00113 break; 00114 root = ReadSubtree(0, m_defaultSize, SG_PROPPOINTFMT_GO); 00115 if (root) 00116 root = root->Root(); 00117 if (root) 00118 break; 00119 } 00120 return root; 00121 } 00122 00123 void SgGameReader::ReadGames(SgVectorOf<SgNode>* rootList) 00124 { 00125 m_warnings.reset(); 00126 SG_ASSERT(rootList); 00127 rootList->Clear(); 00128 while (true) 00129 { 00130 SgNode* root = ReadGame(false); 00131 if (root) 00132 rootList->PushBack(root); 00133 else 00134 break; 00135 } 00136 } 00137 00138 string SgGameReader::ReadLabel(int c) 00139 { 00140 // Precondition: Character 'c' is in range 'A'..'Z', to be interpreted 00141 // as the first letter of a property label. Second letter can be capital 00142 // letter or digit, lower case letters are ignored. 00143 string label; 00144 label += static_cast<char>(c); 00145 while ((c = m_in.get()) != EOF 00146 && (('A' <= c && c <= 'Z') 00147 || ('a' <= c && c <= 'z') 00148 || ('0' <= c && c <= '9'))) 00149 label += static_cast<char>(c); 00150 if (c != EOF) 00151 m_in.unget(); 00152 return label; 00153 } 00154 00155 SgNode* SgGameReader::ReadSubtree(SgNode* node, int boardSize, 00156 SgPropPointFmt fmt) 00157 { 00158 RawProperties properties; 00159 int c; 00160 while ((c = m_in.get()) != EOF && c != ')') 00161 { 00162 if ('A' <= c && c <= 'Z') 00163 { 00164 string label = ReadLabel(c); 00165 m_in >> ws; 00166 string value; 00167 while (ReadValue(value)) 00168 properties[label].push_back(value); 00169 } 00170 else if (c == ';') 00171 { 00172 if (node) 00173 { 00174 HandleProperties(node, properties, boardSize, fmt); 00175 properties.clear(); 00176 node = node->NewRightMostSon(); 00177 } 00178 else 00179 node = new SgNode(); // first node 00180 } 00181 else if (c == '(') 00182 { 00183 HandleProperties(node, properties, boardSize, fmt); 00184 properties.clear(); 00185 ReadSubtree(node, boardSize, fmt); 00186 } 00187 } 00188 HandleProperties(node, properties, boardSize, fmt); 00189 return node; 00190 } 00191 00192 bool SgGameReader::ReadValue(string& value) 00193 { 00194 m_in >> ws; 00195 value = ""; 00196 int c; 00197 if ((c = m_in.get()) == EOF) 00198 return false; 00199 if (c != '[') 00200 { 00201 m_in.unget(); 00202 return false; 00203 } 00204 bool inEscape = false; 00205 while ((c = m_in.get()) != EOF && (c != ']' || inEscape)) 00206 { 00207 if (c != '\n') 00208 value += static_cast<char>(c); 00209 if (inEscape) 00210 inEscape = false; 00211 else if (c == '\\') 00212 inEscape = true; 00213 } 00214 return true; 00215 } 00216 00217 //----------------------------------------------------------------------------