]> git.lyx.org Git - lyx.git/blobdiff - src/mathed/math_extern.C
more const correctness
[lyx.git] / src / mathed / math_extern.C
index e3064c107ee41ff4ef0deab3772d01e9d2f95117..da2e448a4b0520df92c17f2a9681cd93c15eef83 100644 (file)
-
 // This file contains most of the magic that extracts "context
 // information" from the unstructered layout-oriented stuff in an
 // MathArray.
 
-#include <algorithm>
+#include <config.h>
 
+#include "math_amsarrayinset.h"
+#include "math_arrayinset.h"
 #include "math_charinset.h"
 #include "math_deliminset.h"
+#include "math_diffinset.h"
 #include "math_exfuncinset.h"
 #include "math_exintinset.h"
-#include "math_funcinset.h"
+#include "math_fracinset.h"
+#include "math_liminset.h"
 #include "math_matrixinset.h"
 #include "math_mathmlstream.h"
+#include "math_numberinset.h"
 #include "math_scriptinset.h"
 #include "math_stringinset.h"
 #include "math_symbolinset.h"
+#include "math_unknowninset.h"
+#include "math_parser.h"
+#include "Lsstream.h"
 #include "debug.h"
+#include "support/lyxlib.h"
+#include "support/systemcall.h"
+#include "support/filetools.h"
+
+#include <algorithm>
+
+using std::ostream;
+using std::istringstream;
+using std::find_if;
+using std::endl;
 
 
-std::ostream & operator<<(std::ostream & os, MathArray const & ar)
+ostream & operator<<(ostream & os, MathArray const & ar)
 {
-       NormalStream ns(os);    
+       NormalStream ns(os);
        ns << ar;
        return os;
 }
 
 
-MathScriptInset const * asScript(MathArray::const_iterator it)
+// define a function for tests
+typedef bool TestItemFunc(MathAtom const &);
+
+// define a function for replacing subexpressions
+typedef MathInset * ReplaceArgumentFunc(const MathArray & ar);
+
+
+
+// try to extract a super/subscript
+// modify iterator position to point behind the thing
+bool extractScript(MathArray & ar,
+       MathArray::iterator & pos, MathArray::iterator last)
 {
-       if (it->nucleus()->asScriptInset())
-               return 0;
-       ++it;
-       if (!it->nucleus())
-               return 0;
-       return it->nucleus()->asScriptInset();
+       // nothing to get here
+       if (pos == last)
+               return false;
+
+       // is this a scriptinset?
+       if (!(*pos)->asScriptInset())
+               return false;
+
+       // it is a scriptinset, use it.
+       ar.push_back(*pos);
+       ++pos;
+       return true;
 }
 
 
+// try to extract an "argument" to some function.
+// returns position behind the argument
+MathArray::iterator extractArgument(MathArray & ar,
+       MathArray::iterator pos, MathArray::iterator last, string const & = "")
+{
+       // nothing to get here
+       if (pos == last)
+               return pos;
+
+       // something deliminited _is_ an argument
+       if ((*pos)->asDelimInset()) {
+               ar.push_back(*pos);
+               return pos + 1;
+       }
+
+       // always take the first thing, no matter what it is
+       ar.push_back(*pos);
+
+       // go ahead if possible
+       ++pos;
+       if (pos == last)
+               return pos;
+
+       // if the next item is a subscript, it most certainly belongs to the
+       // thing we have
+       extractScript(ar, pos, last);
+       if (pos == last)
+               return pos;
+
+       // but it might be more than that.
+       // FIXME: not implemented
+       //for (MathArray::iterator it = pos + 1; it != last; ++it) {
+       //      // always take the first thing, no matter
+       //      if (it == pos) {
+       //              ar.push_back(*it);
+       //              continue;
+       //      }
+       //}
+       return pos;
+}
+
 
 // returns sequence of char with same code starting at it up to end
 // it might be less, though...
-string charSequence(MathArray::const_iterator it, MathArray::const_iterator end)
+string charSequence
+       (MathArray::const_iterator it, MathArray::const_iterator end)
 {
        string s;
-       MathCharInset const * p = it->nucleus()->asCharInset();
-       if (p) {
-               for (MathTextCodes c = p->code(); it != end; ++it) {
-                       p = it->nucleus()->asCharInset();
-                       if (!p || p->code() != c)
-                               break;
-                       s += p->getChar();
-               }
-       }
+       for (; it != end && (*it)->asCharInset(); ++it)
+               s += (*it)->getChar();
        return s;
 }
 
 
