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