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.fail(); 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 void SgGameReader::HandleProperties(SgNode* node, 00059 const RawProperties& properties, 00060 int& boardSize, SgPropPointFmt& fmt) 00061 { 00062 int value; 00063 if (GetIntProp(properties, "SZ", value)) 00064 { 00065 if (value < SG_MIN_SIZE || value > SG_MAX_SIZE) 00066 m_warnings.set(INVALID_BOARDSIZE); 00067 else 00068 boardSize = value; 00069 } 00070 if (GetIntProp(properties, "GM", value)) 00071 fmt = SgPropUtil::GetPointFmt(value); 00072 for (RawProperties::const_iterator it = properties.begin(); 00073 it != properties.end(); ++it) 00074 { 00075 const string& label = it->first; 00076 const vector<string>& values = it->second; 00077 if (values.size() == 0) 00078 m_warnings.set(PROPERTY_WITHOUT_VALUE); 00079 SgProp* prop; 00080 SgPropID id = SgProp::GetIDOfLabel(label); 00081 if (id != SG_PROP_NONE) 00082 prop = SgProp::CreateProperty(id); 00083 else 00084 prop = 00085 new SgPropUnknown(SG_PROP_UNKNOWN, label, vector<string>()); 00086 if (prop->FromString(values, boardSize, fmt)) 00087 node->Add(prop); 00088 } 00089 } 00090 00091 void SgGameReader::PrintWarnings(ostream& out) const 00092 { 00093 Warnings warnings = m_warnings; 00094 // Print more severe warnings first, less severe warnings later 00095 PrintWarning(out, warnings, INVALID_BOARDSIZE, "Invalid board size"); 00096 PrintWarning(out, warnings, PROPERTY_WITHOUT_VALUE, 00097 "Property withour value"); 00098 SG_ASSERT(warnings.none()); 00099 } 00100 00101 SgNode* SgGameReader::ReadGame(bool resetWarnings) 00102 { 00103 if (resetWarnings) 00104 m_warnings.reset(); 00105 SgNode* root = 0; 00106 int c; 00107 while ((c = m_in.get()) != EOF) 00108 { 00109 while (c != '(' && c != EOF) 00110 c = m_in.get(); 00111 if (c == EOF) 00112 break; 00113 root = ReadSubtree(0, m_defaultSize, SG_PROPPOINTFMT_GO); 00114 if (root) 00115 root = root->Root(); 00116 if (root) 00117 break; 00118 } 00119 return root; 00120 } 00121 00122 void SgGameReader::ReadGames(SgVectorOf<SgNode>* rootList) 00123 { 00124 m_warnings.reset(); 00125 SG_ASSERT(rootList); 00126 rootList->Clear(); 00127 while (true) 00128 { 00129 SgNode* root = ReadGame(false); 00130 if (root) 00131 rootList->PushBack(root); 00132 else 00133 break; 00134 } 00135 } 00136 00137 string SgGameReader::ReadLabel(int c) 00138 { 00139 // Precondition: Character 'c' is in range 'A'..'Z', to be interpreted 00140 // as the first letter of a property label. Second letter can be capital 00141 // letter or digit, lower case letters are ignored. 00142 string label; 00143 label += static_cast<char>(c); 00144 while ((c = m_in.get()) != EOF 00145 && (('A' <= c && c <= 'Z') 00146 || ('a' <= c && c <= 'z') 00147 || ('0' <= c && c <= '9'))) 00148 label += static_cast<char>(c); 00149 if (c != EOF) 00150 m_in.unget(); 00151 return label; 00152 } 00153 00154 SgNode* SgGameReader::ReadSubtree(SgNode* node, int boardSize, 00155 SgPropPointFmt fmt) 00156 { 00157 RawProperties properties; 00158 int c; 00159 while ((c = m_in.get()) != EOF && c != ')') 00160 { 00161 if ('A' <= c && c <= 'Z') 00162 { 00163 string label = ReadLabel(c); 00164 m_in >> ws; 00165 string value; 00166 while (ReadValue(value)) 00167 properties[label].push_back(value); 00168 } 00169 else if (c == ';') 00170 { 00171 if (node) 00172 { 00173 HandleProperties(node, properties, boardSize, fmt); 00174 properties.clear(); 00175 node = node->NewRightMostSon(); 00176 } 00177 else 00178 node = new SgNode(); // first node 00179 } 00180 else if (c == '(') 00181 { 00182 HandleProperties(node, properties, boardSize, fmt); 00183 properties.clear(); 00184 ReadSubtree(node, boardSize, fmt); 00185 } 00186 } 00187 HandleProperties(node, properties, boardSize, fmt); 00188 return node; 00189 } 00190 00191 bool SgGameReader::ReadValue(string& value) 00192 { 00193 m_in >> ws; 00194 value = ""; 00195 int c; 00196 if ((c = m_in.get()) == EOF) 00197 return false; 00198 if (c != '[') 00199 { 00200 m_in.unget(); 00201 return false; 00202 } 00203 bool inEscape = false; 00204 while ((c = m_in.get()) != EOF && (c != ']' || inEscape)) 00205 { 00206 if (c != '\n') 00207 value += static_cast<char>(c); 00208 if (inEscape) 00209 inEscape = false; 00210 else if (c == '\\') 00211 inEscape = true; 00212 } 00213 return true; 00214 } 00215 00216 //----------------------------------------------------------------------------