-void extractStrings(MathArray & dat)
+void extractStrings(MathArray & ar)
 {
        //lyxerr << "\nStrings from: " << ar << "\n";
-       MathArray ar;
-       MathArray::const_iterator it = dat.begin();
-       while (it != dat.end()) {
-               if (it->nucleus() && it->nucleus()->asCharInset()) {
-                       string s = charSequence(it, dat.end());
-                       MathTextCodes c = it->nucleus()->asCharInset()->code();
-                       ar.push_back(MathAtom(new MathStringInset(s, c)));
-                       it += s.size();
-               } else {
-                       ar.push_back(*it);
-                       ++it;
-               }
+       for (MathArray::size_type i = 0; i < ar.size(); ++i) {
+               if (!ar[i]->asCharInset())
+                       continue;
+               string s = charSequence(ar.begin() + i, ar.end());
+               ar[i] = MathAtom(new MathStringInset(s));
+               ar.erase(i + 1, i + s.size());
        }
-       ar.swap(dat);
        //lyxerr << "\nStrings to: " << ar << "\n";
 }
 
 
-MathInset * singleItem(MathArray & ar)
+MathInset const * singleItem(MathArray const & ar)
 {
        return ar.size() == 1 ? ar.begin()->nucleus() : 0;
 }
@@ -85,35 +147,66 @@ MathInset * singleItem(MathArray & ar)
 
 void extractMatrices(MathArray & ar)
 {
-       lyxerr << "\nMatrices from: " << ar << "\n";
+       //lyxerr << "\nMatrices from: " << ar << "\n";
+       // first pass for explicitly delimited stuff
        for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it) {
-               MathDelimInset * del = (*it)->asDelimInset();
+               MathDelimInset const * del = (*it)->asDelimInset();
                if (!del)
                        continue;
-               MathInset * arr = singleItem(del->cell(0));
-               if (!arr || !arr->asArrayInset())
+               MathInset const * arr = singleItem(del->cell(0));
+               if (!arr || !arr->asGridInset())
+                       continue;
+               *it = MathAtom(new MathMatrixInset(*(arr->asGridInset())));
+       }
+
+       // second pass for AMS "pmatrix" etc
+       for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it) {
+               MathAMSArrayInset const * ams = (*it)->asAMSArrayInset();
+               if (!ams)
                        continue;
-               *it = MathAtom(new MathMatrixInset(*(arr->asArrayInset())));
-               lyxerr << "\nMatrices to: " << ar << "\n";
+               *it = MathAtom(new MathMatrixInset(*ams));
        }
-} 
+       //lyxerr << "\nMatrices to: " << ar << "\n";
+}
+
 
 // convert this inset somehow to a string
-string extractString(MathInset * p)
+bool extractString(MathAtom const & at, string & str)
 {
-       if (p && p->getChar())
-               return string(1, p->getChar());
-       if (p && p->asStringInset())
-               return p->asStringInset()->str();
-       return string();
+       if (at->getChar()) {
+               str = string(1, at->getChar());
+               return true;
+       }
+       if (at->asStringInset()) {
+               str = at->asStringInset()->str();
+               return true;
+       }
+       return false;
 }
 
 
-// define a function for tests
-typedef bool TestItemFunc(MathInset *);
+// convert this inset somehow to a number
+bool extractNumber(MathArray const & ar, int & i)
+{
+       istringstream is(charSequence(ar.begin(), ar.end()).c_str());
+       is >> i;
+       return is;
+}
 
-// define a function for replacing subexpressions
-typedef MathInset * ReplaceArgumentFunc(const MathArray & ar);
+
+bool extractNumber(MathArray const & ar, double & d)
+{
+       istringstream is(charSequence(ar.begin(), ar.end()).c_str());
+       is >> d;
+       return is;
+}
+
+
+bool testString(MathAtom const & at, const string & str)
+{
+       string s;
+       return extractString(at, s) && str == s;
+}
 
 
 // search end of nested sequence
