]> git.lyx.org Git - lyx.git/blob - src/mathed/MathExtern.cpp
We do not need to extract limits for MathML.
[lyx.git] / src / mathed / MathExtern.cpp
1 /**
2  * \file MathExtern.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 // This file contains most of the magic that extracts "context
12 // information" from the unstructered layout-oriented stuff in
13 // MathData.
14
15 #include <config.h>
16
17 #include "MathExtern.h"
18
19 #include "InsetMathAMSArray.h"
20 #include "InsetMathArray.h"
21 #include "InsetMathChar.h"
22 #include "InsetMathDelim.h"
23 #include "InsetMathDiff.h"
24 #include "InsetMathExFunc.h"
25 #include "InsetMathExInt.h"
26 #include "InsetMathFont.h"
27 #include "InsetMathFrac.h"
28 #include "InsetMathLim.h"
29 #include "InsetMathMatrix.h"
30 #include "InsetMathNumber.h"
31 #include "InsetMathScript.h"
32 #include "InsetMathString.h"
33 #include "InsetMathSymbol.h"
34 #include "MathData.h"
35 #include "MathParser.h"
36 #include "MathStream.h"
37
38 #include "support/debug.h"
39 #include "support/docstream.h"
40 #include "support/FileName.h"
41 #include "support/filetools.h"
42 #include "support/lstrings.h"
43
44 #include <algorithm>
45 #include <sstream>
46 #include <fstream>
47 #include <memory>
48
49 using namespace std;
50 using namespace lyx::support;
51
52 namespace lyx {
53
54 namespace {
55
56 enum ExternalMath {
57         MAPLE,
58         MAXIMA,
59         MATHEMATICA,
60         MATHML,
61         OCTAVE
62 };
63
64
65 static char const * function_names[] = {
66         "arccos", "arcsin", "arctan", "arg", "bmod",
67         "cos", "cosh", "cot", "coth", "csc", "deg",
68         "det", "dim", "exp", "gcd", "hom", "inf", "ker",
69         "lg", "lim", "liminf", "limsup", "ln", "log",
70         "max", "min", "sec", "sin", "sinh", "sup",
71         "tan", "tanh", "Pr", 0
72 };
73
74 static size_t const npos = lyx::docstring::npos;
75
76 // define a function for tests
77 typedef bool TestItemFunc(MathAtom const &);
78
79 // define a function for replacing subexpressions
80 typedef MathAtom ReplaceArgumentFunc(const MathData & ar);
81
82
83
84 // try to extract a super/subscript
85 // modify iterator position to point behind the thing
86 bool extractScript(MathData & ar,
87         MathData::iterator & pos, MathData::iterator last, bool superscript)
88 {
89         // nothing to get here
90         if (pos == last)
91                 return false;
92
93         // is this a scriptinset?
94         if (!(*pos)->asScriptInset())
95                 return false;
96
97         // do we want superscripts only?
98         if (superscript && !(*pos)->asScriptInset()->hasUp())
99                 return false;
100
101         // it is a scriptinset, use it.
102         ar.push_back(*pos);
103         ++pos;
104         return true;
105 }
106
107
108 // try to extract an "argument" to some function.
109 // returns position behind the argument
110 MathData::iterator extractArgument(MathData & ar,
111         MathData::iterator pos, MathData::iterator last, 
112         ExternalMath kind, bool function = false)
113 {
114         // nothing to get here
115         if (pos == last)
116                 return pos;
117
118         // something delimited _is_ an argument
119         if ((*pos)->asDelimInset()) {
120                 // leave out delimiters if this is a function argument
121                 // unless we are doing MathML, in which case we do want
122                 // the delimiters
123                 if (function && kind != MATHML) {
124                         MathData const & arg = (*pos)->asDelimInset()->cell(0);
125                         MathData::const_iterator cur = arg.begin();
126                         MathData::const_iterator end = arg.end();
127                         while (cur != end)
128                                 ar.push_back(*cur++);
129                 } else
130                         ar.push_back(*pos);
131                 ++pos;
132                 if (pos == last)
133                         return pos;
134                 // if there's one, get following superscript only if this
135                 // isn't a function argument
136                 if (!function)
137                         extractScript(ar, pos, last, true);
138                 return pos;
139         }
140
141         // always take the first thing, no matter what it is
142         ar.push_back(*pos);
143
144         // go ahead if possible
145         ++pos;
146         if (pos == last)
147                 return pos;
148
149         // if the next item is a super/subscript, it most certainly belongs
150         // to the thing we have
151         extractScript(ar, pos, last, false);
152         if (pos == last)
153                 return pos;
154
155         // but it might be more than that.
156         // FIXME: not implemented
157         //for (MathData::iterator it = pos + 1; it != last; ++it) {
158         //      // always take the first thing, no matter
159         //      if (it == pos) {
160         //              ar.push_back(*it);
161         //              continue;
162         //      }
163         //}
164         return pos;
165 }
166
167
168 // returns sequence of char with same code starting at it up to end
169 // it might be less, though...
170 docstring charSequence
171         (MathData::const_iterator it, MathData::const_iterator end)
172 {
173         docstring s;
174         for (; it != end && (*it)->asCharInset(); ++it)
175                 s += (*it)->getChar();
176         return s;
177 }
178
179
180 void extractStrings(MathData & ar)
181 {
182         //lyxerr << "\nStrings from: " << ar << endl;
183         for (size_t i = 0; i < ar.size(); ++i) {
184                 if (!ar[i]->asCharInset())
185                         continue;
186                 docstring s = charSequence(ar.begin() + i, ar.end());
187                 ar[i] = MathAtom(new InsetMathString(s));
188                 ar.erase(i + 1, i + s.size());
189         }
190         //lyxerr << "\nStrings to: " << ar << endl;
191 }
192
193
194 void extractMatrices(MathData & ar)
195 {
196         //lyxerr << "\nMatrices from: " << ar << endl;
197         // first pass for explicitly delimited stuff
198         for (size_t i = 0; i < ar.size(); ++i) {
199                 InsetMathDelim const * const inset = ar[i]->asDelimInset();
200                 if (!inset)
201                         continue;
202                 MathData const & arr = inset->cell(0);
203                 if (arr.size() != 1)
204                         continue;
205                 if (!arr.front()->asGridInset())
206                         continue;
207                 ar[i] = MathAtom(new InsetMathMatrix(*(arr.front()->asGridInset()), 
208                                  inset->left_, inset->right_));
209         }
210
211         // second pass for AMS "pmatrix" etc
212         for (size_t i = 0; i < ar.size(); ++i) {
213                 InsetMathAMSArray const * const inset = ar[i]->asAMSArrayInset();
214                 if (inset) {
215                         string left = inset->name_left();
216                         if (left == "Vert")
217                                 left = "[";
218                         string right = inset->name_right();
219                         if (right == "Vert")
220                                 right = "]";
221                         ar[i] = MathAtom(new InsetMathMatrix(*inset, from_ascii(left), from_ascii(right)));
222                 }
223         }
224         //lyxerr << "\nMatrices to: " << ar << endl;
225 }
226
227
228 // convert this inset somehow to a string
229 bool extractString(MathAtom const & at, docstring & str)
230 {
231         if (at->getChar()) {
232                 str = docstring(1, at->getChar());
233                 return true;
234         }
235         if (at->asStringInset()) {
236                 str = at->asStringInset()->str();
237                 return true;
238         }
239         return false;
240 }
241
242
243 // is this a known function?
244 bool isKnownFunction(docstring const & str)
245 {
246         for (int i = 0; function_names[i]; ++i) {
247                 if (str == function_names[i])
248                         return true;
249         }
250         return false;
251 }
252
253
254 // extract a function name from this inset
255 bool extractFunctionName(MathAtom const & at, docstring & str)
256 {
257         if (at->asSymbolInset()) {
258                 str = at->asSymbolInset()->name();
259                 return isKnownFunction(str);
260         }
261         if (at->asUnknownInset()) {
262                 // assume it is well known...
263                 str = at->name();
264                 return true;
265         }
266         if (at->asFontInset() && at->name() == "mathrm") {
267                 // assume it is well known...
268                 MathData const & ar = at->asFontInset()->cell(0);
269                 str = charSequence(ar.begin(), ar.end());
270                 return ar.size() == str.size();
271         }
272         return false;
273 }
274
275
276 bool testString(MathAtom const & at, docstring const & str)
277 {
278         docstring s;
279         return extractString(at, s) && str == s;
280 }
281
282
283 bool testString(MathAtom const & at, char const * const str)
284 {
285         return testString(at, from_ascii(str));
286 }
287
288 // search end of nested sequence
289 MathData::iterator endNestSearch(
290         MathData::iterator it,
291         MathData::iterator last,
292         TestItemFunc testOpen,
293         TestItemFunc testClose
294 )
295 {
296         for (int level = 0; it != last; ++it) {
297                 if (testOpen(*it))
298                         ++level;
299                 if (testClose(*it))
300                         --level;
301                 if (level == 0)
302                         break;
303         }
304         return it;
305 }
306
307
308 // replace nested sequences by a real Insets
309 void replaceNested(
310         MathData & ar,
311         TestItemFunc testOpen,
312         TestItemFunc testClose,
313         ReplaceArgumentFunc replaceArg)
314 {
315         Buffer * buf = ar.buffer();
316         // use indices rather than iterators for the loop  because we are going
317         // to modify the array.
318         for (size_t i = 0; i < ar.size(); ++i) {
319                 // check whether this is the begin of the sequence
320                 if (!testOpen(ar[i]))
321                         continue;
322
323                 // search end of sequence
324                 MathData::iterator it = ar.begin() + i;
325                 MathData::iterator jt = endNestSearch(it, ar.end(), testOpen, testClose);
326                 if (jt == ar.end())
327                         continue;
328
329                 // replace the original stuff by the new inset
330                 ar[i] = replaceArg(MathData(buf, it + 1, jt));
331                 ar.erase(it + 1, jt + 1);
332         }
333 }
334
335
336
337 //
338 // split scripts into seperate super- and subscript insets. sub goes in
339 // front of super...
340 //
341
342 void splitScripts(MathData & ar)
343 {
344         Buffer * buf = ar.buffer();
345         //lyxerr << "\nScripts from: " << ar << endl;
346         for (size_t i = 0; i < ar.size(); ++i) {
347                 InsetMathScript const * script = ar[i]->asScriptInset();
348
349                 // is this a script inset and do we also have a superscript?
350                 if (!script || !script->hasUp())
351                         continue;
352
353                 // we must have a nucleus if we only have a superscript
354                 if (!script->hasDown() && script->nuc().size() == 0)
355                         continue;
356
357                 if (script->nuc().size() == 1) {
358                         // leave alone sums and integrals
359                         InsetMathSymbol const * sym =
360                                 script->nuc().front()->asSymbolInset();
361                         if (sym && (sym->name() == "sum" || sym->name() == "int"))
362                                 continue;
363                 }
364
365                 // create extra script inset and move superscript over
366                 InsetMathScript * p = ar[i].nucleus()->asScriptInset();
367                 auto_ptr<InsetMathScript> q(new InsetMathScript(buf, true));
368                 swap(q->up(), p->up());
369                 p->removeScript(true);
370
371                 // if we don't have a subscript, get rid of the ScriptInset
372                 if (!script->hasDown()) {
373                         MathData arg(p->nuc());
374                         MathData::const_iterator it = arg.begin();
375                         MathData::const_iterator et = arg.end();
376                         ar.erase(i);
377                         while (it != et)
378                                 ar.insert(i++, *it++);
379                 } else
380                         ++i;
381
382                 // insert new inset behind
383                 ar.insert(i, MathAtom(q.release()));
384         }
385         //lyxerr << "\nScripts to: " << ar << endl;
386 }
387
388
389 //
390 // extract exp(...)
391 //
392
393 void extractExps(MathData & ar)
394 {
395         Buffer * buf = ar.buffer();
396         //lyxerr << "\nExps from: " << ar << endl;
397         for (size_t i = 0; i + 1 < ar.size(); ++i) {
398                 // is this 'e'?
399                 if (ar[i]->getChar() != 'e')
400                         continue;
401
402                 // we need an exponent but no subscript
403                 InsetMathScript const * sup = ar[i + 1]->asScriptInset();
404                 if (!sup || sup->hasDown())
405                         continue;
406
407                 // create a proper exp-inset as replacement
408                 ar[i] = MathAtom(new InsetMathExFunc(buf, from_ascii("exp"), sup->cell(1)));
409                 ar.erase(i + 1);
410         }
411         //lyxerr << "\nExps to: " << ar << endl;
412 }
413
414
415 //
416 // extract det(...)  from |matrix|
417 //
418 void extractDets(MathData & ar)
419 {
420         Buffer * buf = ar.buffer();
421         //lyxerr << "\ndet from: " << ar << endl;
422         for (MathData::iterator it = ar.begin(); it != ar.end(); ++it) {
423                 InsetMathDelim const * del = (*it)->asDelimInset();
424                 if (!del)
425                         continue;
426                 if (!del->isAbs())
427                         continue;
428                 *it = MathAtom(new InsetMathExFunc(buf, from_ascii("det"), del->cell(0)));
429         }
430         //lyxerr << "\ndet to: " << ar << endl;
431 }
432
433
434 //
435 // search numbers
436 //
437
438 bool isDigitOrSimilar(char_type c)
439 {
440         return ('0' <= c && c <= '9') || c == '.';
441 }
442
443
444 // returns sequence of digits
445 docstring digitSequence
446         (MathData::const_iterator it, MathData::const_iterator end)
447 {
448         docstring s;
449         for (; it != end && (*it)->asCharInset(); ++it) {
450                 if (!isDigitOrSimilar((*it)->getChar()))
451                         break;
452                 s += (*it)->getChar();
453         }
454         return s;
455 }
456
457
458 void extractNumbers(MathData & ar)
459 {
460         //lyxerr << "\nNumbers from: " << ar << endl;
461         for (size_t i = 0; i < ar.size(); ++i) {
462                 if (!ar[i]->asCharInset())
463                         continue;
464                 if (!isDigitOrSimilar(ar[i]->asCharInset()->getChar()))
465                         continue;
466
467                 docstring s = digitSequence(ar.begin() + i, ar.end());
468
469                 ar[i] = MathAtom(new InsetMathNumber(s));
470                 ar.erase(i + 1, i + s.size());
471         }
472         //lyxerr << "\nNumbers to: " << ar << endl;
473 }
474
475
476
477 //
478 // search delimiters
479 //
480
481 bool testOpenParen(MathAtom const & at)
482 {
483         return testString(at, "(");
484 }
485
486
487 bool testCloseParen(MathAtom const & at)
488 {
489         return testString(at, ")");
490 }
491
492
493 MathAtom replaceParenDelims(const MathData & ar)
494 {
495         return MathAtom(new InsetMathDelim(const_cast<Buffer *>(ar.buffer()),
496                 from_ascii("("), from_ascii(")"), ar));
497 }
498
499
500 bool testOpenBracket(MathAtom const & at)
501 {
502         return testString(at, "[");
503 }
504
505
506 bool testCloseBracket(MathAtom const & at)
507 {
508         return testString(at, "]");
509 }
510
511
512 MathAtom replaceBracketDelims(const MathData & ar)
513 {
514         return MathAtom(new InsetMathDelim(const_cast<Buffer *>(ar.buffer()),
515                 from_ascii("["), from_ascii("]"), ar));
516 }
517
518
519 // replace '('...')' and '['...']' sequences by a real InsetMathDelim
520 void extractDelims(MathData & ar)
521 {
522         //lyxerr << "\nDelims from: " << ar << endl;
523         replaceNested(ar, testOpenParen, testCloseParen, replaceParenDelims);
524         replaceNested(ar, testOpenBracket, testCloseBracket, replaceBracketDelims);
525         //lyxerr << "\nDelims to: " << ar << endl;
526 }
527
528
529
530 //
531 // search well-known functions
532 //
533
534
535 // replace 'f' '(...)' and 'f' '^n' '(...)' sequences by a real InsetMathExFunc
536 // assume 'extractDelims' ran before
537 void extractFunctions(MathData & ar, ExternalMath kind)
538 {
539         // we need at least two items...
540         if (ar.size() < 2)
541                 return;
542
543         Buffer * buf = ar.buffer();
544
545         //lyxerr << "\nFunctions from: " << ar << endl;
546         for (size_t i = 0; i + 1 < ar.size(); ++i) {
547                 MathData::iterator it = ar.begin() + i;
548                 MathData::iterator jt = it + 1;
549
550                 docstring name;
551                 // is it a function?
552                 // it certainly is if it is well known...
553                 if (!extractFunctionName(*it, name)) {
554                         // is this a user defined function?
555                         // it it probably not, if it doesn't have a name.
556                         if (!extractString(*it, name))
557                                 continue;
558                         // it is not if it has no argument
559                         if (jt == ar.end())
560                                 continue;
561                         // guess so, if this is followed by
562                         // a DelimInset with a single item in the cell
563                         InsetMathDelim const * del = (*jt)->asDelimInset();
564                         if (!del || del->cell(0).size() != 1)
565                                 continue;
566                         // fall trough into main branch
567                 }
568
569                 // do we have an exponent like in
570                 // 'sin' '^2' 'x' -> 'sin(x)' '^2'
571                 MathData exp;
572                 extractScript(exp, jt, ar.end(), true);
573
574                 // create a proper inset as replacement
575                 auto_ptr<InsetMathExFunc> p(new InsetMathExFunc(buf, name));
576
577                 // jt points to the "argument". Get hold of this.
578                 MathData::iterator st = 
579                                 extractArgument(p->cell(0), jt, ar.end(), kind, true);
580
581                 // replace the function name by a real function inset
582                 *it = MathAtom(p.release());
583
584                 // remove the source of the argument from the array
585                 ar.erase(it + 1, st);
586
587                 // re-insert exponent
588                 ar.insert(i + 1, exp);
589                 //lyxerr << "\nFunctions to: " << ar << endl;
590         }
591 }
592
593
594 //
595 // search integrals
596 //
597
598 bool testSymbol(MathAtom const & at, docstring const & name)
599 {
600         return at->asSymbolInset() && at->asSymbolInset()->name() == name;
601 }
602
603
604 bool testSymbol(MathAtom const & at, char const * const name)
605 {
606         return at->asSymbolInset() && at->asSymbolInset()->name() == from_ascii(name);
607 }
608
609
610 bool testIntSymbol(MathAtom const & at)
611 {
612         return testSymbol(at, from_ascii("int"));
613 }
614
615
616 bool testIntegral(MathAtom const & at)
617 {
618         return
619          testIntSymbol(at) ||
620                 ( at->asScriptInset()
621                   && at->asScriptInset()->nuc().size()
622                         && testIntSymbol(at->asScriptInset()->nuc().back()) );
623 }
624
625
626
627 bool testIntDiff(MathAtom const & at)
628 {
629         return testString(at, "d");
630 }
631
632
633 // replace '\int' ['_^'] x 'd''x'(...)' sequences by a real InsetMathExInt
634 // assume 'extractDelims' ran before
635 void extractIntegrals(MathData & ar, ExternalMath kind)
636 {
637         // we need at least three items...
638         if (ar.size() < 3)
639                 return;
640
641         Buffer * buf = ar.buffer();
642
643         //lyxerr << "\nIntegrals from: " << ar << endl;
644         for (size_t i = 0; i + 1 < ar.size(); ++i) {
645                 MathData::iterator it = ar.begin() + i;
646
647                 // search 'd'
648                 MathData::iterator jt =
649                         endNestSearch(it, ar.end(), testIntegral, testIntDiff);
650
651                 // something sensible found?
652                 if (jt == ar.end())
653                         continue;
654
655                 // is this a integral name?
656                 if (!testIntegral(*it))
657                         continue;
658
659                 // core ist part from behind the scripts to the 'd'
660                 auto_ptr<InsetMathExInt> p(new InsetMathExInt(buf, from_ascii("int")));
661
662                 // handle scripts if available
663                 if (!testIntSymbol(*it)) {
664                         p->cell(2) = (*it)->asScriptInset()->down();
665                         p->cell(3) = (*it)->asScriptInset()->up();
666                 }
667                 p->cell(0) = MathData(buf, it + 1, jt);
668
669                 // use the "thing" behind the 'd' as differential
670                 MathData::iterator tt = extractArgument(p->cell(1), jt + 1, ar.end(), kind);
671
672                 // remove used parts
673                 ar.erase(it + 1, tt);
674                 *it = MathAtom(p.release());
675         }
676         //lyxerr << "\nIntegrals to: " << ar << endl;
677 }
678
679
680 bool testTermDelimiter(MathAtom const & at)
681 {
682         return testString(at, "+") || testString(at, "-");
683 }
684
685
686 // try to extract a "term", i.e., something delimited by '+' or '-'.
687 // returns position behind the term
688 MathData::iterator extractTerm(MathData & ar,
689         MathData::iterator pos, MathData::iterator last)
690 {
691         while (pos != last && !testTermDelimiter(*pos)) {
692                 ar.push_back(*pos);
693                 ++pos;
694         }
695         return pos;
696 }
697
698
699 //
700 // search sums
701 //
702
703
704 bool testEqualSign(MathAtom const & at)
705 {
706         return testString(at, "=");
707 }
708
709
710 bool testSumSymbol(MathAtom const & p)
711 {
712         return testSymbol(p, from_ascii("sum"));
713 }
714
715
716 bool testSum(MathAtom const & at)
717 {
718         return
719          testSumSymbol(at) ||
720                 ( at->asScriptInset()
721                   && at->asScriptInset()->nuc().size()
722                         && testSumSymbol(at->asScriptInset()->nuc().back()) );
723 }
724
725
726 // replace '\sum' ['_^'] f(x) sequences by a real InsetMathExInt
727 // assume 'extractDelims' ran before
728 void extractSums(MathData & ar)
729 {
730         // we need at least two items...
731         if (ar.size() < 2)
732                 return;
733
734         Buffer * buf = ar.buffer();
735
736         //lyxerr << "\nSums from: " << ar << endl;
737         for (size_t i = 0; i + 1 < ar.size(); ++i) {
738                 MathData::iterator it = ar.begin() + i;
739
740                 // is this a sum name?
741                 if (!testSum(ar[i]))
742                         continue;
743
744                 // create a proper inset as replacement
745                 auto_ptr<InsetMathExInt> p(new InsetMathExInt(buf, from_ascii("sum")));
746
747                 // collect lower bound and summation index
748                 InsetMathScript const * sub = ar[i]->asScriptInset();
749                 if (sub && sub->hasDown()) {
750                         // try to figure out the summation index from the subscript
751                         MathData const & ar = sub->down();
752                         MathData::const_iterator xt =
753                                 find_if(ar.begin(), ar.end(), &testEqualSign);
754                         if (xt != ar.end()) {
755                                 // we found a '=', use everything in front of that as index,
756                                 // and everything behind as lower index
757                                 p->cell(1) = MathData(buf, ar.begin(), xt);
758                                 p->cell(2) = MathData(buf, xt + 1, ar.end());
759                         } else {
760                                 // use everything as summation index, don't use scripts.
761                                 p->cell(1) = ar;
762                         }
763                 }
764
765                 // collect upper bound
766                 if (sub && sub->hasUp())
767                         p->cell(3) = sub->up();
768
769                 // use something  behind the script as core
770                 MathData::iterator tt = extractTerm(p->cell(0), it + 1, ar.end());
771
772                 // cleanup
773                 ar.erase(it + 1, tt);
774                 *it = MathAtom(p.release());
775         }
776         //lyxerr << "\nSums to: " << ar << endl;
777 }
778
779
780 //
781 // search differential stuff
782 //
783
784 // tests for 'd' or '\partial'
785 bool testDiffItem(MathAtom const & at)
786 {
787         if (testString(at, "d") || testSymbol(at, "partial"))
788                 return true;
789
790         // we may have d^n .../d and splitScripts() has not yet seen it
791         InsetMathScript const * sup = at->asScriptInset();
792         if (sup && !sup->hasDown() && sup->hasUp() && sup->nuc().size() == 1) {
793                 MathAtom const & ma = sup->nuc().front();
794                 return testString(ma, "d") || testSymbol(ma, "partial");
795         }
796         return false;
797 }
798
799
800 bool testDiffArray(MathData const & ar)
801 {
802         return ar.size() && testDiffItem(ar.front());
803 }
804
805
806 bool testDiffFrac(MathAtom const & at)
807 {
808         return
809                 at->asFracInset()
810                         && testDiffArray(at->asFracInset()->cell(0))
811                         && testDiffArray(at->asFracInset()->cell(1));
812 }
813
814
815 void extractDiff(MathData & ar)
816 {
817         Buffer * buf = ar.buffer();
818         //lyxerr << "\nDiffs from: " << ar << endl;
819         for (size_t i = 0; i < ar.size(); ++i) {
820                 MathData::iterator it = ar.begin() + i;
821
822                 // is this a "differential fraction"?
823                 if (!testDiffFrac(*it))
824                         continue;
825
826                 InsetMathFrac const * f = (*it)->asFracInset();
827                 if (!f) {
828                         lyxerr << "should not happen" << endl;
829                         continue;
830                 }
831
832                 // create a proper diff inset
833                 auto_ptr<InsetMathDiff> diff(new InsetMathDiff(buf));
834
835                 // collect function, let jt point behind last used item
836                 MathData::iterator jt = it + 1;
837                 //int n = 1;
838                 MathData numer(f->cell(0));
839                 splitScripts(numer);
840                 if (numer.size() > 1 && numer[1]->asScriptInset()) {
841                         // this is something like  d^n f(x) / d... or  d^n / d...
842                         // FIXME
843                         //n = 1;
844                         if (numer.size() > 2)
845                                 diff->cell(0) = MathData(buf, numer.begin() + 2, numer.end());
846                         else
847                                 jt = extractTerm(diff->cell(0), jt, ar.end());
848                 } else {
849                         // simply d f(x) / d... or  d/d...
850                         if (numer.size() > 1)
851                                 diff->cell(0) = MathData(buf, numer.begin() + 1, numer.end());
852                         else
853                                 jt = extractTerm(diff->cell(0), jt, ar.end());
854                 }
855
856                 // collect denominator parts
857                 MathData denom(f->cell(1));
858                 splitScripts(denom);
859                 for (MathData::iterator dt = denom.begin(); dt != denom.end();) {
860                         // find the next 'd'
861                         MathData::iterator et
862                                 = find_if(dt + 1, denom.end(), &testDiffItem);
863
864                         // point before this
865                         MathData::iterator st = et - 1;
866                         InsetMathScript const * script = (*st)->asScriptInset();
867                         if (script && script->hasUp()) {
868                                 // things like   d.../dx^n
869                                 int mult = 1;
870                                 if (extractNumber(script->up(), mult)) {
871                                         //lyxerr << "mult: " << mult << endl;
872                                         for (int i = 0; i < mult; ++i)
873                                                 diff->addDer(MathData(buf, dt + 1, st));
874                                 }
875                         } else {
876                                 // just  d.../dx
877                                 diff->addDer(MathData(buf, dt + 1, et));
878                         }
879                         dt = et;
880                 }
881
882                 // cleanup
883                 ar.erase(it + 1, jt);
884                 *it = MathAtom(diff.release());
885         }
886         //lyxerr << "\nDiffs to: " << ar << endl;
887 }
888
889
890 //
891 // search limits
892 //
893
894
895 bool testRightArrow(MathAtom const & at)
896 {
897         return testSymbol(at, "to") || testSymbol(at, "rightarrow");
898 }
899
900
901
902 // replace '\lim_{x->x0} f(x)' sequences by a real InsetMathLim
903 // assume 'extractDelims' ran before
904 void extractLims(MathData & ar)
905 {
906         Buffer * buf = ar.buffer();
907         //lyxerr << "\nLimits from: " << ar << endl;
908         for (size_t i = 0; i < ar.size(); ++i) {
909                 MathData::iterator it = ar.begin() + i;
910
911                 // must be a script inset with a subscript (without superscript)
912                 InsetMathScript const * sub = (*it)->asScriptInset();
913                 if (!sub || !sub->hasDown() || sub->hasUp() || sub->nuc().size() != 1)
914                         continue;
915
916                 // is this a limit function?
917                 if (!testSymbol(sub->nuc().front(), "lim"))
918                         continue;
919
920                 // subscript must contain a -> symbol
921                 MathData const & s = sub->down();
922                 MathData::const_iterator st = find_if(s.begin(), s.end(), &testRightArrow);
923                 if (st == s.end())
924                         continue;
925
926                 // the -> splits the subscript int x and x0
927                 MathData x  = MathData(buf, s.begin(), st);
928                 MathData x0 = MathData(buf, st + 1, s.end());
929
930                 // use something behind the script as core
931                 MathData f;
932                 MathData::iterator tt = extractTerm(f, it + 1, ar.end());
933
934                 // cleanup
935                 ar.erase(it + 1, tt);
936
937                 // create a proper inset as replacement
938                 *it = MathAtom(new InsetMathLim(buf, f, x, x0));
939         }
940         //lyxerr << "\nLimits to: " << ar << endl;
941 }
942
943
944 //
945 // combine searches
946 //
947
948 void extractStructure(MathData & ar, ExternalMath kind)
949 {
950         //lyxerr << "\nStructure from: " << ar << endl;
951         if (kind != MATHML)
952                 splitScripts(ar);
953         extractDelims(ar);
954         extractIntegrals(ar, kind);
955         if (kind != MATHML)
956                 extractSums(ar);
957         extractNumbers(ar);
958         extractMatrices(ar);
959         extractFunctions(ar, kind);
960         if (kind != MATHML) {
961                 extractDets(ar);
962                 extractDiff(ar);
963         }
964         extractExps(ar);
965         if (kind != MATHML) {
966                 extractLims(ar);
967                 extractStrings(ar);
968         }
969         //lyxerr << "\nStructure to: " << ar << endl;
970 }
971
972
973 namespace {
974
975         string captureOutput(string const & cmd, string const & data)
976         {
977                 // In order to avoid parsing problems with command interpreters
978                 // we pass input data through a file
979                 FileName const cas_tmpfile = FileName::tempName("casinput");
980                 if (cas_tmpfile.empty()) {
981                         lyxerr << "Warning: cannot create temporary file."
982                                << endl;
983                         return string();
984                 }
985                 ofstream os(cas_tmpfile.toFilesystemEncoding().c_str());
986                 os << data << endl;
987                 os.close();
988                 string command =  cmd + " < "
989                         + quoteName(cas_tmpfile.toFilesystemEncoding());
990                 lyxerr << "calling: " << cmd
991                        << "\ninput: '" << data << "'" << endl;
992                 cmd_ret const ret = runCommand(command);
993                 cas_tmpfile.removeFile();
994                 return ret.second;
995         }
996
997         size_t get_matching_brace(string const & str, size_t i)
998         {
999                 int count = 1;
1000                 size_t n = str.size();
1001                 while (i < n) {
1002                         i = str.find_first_of("{}", i+1);
1003                         if (i == npos)
1004                                 return i;
1005                         if (str[i] == '{')
1006                                 ++count;
1007                         else
1008                                 --count;
1009                         if (count == 0)
1010                                 return i;
1011                 }
1012                 return npos;
1013         }
1014
1015         size_t get_matching_brace_back(string const & str, size_t i)
1016         {
1017                 int count = 1;
1018                 while (i > 0) {
1019                         i = str.find_last_of("{}", i-1);
1020                         if (i == npos)
1021                                 return i;
1022                         if (str[i] == '}')
1023                                 ++count;
1024                         else
1025                                 --count;
1026                         if (count == 0)
1027                                 return i;
1028                 }
1029                 return npos;
1030         }
1031
1032         MathData pipeThroughMaxima(docstring const &, MathData const & ar)
1033         {
1034                 odocstringstream os;
1035                 MaximaStream ms(os);
1036                 ms << ar;
1037                 docstring expr = os.str();
1038                 docstring const header = from_ascii("simpsum:true;");
1039
1040                 string out;
1041                 for (int i = 0; i < 100; ++i) { // at most 100 attempts
1042                         // try to fix missing '*' the hard way
1043                         //
1044                         // > echo "2x;" | maxima
1045                         // ...
1046                         // (C1) Incorrect syntax: x is not an infix operator
1047                         // 2x;
1048                         //  ^
1049                         //
1050                         lyxerr << "checking expr: '" << to_utf8(expr) << "'" << endl;
1051                         docstring full = header + "tex(" + expr + ");";
1052                         out = captureOutput("maxima", to_utf8(full));
1053
1054                         // leave loop if expression syntax is probably ok
1055                         if (out.find("Incorrect syntax") == npos)
1056                                 break;
1057
1058                         // search line with "Incorrect syntax"
1059                         istringstream is(out);
1060                         string line;
1061                         while (is) {
1062                                 getline(is, line);
1063                                 if (line.find("Incorrect syntax") != npos)
1064                                         break;
1065                         }
1066
1067                         // 2nd next line is the one with caret
1068                         getline(is, line);
1069                         getline(is, line);
1070                         size_t pos = line.find('^');
1071                         lyxerr << "found caret at pos: '" << pos << "'" << endl;
1072                         if (pos == npos || pos < 4)
1073                                 break; // caret position not found
1074                         pos -= 4; // skip the "tex(" part
1075                         if (expr[pos] == '*')
1076                                 break; // two '*' in a row are definitely bad
1077                         expr.insert(pos, from_ascii("*"));
1078                 }
1079
1080                 vector<string> tmp = getVectorFromString(out, "$$");
1081                 if (tmp.size() < 2)
1082                         return MathData();
1083
1084                 out = subst(tmp[1], "\\>", string());
1085                 lyxerr << "output: '" << out << "'" << endl;
1086
1087                 // Ugly code that tries to make the result prettier
1088                 size_t i = out.find("\\mathchoice");
1089                 while (i != npos) {
1090                         size_t j = get_matching_brace(out, i + 12);
1091                         size_t k = get_matching_brace(out, j + 1);
1092                         k = get_matching_brace(out, k + 1);
1093                         k = get_matching_brace(out, k + 1);
1094                         string mid = out.substr(i + 13, j - i - 13);
1095                         if (mid.find("\\over") != npos)
1096                                 mid = '{' + mid + '}';
1097                         out = out.substr(0, i)
1098                                 + mid
1099                                 + out.substr(k + 1);
1100                         //lyxerr << "output: " << out << endl;
1101                         i = out.find("\\mathchoice", i);
1102                         break;
1103                 }
1104
1105                 i = out.find("\\over");
1106                 while (i != npos) {
1107                         size_t j = get_matching_brace_back(out, i - 1);
1108                         if (j == npos || j == 0)
1109                                 break;
1110                         size_t k = get_matching_brace(out, i + 5);
1111                         if (k == npos || k + 1 == out.size())
1112                                 break;
1113                         out = out.substr(0, j - 1)
1114                                 + "\\frac"
1115                                 + out.substr(j, i - j)
1116                                 + out.substr(i + 5, k - i - 4)
1117                                 + out.substr(k + 2);
1118                         //lyxerr << "output: " << out << endl;
1119                         i = out.find("\\over", i + 4);
1120                 }
1121                 MathData res;
1122                 mathed_parse_cell(res, from_utf8(out));
1123                 return res;
1124         }
1125
1126
1127         MathData pipeThroughMaple(docstring const & extra, MathData const & ar)
1128         {
1129                 string header = "readlib(latex):\n";
1130
1131                 // remove the \\it for variable names
1132                 //"#`latex/csname_font` := `\\it `:"
1133                 header +=
1134                         "`latex/csname_font` := ``:\n";
1135
1136                 // export matrices in (...) instead of [...]
1137                 header +=
1138                         "`latex/latex/matrix` := "
1139                                 "subs(`[`=`(`, `]`=`)`,"
1140                                         "eval(`latex/latex/matrix`)):\n";
1141
1142                 // replace \\cdots with proper '*'
1143                 header +=
1144                         "`latex/latex/*` := "
1145                                 "subs(`\\,`=`\\cdot `,"
1146                                         "eval(`latex/latex/*`)):\n";
1147
1148                 // remove spurious \\noalign{\\medskip} in matrix output
1149                 header +=
1150                         "`latex/latex/matrix`:= "
1151                                 "subs(`\\\\\\\\\\\\noalign{\\\\medskip}` = `\\\\\\\\`,"
1152                                         "eval(`latex/latex/matrix`)):\n";
1153
1154                 //"#`latex/latex/symbol` "
1155                 //      " := subs((\\'_\\' = \\'`\\_`\\',eval(`latex/latex/symbol`)): ";
1156
1157                 string trailer = "quit;";
1158                 odocstringstream os;
1159                 MapleStream ms(os);
1160                 ms << ar;
1161                 string expr = to_utf8(os.str());
1162                 lyxerr << "ar: '" << ar << "'\n"
1163                        << "ms: '" << expr << "'" << endl;
1164
1165                 for (int i = 0; i < 100; ++i) { // at most 100 attempts
1166                         // try to fix missing '*' the hard way by using mint
1167                         //
1168                         // ... > echo "1A;" | mint -i 1 -S -s -q
1169                         // on line     1: 1A;
1170                         //                 ^ syntax error -
1171                         //                   Probably missing an operator such as * p
1172                         //
1173                         lyxerr << "checking expr: '" << expr << "'" << endl;
1174                         string out = captureOutput("mint -i 1 -S -s -q -q", expr + ';');
1175                         if (out.empty())
1176                                 break; // expression syntax is ok
1177                         istringstream is(out);
1178                         string line;
1179                         getline(is, line);
1180                         if (line.find("on line") != 0)
1181                                 break; // error message not identified
1182                         getline(is, line);
1183                         size_t pos = line.find('^');
1184                         if (pos == string::npos || pos < 15)
1185                                 break; // caret position not found
1186                         pos -= 15; // skip the "on line ..." part
1187                         if (expr[pos] == '*' || (pos > 0 && expr[pos - 1] == '*'))
1188                                 break; // two '*' in a row are definitely bad
1189                         expr.insert(pos, 1, '*');
1190                 }
1191
1192                 // FIXME UNICODE Is utf8 encoding correct?
1193                 string full = "latex(" + to_utf8(extra) + '(' + expr + "));";
1194                 string out = captureOutput("maple -q", header + full + trailer);
1195
1196                 // change \_ into _
1197
1198                 //
1199                 MathData res;
1200                 mathed_parse_cell(res, from_utf8(out));
1201                 return res;
1202         }
1203
1204
1205         MathData pipeThroughOctave(docstring const &, MathData const & ar)
1206         {
1207                 odocstringstream os;
1208                 OctaveStream vs(os);
1209                 vs << ar;
1210                 string expr = to_utf8(os.str());
1211                 string out;
1212                 // FIXME const cast
1213                 Buffer * buf = const_cast<Buffer *>(ar.buffer());
1214                 lyxerr << "pipe: ar: '" << ar << "'\n"
1215                        << "pipe: expr: '" << expr << "'" << endl;
1216
1217                 for (int i = 0; i < 100; ++i) { // at most 100 attempts
1218                         //
1219                         // try to fix missing '*' the hard way
1220                         // parse error:
1221                         // >>> ([[1 2 3 ];[2 3 1 ];[3 1 2 ]])([[1 2 3 ];[2 3 1 ];[3 1 2 ]])
1222                         //                                   ^
1223                         //
1224                         lyxerr << "checking expr: '" << expr << "'" << endl;
1225                         out = captureOutput("octave -q 2>&1", expr);
1226                         lyxerr << "output: '" << out << "'" << endl;
1227
1228                         // leave loop if expression syntax is probably ok
1229                         if (out.find("parse error:") == string::npos)
1230                                 break;
1231
1232                         // search line with single caret
1233                         istringstream is(out);
1234                         string line;
1235                         while (is) {
1236                                 getline(is, line);
1237                                 lyxerr << "skipping line: '" << line << "'" << endl;
1238                                 if (line.find(">>> ") != string::npos)
1239                                         break;
1240                         }
1241
1242                         // found line with error, next line is the one with caret
1243                         getline(is, line);
1244                         size_t pos = line.find('^');
1245                         lyxerr << "caret line: '" << line << "'" << endl;
1246                         lyxerr << "found caret at pos: '" << pos << "'" << endl;
1247                         if (pos == string::npos || pos < 4)
1248                                 break; // caret position not found
1249                         pos -= 4; // skip the ">>> " part
1250                         if (expr[pos] == '*')
1251                                 break; // two '*' in a row are definitely bad
1252                         expr.insert(pos, 1, '*');
1253                 }
1254
1255                 // remove 'ans = ' taking into account that there may be an
1256                 // ansi control sequence before, such as '\033[?1034hans = '
1257                 size_t i = out.find("ans = ");
1258                 if (i == string::npos)
1259                         return MathData();
1260                 out = out.substr(i + 6);
1261
1262                 // parse output as matrix or single number
1263                 MathAtom at(new InsetMathArray(buf, from_ascii("array"), from_utf8(out)));
1264                 InsetMathArray const * mat = at->asArrayInset();
1265                 MathData res(buf);
1266                 if (mat->ncols() == 1 && mat->nrows() == 1)
1267                         res.append(mat->cell(0));
1268                 else {
1269                         res.push_back(MathAtom(
1270                                 new InsetMathDelim(buf, from_ascii("("), from_ascii(")"))));
1271                         res.back().nucleus()->cell(0).push_back(at);
1272                 }
1273                 return res;
1274         }
1275
1276
1277         string fromMathematicaName(string const & name)
1278         {
1279                 if (name == "Sin")    return "sin";
1280                 if (name == "Sinh")   return "sinh";
1281                 if (name == "ArcSin") return "arcsin";
1282                 if (name == "Cos")    return "cos";
1283                 if (name == "Cosh")   return "cosh";
1284                 if (name == "ArcCos") return "arccos";
1285                 if (name == "Tan")    return "tan";
1286                 if (name == "Tanh")   return "tanh";
1287                 if (name == "ArcTan") return "arctan";
1288                 if (name == "Cot")    return "cot";
1289                 if (name == "Coth")   return "coth";
1290                 if (name == "Csc")    return "csc";
1291                 if (name == "Sec")    return "sec";
1292                 if (name == "Exp")    return "exp";
1293                 if (name == "Log")    return "log";
1294                 if (name == "Arg" )   return "arg";
1295                 if (name == "Det" )   return "det";
1296                 if (name == "GCD" )   return "gcd";
1297                 if (name == "Max" )   return "max";
1298                 if (name == "Min" )   return "min";
1299                 if (name == "Erf" )   return "erf";
1300                 if (name == "Erfc" )  return "erfc";
1301                 return name;
1302         }
1303
1304
1305         void prettifyMathematicaOutput(string & out, string const & macroName,
1306                         bool roman, bool translate)
1307         {
1308                 string const macro = "\\" + macroName + "{";
1309                 size_t const len = macro.length();
1310                 size_t i = out.find(macro);
1311
1312                 while (i != npos) {
1313                         size_t const j = get_matching_brace(out, i + len);
1314                         string const name = out.substr(i + len, j - i - len);
1315                         out = out.substr(0, i)
1316                                 + (roman ? "\\mathrm{" : "")
1317                                 + (translate ? fromMathematicaName(name) : name)
1318                                 + out.substr(roman ? j : j + 1);
1319                         //lyxerr << "output: " << out << endl;
1320                         i = out.find(macro, i);
1321                 }
1322         }
1323
1324
1325         MathData pipeThroughMathematica(docstring const &, MathData const & ar)
1326         {
1327                 odocstringstream os;
1328                 MathematicaStream ms(os);
1329                 ms << ar;
1330                 // FIXME UNICODE Is utf8 encoding correct?
1331                 string const expr = to_utf8(os.str());
1332                 string out;
1333
1334                 lyxerr << "expr: '" << expr << "'" << endl;
1335
1336                 string const full = "TeXForm[" + expr + "]";
1337                 out = captureOutput("math", full);
1338                 lyxerr << "output: '" << out << "'" << endl;
1339
1340                 size_t pos1 = out.find("Out[1]//TeXForm= ");
1341                 size_t pos2 = out.find("In[2]:=");
1342
1343                 if (pos1 == string::npos || pos2 == string::npos)
1344                         return MathData();
1345
1346                 // get everything from pos1+17 to pos2
1347                 out = out.substr(pos1 + 17, pos2 - pos1 - 17);
1348                 out = subst(subst(out, '\r', ' '), '\n', ' ');
1349
1350                 // tries to make the result prettier
1351                 prettifyMathematicaOutput(out, "Mfunction", true, true);
1352                 prettifyMathematicaOutput(out, "Muserfunction", true, false);
1353                 prettifyMathematicaOutput(out, "Mvariable", false, false);
1354
1355                 MathData res;
1356                 mathed_parse_cell(res, from_utf8(out));
1357                 return res;
1358         }
1359
1360 }
1361
1362 } // anon namespace
1363
1364 void write(MathData const & dat, WriteStream & wi)
1365 {
1366         MathData ar = dat;
1367         extractStrings(ar);
1368         wi.firstitem() = true;
1369         for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it) {
1370                 (*it)->write(wi);
1371                 wi.firstitem() = false;
1372         }
1373 }
1374
1375
1376 void normalize(MathData const & ar, NormalStream & os)
1377 {
1378         for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
1379                 (*it)->normalize(os);
1380 }
1381
1382
1383 void octave(MathData const & dat, OctaveStream & os)
1384 {
1385         MathData ar = dat;
1386         extractStructure(ar, OCTAVE);
1387         for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
1388                 (*it)->octave(os);
1389 }
1390
1391
1392 void maple(MathData const & dat, MapleStream & os)
1393 {
1394         MathData ar = dat;
1395         extractStructure(ar, MAPLE);
1396         for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
1397                 (*it)->maple(os);
1398 }
1399
1400
1401 void maxima(MathData const & dat, MaximaStream & os)
1402 {
1403         MathData ar = dat;
1404         extractStructure(ar, MAXIMA);
1405         for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
1406                 (*it)->maxima(os);
1407 }
1408
1409
1410 void mathematica(MathData const & dat, MathematicaStream & os)
1411 {
1412         MathData ar = dat;
1413         extractStructure(ar, MATHEMATICA);
1414         for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
1415                 (*it)->mathematica(os);
1416 }
1417
1418
1419 void mathmlize(MathData const & dat, MathStream & os)
1420 {
1421         MathData ar = dat;
1422         extractStructure(ar, MATHML);
1423         if (ar.size() == 0) {
1424                 if (!os.inText())
1425                         os << "<mrow/>";
1426         } else if (ar.size() == 1)
1427                 os << ar.front();
1428         else {
1429                 if (!os.inText())
1430                         os << MTag("mrow");
1431                 for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
1432                         (*it)->mathmlize(os);
1433                 if (!os.inText())
1434                         os << ETag("mrow");
1435         }
1436 }
1437
1438
1439 // convert this inset somehow to a number
1440 bool extractNumber(MathData const & ar, int & i)
1441 {
1442         idocstringstream is(charSequence(ar.begin(), ar.end()));
1443         is >> i;
1444         return is;
1445 }
1446
1447
1448 bool extractNumber(MathData const & ar, double & d)
1449 {
1450         idocstringstream is(charSequence(ar.begin(), ar.end()));
1451         is >> d;
1452         return is;
1453 }
1454
1455
1456 MathData pipeThroughExtern(string const & lang, docstring const & extra,
1457         MathData const & ar)
1458 {
1459         if (lang == "octave")
1460                 return pipeThroughOctave(extra, ar);
1461
1462         if (lang == "maxima")
1463                 return pipeThroughMaxima(extra, ar);
1464
1465         if (lang == "maple")
1466                 return pipeThroughMaple(extra, ar);
1467
1468         if (lang == "mathematica")
1469                 return pipeThroughMathematica(extra, ar);
1470
1471         // create normalized expression
1472         odocstringstream os;
1473         NormalStream ns(os);
1474         os << '[' << extra << ' ';
1475         ns << ar;
1476         os << ']';
1477         // FIXME UNICODE Is utf8 encoding correct?
1478         string data = to_utf8(os.str());
1479
1480         // search external script
1481         FileName const file = libFileSearch("mathed", "extern_" + lang);
1482         if (file.empty()) {
1483                 lyxerr << "converter to '" << lang << "' not found" << endl;
1484                 return MathData();
1485         }
1486
1487         // run external sript
1488         string out = captureOutput(file.absFilename(), data);
1489         MathData res;
1490         mathed_parse_cell(res, from_utf8(out));
1491         return res;
1492 }
1493
1494
1495 } // namespace lyx