1 // Copyright (c) 2005,2011 John Abbott 2 3 // This file is part of the source of CoCoALib, the CoCoA Library. 4 5 // CoCoALib is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 10 // CoCoALib is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 15 // You should have received a copy of the GNU General Public License 16 // along with CoCoALib. If not, see <http://www.gnu.org/licenses/>. 17 18 19 #include "CoCoA/OpenMathXML.H" 20 #include "CoCoA/error.H" 21 #include "CoCoA/assert.H" 22 #include "CoCoA/convert.H" 23 #include "CoCoA/utils.H" 24 25 #include <iostream> 26 using std::ostream; 27 using std::istream; 28 #include <vector> 29 using std::vector; 30 //#include <string> 31 using std::string; 32 #include <gmp.h> 33 34 35 namespace CoCoA 36 { 37 OpenMathOutputXML(ostream & out)38 OpenMathOutputXML::OpenMathOutputXML(ostream& out): 39 myOut(out), 40 myLevel(0), 41 myTagStack() 42 {} 43 44 ~OpenMathOutputXML()45 OpenMathOutputXML::~OpenMathOutputXML() 46 { 47 CoCoA_ASSERT(myLevel == 0); //??? how to avoid throwing inside dtor??? 48 // ??? Should send token to say "over and out"??? 49 } 50 51 myIndent()52 inline const char* OpenMathOutputXML::myIndent() 53 { 54 static const int SpacesPerLevel = 2; 55 static const int MaxIndent = 20; 56 static const string spaces(SpacesPerLevel*MaxIndent, ' '); 57 static const char* const ptr = spaces.c_str(); 58 if (myLevel >= MaxIndent) return ptr; 59 return ptr+(MaxIndent-myLevel)*SpacesPerLevel; 60 } 61 62 mySend(const MachineInt & n)63 void OpenMathOutputXML::mySend(const MachineInt& n) 64 { 65 myOut << myIndent() << "<OMI> " << n << " </OMI>\n"; 66 } 67 68 mySend(const BigInt & N)69 void OpenMathOutputXML::mySend(const BigInt& N) 70 { 71 myOut << myIndent() << "<OMI> " << N << " </OMI>\n"; 72 } 73 mySend(const OpenMathSymbol & s)74 void OpenMathOutputXML::mySend(const OpenMathSymbol& s) 75 { 76 myOut << myIndent() << "<OMS cd=\"" << CD(s) << "\" name=\"" << name(s) << "\" />\n"; 77 } 78 79 mySendApplyStart()80 void OpenMathOutputXML::mySendApplyStart() 81 { 82 // std::clog<<"ApplyStart at level " << myLevel << endl; 83 // CoCoA_ASSERT(myLevel > 0); 84 myOut << myIndent() << "<OMA>\n"; 85 myTagStack.push(OpenMathApply); 86 ++myLevel; 87 } 88 mySendApplyEnd()89 void OpenMathOutputXML::mySendApplyEnd() 90 { 91 // CoCoA_ASSERT(myLevel > 1); 92 --myLevel; 93 CoCoA_ASSERT(!myTagStack.empty()); 94 CoCoA_ASSERT(myTagStack.top() == OpenMathApply); 95 myTagStack.pop(); 96 myOut << myIndent() << "</OMA>\n"; 97 } 98 99 mySendObjectStart()100 void OpenMathOutputXML::mySendObjectStart() 101 { 102 CoCoA_ASSERT(myLevel == 0); 103 myOut << "<OMOBJ>\n"; 104 myTagStack.push(OpenMathObj); 105 ++myLevel; 106 } 107 mySendObjectEnd()108 void OpenMathOutputXML::mySendObjectEnd() 109 { 110 CoCoA_ASSERT(myLevel == 1); 111 --myLevel; 112 CoCoA_ASSERT(!myTagStack.empty()); 113 CoCoA_ASSERT(myTagStack.top() == OpenMathObj); 114 myTagStack.pop(); 115 myOut << "</OMOBJ>\n"; 116 } 117 118 119 OpenMathInputXML(istream & in)120 OpenMathInputXML::OpenMathInputXML(istream& in): 121 myStatus(AlreadyRead), 122 myCurrTagType(OpenMathEOF), // could be any value 123 myIntValue(-1), // could be any value 124 mySymbol(), // default "unset" OpenMathSymbol 125 myIn(in), // reference to input stream 126 myLevel(0), // nesting level initially 0 127 myTagStack() // stack initially empty 128 { 129 myIn.unsetf(std::ios::skipws); 130 } 131 132 ~OpenMathInputXML()133 OpenMathInputXML::~OpenMathInputXML() 134 { 135 CoCoA_ASSERT(myLevel == 0); 136 } 137 138 advance()139 void OpenMathInputXML::advance() 140 { 141 if (myStatus == NotYetRead) 142 ReadNextNode(); //??? discard next node??? 143 myStatus = NotYetRead; 144 } 145 146 myCurrTag()147 OpenMathTag OpenMathInputXML::myCurrTag() 148 { 149 if (myStatus == NotYetRead) 150 ReadNextNode(); 151 return myCurrTagType; 152 } 153 154 NumDescendants()155 long OpenMathInputXML::NumDescendants() const 156 { return 0; //???????? 157 } 158 159 myRecv(long & n)160 bool OpenMathInputXML::myRecv(long & n) 161 { 162 if (myCurrTag() != OpenMathInt) return false; 163 //??? CoCoA_ERROR("OpenMath node is not OMI","OpenMathInputXML::myRecv(long)"); 164 if (!IsConvertible(n, myIntValue)) 165 CoCoA_ERROR(ERR::ArgTooBig, "OpenMathInputXML::InputInteger"); 166 return true; 167 } 168 169 170 //??????? 171 // bool OpenMathInputXML::myRecv(unsigned long & n) 172 // { 173 // if (myCurrTag() != OpenMathInt) return false; 174 // //??? CoCoA_ERROR("OpenMath node is not OMI","OpenMathInputXML::myRecv(long)"); 175 // if (!IsConvertible(n, myIntegerValue)) 176 // CoCoA_ERROR(ERR::ArgTooBig, "OpenMathInputXML::InputInteger"); 177 // return true; 178 // } 179 180 myRecv(BigInt & N)181 bool OpenMathInputXML::myRecv(BigInt& N) 182 { 183 if (myCurrTag() != OpenMathInt) return false; 184 //??? CoCoA_ERROR("OpenMath node is not OMI","OpenMathInputXML::myRecv(BigInt)"); 185 N = myIntValue; 186 return true; 187 } 188 189 myRecv(OpenMathSymbol & s)190 bool OpenMathInputXML::myRecv(OpenMathSymbol& s) 191 { 192 if (myCurrTag() != OpenMathSym) return false; 193 //??? CoCoA_ERROR("OpenMath node is not OMS", "OpenMathInputXML::myRecv(symbol)"); 194 s = mySymbol; 195 return true; 196 } 197 ReadChar()198 char OpenMathInputXML::ReadChar() 199 { 200 //??? if (myIn.eof()) return '\0'; 201 std::clog<<'['<<char(myIn.peek())<<']'; 202 return myIn.get(); //???? what about EOF???? BUG BUG BUG ??? 203 } 204 SkipWSReadChar()205 char OpenMathInputXML::SkipWSReadChar() 206 { 207 while (!myIn.eof()) 208 { 209 char ch = ReadChar(); 210 if (ch == ' ' || ch == '\t' || ch == '\n') continue; 211 return ch; 212 } 213 return '\0'; 214 } 215 216 217 // NB a space in `expected' is regarded as any amount of whitespace. SkipMatch(const string & expected)218 bool OpenMathInputXML::SkipMatch(const string& expected) 219 { 220 if (myIn.eof()) return false; 221 const long nchars = len(expected); 222 for (long i = 0; i < nchars; ++i) 223 { 224 const char expected_ch = expected[i]; 225 if (expected_ch == ' ') { myIn >> std::ws; continue; } 226 char ch = ReadChar(); 227 // std::clog<<"EXPECTING `"<<expected_ch<<"' READ `"<<ch<<"'"<<std::endl; 228 if (ch != expected_ch) 229 return false; 230 } 231 return true; 232 } 233 ReadDecimalString(string & DecimalDigits)234 bool OpenMathInputXML::ReadDecimalString(string& DecimalDigits) 235 { 236 myIn >> std::ws; 237 if (myIn.eof()) return false; // hit EOF 238 239 DecimalDigits.clear(); 240 // Check to see if number is negative; grab sign if so 241 if (myIn.peek() == '-') 242 { 243 DecimalDigits += ReadChar(); 244 //??? myIn >> ws; // allow whitespace between sign first digit 245 } 246 bool ReadAtLeastOneDigit = false; 247 while (!myIn.eof() && isdigit(myIn.peek())) 248 { 249 ReadAtLeastOneDigit = true; 250 DecimalDigits += ReadChar(); 251 } 252 return ReadAtLeastOneDigit; 253 } 254 255 ReadStringInQuotes(string & QuotedString)256 bool OpenMathInputXML::ReadStringInQuotes(string& QuotedString) 257 { 258 if (!SkipMatch(" \"")) return false; 259 while (!myIn.eof() && myIn.peek() != '"') 260 { 261 QuotedString += ReadChar(); 262 } 263 return SkipMatch("\""); 264 } 265 266 ReadNextNode()267 void OpenMathInputXML::ReadNextNode() 268 { 269 myIn >> std::ws; 270 std::clog<<"ReadNextNode: looking at `"<<char(myIn.peek())<<"'"<<std::endl; 271 if (myIn.eof()) { myCurrTagType = OpenMathEOF; return; } 272 if (ReadChar() != '<'){std::clog<<"DIDN'T FIND `<' WHERE ONE SHOULD BE"<<std::endl;return;} 273 myIn >> std::ws; 274 if (myIn.peek() == '/') 275 { 276 if (!SkipMatch("/ OMA > ")){std::clog<<"DIDN'T FIND `</OMA>' AFTER READING `</'"<<std::endl;return;} 277 if (myTagStack.empty() || myTagStack.top() != OpenMathApply){std::clog<<"MISPLACED OR TOO MANY `</OMA>'s"<<std::endl;return;} 278 myCurrTagType = OpenMathApply; 279 --myLevel; 280 } 281 if (!SkipMatch(" OM")){std::clog<<"DIDN'T FIND `<OM' WHERE ONE SHOULD BE"<<std::endl;return;} 282 char ch = ReadChar(); 283 if (myIn.eof()){std::clog<<"UNEXPECTED EOF"<<std::endl;return;} 284 switch (ch) 285 { 286 case 'A': 287 { 288 myCurrTagType = OpenMathApply; 289 myTagStack.push(OpenMathApply); 290 if (!SkipMatch(" > ")) {std::clog<<"`OMA' not followed by `>': instead found '"<<ReadChar()<<"'"<<std::endl;} 291 ++myLevel; 292 return; 293 } 294 case 'I': 295 { 296 if (!SkipMatch(" >")) {std::clog<<"`OMI' not followed by `>': instead found '"<<ReadChar()<<"'"<<std::endl;} 297 std::clog<<"READING INTEGER..."; 298 myCurrTagType = OpenMathInt; 299 string decimal; 300 if (!ReadDecimalString(decimal)) {std::clog<<"BAD DECIMAL NUMBER"<<std::endl; return;} 301 302 std::clog<<"DECIMAL string is `"<<decimal<<"'"<<std::endl; 303 mpz_set_str(mpzref(myIntValue), decimal.c_str(), 10); 304 myIntValue = BigIntFromString(decimal); 305 306 if (!SkipMatch(" < /OMI > ")) { std::clog<<"DIDN'T FIND '</OMI>' WHERE IT SHOULD BE"<<std::endl; return;} 307 return; 308 } 309 case 'S': 310 { 311 myCurrTagType = OpenMathSym; 312 std::clog<<"READING OMS..."; 313 SkipMatch(" cd = "); 314 string cd; 315 ReadStringInQuotes(cd); 316 std::clog<<"CD part is `"<<cd<<"' "; 317 SkipMatch(" name = "); 318 string name; 319 ReadStringInQuotes(name); 320 std::clog<<"NAME part is `"<<name<<"'"<<std::endl; 321 mySymbol = OpenMathSymbol(cd, name); 322 SkipMatch(" /> "); 323 return; 324 } 325 default: 326 std::clog<<"UNKNOWN NODE TYPE: OM" << ch << std::endl; 327 return; 328 } 329 } 330 331 } // end of namespace CoCoA 332 333 334 // RCS header/log 335 // $Header: /Volumes/Home_1/cocoa/cvs-repository/CoCoALib-0.99/src/AlgebraicCore/OpenMathXML.C,v 1.13 2018/04/20 18:51:25 abbott Exp $ 336 // $Log: OpenMathXML.C,v $ 337 // Revision 1.13 2018/04/20 18:51:25 abbott 338 // Summary: Changed ctors for BigInt/BigRat from string or from MPZ/MPQ 339 // 340 // Revision 1.12 2014/05/06 16:00:39 abbott 341 // Summary: Added basic use of myLevel (to avoid a compiler warning) 342 // Author: JAA 343 // 344 // Revision 1.11 2013/03/26 14:56:06 abbott 345 // Updated the conversion fns (in ptic removed procedure "convert"); 346 // numerous consequential changes. 347 // 348 // Revision 1.10 2011/11/09 14:09:53 bigatti 349 // -- renamed MachineInteger --> MachineInt 350 // 351 // Revision 1.9 2011/08/14 15:52:17 abbott 352 // Changed ZZ into BigInt (phase 1: just the library sources). 353 // 354 // Revision 1.8 2011/08/12 16:07:05 abbott 355 // Added send/recv mem fns for BigInt. 356 // 357 // Revision 1.7 2011/03/11 14:49:08 abbott 358 // Changed size_t into long. 359 // 360 // Revision 1.6 2009/12/11 11:46:32 abbott 361 // Changed fn convert into IsConvertible. 362 // Added template procedure convert. 363 // New version because change is not backward compatible. 364 // 365 // Revision 1.5 2008/12/16 21:10:32 abbott 366 // Replaced the various output fns for different sort of machine integers by a 367 // single one for MachineInt. 368 // 369 // Revision 1.4 2008/10/07 15:46:10 abbott 370 // Added missing log/history lines at the ends of a few files. 371 // 372 // 373