@@ -125,9 +218,9 @@ MathArray::iterator endNestSearch(
 )
 {
        for (int level = 0; it != last; ++it) {
-               if (testOpen(it->nucleus()))
+               if (testOpen(*it))
                        ++level;
-               if (testClose(it->nucleus()))
+               if (testClose(*it))
                        --level;
                if (level == 0)
                        break;
@@ -149,7 +242,7 @@ void replaceNested(
        for (MathArray::size_type i = 0; i < ar.size(); ++i) {
                // check whether this is the begin of the sequence
                MathArray::iterator it = ar.begin() + i;
-               if (!testOpen(it->nucleus()))
+               if (!testOpen(*it))
                        continue;
 
                // search end of sequence
@@ -162,40 +255,164 @@ void replaceNested(
 
                // replace the original stuff by the new inset
                ar.erase(it + 1, jt + 1);
-               (*it).reset(p);
+               *it = MathAtom(p);
+       }
+}
+
+
+
+//
+// split scripts into seperate super- and subscript insets. sub goes in
+// front of super...
+//
+
+void splitScripts(MathArray & ar)
+{
+       //lyxerr << "\nScripts from: " << ar << "\n";
+       for (MathArray::size_type i = 0; i < ar.size(); ++i) {
+               MathArray::iterator it = ar.begin() + i;
+
+               // is this script inset?
+               MathScriptInset * p = (*it).nucleus()->asScriptInset();
+               if (!p)
+                       continue;
+
+               // no problem if we don't have both...
+               if (!p->hasUp() || !p->hasDown())
+                       continue;
+
+               // create extra script inset and move superscript over
+               MathScriptInset * q = new MathScriptInset;
+               q->ensure(true);
+               std::swap(q->up(), p->up());
+               p->removeScript(true);
+
+               // insert new inset behind
+               ++i;
+               ar.insert(i, MathAtom(q));
        }
-} 
+       //lyxerr << "\nScripts to: " << ar << "\n";
+}
+
+
+//
+// extract exp(...)
+//
+
+void extractExps(MathArray & ar)
+{
+       //lyxerr << "\nExps from: " << ar << "\n";
+
+       for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
+               MathArray::iterator it = ar.begin() + i;
+
+               // is this 'e'?
+               MathCharInset const * p = (*it)->asCharInset();
+               if (!p || p->getChar() != 'e')
+                       continue;
+
+               // we need an exponent but no subscript
+               MathScriptInset const * sup = (*(it + 1))->asScriptInset();
+               if (!sup || sup->hasDown())
+                       continue;
+
+               // create a proper exp-inset as replacement 
+               *it = MathAtom(new MathExFuncInset("exp", sup->cell(1)));
+               ar.erase(it + 1);
+       }
+       //lyxerr << "\nExps to: " << ar << "\n";
+}
+
+
+//
+// extract det(...)  from |matrix|
+//
+void extractDets(MathArray & ar)
+{
+       //lyxerr << "\ndet from: " << ar << "\n";
+       for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it) {
+               MathDelimInset const * del = (*it)->asDelimInset();
+               if (!del)
+                       continue;
+               if (!del->isAbs())
+                       continue;
+               *it = MathAtom(new MathExFuncInset("det", del->cell(0)));
+       }
+       //lyxerr << "\ndet to: " << ar << "\n";
+}
+
+
+//
+// search numbers
+//
+
+bool isDigitOrSimilar(char c)
+{
+       return ('0' <= c && c <= '9') || c == '.';
+}
+
+
+// returns sequence of digits
+string digitSequence
+       (MathArray::const_iterator it, MathArray::const_iterator end)
+{
+       string s;
+       for (; it != end && (*it)->asCharInset(); ++it) {
+               if (!isDigitOrSimilar((*it)->getChar()))
+                       break;
+               s += (*it)->getChar();
+       }
+       return s;
+}
+
+
+void extractNumbers(MathArray & ar)
+{
+       //lyxerr << "\nNumbers from: " << ar << "\n";
+       for (MathArray::size_type i = 0; i < ar.size(); ++i) {
+               if (!ar[i]->asCharInset())
+                       continue;
+               if (!isDigitOrSimilar(ar[i]->asCharInset()->getChar()))
+                       continue;
+
+               string s = digitSequence(ar.begin() + i, ar.end());
+
+               ar[i] = MathAtom(new MathNumberInset(s));
+               ar.erase(i + 1, i + s.size());
+       }
+       //lyxerr << "\nNumbers to: " << ar << "\n";
+}
+
 
 
 //
 // search deliminiters
 //
 
-bool openParanTest(MathInset * p)
+bool testOpenParan(MathAtom const & at)
 {
-       return extractString(p) == "(";
+       return testString(at, "(");
 }
 
 
-bool closeParanTest(MathInset * p)
+bool testCloseParan(MathAtom const & at)
 {
-       return extractString(p) == ")";
+       return testString(at, ")");
 }
 
 
-MathInset * delimReplacement(const MathArray & ar)
+MathInset * replaceDelims(const MathArray & ar)
 {
-       MathDelimInset * del = new MathDelimInset("(", ")");
-       del->cell(0) = ar;
-       return del;
+       return new MathDelimInset("(", ")", ar);
 }
 
 
 // replace '('...')' sequences by a real MathDelimInset
