]> git.lyx.org Git - features.git/blob - src/mathed/math_extern.C
cfdc432cbd027cc547d6b0fbb613bb4694f652c6
[features.git] / src / mathed / math_extern.C
1
2 // This file contains most of the magic that extracts "context
3 // information" from the unstructered layout-oriented stuff in an
4 // MathArray.
5
6 #include <algorithm>
7
8 #include "math_charinset.h"
9 #include "math_deliminset.h"
10 #include "math_exfuncinset.h"
11 #include "math_exintinset.h"
12 #include "math_funcinset.h"
13 #include "math_matrixinset.h"
14 #include "math_mathmlstream.h"
15 #include "math_scriptinset.h"
16 #include "math_stringinset.h"
17 #include "math_symbolinset.h"
18 #include "debug.h"
19
20
21 std::ostream & operator<<(std::ostream & os, MathArray const & ar)
22 {
23         NormalStream ns(os);    
24         ns << ar;
25         return os;
26 }
27
28
29 MathScriptInset const * asScript(MathArray::const_iterator it)
30 {
31         if (it->nucleus()->asScriptInset())
32                 return 0;
33         ++it;
34         if (!it->nucleus())
35                 return 0;
36         return it->nucleus()->asScriptInset();
37 }
38
39
40
41 // returns sequence of char with same code starting at it up to end
42 // it might be less, though...
43 string charSequence(MathArray::const_iterator it, MathArray::const_iterator end)
44 {
45         string s;
46         MathCharInset const * p = it->nucleus()->asCharInset();
47         if (p) {
48                 for (MathTextCodes c = p->code(); it != end; ++it) {
49                         p = it->nucleus()->asCharInset();
50                         if (!p || p->code() != c)
51                                 break;
52                         s += p->getChar();
53                 }
54         }
55         return s;
56 }
57
58
59 void extractStrings(MathArray & dat)
60 {
61         //lyxerr << "\nStrings from: " << ar << "\n";
62         MathArray ar;
63         MathArray::const_iterator it = dat.begin();
64         while (it != dat.end()) {
65                 if (it->nucleus() && it->nucleus()->asCharInset()) {
66                         string s = charSequence(it, dat.end());
67                         MathTextCodes c = it->nucleus()->asCharInset()->code();
68                         ar.push_back(MathAtom(new MathStringInset(s, c)));
69                         it += s.size();
70                 } else {
71                         ar.push_back(*it);
72                         ++it;
73                 }
74         }
75         ar.swap(dat);
76         //lyxerr << "\nStrings to: " << ar << "\n";
77 }
78
79
80 MathInset * singleItem(MathArray & ar)
81 {
82         return ar.size() == 1 ? ar.begin()->nucleus() : 0;
83 }
84
85
86 void extractMatrices(MathArray & ar)
87 {
88         lyxerr << "\nMatrices from: " << ar << "\n";
89         for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it) {
90                 MathDelimInset * del = (*it)->asDelimInset();
91                 if (!del)
92                         continue;
93                 MathInset * arr = singleItem(del->cell(0));
94                 if (!arr || !arr->asArrayInset())
95                         continue;
96                 *it = MathAtom(new MathMatrixInset(*(arr->asArrayInset())));
97                 lyxerr << "\nMatrices to: " << ar << "\n";
98         }
99
100
101 // convert this inset somehow to a string
102 string extractString(MathInset * p)
103 {
104         if (p && p->getChar())
105                 return string(1, p->getChar());
106         if (p && p->asStringInset())
107                 return p->asStringInset()->str();
108         return string();
109 }
110
111
112 // define a function for tests
113 typedef bool TestItemFunc(MathInset *);
114
115 // define a function for replacing subexpressions
116 typedef MathInset * ReplaceArgumentFunc(const MathArray & ar);
117
118
119 // search end of nested sequence
120 MathArray::iterator endNestSearch(
121         MathArray::iterator it,
122         MathArray::iterator last,
123         TestItemFunc testOpen,
124         TestItemFunc testClose
125 )
126 {
127         for (int level = 0; it != last; ++it) {
128                 if (testOpen(it->nucleus()))
129                         ++level;
130                 if (testClose(it->nucleus()))
131                         --level;
132                 if (level == 0)
133                         break;
134         }
135         return it;
136 }
137
138
139 // replace nested sequences by a real Insets
140 void replaceNested(
141         MathArray & ar,
142         TestItemFunc testOpen,
143         TestItemFunc testClose,
144         ReplaceArgumentFunc replaceArg
145 )
146 {
147         // use indices rather than iterators for the loop  because we are going
148         // to modify the array.
149         for (MathArray::size_type i = 0; i < ar.size(); ++i) {
150                 // check whether this is the begin of the sequence
151                 MathArray::iterator it = ar.begin() + i;
152                 if (!testOpen(it->nucleus()))
153                         continue;
154
155                 // search end of sequence
156                 MathArray::iterator jt = endNestSearch(it, ar.end(), testOpen, testClose);
157                 if (jt == ar.end())
158                         continue;
159
160                 // create a proper inset as replacement
161                 MathInset * p = replaceArg(MathArray(it + 1, jt));
162
163                 // replace the original stuff by the new inset
164                 ar.erase(it + 1, jt + 1);
165                 (*it).reset(p);
166         }
167
168
169
170 //
171 // search deliminiters
172 //
173
174 bool openParanTest(MathInset * p)
175 {
176         return extractString(p) == "(";
177 }
178
179
180 bool closeParanTest(MathInset * p)
181 {
182         return extractString(p) == ")";
183 }
184
185
186 MathInset * delimReplacement(const MathArray & ar)
187 {
188         MathDelimInset * del = new MathDelimInset("(", ")");
189         del->cell(0) = ar;
190         return del;
191 }
192
193
194 // replace '('...')' sequences by a real MathDelimInset
195 void extractDelims(MathArray & ar) {
196         lyxerr << "\nDelims from: " << ar << "\n";
197         replaceNested(ar, openParanTest, closeParanTest, delimReplacement);
198         lyxerr << "\nDelims to: " << ar << "\n";
199 }
200
201
202
203 //
204 // search well-known functions
205 //
206
207
208 // replace 'f' '(...)' and 'f' '^n' '(...)' sequences by a real MathExFuncInset
209 // assume 'extractDelims' ran before
210 void extractFunctions(MathArray & ar)
211 {
212         // we need at least two items...
213         if (ar.size() <= 1)
214                 return;
215
216         lyxerr << "\nFunctions from: " << ar << "\n";
217         for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
218                 MathArray::iterator it = ar.begin() + i;
219
220                 // is this a function name?
221                 MathFuncInset * func = (*it)->asFuncInset();
222                 if (!func)
223                         continue;
224
225                 // do we have an exponent?
226                 // simply skippping the postion does the right thing:
227                 // 'sin' '^2' 'x' -> 'sin(x)' '^2'
228                 MathArray::iterator jt = it + 1;
229                 if (MathScriptInset * script = (*jt)->asScriptInset()) {
230                         // allow superscripts only
231                         if (script->hasDown())
232                                 continue;
233                         ++jt;
234                         if (jt == ar.end())
235                                 continue;
236                 }
237
238                 // jt points now to the "argument". Since we had run "extractDelims"
239                 // before, this could be a single argument only. Get hold of this.
240                 MathArray arg;
241                 MathDelimInset * del = (*jt)->asDelimInset();
242                 if (del && del->isParanthesis()) 
243                         arg = del->cell(0);
244                 else
245                         arg.push_back(*jt);
246
247                 // replace the function name by a real function inset
248                 (*it).reset(new MathExFuncInset(func->name(), arg));
249                 
250                 // remove the source of the argument from the array
251                 ar.erase(jt);
252                 lyxerr << "\nFunctions to: " << ar << "\n";
253         }
254
255
256
257 //
258 // search integrals
259 //
260
261 bool intSymbolTest(MathInset * p)
262 {
263         return p->asSymbolInset() && p->asSymbolInset()->name() == "int";
264 }
265
266
267 bool differentialTest(MathInset * p)
268 {
269         return extractString(p) == "d";
270 }
271
272
273 // replace '\int' ['_^'] x 'd''x'(...)' sequences by a real MathExIntInset
274 // assume 'extractDelims' ran before
275 void extractIntegrals(MathArray & ar)
276 {
277         // we need at least three items...
278         if (ar.size() <= 2)
279                 return;
280
281         lyxerr << "\nIntegrals from: " << ar << "\n";
282         for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
283                 MathArray::iterator it = ar.begin() + i;
284
285                 // is this a integral name?
286                 if (!intSymbolTest(it->nucleus()))
287                         continue;
288
289                 // search 'd'
290                 MathArray::iterator jt =
291                         endNestSearch(it, ar.end(), intSymbolTest, differentialTest);
292
293                 // something sensible found?
294                 if (jt == ar.end())
295                         continue;
296
297                 // create a proper inset as replacement
298                 MathExIntInset * p = new MathExIntInset("int");
299
300                 // collect scripts
301                 MathArray::iterator st = it + 1;
302                 if ((*st)->asScriptInset()) {
303                         p->scripts(*st);
304                         p->core(MathArray(st + 1, jt));
305                 } else {
306                         p->core(MathArray(st, jt));
307                 }
308
309                 // use the atom behind the 'd' as differential
310                 MathArray ind;
311                 if (jt + 1 != ar.end()) {
312                         ind.push_back(*(jt + 1));
313                         ++jt;
314                 }
315                 ar.erase(it + 1, jt + 1);
316
317                 p->index(ind);
318                 (*it).reset(p);
319         }
320         lyxerr << "\nIntegrals to: " << ar << "\n";
321 }
322
323
324 //
325 // search sums
326 //
327
328 bool sumSymbolTest(MathInset * p)
329 {
330         return p->asSymbolInset() && p->asSymbolInset()->name() == "sum";
331 }
332
333
334 bool equalSign(MathInset * p)
335 {
336         return extractString(p) == "=";
337 }
338
339
340 bool equalSign1(MathAtom const & at)
341 {
342         return equalSign(at.nucleus());
343 }
344
345
346
347 // replace '\sum' ['_^'] f(x) sequences by a real MathExIntInset
348 // assume 'extractDelims' ran before
349 void extractSums(MathArray & ar)
350 {
351         // we need at least two items...
352         if (ar.size() <= 1)
353                 return;
354
355         lyxerr << "\nSums from: " << ar << "\n";
356         for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
357                 MathArray::iterator it = ar.begin() + i;
358
359                 // is this a sum name?
360                 if (!sumSymbolTest(it->nucleus()))
361                         continue;
362
363                 // create a proper inset as replacement
364                 MathExIntInset * p = new MathExIntInset("sum");
365
366                 // collect scripts
367                 MathArray::iterator st = it + 1;
368                 if (st != ar.end() && (*st)->asScriptInset()) {
369                         p->scripts(*st);
370                         ++st;
371
372                         // try to figure out the summation index from the subscript
373                         MathScriptInset * script = p->scripts()->asScriptInset();
374                         if (script->hasDown()) {
375                                 MathArray & ar = script->down().data_;
376                                 MathArray::iterator it =
377                                         std::find_if(ar.begin(), ar.end(), &equalSign1);
378                                 if (it != ar.end()) {
379                                         // we found a '=', use everything in front of that as index,
380                                         // and everything behind as start value
381                                         p->index(MathArray(ar.begin(), it));
382                                         ar.erase(ar.begin(), it + 1);
383                                 } else {
384                                         // use everything as summation index
385                                         p->index(ar);
386                                         p->scripts().reset(0);
387                                 }
388                         }
389                 }
390
391                 // use the atom behind the script as core
392                 MathArray ind;
393                 if (st != ar.end()) {
394                         MathArray core;
395                         core.push_back(*st);
396                         p->core(core);
397                         ++st;
398                 }
399                 ar.erase(it + 1, st);
400                 (*it).reset(p);
401         }
402         lyxerr << "\nSums to: " << ar << "\n";
403 }
404
405
406 void extractStructure(MathArray & ar)
407 {
408         extractMatrices(ar);
409         extractDelims(ar);
410         extractFunctions(ar);
411         extractIntegrals(ar);
412         extractSums(ar);
413         extractStrings(ar);
414 }
415
416
417 void write(MathArray const & dat, WriteStream & wi)
418 {
419         MathArray ar = dat;
420         extractStrings(ar);
421         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
422                 wi.firstitem = (it == ar.begin());
423                 MathInset const * p = it->nucleus();
424                 if (it + 1 != ar.end()) {
425                         if (MathScriptInset const * q = asScript(it)) {
426                                 q->write(p, wi);
427                                 ++it;
428                                 continue;
429                         } 
430                 }
431                 p->write(wi);
432         }
433 }
434
435
436 void normalize(MathArray const & ar, NormalStream & os)
437 {
438         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
439                 (*it)->normalize(os);
440 }
441
442
443 void octavize(MathArray const & dat, OctaveStream & os)
444 {
445         MathArray ar = dat;
446         extractStructure(ar);
447         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
448                 MathInset const * p = it->nucleus();
449                 if (it + 1 != ar.end()) {
450                         if (MathScriptInset const * q = asScript(it)) {
451                                 q->octavize(p, os);
452                                 ++it;   
453                                 continue;
454                         }
455                 }
456                 p->octavize(os);
457         }
458 }
459
460
461 void maplize(MathArray const & dat, MapleStream & os)
462 {
463         MathArray ar = dat;
464         extractStructure(ar);
465         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
466                 MathInset const * p = it->nucleus();
467                 if (it + 1 != ar.end()) {
468                         if (MathScriptInset const * q = asScript(it)) {
469                                 q->maplize(p, os);
470                                 ++it;   
471                                 continue;
472                         }
473                 }
474                 p->maplize(os);
475         }
476 }
477
478
479 void mathmlize(MathArray const & dat, MathMLStream & os)
480 {
481         MathArray ar = dat;
482         extractStructure(ar);
483         if (ar.size() == 0)
484                 os << "<mrow/>";
485         else if (ar.size() == 1)
486                 os << ar.begin()->nucleus();
487         else {
488                 os << MTag("mrow");
489                 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
490                         MathInset const * p = it->nucleus();
491                         if (it + 1 != ar.end()) {
492                                 if (MathScriptInset const * q = asScript(it)) {
493                                         q->mathmlize(p, os);
494                                         ++it;   
495                                         continue;
496                                 }
497                         }
498                         p->mathmlize(os);
499                 }
500                 os << ETag("mrow");
501         }
502 }
503