3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
11 // This file contains most of the magic that extracts "context
12 // information" from the unstructered layout-oriented stuff in an
17 #include "MathExtern.h"
18 #include "InsetMathArray.h"
19 #include "InsetMathChar.h"
20 #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 "MathStream.h"
30 #include "InsetMathNumber.h"
31 #include "InsetMathScript.h"
32 #include "InsetMathString.h"
33 #include "InsetMathSymbol.h"
34 #include "MathParser.h"
36 #include "support/filetools.h"
37 #include "support/lstrings.h"
38 #include "frontends/controllers/ControlMath.h"
46 using support::cmd_ret;
47 using support::getVectorFromString;
48 using support::libFileSearch;
49 using support::runCommand;
52 using frontend::function_names;
57 using std::istringstream;
63 static size_t const npos = lyx::docstring::npos;
65 // define a function for tests
66 typedef bool TestItemFunc(MathAtom const &);
68 // define a function for replacing subexpressions
69 typedef MathAtom ReplaceArgumentFunc(const MathArray & ar);
73 // try to extract a super/subscript
74 // modify iterator position to point behind the thing
75 bool extractScript(MathArray & ar,
76 MathArray::iterator & pos, MathArray::iterator last, bool superscript)
78 // nothing to get here
82 // is this a scriptinset?
83 if (!(*pos)->asScriptInset())
86 // do we want superscripts only?
87 if (superscript && !(*pos)->asScriptInset()->hasUp())
90 // it is a scriptinset, use it.
97 // try to extract an "argument" to some function.
98 // returns position behind the argument
99 MathArray::iterator extractArgument(MathArray & ar,
100 MathArray::iterator pos, MathArray::iterator last, bool function = false)
102 // nothing to get here
106 // something delimited _is_ an argument
107 if ((*pos)->asDelimInset()) {
108 // leave out delimiters if this is a function argument
110 MathArray const & arg = (*pos)->asDelimInset()->cell(0);
111 MathArray::const_iterator cur = arg.begin();
112 MathArray::const_iterator end = arg.end();
114 ar.push_back(*cur++);
120 // if there's one, get following superscript only if this
121 // isn't a function argument
123 extractScript(ar, pos, last, true);
127 // always take the first thing, no matter what it is
130 // go ahead if possible
135 // if the next item is a super/subscript, it most certainly belongs
136 // to the thing we have
137 extractScript(ar, pos, last, false);
141 // but it might be more than that.
142 // FIXME: not implemented
143 //for (MathArray::iterator it = pos + 1; it != last; ++it) {
144 // // always take the first thing, no matter
146 // ar.push_back(*it);
154 // returns sequence of char with same code starting at it up to end
155 // it might be less, though...
156 docstring charSequence
157 (MathArray::const_iterator it, MathArray::const_iterator end)
160 for (; it != end && (*it)->asCharInset(); ++it)
161 s += (*it)->getChar();
166 void extractStrings(MathArray & ar)
168 //lyxerr << "\nStrings from: " << ar << endl;
169 for (size_t i = 0; i < ar.size(); ++i) {
170 if (!ar[i]->asCharInset())
172 docstring s = charSequence(ar.begin() + i, ar.end());
173 ar[i] = MathAtom(new InsetMathString(s));
174 ar.erase(i + 1, i + s.size());
176 //lyxerr << "\nStrings to: " << ar << endl;
180 void extractMatrices(MathArray & ar)
182 //lyxerr << "\nMatrices from: " << ar << endl;
183 // first pass for explicitly delimited stuff
184 for (size_t i = 0; i < ar.size(); ++i) {
185 if (!ar[i]->asDelimInset())
187 MathArray const & arr = ar[i]->asDelimInset()->cell(0);
190 if (!arr.front()->asGridInset())
192 ar[i] = MathAtom(new InsetMathMatrix(*(arr.front()->asGridInset())));
195 // second pass for AMS "pmatrix" etc
196 for (size_t i = 0; i < ar.size(); ++i)
197 if (ar[i]->asAMSArrayInset())
198 ar[i] = MathAtom(new InsetMathMatrix(*(ar[i]->asGridInset())));
199 //lyxerr << "\nMatrices to: " << ar << endl;
203 // convert this inset somehow to a string
204 bool extractString(MathAtom const & at, docstring & str)
207 str = docstring(1, at->getChar());
210 if (at->asStringInset()) {
211 str = at->asStringInset()->str();
218 // is this a known function?
219 bool isKnownFunction(docstring const & str)
221 for (int i = 0; *function_names[i]; ++i) {
222 if (str == function_names[i])
229 // extract a function name from this inset
230 bool extractFunctionName(MathAtom const & at, docstring & str)
232 if (at->asSymbolInset()) {
233 str = at->asSymbolInset()->name();
234 return isKnownFunction(str);
236 if (at->asUnknownInset()) {
237 // assume it is well known...
241 if (at->asFontInset() && at->name() == "mathrm") {
242 // assume it is well known...
243 MathArray const & ar = at->asFontInset()->cell(0);
244 str = charSequence(ar.begin(), ar.end());
245 return ar.size() == str.size();
251 // convert this inset somehow to a number
252 bool extractNumber(MathArray const & ar, int & i)
254 idocstringstream is(charSequence(ar.begin(), ar.end()));
260 bool extractNumber(MathArray const & ar, double & d)
262 idocstringstream is(charSequence(ar.begin(), ar.end()));
268 bool testString(MathAtom const & at, docstring const & str)
271 return extractString(at, s) && str == s;
275 bool testString(MathAtom const & at, char const * const str)
277 return testString(at, from_ascii(str));
280 // search end of nested sequence
281 MathArray::iterator endNestSearch(
282 MathArray::iterator it,
283 MathArray::iterator last,
284 TestItemFunc testOpen,
285 TestItemFunc testClose
288 for (int level = 0; it != last; ++it) {
300 // replace nested sequences by a real Insets
303 TestItemFunc testOpen,
304 TestItemFunc testClose,
305 ReplaceArgumentFunc replaceArg
308 // use indices rather than iterators for the loop because we are going
309 // to modify the array.
310 for (size_t i = 0; i < ar.size(); ++i) {
311 // check whether this is the begin of the sequence
312 if (!testOpen(ar[i]))
315 // search end of sequence
316 MathArray::iterator it = ar.begin() + i;
317 MathArray::iterator jt = endNestSearch(it, ar.end(), testOpen, testClose);
321 // replace the original stuff by the new inset
322 ar[i] = replaceArg(MathArray(it + 1, jt));
323 ar.erase(it + 1, jt + 1);
330 // split scripts into seperate super- and subscript insets. sub goes in
334 void splitScripts(MathArray & ar)
336 //lyxerr << "\nScripts from: " << ar << endl;
337 for (size_t i = 0; i < ar.size(); ++i) {
338 InsetMathScript const * script = ar[i]->asScriptInset();
340 // is this a script inset and do we also have a superscript?
341 if (!script || !script->hasUp())
344 // we must have a nucleus if we only have a superscript
345 if (!script->hasDown() && script->nuc().size() == 0)
348 if (script->nuc().size() == 1) {
349 // leave alone sums and integrals
350 InsetMathSymbol const * sym =
351 script->nuc().front()->asSymbolInset();
352 if (sym && (sym->name() == "sum" || sym->name() == "int"))
356 // create extra script inset and move superscript over
357 InsetMathScript * p = ar[i].nucleus()->asScriptInset();
358 auto_ptr<InsetMathScript> q(new InsetMathScript(true));
359 swap(q->up(), p->up());
360 p->removeScript(true);
362 // if we don't have a subscript, get rid of the ScriptInset
363 if (!script->hasDown()) {
364 MathArray arg(p->nuc());
365 MathArray::const_iterator it = arg.begin();
366 MathArray::const_iterator et = arg.end();
369 ar.insert(i++, *it++);
373 // insert new inset behind
374 ar.insert(i, MathAtom(q.release()));
376 //lyxerr << "\nScripts to: " << ar << endl;
384 void extractExps(MathArray & ar)
386 //lyxerr << "\nExps from: " << ar << endl;
387 for (size_t i = 0; i + 1 < ar.size(); ++i) {
389 if (ar[i]->getChar() != 'e')
392 // we need an exponent but no subscript
393 InsetMathScript const * sup = ar[i + 1]->asScriptInset();
394 if (!sup || sup->hasDown())
397 // create a proper exp-inset as replacement
398 ar[i] = MathAtom(new InsetMathExFunc(from_ascii("exp"), sup->cell(1)));
401 //lyxerr << "\nExps to: " << ar << endl;
406 // extract det(...) from |matrix|
408 void extractDets(MathArray & ar)
410 //lyxerr << "\ndet from: " << ar << endl;
411 for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it) {
412 InsetMathDelim const * del = (*it)->asDelimInset();
417 *it = MathAtom(new InsetMathExFunc(from_ascii("det"), del->cell(0)));
419 //lyxerr << "\ndet to: " << ar << endl;
427 bool isDigitOrSimilar(char_type c)
429 return ('0' <= c && c <= '9') || c == '.';
433 // returns sequence of digits
434 docstring digitSequence
435 (MathArray::const_iterator it, MathArray::const_iterator end)
438 for (; it != end && (*it)->asCharInset(); ++it) {
439 if (!isDigitOrSimilar((*it)->getChar()))
441 s += (*it)->getChar();
447 void extractNumbers(MathArray & ar)
449 //lyxerr << "\nNumbers from: " << ar << endl;
450 for (size_t i = 0; i < ar.size(); ++i) {
451 if (!ar[i]->asCharInset())
453 if (!isDigitOrSimilar(ar[i]->asCharInset()->getChar()))
456 docstring s = digitSequence(ar.begin() + i, ar.end());
458 ar[i] = MathAtom(new InsetMathNumber(s));
459 ar.erase(i + 1, i + s.size());
461 //lyxerr << "\nNumbers to: " << ar << endl;
470 bool testOpenParen(MathAtom const & at)
472 return testString(at, "(");
476 bool testCloseParen(MathAtom const & at)
478 return testString(at, ")");
482 MathAtom replaceParenDelims(const MathArray & ar)
484 return MathAtom(new InsetMathDelim(from_ascii("("), from_ascii(")"), ar));
488 bool testOpenBracket(MathAtom const & at)
490 return testString(at, "[");
494 bool testCloseBracket(MathAtom const & at)
496 return testString(at, "]");
500 MathAtom replaceBracketDelims(const MathArray & ar)
502 return MathAtom(new InsetMathDelim(from_ascii("["), from_ascii("]"), ar));
506 // replace '('...')' and '['...']' sequences by a real InsetMathDelim
507 void extractDelims(MathArray & ar)
509 //lyxerr << "\nDelims from: " << ar << endl;
510 replaceNested(ar, testOpenParen, testCloseParen, replaceParenDelims);
511 replaceNested(ar, testOpenBracket, testCloseBracket, replaceBracketDelims);
512 //lyxerr << "\nDelims to: " << ar << endl;
518 // search well-known functions
522 // replace 'f' '(...)' and 'f' '^n' '(...)' sequences by a real InsetMathExFunc
523 // assume 'extractDelims' ran before
524 void extractFunctions(MathArray & ar)
526 // we need at least two items...
530 //lyxerr << "\nFunctions from: " << ar << endl;
531 for (size_t i = 0; i + 1 < ar.size(); ++i) {
532 MathArray::iterator it = ar.begin() + i;
533 MathArray::iterator jt = it + 1;
537 // it certainly is if it is well known...
538 if (!extractFunctionName(*it, name)) {
539 // is this a user defined function?
540 // it it probably not, if it doesn't have a name.
541 if (!extractString(*it, name))
543 // it is not if it has no argument
546 // guess so, if this is followed by
547 // a DelimInset with a single item in the cell
548 InsetMathDelim const * del = (*jt)->asDelimInset();
549 if (!del || del->cell(0).size() != 1)
551 // fall trough into main branch
554 // do we have an exponent like in
555 // 'sin' '^2' 'x' -> 'sin(x)' '^2'
557 extractScript(exp, jt, ar.end(), true);
559 // create a proper inset as replacement
560 auto_ptr<InsetMathExFunc> p(new InsetMathExFunc(name));
562 // jt points to the "argument". Get hold of this.
563 MathArray::iterator st = extractArgument(p->cell(0), jt, ar.end(), true);
565 // replace the function name by a real function inset
566 *it = MathAtom(p.release());
568 // remove the source of the argument from the array
569 ar.erase(it + 1, st);
571 // re-insert exponent
572 ar.insert(i + 1, exp);
573 //lyxerr << "\nFunctions to: " << ar << endl;
582 bool testSymbol(MathAtom const & at, docstring const & name)
584 return at->asSymbolInset() && at->asSymbolInset()->name() == name;
588 bool testSymbol(MathAtom const & at, char const * const name)
590 return at->asSymbolInset() && at->asSymbolInset()->name() == from_ascii(name);
594 bool testIntSymbol(MathAtom const & at)
596 return testSymbol(at, from_ascii("int"));
600 bool testIntegral(MathAtom const & at)
604 ( at->asScriptInset()
605 && at->asScriptInset()->nuc().size()
606 && testIntSymbol(at->asScriptInset()->nuc().back()) );
611 bool testIntDiff(MathAtom const & at)
613 return testString(at, "d");
617 // replace '\int' ['_^'] x 'd''x'(...)' sequences by a real InsetMathExInt
618 // assume 'extractDelims' ran before
619 void extractIntegrals(MathArray & ar)
621 // we need at least three items...
625 //lyxerr << "\nIntegrals from: " << ar << endl;
626 for (size_t i = 0; i + 1 < ar.size(); ++i) {
627 MathArray::iterator it = ar.begin() + i;
630 MathArray::iterator jt =
631 endNestSearch(it, ar.end(), testIntegral, testIntDiff);
633 // something sensible found?
637 // is this a integral name?
638 if (!testIntegral(*it))
641 // core ist part from behind the scripts to the 'd'
642 auto_ptr<InsetMathExInt> p(new InsetMathExInt(from_ascii("int")));
644 // handle scripts if available
645 if (!testIntSymbol(*it)) {
646 p->cell(2) = (*it)->asScriptInset()->down();
647 p->cell(3) = (*it)->asScriptInset()->up();
649 p->cell(0) = MathArray(it + 1, jt);
651 // use the "thing" behind the 'd' as differential
652 MathArray::iterator tt = extractArgument(p->cell(1), jt + 1, ar.end());
655 ar.erase(it + 1, tt);
656 *it = MathAtom(p.release());
658 //lyxerr << "\nIntegrals to: " << ar << endl;
662 bool testTermDelimiter(MathAtom const & at)
664 return testString(at, "+") || testString(at, "-");
668 // try to extract a "term", i.e., something delimited by '+' or '-'.
669 // returns position behind the term
670 MathArray::iterator extractTerm(MathArray & ar,
671 MathArray::iterator pos, MathArray::iterator last)
673 while (pos != last && !testTermDelimiter(*pos)) {
686 bool testEqualSign(MathAtom const & at)
688 return testString(at, "=");
692 bool testSumSymbol(MathAtom const & p)
694 return testSymbol(p, from_ascii("sum"));
698 bool testSum(MathAtom const & at)
702 ( at->asScriptInset()
703 && at->asScriptInset()->nuc().size()
704 && testSumSymbol(at->asScriptInset()->nuc().back()) );
708 // replace '\sum' ['_^'] f(x) sequences by a real InsetMathExInt
709 // assume 'extractDelims' ran before
710 void extractSums(MathArray & ar)
712 // we need at least two items...
716 //lyxerr << "\nSums from: " << ar << endl;
717 for (size_t i = 0; i + 1 < ar.size(); ++i) {
718 MathArray::iterator it = ar.begin() + i;
720 // is this a sum name?
724 // create a proper inset as replacement
725 auto_ptr<InsetMathExInt> p(new InsetMathExInt(from_ascii("sum")));
727 // collect lower bound and summation index
728 InsetMathScript const * sub = ar[i]->asScriptInset();
729 if (sub && sub->hasDown()) {
730 // try to figure out the summation index from the subscript
731 MathArray const & ar = sub->down();
732 MathArray::const_iterator xt =
733 find_if(ar.begin(), ar.end(), &testEqualSign);
734 if (xt != ar.end()) {
735 // we found a '=', use everything in front of that as index,
736 // and everything behind as lower index
737 p->cell(1) = MathArray(ar.begin(), xt);
738 p->cell(2) = MathArray(xt + 1, ar.end());
740 // use everything as summation index, don't use scripts.
745 // collect upper bound
746 if (sub && sub->hasUp())
747 p->cell(3) = sub->up();
749 // use something behind the script as core
750 MathArray::iterator tt = extractTerm(p->cell(0), it + 1, ar.end());
753 ar.erase(it + 1, tt);
754 *it = MathAtom(p.release());
756 //lyxerr << "\nSums to: " << ar << endl;
761 // search differential stuff
764 // tests for 'd' or '\partial'
765 bool testDiffItem(MathAtom const & at)
767 if (testString(at, "d") || testSymbol(at, "partial"))
770 // we may have d^n .../d and splitScripts() has not yet seen it
771 InsetMathScript const * sup = at->asScriptInset();
772 if (sup && !sup->hasDown() && sup->hasUp() && sup->nuc().size() == 1) {
773 MathAtom const & ma = sup->nuc().front();
774 return testString(ma, "d") || testSymbol(ma, "partial");
780 bool testDiffArray(MathArray const & ar)
782 return ar.size() && testDiffItem(ar.front());
786 bool testDiffFrac(MathAtom const & at)
790 && testDiffArray(at->asFracInset()->cell(0))
791 && testDiffArray(at->asFracInset()->cell(1));
795 void extractDiff(MathArray & ar)
797 //lyxerr << "\nDiffs from: " << ar << endl;
798 for (size_t i = 0; i < ar.size(); ++i) {
799 MathArray::iterator it = ar.begin() + i;
801 // is this a "differential fraction"?
802 if (!testDiffFrac(*it))
805 InsetMathFrac const * f = (*it)->asFracInset();
807 lyxerr << "should not happen" << endl;
811 // create a proper diff inset
812 auto_ptr<InsetMathDiff> diff(new InsetMathDiff);
814 // collect function, let jt point behind last used item
815 MathArray::iterator jt = it + 1;
817 MathArray numer(f->cell(0));
819 if (numer.size() > 1 && numer[1]->asScriptInset()) {
820 // this is something like d^n f(x) / d... or d^n / d...
823 if (numer.size() > 2)
824 diff->cell(0) = MathArray(numer.begin() + 2, numer.end());
826 jt = extractTerm(diff->cell(0), jt, ar.end());
828 // simply d f(x) / d... or d/d...
829 if (numer.size() > 1)
830 diff->cell(0) = MathArray(numer.begin() + 1, numer.end());
832 jt = extractTerm(diff->cell(0), jt, ar.end());
835 // collect denominator parts
836 MathArray denom(f->cell(1));
838 for (MathArray::iterator dt = denom.begin(); dt != denom.end();) {
840 MathArray::iterator et
841 = find_if(dt + 1, denom.end(), &testDiffItem);
844 MathArray::iterator st = et - 1;
845 InsetMathScript const * script = (*st)->asScriptInset();
846 if (script && script->hasUp()) {
847 // things like d.../dx^n
849 if (extractNumber(script->up(), mult)) {
850 //lyxerr << "mult: " << mult << endl;
851 for (int i = 0; i < mult; ++i)
852 diff->addDer(MathArray(dt + 1, st));
856 diff->addDer(MathArray(dt + 1, et));
862 ar.erase(it + 1, jt);
863 *it = MathAtom(diff.release());
865 //lyxerr << "\nDiffs to: " << ar << endl;
874 bool testRightArrow(MathAtom const & at)
876 return testSymbol(at, "to") || testSymbol(at, "rightarrow");
881 // replace '\lim_{x->x0} f(x)' sequences by a real InsetMathLim
882 // assume 'extractDelims' ran before
883 void extractLims(MathArray & ar)
885 //lyxerr << "\nLimits from: " << ar << endl;
886 for (size_t i = 0; i < ar.size(); ++i) {
887 MathArray::iterator it = ar.begin() + i;
889 // must be a script inset with a subscript (without superscript)
890 InsetMathScript const * sub = (*it)->asScriptInset();
891 if (!sub || !sub->hasDown() || sub->hasUp() || sub->nuc().size() != 1)
894 // is this a limit function?
895 if (!testSymbol(sub->nuc().front(), "lim"))
898 // subscript must contain a -> symbol
899 MathArray const & s = sub->down();
900 MathArray::const_iterator st = find_if(s.begin(), s.end(), &testRightArrow);
904 // the -> splits the subscript int x and x0
905 MathArray x = MathArray(s.begin(), st);
906 MathArray x0 = MathArray(st + 1, s.end());
908 // use something behind the script as core
910 MathArray::iterator tt = extractTerm(f, it + 1, ar.end());
913 ar.erase(it + 1, tt);
915 // create a proper inset as replacement
916 *it = MathAtom(new InsetMathLim(f, x, x0));
918 //lyxerr << "\nLimits to: " << ar << endl;
926 void extractStructure(MathArray & ar)
928 //lyxerr << "\nStructure from: " << ar << endl;
931 extractIntegrals(ar);
935 extractFunctions(ar);
941 //lyxerr << "\nStructure to: " << ar << endl;
945 void write(MathArray const & dat, WriteStream & wi)
949 wi.firstitem() = true;
950 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
952 wi.firstitem() = false;
957 void normalize(MathArray const & ar, NormalStream & os)
959 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
960 (*it)->normalize(os);
964 void octave(MathArray const & dat, OctaveStream & os)
967 extractStructure(ar);
968 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
973 void maple(MathArray const & dat, MapleStream & os)
976 extractStructure(ar);
977 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
982 void maxima(MathArray const & dat, MaximaStream & os)
985 extractStructure(ar);
986 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
991 void mathematica(MathArray const & dat, MathematicaStream & os)
994 extractStructure(ar);
995 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
996 (*it)->mathematica(os);
1000 void mathmlize(MathArray const & dat, MathStream & os)
1003 extractStructure(ar);
1006 else if (ar.size() == 1)
1010 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
1011 (*it)->mathmlize(os);
1021 std::string captureOutput(std::string const & cmd, std::string const & data)
1023 std::string command = "echo '" + data + "' | " + cmd;
1024 lyxerr << "calling: " << command << endl;
1025 cmd_ret const ret = runCommand(command);
1029 size_t get_matching_brace(std::string const & str, size_t i)
1032 size_t n = str.size();
1034 i = str.find_first_of("{}", i+1);
1047 size_t get_matching_brace_back(std::string const & str, size_t i)
1051 i = str.find_last_of("{}", i-1);
1064 MathArray pipeThroughMaxima(docstring const &, MathArray const & ar)
1066 odocstringstream os;
1067 MaximaStream ms(os);
1069 docstring expr = os.str();
1070 docstring const header = from_ascii("simpsum:true;");
1073 for (int i = 0; i < 100; ++i) { // at most 100 attempts
1074 // try to fix missing '*' the hard way
1076 // > echo "2x;" | maxima
1078 // (C1) Incorrect syntax: x is not an infix operator
1082 lyxerr << "checking expr: '" << to_utf8(expr) << "'" << endl;
1083 docstring full = header + "tex(" + expr + ");";
1084 out = captureOutput("maxima", to_utf8(full));
1086 // leave loop if expression syntax is probably ok
1087 if (out.find("Incorrect syntax") == npos)
1090 // search line with "Incorrect syntax"
1091 istringstream is(out);
1095 if (line.find("Incorrect syntax") != npos)
1099 // 2nd next line is the one with caret
1102 size_t pos = line.find('^');
1103 lyxerr << "found caret at pos: '" << pos << "'" << endl;
1104 if (pos == npos || pos < 4)
1105 break; // caret position not found
1106 pos -= 4; // skip the "tex(" part
1107 if (expr[pos] == '*')
1108 break; // two '*' in a row are definitely bad
1109 expr.insert(pos, from_ascii("*"));
1112 vector<std::string> tmp = getVectorFromString(out, "$$");
1116 out = subst(tmp[1], "\\>", std::string());
1117 lyxerr << "out: '" << out << "'" << endl;
1119 // Ugly code that tries to make the result prettier
1120 size_t i = out.find("\\mathchoice");
1122 size_t j = get_matching_brace(out, i + 12);
1123 size_t k = get_matching_brace(out, j + 1);
1124 k = get_matching_brace(out, k + 1);
1125 k = get_matching_brace(out, k + 1);
1126 std::string mid = out.substr(i + 13, j - i - 13);
1127 if (mid.find("\\over") != npos)
1128 mid = '{' + mid + '}';
1129 out = out.substr(0,i)
1131 + out.substr(k + 1);
1132 //lyxerr << "out: " << out << endl;
1133 i = out.find("\\mathchoice", i);
1137 i = out.find("\\over");
1139 size_t j = get_matching_brace_back(out, i - 1);
1140 if (j == npos || j == 0)
1142 size_t k = get_matching_brace(out, i + 5);
1143 if (k == npos || k + 1 == out.size())
1145 out = out.substr(0,j - 1)
1147 + out.substr(j,i - j)
1148 + out.substr(i + 5,k - i - 4)
1149 + out.substr(k + 2);
1150 //lyxerr << "out: " << out << endl;
1151 i = out.find("\\over", i + 4);
1154 mathed_parse_cell(res, from_utf8(out));
1159 MathArray pipeThroughMaple(docstring const & extra, MathArray const & ar)
1161 std::string header = "readlib(latex):\n";
1163 // remove the \\it for variable names
1164 //"#`latex/csname_font` := `\\it `:"
1166 "`latex/csname_font` := ``:\n";
1168 // export matrices in (...) instead of [...]
1170 "`latex/latex/matrix` := "
1171 "subs(`[`=`(`, `]`=`)`,"
1172 "eval(`latex/latex/matrix`)):\n";
1174 // replace \\cdots with proper '*'
1176 "`latex/latex/*` := "
1177 "subs(`\\,`=`\\cdot `,"
1178 "eval(`latex/latex/*`)):\n";
1180 // remove spurious \\noalign{\\medskip} in matrix output
1182 "`latex/latex/matrix`:= "
1183 "subs(`\\\\\\\\\\\\noalign{\\\\medskip}` = `\\\\\\\\`,"
1184 "eval(`latex/latex/matrix`)):\n";
1186 //"#`latex/latex/symbol` "
1187 // " := subs((\\'_\\' = \\'`\\_`\\',eval(`latex/latex/symbol`)): ";
1189 std::string trailer = "quit;";
1190 odocstringstream os;
1193 std::string expr = to_utf8(os.str());
1194 lyxerr << "ar: '" << ar << "'\n"
1195 << "ms: '" << expr << "'" << endl;
1197 for (int i = 0; i < 100; ++i) { // at most 100 attempts
1198 // try to fix missing '*' the hard way by using mint
1200 // ... > echo "1A;" | mint -i 1 -S -s -q
1203 // Probably missing an operator such as * p
1205 lyxerr << "checking expr: '" << expr << "'" << endl;
1206 string out = captureOutput("mint -i 1 -S -s -q -q", expr + ';');
1208 break; // expression syntax is ok
1209 istringstream is(out);
1212 if (line.find("on line") != 0)
1213 break; // error message not identified
1215 size_t pos = line.find('^');
1216 if (pos == string::npos || pos < 15)
1217 break; // caret position not found
1218 pos -= 15; // skip the "on line ..." part
1219 if (expr[pos] == '*' || (pos > 0 && expr[pos - 1] == '*'))
1220 break; // two '*' in a row are definitely bad
1221 expr.insert(pos, 1, '*');
1224 // FIXME UNICODE Is utf8 encoding correct?
1225 string full = "latex(" + to_utf8(extra) + '(' + expr + "));";
1226 string out = captureOutput("maple -q", header + full + trailer);
1232 mathed_parse_cell(res, from_utf8(out));
1237 MathArray pipeThroughOctave(docstring const &, MathArray const & ar)
1239 odocstringstream os;
1240 OctaveStream vs(os);
1242 string expr = to_utf8(os.str());
1245 lyxerr << "pipe: ar: '" << ar << "'\n"
1246 << "pipe: expr: '" << expr << "'" << endl;
1248 for (int i = 0; i < 100; ++i) { // at most 100 attempts
1250 // try to fix missing '*' the hard way
1252 // >>> ([[1 2 3 ];[2 3 1 ];[3 1 2 ]])([[1 2 3 ];[2 3 1 ];[3 1 2 ]])
1255 lyxerr << "checking expr: '" << expr << "'" << endl;
1256 out = captureOutput("octave -q 2>&1", expr);
1257 lyxerr << "checking out: '" << out << "'" << endl;
1259 // leave loop if expression syntax is probably ok
1260 if (out.find("parse error:") == string::npos)
1263 // search line with single caret
1264 istringstream is(out);
1268 lyxerr << "skipping line: '" << line << "'" << endl;
1269 if (line.find(">>> ") != string::npos)
1273 // found line with error, next line is the one with caret
1275 size_t pos = line.find('^');
1276 lyxerr << "caret line: '" << line << "'" << endl;
1277 lyxerr << "found caret at pos: '" << pos << "'" << endl;
1278 if (pos == string::npos || pos < 4)
1279 break; // caret position not found
1280 pos -= 4; // skip the ">>> " part
1281 if (expr[pos] == '*')
1282 break; // two '*' in a row are definitely bad
1283 expr.insert(pos, 1, '*');
1290 out = out.substr(6);
1292 // parse output as matrix or single number
1293 MathAtom at(new InsetMathArray(from_ascii("array"), from_utf8(out)));
1294 InsetMathArray const * mat = at->asArrayInset();
1296 if (mat->ncols() == 1 && mat->nrows() == 1)
1297 res.append(mat->cell(0));
1299 res.push_back(MathAtom(new InsetMathDelim(from_ascii("("), from_ascii(")"))));
1300 res.back().nucleus()->cell(0).push_back(at);
1306 string fromMathematicaName(string const & name)
1308 if (name == "Sin") return "sin";
1309 if (name == "Sinh") return "sinh";
1310 if (name == "ArcSin") return "arcsin";
1311 if (name == "Cos") return "cos";
1312 if (name == "Cosh") return "cosh";
1313 if (name == "ArcCos") return "arccos";
1314 if (name == "Tan") return "tan";
1315 if (name == "Tanh") return "tanh";
1316 if (name == "ArcTan") return "arctan";
1317 if (name == "Cot") return "cot";
1318 if (name == "Coth") return "coth";
1319 if (name == "Csc") return "csc";
1320 if (name == "Sec") return "sec";
1321 if (name == "Exp") return "exp";
1322 if (name == "Log") return "log";
1323 if (name == "Arg" ) return "arg";
1324 if (name == "Det" ) return "det";
1325 if (name == "GCD" ) return "gcd";
1326 if (name == "Max" ) return "max";
1327 if (name == "Min" ) return "min";
1328 if (name == "Erf" ) return "erf";
1329 if (name == "Erfc" ) return "erfc";
1334 void prettifyMathematicaOutput(string & out, string const & macroName,
1335 bool roman, bool translate)
1337 string const macro = "\\" + macroName + "{";
1338 size_t const len = macro.length();
1339 size_t i = out.find(macro);
1342 size_t const j = get_matching_brace(out, i + len);
1343 string const name = out.substr(i + len, j - i - len);
1344 out = out.substr(0, i)
1345 + (roman ? "\\mathrm{" : "")
1346 + (translate ? fromMathematicaName(name) : name)
1347 + out.substr(roman ? j : j + 1);
1348 //lyxerr << "out: " << out << endl;
1349 i = out.find(macro, i);
1354 MathArray pipeThroughMathematica(docstring const &, MathArray const & ar)
1356 odocstringstream os;
1357 MathematicaStream ms(os);
1359 // FIXME UNICODE Is utf8 encoding correct?
1360 string const expr = to_utf8(os.str());
1363 lyxerr << "expr: '" << expr << "'" << endl;
1365 string const full = "TeXForm[" + expr + "]";
1366 out = captureOutput("math", full);
1367 lyxerr << "out: '" << out << "'" << endl;
1369 size_t pos1 = out.find("Out[1]//TeXForm= ");
1370 size_t pos2 = out.find("In[2]:=");
1372 if (pos1 == string::npos || pos2 == string::npos)
1375 // get everything from pos1+17 to pos2
1376 out = out.substr(pos1 + 17, pos2 - pos1 - 17);
1377 out = subst(subst(out, '\r', ' '), '\n', ' ');
1379 // tries to make the result prettier
1380 prettifyMathematicaOutput(out, "Mfunction", true, true);
1381 prettifyMathematicaOutput(out, "Muserfunction", true, false);
1382 prettifyMathematicaOutput(out, "Mvariable", false, false);
1385 mathed_parse_cell(res, from_utf8(out));
1392 MathArray pipeThroughExtern(string const & lang, docstring const & extra,
1393 MathArray const & ar)
1395 if (lang == "octave")
1396 return pipeThroughOctave(extra, ar);
1398 if (lang == "maxima")
1399 return pipeThroughMaxima(extra, ar);
1401 if (lang == "maple")
1402 return pipeThroughMaple(extra, ar);
1404 if (lang == "mathematica")
1405 return pipeThroughMathematica(extra, ar);
1407 // create normalized expression
1408 odocstringstream os;
1409 NormalStream ns(os);
1410 os << '[' << extra << ' ';
1413 // FIXME UNICODE Is utf8 encoding correct?
1414 string data = to_utf8(os.str());
1416 // search external script
1417 string file = libFileSearch("mathed", "extern_" + lang);
1419 lyxerr << "converter to '" << lang << "' not found" << endl;
1423 // run external sript
1424 string out = captureOutput(file, data);
1426 mathed_parse_cell(res, from_utf8(out));