-void extractDelims(MathArray & ar) {
-       lyxerr << "\nDelims from: " << ar << "\n";
-       replaceNested(ar, openParanTest, closeParanTest, delimReplacement);
-       lyxerr << "\nDelims to: " << ar << "\n";
+void extractDelims(MathArray & ar)
+{
+       //lyxerr << "\nDelims from: " << ar << "\n";
+       replaceNested(ar, testOpenParan, testCloseParan, replaceDelims);
+       //lyxerr << "\nDelims to: " << ar << "\n";
 }
 
 
@@ -210,63 +427,89 @@ void extractDelims(MathArray & ar) {
 void extractFunctions(MathArray & ar)
 {
        // we need at least two items...
-       if (ar.size() <= 1)
+       if (ar.size() < 2)
                return;
 
-       lyxerr << "\nFunctions from: " << ar << "\n";
-       for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
+       //lyxerr << "\nFunctions from: " << ar << "\n";
+       for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
                MathArray::iterator it = ar.begin() + i;
-
-               // is this a function name?
-               MathFuncInset * func = (*it)->asFuncInset();
-               if (!func)
-                       continue;
-
-               // do we have an exponent?
-               // simply skippping the postion does the right thing:
-               // 'sin' '^2' 'x' -> 'sin(x)' '^2'
                MathArray::iterator jt = it + 1;
-               if (MathScriptInset * script = (*jt)->asScriptInset()) {
-                       // allow superscripts only
-                       if (script->hasDown())
+
+               string name;
+               // is it a function?
+               if ((*it)->asUnknownInset()) {
+                       // it certainly is if it is well known...
+                       name = (*it)->name();
+               } else {
+                       // is this a user defined function?
+                       // it it probably not, if it doesn't have a name.
+                       if (!extractString(*it, name))
                                continue;
-                       ++jt;
+                       // it is not if it has no argument
                        if (jt == ar.end())
                                continue;
+                       // guess so, if this is followed by
+                       // a DelimInset with a single item in the cell
+                       MathDelimInset const * del = (*jt)->asDelimInset();
+                       if (!del || del->cell(0).size() != 1)
+                               continue;
+                       // fall trough into main branch
                }
 
-               // jt points now to the "argument". Since we had run "extractDelims"
-               // before, this could be a single argument only. Get hold of this.
-               MathArray arg;
-               MathDelimInset * del = (*jt)->asDelimInset();
-               if (del && del->isParanthesis()) 
-                       arg = del->cell(0);
-               else
-                       arg.push_back(*jt);
+               // do we have an exponent like in
+               // 'sin' '^2' 'x' -> 'sin(x)' '^2'
+               MathArray exp;
+               extractScript(exp, jt, ar.end());
+
+               // create a proper inset as replacement
+               MathExFuncInset * p = new MathExFuncInset(name);
+
+               // jt points to the "argument". Get hold of this.
+               MathArray::iterator st = extractArgument(p->cell(0), jt, ar.end());
 
                // replace the function name by a real function inset
-               (*it).reset(new MathExFuncInset(func->name(), arg));
-               
+               *it = MathAtom(p);
+
                // remove the source of the argument from the array
-               ar.erase(jt);
-               lyxerr << "\nFunctions to: " << ar << "\n";
+               ar.erase(it + 1, st);
+
+               // re-insert exponent
+               ar.insert(i + 1, exp);
+               //lyxerr << "\nFunctions to: " << ar << "\n";
        }
-} 
+}
 
 
 //
 // search integrals
 //
 
-bool intSymbolTest(MathInset * p)
+bool testSymbol(MathAtom const & at, string const & name)
 {
-       return p->asSymbolInset() && p->asSymbolInset()->name() == "int";
+       return at->asSymbolInset() && at->asSymbolInset()->name() == name;
 }
 
 
-bool differentialTest(MathInset * p)
+bool testIntSymbol(MathAtom const & at)
 {
-       return extractString(p) == "d";
+       return testSymbol(at, "int");
+}
+
+
+bool testIntegral(MathAtom const & at)
+{
+       return
+        testIntSymbol(at) ||
+               ( at->asScriptInset() 
+                 && at->asScriptInset()->nuc().size()
+                       && testIntSymbol(at->asScriptInset()->nuc().back()) );
+}
+
+
+
+bool testIntDiff(MathAtom const & at)
+{
+       return testString(at, "d");
 }
 
 
@@ -275,49 +518,43 @@ bool differentialTest(MathInset * p)
 void extractIntegrals(MathArray & ar)
 {
        // we need at least three items...
-       if (ar.size() <= 2)
+       if (ar.size() < 3)
                return;
 
-       lyxerr << "\nIntegrals from: " << ar << "\n";
-       for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
+       //lyxerr << "\nIntegrals from: " << ar << "\n";
+       for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
                MathArray::iterator it = ar.begin() + i;
 
-               // is this a integral name?
-               if (!intSymbolTest(it->nucleus()))
-                       continue;
-
                // search 'd'
                MathArray::iterator jt =
-                       endNestSearch(it, ar.end(), intSymbolTest, differentialTest);
+                       endNestSearch(it, ar.end(), testIntegral, testIntDiff);
 
                // something sensible found?
                if (jt == ar.end())
                        continue;
 
-               // create a proper inset as replacement
+               // is this a integral name?
+               if (!testIntegral(*it))
+                       continue;
+
+               // core ist part from behind the scripts to the 'd'
                MathExIntInset * p = new MathExIntInset("int");
 
-               // collect scripts
-               MathArray::iterator st = it + 1;
-               if ((*st)->asScriptInset()) {
-                       p->scripts(*st);
-                       p->core(MathArray(st + 1, jt));
-               } else {
-                       p->core(MathArray(st, jt));
+               // handle scripts if available
+               if (!testIntSymbol(*it)) {
+                       p->cell(2) = (*it)->asScriptInset()->down();
+                       p->cell(3) = (*it)->asScriptInset()->up();
                }
+               p->cell(0) = MathArray(it + 1, jt);
 
-               // use the atom behind the 'd' as differential
-               MathArray ind;
-               if (jt + 1 != ar.end()) {
-                       ind.push_back(*(jt + 1));
-                       ++jt;
-               }
-               ar.erase(it + 1, jt + 1);
+               // use the "thing" behind the 'd' as differential
+               MathArray::iterator tt = extractArgument(p->cell(1), jt + 1, ar.end());
 
-               p->index(ind);
-               (*it).reset(p);
+               // remove used parts
+               ar.erase(it + 1, tt);
+               *it = MathAtom(p);
        }
-       lyxerr << "\nIntegrals to: " << ar << "\n";
+       //lyxerr << "\nIntegrals to: " << ar << "\n";
 }
 
 
@@ -325,91 +562,253 @@ void extractIntegrals(MathArray & ar)
 // search sums
 //
 
-bool sumSymbolTest(MathInset * p)
+
+bool testEqualSign(MathAtom const & at)
 {
-       return p->asSymbolInset() && p->asSymbolInset()->name() == "sum";
+       return testString(at, "=");
 }
 
 
-bool equalSign(MathInset * p)
+bool testSumSymbol(MathAtom const & p)
 {
-       return extractString(p) == "=";
+       return testSymbol(p, "sum");
 }
 
 
-bool equalSign1(MathAtom const & at)
+bool testSum(MathAtom const & at)
 {
-       return equalSign(at.nucleus());
+       return
+        testSumSymbol(at) ||
+               ( at->asScriptInset() 
+                 && at->asScriptInset()->nuc().size()
+                       && testSumSymbol(at->asScriptInset()->nuc().back()) );
 }
 
 
-
 // replace '\sum' ['_^'] f(x) sequences by a real MathExIntInset
 // assume 'extractDelims' ran before
 void extractSums(MathArray & ar)
 {
        // we need at least two items...
-       if (ar.size() <= 1)
+       if (ar.size() < 2)
                return;
 
-       lyxerr << "\nSums from: " << ar << "\n";
-       for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
+       //lyxerr << "\nSums from: " << ar << "\n";
+       for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
                MathArray::iterator it = ar.begin() + i;
 
                // is this a sum name?
-               if (!sumSymbolTest(it->nucleus()))
+               if (!testSum(*it))
                        continue;
 
                // create a proper inset as replacement
                MathExIntInset * p = new MathExIntInset("sum");
 
-               // collect scripts
-               MathArray::iterator st = it + 1;
-               if (st != ar.end() && (*st)->asScriptInset()) {
-                       p->scripts(*st);
-                       ++st;
-
+               // collect lower bound and summation index
+               MathScriptInset const * sub = (*it)->asScriptInset();
+               if (sub && sub->hasDown()) {
                        // try to figure out the summation index from the subscript
-                       MathScriptInset * script = p->scripts()->asScriptInset();
-                       if (script->hasDown()) {
-                               MathArray & ar = script->down().data_;
-                               MathArray::iterator it =
-                                       std::find_if(ar.begin(), ar.end(), &equalSign1);
-                               if (it != ar.end()) {
-                                       // we found a '=', use everything in front of that as index,
-                                       // and everything behind as start value
-                                       p->index(MathArray(ar.begin(), it));
-                                       ar.erase(ar.begin(), it + 1);
-                               } else {
-                                       // use everything as summation index
-                                       p->index(ar);
-                                       p->scripts().reset(0);
-                               }
+                       MathArray const & ar = sub->down();
+                       MathArray::const_iterator xt =
+                               find_if(ar.begin(), ar.end(), &testEqualSign);
+                       if (xt != ar.end()) {
+                               // we found a '=', use everything in front of that as index,
+                               // and everything behind as lower index
+                               p->cell(1) = MathArray(ar.begin(), xt);
+                               p->cell(2) = MathArray(xt + 1, ar.end());
+                       } else {
+                               // use everything as summation index, don't use scripts.
+                               p->cell(1) = ar;
                        }
                }
 
-               // use the atom behind the script as core
-               MathArray ind;
-               if (st != ar.end()) {
-                       MathArray core;
-                       core.push_back(*st);
-                       p->core(core);
-                       ++st;
+               // collect upper bound
+               if (sub && sub->hasUp())
+                       p->cell(3) = sub->up();
+
+               // use something  behind the script as core
+               MathArray::iterator tt = extractArgument(p->cell(0), it + 1, ar.end());
+
+               // cleanup
+               ar.erase(it + 1, tt);
+               *it = MathAtom(p);
+       }
+       //lyxerr << "\nSums to: " << ar << "\n";
+}
+
+
+//
+// search differential stuff
+//
+
+// tests for 'd' or '\partial'
+bool testDiffItem(MathAtom const & at)
+{
+       return testString(at, "d");
+}
+
+
+bool testDiffArray(MathArray const & ar)
+{
+       return ar.size() && testDiffItem(ar.front());
+}
+
+
+bool testDiffFrac(MathAtom const & at)
+{
+       return
+               at->asFracInset()
+                       && testDiffArray(at->asFracInset()->cell(0))
+                       && testDiffArray(at->asFracInset()->cell(1));
+}
+
+
+void extractDiff(MathArray & ar)
+{
+       //lyxerr << "\nDiffs from: " << ar << "\n";
+       for (MathArray::size_type i = 0; i < ar.size(); ++i) {
+               MathArray::iterator it = ar.begin() + i;
+
+               // is this a "differential fraction"?
+               if (!testDiffFrac(*it))
+                       continue;
+
+               MathFracInset const * f = (*it)->asFracInset();
+               if (!f) {
+                       lyxerr << "should not happen\n";
+                       continue;
+               }
+
+               // create a proper diff inset
+               MathDiffInset * diff = new MathDiffInset;
+
+               // collect function, let jt point behind last used item
+               MathArray::iterator jt = it + 1;
+               //int n = 1;
+               MathArray const & numer = f->cell(0);
+               if (numer.size() > 1 && numer[1]->asScriptInset()) {
+                       // this is something like  d^n f(x) / d... or  d^n / d...
+                       // FIXME
+                       //n = 1;
+                       if (numer.size() > 2)
+                               diff->cell(0) = MathArray(numer.begin() + 2, numer.end());
+                       else
+                               jt = extractArgument(diff->cell(0), jt, ar.end());
+               } else {
+                       // simply d f(x) / d... or  d/d...
+                       if (numer.size() > 1)
+                               diff->cell(0) = MathArray(numer.begin() + 1, numer.end());
+                       else
+                               jt = extractArgument(diff->cell(0), jt, ar.end());
+               }
+
+               // collect denominator parts
+               MathArray const & denom = f->cell(1);
+               for (MathArray::const_iterator dt = denom.begin(); dt != denom.end();) {
+                       // find the next 'd'
+                       MathArray::const_iterator et
+                               = find_if(dt + 1, denom.end(), &testDiffItem);
+
+                       // point before this
+                       MathArray::const_iterator st = et - 1;
+                       MathScriptInset const * script = (*st)->asScriptInset();
+                       if (script && script->hasUp()) {
+                               // things like   d.../dx^n
+                               int mult = 1;
+                               if (extractNumber(script->up(), mult)) {
+                                       //lyxerr << "mult: " << mult << endl;
+                                       for (int i = 0; i < mult; ++i)
+                                               diff->addDer(MathArray(dt + 1, st));
+                               }
+                       } else {
+                               // just  d.../dx
+                               diff->addDer(MathArray(dt + 1, et));
+                       }
+                       dt = et;
                }
-               ar.erase(it + 1, st);
-               (*it).reset(p);
+
+               // cleanup
+               ar.erase(it + 1, jt);
+               *it = MathAtom(diff);
        }
-       lyxerr << "\nSums to: " << ar << "\n";
+       //lyxerr << "\nDiffs to: " << ar << "\n";
+}
+
+
+//
+// search limits
+//
+
+
+bool testRightArrow(MathAtom const & at)
+{
+       return testSymbol(at, "to") || testSymbol(at, "rightarrow");
 }
 
 
+
+// replace '\lim_{x->x0} f(x)' sequences by a real MathLimInset
+// assume 'extractDelims' ran before
+void extractLims(MathArray & ar)
+{
+       // we need at least three items...
+       if (ar.size() < 3)
+               return;
+
+       //lyxerr << "\nLimits from: " << ar << "\n";
+       for (MathArray::size_type i = 0; i + 2 < ar.size(); ++i) {
+               MathArray::iterator it = ar.begin() + i;
+
+               // is this a limit function?
+               if (!testSymbol(*it, "lim")) 
+                       continue;
+
+               // the next one must be a subscript (without superscript)
+               MathScriptInset const * sub = (*(it + 1))->asScriptInset();
+               if (!sub || !sub->hasDown() || sub->hasUp())
+                       continue;
+
+               // and it must contain a -> symbol
+               MathArray const & s = sub->down();
+               MathArray::const_iterator st = find_if(s.begin(), s.end(), &testRightArrow);
+               if (st == s.end())
+                       continue;
+
+               // the -> splits the subscript int x and x0
+               MathArray x  = MathArray(s.begin(), st);
+               MathArray x0 = MathArray(st + 1, s.end());
+               
+               // use something behind the script as core
+               MathArray f;
+               MathArray::iterator tt = extractArgument(f, it + 2, ar.end());
+
+               // cleanup
+               ar.erase(it + 1, tt);
+
+               // create a proper inset as replacement
+               *it = MathAtom(new MathLimInset(f, x, x0));
+       }
+       //lyxerr << "\nLimits to: " << ar << "\n";
+}
+
+
+//
+// combine searches
+//
+
 void extractStructure(MathArray & ar)
 {
+       extractIntegrals(ar);
+       extractSums(ar);
+       splitScripts(ar);
+       extractNumbers(ar);
        extractMatrices(ar);
        extractDelims(ar);
        extractFunctions(ar);
-       extractIntegrals(ar);
-       extractSums(ar);
+       extractDets(ar);
+       extractDiff(ar);
+       extractExps(ar);
+       extractLims(ar);
        extractStrings(ar);
 }
 
@@ -418,16 +817,10 @@ void write(MathArray const & dat, WriteStream & wi)
 {
        MathArray ar = dat;
        extractStrings(ar);
+       wi.firstitem() = true;
        for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
-               MathInset const * p = it->nucleus();
-               if (it + 1 != ar.end()) {
-                       if (MathScriptInset const * q = asScript(it)) {
-                               q->write(p, wi);
-                               ++it;
-                               continue;
-                       } 
-               }
-               p->write(wi);
+               (*it)->write(wi);
+               wi.firstitem() = false;
        }
 }
 
@@ -443,17 +836,8 @@ void octavize(MathArray const & dat, OctaveStream & os)
 {
        MathArray ar = dat;
        extractStructure(ar);
-       for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
-               MathInset const * p = it->nucleus();
-               if (it + 1 != ar.end()) {
-                       if (MathScriptInset const * q = asScript(it)) {
-                               q->octavize(p, os);
-                               ++it;   
-                               continue;
-                       }
-               }
-               p->octavize(os);
-       }
+       for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
+               (*it)->octavize(os);
 }
 
 
@@ -461,17 +845,17 @@ void maplize(MathArray const & dat, MapleStream & os)
 {
        MathArray ar = dat;
        extractStructure(ar);
-       for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
-               MathInset const * p = it->nucleus();
-               if (it + 1 != ar.end()) {
-                       if (MathScriptInset const * q = asScript(it)) {
-                               q->maplize(p, os);
-                               ++it;   
-                               continue;
-                       }
-               }
-               p->maplize(os);
-       }
+       for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
+               (*it)->maplize(os);
+}
+
+
+void mathematicize(MathArray const & dat, MathematicaStream & os)
+{
+       MathArray ar = dat;
+       extractStructure(ar);
+       for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
+               (*it)->mathematicize(os);
 }
 
 
