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