]> git.lyx.org Git - lyx.git/blob - src/mathed/math_extern.C
4846bf146fb9e12b295135be2e18169bb5b28f0d
[lyx.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_diffinset.h"
11 #include "math_exfuncinset.h"
12 #include "math_exintinset.h"
13 #include "math_funcinset.h"
14 #include "math_fracinset.h"
15 #include "math_matrixinset.h"
16 #include "math_mathmlstream.h"
17 #include "math_scriptinset.h"
18 #include "math_stringinset.h"
19 #include "math_symbolinset.h"
20 #include "debug.h"
21
22
23 std::ostream & operator<<(std::ostream & os, MathArray const & ar)
24 {
25         NormalStream ns(os);    
26         ns << ar;
27         return os;
28 }
29
30
31 // define a function for tests
32 typedef bool TestItemFunc(MathInset *);
33
34 // define a function for replacing subexpressions
35 typedef MathInset * ReplaceArgumentFunc(const MathArray & ar);
36
37
38
39 // try to extract an "argument" to some function.
40 // returns position behind the argument
41 MathArray::iterator extractArgument(MathArray & ar,
42         MathArray::iterator pos, MathArray::iterator last, string const & = "")
43 {
44         // nothing to get here
45         if (pos == last)
46                 return pos;
47
48         // something deliminited _is_ an argument
49         if ((*pos)->asDelimInset()) {
50                 ar.push_back(*pos);
51                 return pos + 1;
52         }
53
54         // always take the first thing, no matter what it is
55         ar.push_back(*pos);
56
57         // go ahead if possible
58         ++pos;
59         if (pos == last)
60                 return pos;
61
62         // if the next item is a subscript, it most certainly belongs to the
63         // thing we have
64         if ((*pos)->asScriptInset()) {
65                 ar.push_back(*pos);
66                 // go ahead if possible
67                 ++pos;
68                 if (pos == last)
69                         return pos;
70         }
71
72         // but it might be more than that.
73         // FIXME: not implemented
74         //for (MathArray::iterator it = pos + 1; it != last; ++it) {
75         //      // always take the first thing, no matter
76         //      if (it == pos) {
77         //              ar.push_back(*it);
78         //              continue;
79         //      }
80         //}
81         return pos;
82 }
83
84
85 MathScriptInset const * asScript(MathArray::const_iterator it)
86 {
87         if (it->nucleus()->asScriptInset())
88                 return 0;
89         ++it;
90         if (!it->nucleus())
91                 return 0;
92         return it->nucleus()->asScriptInset();
93 }
94
95
96
97 // returns sequence of char with same code starting at it up to end
98 // it might be less, though...
99 string charSequence(MathArray::const_iterator it, MathArray::const_iterator end)
100 {
101         string s;
102         MathCharInset const * p = it->nucleus()->asCharInset();
103         if (p) {
104                 for (MathTextCodes c = p->code(); it != end; ++it) {
105                         p = it->nucleus()->asCharInset();
106                         if (!p || p->code() != c)
107                                 break;
108                         s += p->getChar();
109                 }
110         }
111         return s;
112 }
113
114
115 void extractStrings(MathArray & dat)
116 {
117         //lyxerr << "\nStrings from: " << ar << "\n";
118         MathArray ar;
119         MathArray::const_iterator it = dat.begin();
120         while (it != dat.end()) {
121                 if (it->nucleus() && it->nucleus()->asCharInset()) {
122                         string s = charSequence(it, dat.end());
123                         MathTextCodes c = it->nucleus()->asCharInset()->code();
124                         ar.push_back(MathAtom(new MathStringInset(s, c)));
125                         it += s.size();
126                 } else {
127                         ar.push_back(*it);
128                         ++it;
129                 }
130         }
131         ar.swap(dat);
132         //lyxerr << "\nStrings to: " << ar << "\n";
133 }
134
135
136 MathInset * singleItem(MathArray & ar)
137 {
138         return ar.size() == 1 ? ar.begin()->nucleus() : 0;
139 }
140
141
142 void extractMatrices(MathArray & ar)
143 {
144         lyxerr << "\nMatrices from: " << ar << "\n";
145         for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it) {
146                 MathDelimInset * del = (*it)->asDelimInset();
147                 if (!del)
148                         continue;
149                 MathInset * arr = singleItem(del->cell(0));
150                 if (!arr || !arr->asArrayInset())
151                         continue;
152                 *it = MathAtom(new MathMatrixInset(*(arr->asArrayInset())));
153         }
154         lyxerr << "\nMatrices to: " << ar << "\n";
155 }
156
157
158 // convert this inset somehow to a string
159 string extractString(MathInset * p)
160 {
161         if (p && p->getChar())
162                 return string(1, p->getChar());
163         if (p && p->asStringInset())
164                 return p->asStringInset()->str();
165         return string();
166 }
167
168
169 bool stringTest(MathInset * p, const string & str)
170 {
171         return extractString(p) == str;
172 }
173
174
175 // search end of nested sequence
176 MathArray::iterator endNestSearch(
177         MathArray::iterator it,
178         MathArray::iterator last,
179         TestItemFunc testOpen,
180         TestItemFunc testClose
181 )
182 {
183         for (int level = 0; it != last; ++it) {
184                 if (testOpen(it->nucleus()))
185                         ++level;
186                 if (testClose(it->nucleus()))
187                         --level;
188                 if (level == 0)
189                         break;
190         }
191         return it;
192 }
193
194
195 // replace nested sequences by a real Insets
196 void replaceNested(
197         MathArray & ar,
198         TestItemFunc testOpen,
199         TestItemFunc testClose,
200         ReplaceArgumentFunc replaceArg
201 )
202 {
203         // use indices rather than iterators for the loop  because we are going
204         // to modify the array.
205         for (MathArray::size_type i = 0; i < ar.size(); ++i) {
206                 // check whether this is the begin of the sequence
207                 MathArray::iterator it = ar.begin() + i;
208                 if (!testOpen(it->nucleus()))
209                         continue;
210
211                 // search end of sequence
212                 MathArray::iterator jt = endNestSearch(it, ar.end(), testOpen, testClose);
213                 if (jt == ar.end())
214                         continue;
215
216                 // create a proper inset as replacement
217                 MathInset * p = replaceArg(MathArray(it + 1, jt));
218
219                 // replace the original stuff by the new inset
220                 ar.erase(it + 1, jt + 1);
221                 (*it).reset(p);
222         }
223
224
225
226 //
227 // search deliminiters
228 //
229
230 bool openParanTest(MathInset * p)
231 {
232         return stringTest(p, "(");
233 }
234
235
236 bool closeParanTest(MathInset * p)
237 {
238         return stringTest(p, ")");
239 }
240
241
242 MathInset * delimReplacement(const MathArray & ar)
243 {
244         MathDelimInset * del = new MathDelimInset("(", ")");
245         del->cell(0) = ar;
246         return del;
247 }
248
249
250 // replace '('...')' sequences by a real MathDelimInset
251 void extractDelims(MathArray & ar)
252 {
253         lyxerr << "\nDelims from: " << ar << "\n";
254         replaceNested(ar, openParanTest, closeParanTest, delimReplacement);
255         lyxerr << "\nDelims to: " << ar << "\n";
256 }
257
258
259
260 //
261 // search well-known functions
262 //
263
264
265 // replace 'f' '(...)' and 'f' '^n' '(...)' sequences by a real MathExFuncInset
266 // assume 'extractDelims' ran before
267 void extractFunctions(MathArray & ar)
268 {
269         // we need at least two items...
270         if (ar.size() <= 1)
271                 return;
272
273         lyxerr << "\nFunctions from: " << ar << "\n";
274         for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
275                 MathArray::iterator it = ar.begin() + i;
276
277                 // is this a function name?
278                 MathFuncInset * func = (*it)->asFuncInset();
279                 if (!func)
280                         continue;
281
282                 // do we have an exponent?
283                 // simply skippping the postion does the right thing:
284                 // 'sin' '^2' 'x' -> 'sin(x)' '^2'
285                 MathArray::iterator jt = it + 1;
286                 if (MathScriptInset * script = (*jt)->asScriptInset()) {
287                         // allow superscripts only
288                         if (script->hasDown())
289                                 continue;
290                         ++jt;
291                         if (jt == ar.end())
292                                 continue;
293                 }
294         
295                 // create a proper inset as replacement
296                 MathExFuncInset * p = new MathExFuncInset(func->name());
297
298                 // jt points to the "argument". Get hold of this.
299                 MathArray::iterator st = extractArgument(p->cell(0), jt, ar.end());
300
301                 // replace the function name by a real function inset
302                 (*it).reset(p);
303                 
304                 // remove the source of the argument from the array
305                 ar.erase(jt, st);
306                 lyxerr << "\nFunctions to: " << ar << "\n";
307         }
308
309
310
311 //
312 // search integrals
313 //
314
315 bool symbolTest(MathInset * p, string const & name)
316 {
317         return p->asSymbolInset() && p->asSymbolInset()->name() == name;
318 }
319
320
321 bool intSymbolTest(MathInset * p)
322 {
323         return symbolTest(p, "int");
324 }
325
326
327 bool intDiffTest(MathInset * p)
328 {
329         return stringTest(p, "d");
330 }
331
332
333 // replace '\int' ['_^'] x 'd''x'(...)' sequences by a real MathExIntInset
334 // assume 'extractDelims' ran before
335 void extractIntegrals(MathArray & ar)
336 {
337         // we need at least three items...
338         if (ar.size() <= 2)
339                 return;
340
341         lyxerr << "\nIntegrals from: " << ar << "\n";
342         for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
343                 MathArray::iterator it = ar.begin() + i;
344
345                 // is this a integral name?
346                 if (!intSymbolTest(it->nucleus()))
347                         continue;
348
349                 // search 'd'
350                 MathArray::iterator jt =
351                         endNestSearch(it, ar.end(), intSymbolTest, intDiffTest);
352
353                 // something sensible found?
354                 if (jt == ar.end())
355                         continue;
356
357                 // create a proper inset as replacement
358                 MathExIntInset * p = new MathExIntInset("int");
359
360                 // collect scripts
361                 MathArray::iterator st = it + 1;
362                 if ((*st)->asScriptInset()) {
363                         p->scripts(*st);
364                         p->cell(0) = MathArray(st + 1, jt);
365                 } else {
366                         p->cell(0) = MathArray(st, jt);
367                 }
368
369                 // use the atom behind the 'd' as differential
370                 MathArray::iterator tt = extractArgument(p->cell(1), jt + 1, ar.end());
371                 
372                 // remove used parts
373                 ar.erase(it + 1, tt);
374                 (*it).reset(p);
375         }
376         lyxerr << "\nIntegrals to: " << ar << "\n";
377 }
378
379
380 //
381 // search sums
382 //
383
384 bool sumSymbolTest(MathInset * p)
385 {
386         return p->asSymbolInset() && p->asSymbolInset()->name() == "sum";
387 }
388
389
390 bool equalSign(MathInset * p)
391 {
392         return stringTest(p, "=");
393 }
394
395
396 bool equalSign1(MathAtom const & at)
397 {
398         return equalSign(at.nucleus());
399 }
400
401
402
403 // replace '\sum' ['_^'] f(x) sequences by a real MathExIntInset
404 // assume 'extractDelims' ran before
405 void extractSums(MathArray & ar)
406 {
407         // we need at least two items...
408         if (ar.size() <= 1)
409                 return;
410
411         lyxerr << "\nSums from: " << ar << "\n";
412         for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
413                 MathArray::iterator it = ar.begin() + i;
414
415                 // is this a sum name?
416                 if (!sumSymbolTest(it->nucleus()))
417                         continue;
418
419                 // create a proper inset as replacement
420                 MathExIntInset * p = new MathExIntInset("sum");
421
422                 // collect scripts
423                 MathArray::iterator st = it + 1;
424                 if (st != ar.end() && (*st)->asScriptInset()) {
425                         p->scripts(*st);
426                         ++st;
427
428                         // try to figure out the summation index from the subscript
429                         MathScriptInset * script = p->scripts()->asScriptInset();
430                         if (script->hasDown()) {
431                                 MathArray & ar = script->down().data_;
432                                 MathArray::iterator it =
433                                         std::find_if(ar.begin(), ar.end(), &equalSign1);
434                                 if (it != ar.end()) {
435                                         // we found a '=', use everything in front of that as index,
436                                         // and everything behind as start value
437                                         p->cell(1) = MathArray(ar.begin(), it);
438                                         ar.erase(ar.begin(), it + 1);
439                                 } else {
440                                         // use everything as summation index, don't use scripts.
441                                         p->cell(1) = ar;
442                                         p->scripts().reset(0);
443                                 }
444                         }
445                 }
446
447                 // use some  behind the script as core
448                 MathArray::iterator tt = extractArgument(p->cell(0), st, ar.end());
449
450                 // cleanup
451                 ar.erase(it + 1, tt);
452                 (*it).reset(p);
453         }
454         lyxerr << "\nSums to: " << ar << "\n";
455 }
456
457
458 //
459 // search differential stuff
460 //
461
462 // tests for 'd' or '\partial'
463 bool diffItemTest(MathInset * p)
464 {
465         return stringTest(p, "d");
466 }
467
468
469 bool diffItemTest(MathArray const & ar)
470 {
471         return ar.size() && diffItemTest(ar.front().nucleus());
472 }
473
474
475 bool diffFracTest(MathInset * p)
476 {
477         return
478                 p->asFracInset() &&
479                 diffItemTest(p->asFracInset()->cell(0)) &&
480                 diffItemTest(p->asFracInset()->cell(1));
481 }
482
483 void extractDiff(MathArray & ar)
484 {
485         lyxerr << "\nDiffs from: " << ar << "\n";
486         for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
487                 MathArray::iterator it = ar.begin() + i;
488
489                 // is this a "differential fraction"?
490                 if (!diffFracTest(it->nucleus()))
491                         continue;
492                 
493                 MathFracInset * f = (*it)->asFracInset();
494                 if (!f) {
495                         lyxerr << "should not happen\n";
496                         continue;
497                 }
498
499                 // create a proper diff inset
500                 MathDiffInset * p = new MathDiffInset;
501
502                 // collect function
503                 MathArray::iterator jt = it + 1; 
504                 if (f->cell(0).size() > 1)
505                         p->cell(0) = MathArray(f->cell(0).begin() + 1, f->cell(0).end());
506                 else 
507                         jt = extractArgument(p->cell(0), jt, ar.end());
508                 
509                 // collect denominator
510                 
511
512                 // cleanup
513                 ar.erase(it + 1, jt);
514                 (*it).reset(p);
515         }
516         lyxerr << "\nDiffs to: " << ar << "\n";
517 }
518
519 //
520 // combine searches
521 //
522
523 void extractStructure(MathArray & ar)
524 {
525         extractMatrices(ar);
526         extractDelims(ar);
527         extractFunctions(ar);
528         extractIntegrals(ar);
529         extractSums(ar);
530         extractDiff(ar);
531         extractStrings(ar);
532 }
533
534
535 void write(MathArray const & dat, WriteStream & wi)
536 {
537         MathArray ar = dat;
538         extractStrings(ar);
539         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
540                 wi.firstitem = (it == ar.begin());
541                 MathInset const * p = it->nucleus();
542                 if (it + 1 != ar.end()) {
543                         if (MathScriptInset const * q = asScript(it)) {
544                                 q->write(p, wi);
545                                 ++it;
546                                 continue;
547                         } 
548                 }
549                 p->write(wi);
550         }
551 }
552
553
554 void normalize(MathArray const & ar, NormalStream & os)
555 {
556         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
557                 (*it)->normalize(os);
558 }
559
560
561 void octavize(MathArray const & dat, OctaveStream & os)
562 {
563         MathArray ar = dat;
564         extractStructure(ar);
565         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
566                 MathInset const * p = it->nucleus();
567                 if (it + 1 != ar.end()) {
568                         if (MathScriptInset const * q = asScript(it)) {
569                                 q->octavize(p, os);
570                                 ++it;   
571                                 continue;
572                         }
573                 }
574                 p->octavize(os);
575         }
576 }
577
578
579 void maplize(MathArray const & dat, MapleStream & os)
580 {
581         MathArray ar = dat;
582         extractStructure(ar);
583         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
584                 MathInset const * p = it->nucleus();
585                 if (it + 1 != ar.end()) {
586                         if (MathScriptInset const * q = asScript(it)) {
587                                 q->maplize(p, os);
588                                 ++it;   
589                                 continue;
590                         }
591                 }
592                 p->maplize(os);
593         }
594 }
595
596
597 void mathmlize(MathArray const & dat, MathMLStream & os)
598 {
599         MathArray ar = dat;
600         extractStructure(ar);
601         if (ar.size() == 0)
602                 os << "<mrow/>";
603         else if (ar.size() == 1)
604                 os << ar.begin()->nucleus();
605         else {
606                 os << MTag("mrow");
607                 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
608                         MathInset const * p = it->nucleus();
609                         if (it + 1 != ar.end()) {
610                                 if (MathScriptInset const * q = asScript(it)) {
611                                         q->mathmlize(p, os);
612                                         ++it;   
613                                         continue;
614                                 }
615                         }
616                         p->mathmlize(os);
617                 }
618                 os << ETag("mrow");
619         }
620 }
621