@@ -482,21 +866,209 @@ void mathmlize(MathArray const & dat, MathMLStream & os)
        if (ar.size() == 0)
                os << "<mrow/>";
        else if (ar.size() == 1)
-               os << ar.begin()->nucleus();
+               os << ar.front();
        else {
                os << MTag("mrow");
-               for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
-                       MathInset const * p = it->nucleus();
-                       if (it + 1 != ar.end()) {
-                               if (MathScriptInset const * q = asScript(it)) {
-                                       q->mathmlize(p, os);
-                                       ++it;   
-                                       continue;
-                               }
+               for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
+                       (*it)->mathmlize(os);
+               os << ETag("mrow");
+       }
+}
+
+
+
+
+namespace {
+
+       string captureOutput(string const & cmd, string const & data)
+       {
+               string outfile = lyx::tempName(string(), "mathextern");
+               string full =  "echo '" + data + "' | (" + cmd + ") > " + outfile;
+               lyxerr << "calling: " << full << endl;
+               Systemcall dummy;
+               dummy.startscript(Systemcall::Wait, full);
+               string out = GetFileContents(outfile);
+               lyx::unlink(outfile);
+               lyxerr << "result: '" << out << "'" << endl;
+               return out;
+       }
+
+
+       MathArray pipeThroughMaple(string const & extra, MathArray const & ar)
+       {
+               string header = "readlib(latex):\n";
+
+               // remove the \\it for variable names
+               //"#`latex/csname_font` := `\\it `:"
+               header +=
+                       "`latex/csname_font` := ``:\n";
+
+               // export matrices in (...) instead of [...]
+               header +=
+                       "`latex/latex/matrix` := "
+                               "subs(`[`=`(`, `]`=`)`,"
+                                       "eval(`latex/latex/matrix`)):\n";
+
+               // replace \\cdots with proper '*'
+               header +=
+                       "`latex/latex/*` := "
+                               "subs(`\\,`=`\\cdot `,"
+                                       "eval(`latex/latex/*`)):\n";
+
+               // remove spurious \\noalign{\\medskip} in matrix output
+               header +=
+                       "`latex/latex/matrix`:= "
+                               "subs(`\\\\\\\\\\\\noalign{\\\\medskip}` = `\\\\\\\\`,"
+                                       "eval(`latex/latex/matrix`)):\n";
+
+               //"#`latex/latex/symbol` "
+               //      " := subs((\\'_\\' = \\'`\\_`\\',eval(`latex/latex/symbol`)): ";
+
+               string trailer = "quit;";
+               ostringstream os;
+               MapleStream ms(os);
+               ms << ar;
+               string expr = os.str().c_str();
+               lyxerr << "ar: '" << ar << "'\n";
+               lyxerr << "ms: '" << os.str() << "'\n";
+
+               for (int i = 0; i < 100; ++i) { // at most 100 attempts
+                       // try to fix missing '*' the hard way by using mint
+                       //
+                       // ... > echo "1A;" | mint -i 1 -S -s -q
+                       // on line     1: 1A;
+                       //                 ^ syntax error -
+                       //                   Probably missing an operator such as * p
+                       //
+                       lyxerr << "checking expr: '" << expr << "'\n";
+                       string out = captureOutput("mint -i 1 -S -s -q -q", expr + ";");
+                       if (out.empty())
+                               break; // expression syntax is ok
+                       istringstream is(out.c_str());
+                       string line;
+                       getline(is, line);
+                       if (line.find("on line") != 0)
+                               break; // error message not identified
+                       getline(is, line);
+                       string::size_type pos = line.find('^');
+                       if (pos == string::npos || pos < 15)
+                               break; // caret position not found
+                       pos -= 15; // skip the "on line ..." part
+                       if (expr[pos] == '*' || (pos > 0 && expr[pos - 1] == '*'))
+                               break; // two '*' in a row are definitely bad
+                       expr.insert(pos,  "*");
+               }
+
+               string full = "latex(" +  extra + '(' + expr + "));";
+               string out = captureOutput("maple -q", header + full + trailer);
+
+               // change \_ into _
+
+               //
+               MathArray res;
+               mathed_parse_cell(res, out);
+               return res;
+       }
+
+
+       MathArray pipeThroughOctave(string const &, MathArray const & ar)
+       {
+               ostringstream os;
+               OctaveStream vs(os);
+               vs << ar;
+               string expr = os.str().c_str();
+               string out;
+
+               lyxerr << "pipe: ar: '" << ar << "'\n";
+               lyxerr << "pipe: expr: '" << expr << "'\n";
+
+               for (int i = 0; i < 100; ++i) { // at most 100 attempts
+                       //
+                       // try to fix missing '*' the hard way
+                       // parse error:
+                       // >>> ([[1 2 3 ];[2 3 1 ];[3 1 2 ]])([[1 2 3 ];[2 3 1 ];[3 1 2 ]])
+                       //                                   ^
+                       //
+                       lyxerr << "checking expr: '" << expr << "'\n";
+                       out = captureOutput("octave -q 2>&1", expr);
+                       lyxerr << "checking out: '" << out << "'\n";
+
+                       // leave loop if expression syntax is probably ok
+                       if (out.find("parse error:") == string::npos)
+                               break;
+
+                       // search line with single caret
+                       istringstream is(out.c_str());
+                       string line;
+                       while (is) {
+                               getline(is, line);
+                               lyxerr << "skipping line: '" << line << "'\n";
+                               if (line.find(">>> ") != string::npos)
+                                       break;
                        }
-                       p->mathmlize(os);
+
+                       // found line with error, next line is the one with caret
+                       getline(is, line);
+                       string::size_type pos = line.find('^');
+                       lyxerr << "caret line: '" << line << "'\n";
+                       lyxerr << "found caret at pos: '" << pos << "'\n";
+                       if (pos == string::npos || pos < 4)
+                               break; // caret position not found
+                       pos -= 4; // skip the ">>> " part
+                       if (expr[pos] == '*')
+                               break; // two '*' in a row are definitely bad
+                       expr.insert(pos,  "*");
                }
-               os << ETag("mrow");
+
+               if (out.size() < 6)
+                       return MathArray();
+
+               // remove 'ans = '
+               out = out.substr(6);
+
+               // parse output as matrix or single number
+               MathAtom at(new MathArrayInset("array", out));
+               MathArrayInset const * mat = at.nucleus()->asArrayInset();
+               MathArray res;
+               if (mat->ncols() == 1 && mat->nrows() == 1)
+                       res.append(mat->cell(0));
+               else {
+                       res.push_back(MathAtom(new MathDelimInset("(", ")")));
+                       res.back().nucleus()->cell(0).push_back(at);
+               }
+               return res;
        }
+
 }
 
+
+MathArray pipeThroughExtern(string const & lang, string const & extra,
+       MathArray const & ar)
+{
+       if (lang == "octave")
+               return pipeThroughOctave(extra, ar);
+
+       if (lang == "maple")
+               return pipeThroughMaple(extra, ar);
+
+       // create normalized expression
+       ostringstream os;
+       NormalStream ns(os);
+       os << "[" << extra << ' ';
+       ns << ar;
+       os << "]";
+       string data = os.str().c_str();
+
+       // search external script
+       string file = LibFileSearch("mathed", "extern_" + lang);
+       if (file.empty()) {
+               lyxerr << "converter to '" << lang << "' not found\n";
+               return MathArray();
+       }
+
+       // run external sript
+       string out = captureOutput(file, data);
+       MathArray res;
+       mathed_parse_cell(res, out);
+       return res;
+}