]> git.lyx.org Git - lyx.git/blob - src/mathed/MathExtern.cpp
We do not need to extract exponents, either.
[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                 extractExps(ar);
964                 extractLims(ar);
965                 extractStrings(ar);
966         }
967         //lyxerr << "\nStructure to: " << ar << endl;
968 }
969
970
971 namespace {
972
973         string captureOutput(string const & cmd, string const & data)
974         {
975                 // In order to avoid parsing problems with command interpreters
976                 // we pass input data through a file
977                 FileName const cas_tmpfile = FileName::tempName("casinput");
978                 if (cas_tmpfile.empty()) {
979                         lyxerr << "Warning: cannot create temporary file."
980                                << endl;
981                         return string();
982                 }
983                 ofstream os(cas_tmpfile.toFilesystemEncoding().c_str());
984                 os << data << endl;
985                 os.close();
986                 string command =  cmd + " < "
987                         + quoteName(cas_tmpfile.toFilesystemEncoding());
988                 lyxerr << "calling: " << cmd
989                        << "\ninput: '" << data << "'" << endl;
990                 cmd_ret const ret = runCommand(command);
991                 cas_tmpfile.removeFile();
992                 return ret.second;
993         }
994
995         size_t get_matching_brace(string const & str, size_t i)
996         {
997                 int count = 1;
998                 size_t n = str.size();
999                 while (i < n) {
1000                         i = str.find_first_of("{}", i+1);
1001                         if (i == npos)
1002                                 return i;
1003                         if (str[i] == '{')
1004                                 ++count;
1005                         else
1006                                 --count;
1007                         if (count == 0)
1008                                 return i;
1009                 }
1010                 return npos;
1011         }
1012
1013         size_t get_matching_brace_back(string const & str, size_t i)
1014         {
1015                 int count = 1;
1016                 while (i > 0) {
1017                         i = str.find_last_of("{}", i-1);
1018                         if (i == npos)
1019                                 return i;
1020                         if (str[i] == '}')
1021                                 ++count;
1022                         else
1023                                 --count;
1024                         if (count == 0)
1025                                 return i;
1026                 }
1027                 return npos;
1028         }
1029
1030         MathData pipeThroughMaxima(docstring const &, MathData const & ar)
1031         {
1032                 odocstringstream os;
1033                 MaximaStream ms(os);
1034                 ms << ar;
1035                 docstring expr = os.str();
1036                 docstring const header = from_ascii("simpsum:true;");
1037
1038                 string out;
1039                 for (int i = 0; i < 100; ++i) { // at most 100 attempts
1040                         // try to fix missing '*' the hard way
1041                         //
1042                         // > echo "2x;" | maxima
1043                         // ...
1044                         // (C1) Incorrect syntax: x is not an infix operator
1045                         // 2x;
1046                         //  ^
1047                         //
1048                         lyxerr << "checking expr: '" << to_utf8(expr) << "'" << endl;
1049                         docstring full = header + "tex(" + expr + ");";
1050                         out = captureOutput("maxima", to_utf8(full));
1051
1052                         // leave loop if expression syntax is probably ok
1053                         if (out.find("Incorrect syntax") == npos)
1054                                 break;
1055
1056                         // search line with "Incorrect syntax"
1057                         istringstream is(out);
1058                         string line;
1059                         while (is) {
1060                                 getline(is, line);
1061                                 if (line.find("Incorrect syntax") != npos)
1062                                         break;
1063                         }
1064
1065                         // 2nd next line is the one with caret
1066                         getline(is, line);
1067                         getline(is, line);
1068                         size_t pos = line.find('^');
1069                         lyxerr << "found caret at pos: '" << pos << "'" << endl;
1070                         if (pos == npos || pos < 4)
1071                                 break; // caret position not found
1072                         pos -= 4; // skip the "tex(" part
1073                         if (expr[pos] == '*')
1074                                 break; // two '*' in a row are definitely bad
1075                         expr.insert(pos, from_ascii("*"));
1076                 }
1077
1078                 vector<string> tmp = getVectorFromString(out, "$$");
1079                 if (tmp.size() < 2)
1080                         return MathData();
1081
1082                 out = subst(tmp[1], "\\>", string());
1083                 lyxerr << "output: '" << out << "'" << endl;
1084
1085                 // Ugly code that tries to make the result prettier
1086                 size_t i = out.find("\\mathchoice");
1087                 while (i != npos) {
1088                         size_t j = get_matching_brace(out, i + 12);
1089                         size_t k = get_matching_brace(out, j + 1);
1090                         k = get_matching_brace(out, k + 1);
1091                         k = get_matching_brace(out, k + 1);
1092                         string mid = out.substr(i + 13, j - i - 13);
1093                         if (mid.find("\\over") != npos)
1094                                 mid = '{' + mid + '}';
1095                         out = out.substr(0, i)
1096                                 + mid
1097                                 + out.substr(k + 1);
1098                         //lyxerr << "output: " << out << endl;
1099                         i = out.find("\\mathchoice", i);
1100                         break;
1101                 }
1102
1103                 i = out.find("\\over");
1104                 while (i != npos) {
1105                         size_t j = get_matching_brace_back(out, i - 1);
1106                         if (j == npos || j == 0)
1107                                 break;
1108                         size_t k = get_matching_brace(out, i + 5);
1109                         if (k == npos || k + 1 == out.size())
1110                                 break;
1111                         out = out.substr(0, j - 1)
1112                                 + "\\frac"
1113                                 + out.substr(j, i - j)
1114                                 + out.substr(i + 5, k - i - 4)
1115                                 + out.substr(k + 2);
1116                         //lyxerr << "output: " << out << endl;
1117                         i = out.find("\\over", i + 4);
1118                 }
1119                 MathData res;
1120                 mathed_parse_cell(res, from_utf8(out));
1121                 return res;
1122         }
1123
1124
1125         MathData pipeThroughMaple(docstring const & extra, MathData const & ar)
1126         {
1127                 string header = "readlib(latex):\n";
1128
1129                 // remove the \\it for variable names
1130                 //"#`latex/csname_font` := `\\it `:"
1131                 header +=
1132                         "`latex/csname_font` := ``:\n";
1133
1134                 // export matrices in (...) instead of [...]
1135                 header +=
1136                         "`latex/latex/matrix` := "
1137                                 "subs(`[`=`(`, `]`=`)`,"
1138                                         "eval(`latex/latex/matrix`)):\n";
1139
1140                 // replace \\cdots with proper '*'
1141                 header +=
1142                         "`latex/latex/*` := "
1143                                 "subs(`\\,`=`\\cdot `,"
1144                                         "eval(`latex/latex/*`)):\n";
1145
1146                 // remove spurious \\noalign{\\medskip} in matrix output
1147                 header +=
1148                         "`latex/latex/matrix`:= "
1149                                 "subs(`\\\\\\\\\\\\noalign{\\\\medskip}` = `\\\\\\\\`,"
1150                                         "eval(`latex/latex/matrix`)):\n";
1151
1152                 //"#`latex/latex/symbol` "
1153                 //      " := subs((\\'_\\' = \\'`\\_`\\',eval(`latex/latex/symbol`)): ";
1154
1155                 string trailer = "quit;";
1156                 odocstringstream os;
1157                 MapleStream ms(os);
1158                 ms << ar;
1159                 string expr = to_utf8(os.str());
1160                 lyxerr << "ar: '" << ar << "'\n"
1161                        << "ms: '" << expr << "'" << endl;
1162
1163                 for (int i = 0; i < 100; ++i) { // at most 100 attempts
1164                         // try to fix missing '*' the hard way by using mint
1165                         //
1166                         // ... > echo "1A;" | mint -i 1 -S -s -q
1167                         // on line     1: 1A;
1168                         //                 ^ syntax error -
1169                         //                   Probably missing an operator such as * p
1170                         //
1171                         lyxerr << "checking expr: '" << expr << "'" << endl;
1172                         string out = captureOutput("mint -i 1 -S -s -q -q", expr + ';');
1173                         if (out.empty())
1174                                 break; // expression syntax is ok
1175                         istringstream is(out);
1176                         string line;
1177                         getline(is, line);
1178                         if (line.find("on line") != 0)
1179                                 break; // error message not identified
1180                         getline(is, line);
1181                         size_t pos = line.find('^');
1182                         if (pos == string::npos || pos < 15)
1183                                 break; // caret position not found
1184                         pos -= 15; // skip the "on line ..." part
1185                         if (expr[pos] == '*' || (pos > 0 && expr[pos - 1] == '*'))
1186                                 break; // two '*' in a row are definitely bad
1187                         expr.insert(pos, 1, '*');
1188                 }
1189
1190                 // FIXME UNICODE Is utf8 encoding correct?
1191                 string full = "latex(" + to_utf8(extra) + '(' + expr + "));";
1192                 string out = captureOutput("maple -q", header + full + trailer);
1193
1194                 // change \_ into _
1195
1196                 //
1197                 MathData res;
1198                 mathed_parse_cell(res, from_utf8(out));
1199                 return res;
1200         }
1201
1202
1203         MathData pipeThroughOctave(docstring const &, MathData const & ar)
1204         {
1205                 odocstringstream os;
1206                 OctaveStream vs(os);
1207                 vs << ar;
1208                 string expr = to_utf8(os.str());
1209                 string out;
1210                 // FIXME const cast
1211                 Buffer * buf = const_cast<Buffer *>(ar.buffer());
1212                 lyxerr << "pipe: ar: '" << ar << "'\n"
1213                        << "pipe: expr: '" << expr << "'" << endl;
1214
1215                 for (int i = 0; i < 100; ++i) { // at most 100 attempts
1216                         //
1217                         // try to fix missing '*' the hard way
1218                         // parse error:
1219                         // >>> ([[1 2 3 ];[2 3 1 ];[3 1 2 ]])([[1 2 3 ];[2 3 1 ];[3 1 2 ]])
1220                         //                                   ^
1221                         //
1222                         lyxerr << "checking expr: '" << expr << "'" << endl;
1223                         out = captureOutput("octave -q 2>&1", expr);
1224                         lyxerr << "output: '" << out << "'" << endl;
1225
1226                         // leave loop if expression syntax is probably ok
1227                         if (out.find("parse error:") == string::npos)
1228                                 break;
1229
1230                         // search line with single caret
1231                         istringstream is(out);
1232                         string line;
1233                         while (is) {
1234                                 getline(is, line);
1235                                 lyxerr << "skipping line: '" << line << "'" << endl;
1236                                 if (line.find(">>> ") != string::npos)
1237                                         break;
1238                         }
1239
1240                         // found line with error, next line is the one with caret
1241                         getline(is, line);
1242                         size_t pos = line.find('^');
1243                         lyxerr << "caret line: '" << line << "'" << endl;
1244                         lyxerr << "found caret at pos: '" << pos << "'" << endl;
1245                         if (pos == string::npos || pos < 4)
1246                                 break; // caret position not found
1247                         pos -= 4; // skip the ">>> " part
1248                         if (expr[pos] == '*')
1249                                 break; // two '*' in a row are definitely bad
1250                         expr.insert(pos, 1, '*');
1251                 }
1252
1253                 // remove 'ans = ' taking into account that there may be an
1254                 // ansi control sequence before, such as '\033[?1034hans = '
1255                 size_t i = out.find("ans = ");
1256                 if (i == string::npos)
1257                         return MathData();
1258                 out = out.substr(i + 6);
1259
1260                 // parse output as matrix or single number
1261                 MathAtom at(new InsetMathArray(buf, from_ascii("array"), from_utf8(out)));
1262                 InsetMathArray const * mat = at->asArrayInset();
1263                 MathData res(buf);
1264                 if (mat->ncols() == 1 && mat->nrows() == 1)
1265                         res.append(mat->cell(0));
1266                 else {
1267                         res.push_back(MathAtom(
1268                                 new InsetMathDelim(buf, from_ascii("("), from_ascii(")"))));
1269                         res.back().nucleus()->cell(0).push_back(at);
1270                 }
1271                 return res;
1272         }
1273
1274
1275         string fromMathematicaName(string const & name)
1276         {
1277                 if (name == "Sin")    return "sin";
1278                 if (name == "Sinh")   return "sinh";
1279                 if (name == "ArcSin") return "arcsin";
1280                 if (name == "Cos")    return "cos";
1281                 if (name == "Cosh")   return "cosh";
1282                 if (name == "ArcCos") return "arccos";
1283                 if (name == "Tan")    return "tan";
1284                 if (name == "Tanh")   return "tanh";
1285                 if (name == "ArcTan") return "arctan";
1286                 if (name == "Cot")    return "cot";
1287                 if (name == "Coth")   return "coth";
1288                 if (name == "Csc")    return "csc";
1289                 if (name == "Sec")    return "sec";
1290                 if (name == "Exp")    return "exp";
1291                 if (name == "Log")    return "log";
1292                 if (name == "Arg" )   return "arg";
1293                 if (name == "Det" )   return "det";
1294                 if (name == "GCD" )   return "gcd";
1295                 if (name == "Max" )   return "max";
1296                 if (name == "Min" )   return "min";
1297                 if (name == "Erf" )   return "erf";
1298                 if (name == "Erfc" )  return "erfc";
1299                 return name;
1300         }
1301
1302
1303         void prettifyMathematicaOutput(string & out, string const & macroName,
1304                         bool roman, bool translate)
1305         {
1306                 string const macro = "\\" + macroName + "{";
1307                 size_t const len = macro.length();
1308                 size_t i = out.find(macro);
1309
1310                 while (i != npos) {
1311                         size_t const j = get_matching_brace(out, i + len);
1312                         string const name = out.substr(i + len, j - i - len);
1313                         out = out.substr(0, i)
1314                                 + (roman ? "\\mathrm{" : "")
1315                                 + (translate ? fromMathematicaName(name) : name)
1316                                 + out.substr(roman ? j : j + 1);
1317                         //lyxerr << "output: " << out << endl;
1318                         i = out.find(macro, i);
1319                 }
1320         }
1321
1322
1323         MathData pipeThroughMathematica(docstring const &, MathData const & ar)
1324         {
1325                 odocstringstream os;
1326                 MathematicaStream ms(os);
1327                 ms << ar;
1328                 // FIXME UNICODE Is utf8 encoding correct?
1329                 string const expr = to_utf8(os.str());
1330                 string out;
1331
1332                 lyxerr << "expr: '" << expr << "'" << endl;
1333
1334                 string const full = "TeXForm[" + expr + "]";
1335                 out = captureOutput("math", full);
1336                 lyxerr << "output: '" << out << "'" << endl;
1337
1338                 size_t pos1 = out.find("Out[1]//TeXForm= ");
1339                 size_t pos2 = out.find("In[2]:=");
1340
1341                 if (pos1 == string::npos || pos2 == string::npos)
1342                         return MathData();
1343
1344                 // get everything from pos1+17 to pos2
1345                 out = out.substr(pos1 + 17, pos2 - pos1 - 17);
1346                 out = subst(subst(out, '\r', ' '), '\n', ' ');
1347
1348                 // tries to make the result prettier
1349                 prettifyMathematicaOutput(out, "Mfunction", true, true);
1350                 prettifyMathematicaOutput(out, "Muserfunction", true, false);
1351                 prettifyMathematicaOutput(out, "Mvariable", false, false);
1352
1353                 MathData res;
1354                 mathed_parse_cell(res, from_utf8(out));
1355                 return res;
1356         }
1357
1358 }
1359
1360 } // anon namespace
1361
1362 void write(MathData const & dat, WriteStream & wi)
1363 {
1364         MathData ar = dat;
1365         extractStrings(ar);
1366         wi.firstitem() = true;
1367         for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it) {
1368                 (*it)->write(wi);
1369                 wi.firstitem() = false;
1370         }
1371 }
1372
1373
1374 void normalize(MathData const & ar, NormalStream & os)
1375 {
1376         for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
1377                 (*it)->normalize(os);
1378 }
1379
1380
1381 void octave(MathData const & dat, OctaveStream & os)
1382 {
1383         MathData ar = dat;
1384         extractStructure(ar, OCTAVE);
1385         for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
1386                 (*it)->octave(os);
1387 }
1388
1389
1390 void maple(MathData const & dat, MapleStream & os)
1391 {
1392         MathData ar = dat;
1393         extractStructure(ar, MAPLE);
1394         for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
1395                 (*it)->maple(os);
1396 }
1397
1398
1399 void maxima(MathData const & dat, MaximaStream & os)
1400 {
1401         MathData ar = dat;
1402         extractStructure(ar, MAXIMA);
1403         for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
1404                 (*it)->maxima(os);
1405 }
1406
1407
1408 void mathematica(MathData const & dat, MathematicaStream & os)
1409 {
1410         MathData ar = dat;
1411         extractStructure(ar, MATHEMATICA);
1412         for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
1413                 (*it)->mathematica(os);
1414 }
1415
1416
1417 void mathmlize(MathData const & dat, MathStream & os)
1418 {
1419         MathData ar = dat;
1420         extractStructure(ar, MATHML);
1421         if (ar.size() == 0) {
1422                 if (!os.inText())
1423                         os << "<mrow/>";
1424         } else if (ar.size() == 1)
1425                 os << ar.front();
1426         else {
1427                 if (!os.inText())
1428                         os << MTag("mrow");
1429                 for (MathData::const_iterator it = ar.begin(); it != ar.end(); ++it)
1430                         (*it)->mathmlize(os);
1431                 if (!os.inText())
1432                         os << ETag("mrow");
1433         }
1434 }
1435
1436
1437 // convert this inset somehow to a number
1438 bool extractNumber(MathData const & ar, int & i)
1439 {
1440         idocstringstream is(charSequence(ar.begin(), ar.end()));
1441         is >> i;
1442         return is;
1443 }
1444
1445
1446 bool extractNumber(MathData const & ar, double & d)
1447 {
1448         idocstringstream is(charSequence(ar.begin(), ar.end()));
1449         is >> d;
1450         return is;
1451 }
1452
1453
1454 MathData pipeThroughExtern(string const & lang, docstring const & extra,
1455         MathData const & ar)
1456 {
1457         if (lang == "octave")
1458                 return pipeThroughOctave(extra, ar);
1459
1460         if (lang == "maxima")
1461                 return pipeThroughMaxima(extra, ar);
1462
1463         if (lang == "maple")
1464                 return pipeThroughMaple(extra, ar);
1465
1466         if (lang == "mathematica")
1467                 return pipeThroughMathematica(extra, ar);
1468
1469         // create normalized expression
1470         odocstringstream os;
1471         NormalStream ns(os);
1472         os << '[' << extra << ' ';
1473         ns << ar;
1474         os << ']';
1475         // FIXME UNICODE Is utf8 encoding correct?
1476         string data = to_utf8(os.str());
1477
1478         // search external script
1479         FileName const file = libFileSearch("mathed", "extern_" + lang);
1480         if (file.empty()) {
1481                 lyxerr << "converter to '" << lang << "' not found" << endl;
1482                 return MathData();
1483         }
1484
1485         // run external sript
1486         string out = captureOutput(file.absFilename(), data);
1487         MathData res;
1488         mathed_parse_cell(res, from_utf8(out));
1489         return res;
1490 }
1491
1492
1493 } // namespace lyx