X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fmathed%2Fmath_extern.C;h=f595b1b1686d040165163cac6a3640b5f3fa61db;hb=254272868f1d891b9d0a778f871afb23cce84081;hp=3d7593540d69da3fd93073cfd2271dd3bba611a9;hpb=3585748871d0928b22543f3e4697d75ad6c164d9;p=lyx.git diff --git a/src/mathed/math_extern.C b/src/mathed/math_extern.C index 3d7593540d..f595b1b168 100644 --- a/src/mathed/math_extern.C +++ b/src/mathed/math_extern.C @@ -1,29 +1,42 @@ - // This file contains most of the magic that extracts "context // information" from the unstructered layout-oriented stuff in an // MathArray. -#include +#include +#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_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 +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; } @@ -37,7 +50,7 @@ typedef MathInset * ReplaceArgumentFunc(const MathArray & ar); -// try to extract a super/subscript +// try to extract a super/subscript // modify iterator position to point behind the thing bool extractScript(MathArray & ar, MathArray::iterator & pos, MathArray::iterator last) @@ -101,6 +114,8 @@ MathArray::iterator extractArgument(MathArray & ar, MathScriptInset const * asScript(MathArray::const_iterator it) { + if (!it->nucleus()) + return 0; if (it->nucleus()->asScriptInset()) return 0; ++it; @@ -113,18 +128,13 @@ MathScriptInset const * asScript(MathArray::const_iterator it) // returns sequence of char with same code starting at it up to end // it might be less, though... -MathArray::const_iterator charSequence(MathArray::const_iterator it, - MathArray::const_iterator end, string & s, MathTextCodes & c) -{ - MathCharInset const * p = (*it)->asCharInset(); - c = p->code(); - for (; it != end; ++it) { - p = (*it)->asCharInset(); - if (!p || p->code() != c) - break; - s += p->getChar(); - } - return it; +string charSequence + (MathArray::const_iterator it, MathArray::const_iterator end) +{ + string s; + for (; it != end && (*it)->asCharInset(); ++it) + s += (*it)->getChar(); + return s; } @@ -132,18 +142,11 @@ void extractStrings(MathArray & ar) { //lyxerr << "\nStrings from: " << ar << "\n"; for (MathArray::size_type i = 0; i < ar.size(); ++i) { - MathArray::iterator it = ar.begin() + i; - if (!(*it)->asCharInset()) + if (!ar[i]->asCharInset()) continue; - - // create proper string inset - MathStringInset * p = new MathStringInset; - MathArray::const_iterator - jt = charSequence(it, ar.end(), p->str_, p->code_); - - // clean up - (*it).reset(p); - ar.erase(i + 1, jt - ar.begin()); + string s = charSequence(ar.begin() + i, ar.end()); + ar[i].reset(new MathStringInset(s)); + ar.erase(i + 1, i + s.size()); } //lyxerr << "\nStrings to: " << ar << "\n"; } @@ -157,17 +160,26 @@ 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(); if (!del) continue; MathInset * arr = singleItem(del->cell(0)); - if (!arr || !arr->asArrayInset()) + if (!arr || !arr->asGridInset()) continue; - *it = MathAtom(new MathMatrixInset(*(arr->asArrayInset()))); + *it = MathAtom(new MathMatrixInset(*(arr->asGridInset()))); } - lyxerr << "\nMatrices to: " << ar << "\n"; + + // second pass for AMS "pmatrix" etc + for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it) { + MathAMSArrayInset * ams = (*it)->asAMSArrayInset(); + if (!ams) + continue; + *it = MathAtom(new MathMatrixInset(*ams)); + } + //lyxerr << "\nMatrices to: " << ar << "\n"; } @@ -191,15 +203,20 @@ bool extractString(MathInset * p, string & str) // convert this inset somehow to a number bool extractNumber(MathArray const & ar, int & i) { - string s; - MathTextCodes c; - charSequence(ar.begin(), ar.end(), s, c); - std::istringstream is(s.c_str()); + istringstream is(charSequence(ar.begin(), ar.end()).c_str()); is >> i; return is; } +bool extractNumber(MathArray const & ar, double & d) +{ + istringstream is(charSequence(ar.begin(), ar.end()).c_str()); + is >> d; + return is; +} + + bool testString(MathInset * p, const string & str) { string s; @@ -255,18 +272,18 @@ void replaceNested( ar.erase(it + 1, jt + 1); (*it).reset(p); } -} +} // // split scripts into seperate super- and subscript insets. sub goes in -// front of super... +// front of super... // void splitScripts(MathArray & ar) { - lyxerr << "\nScripts from: " << ar << "\n"; + //lyxerr << "\nScripts from: " << ar << "\n"; for (MathArray::size_type i = 0; i < ar.size(); ++i) { MathArray::iterator it = ar.begin() + i; @@ -281,15 +298,15 @@ void splitScripts(MathArray & ar) // create extra script inset and move superscript over MathScriptInset * q = new MathScriptInset; - q->ensure(true); + q->ensure(true); q->up().data_.swap(p->up().data_); p->removeScript(true); // insert new inset behind ++i; - ar.insert(i, MathAtom(q)); + ar.insert(i, MathAtom(q)); } - lyxerr << "\nScripts to: " << ar << "\n"; + //lyxerr << "\nScripts to: " << ar << "\n"; } @@ -299,7 +316,7 @@ void splitScripts(MathArray & ar) void extractExps(MathArray & ar) { - lyxerr << "\nExps from: " << ar << "\n"; + //lyxerr << "\nExps from: " << ar << "\n"; for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) { MathArray::iterator it = ar.begin() + i; @@ -314,18 +331,75 @@ void extractExps(MathArray & ar) if (!sup || sup->hasDown()) continue; - // create a proper exp-inset as replacement - MathExFuncInset * func = new MathExFuncInset("exp"); - func->cell(0) = sup->cell(1); - - // clean up - (*it).reset(func); + // create a proper exp-inset as replacement + *it = new MathExFuncInset("exp", sup->cell(1)); ar.erase(it + 1); } - lyxerr << "\nExps to: " << ar << "\n"; + //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 * del = (*it)->asDelimInset(); + if (!del) + continue; + if (!del->isAbs()) + continue; + *it = 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].reset(new MathNumberInset(s)); + ar.erase(i + 1, i + s.size()); + } + //lyxerr << "\nNumbers to: " << ar << "\n"; +} + + + // // search deliminiters // @@ -344,18 +418,16 @@ bool testCloseParan(MathInset * p) 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"; + //lyxerr << "\nDelims from: " << ar << "\n"; replaceNested(ar, testOpenParan, testCloseParan, replaceDelims); - lyxerr << "\nDelims to: " << ar << "\n"; + //lyxerr << "\nDelims to: " << ar << "\n"; } @@ -373,17 +445,17 @@ void extractFunctions(MathArray & ar) if (ar.size() <= 1) return; - lyxerr << "\nFunctions from: " << ar << "\n"; + //lyxerr << "\nFunctions from: " << ar << "\n"; for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) { MathArray::iterator it = ar.begin() + i; MathArray::iterator jt = it + 1; string name; // is it a function? - if ((*it)->asFuncInset()) { + if ((*it)->asUnknownInset()) { // it certainly is if it is well known... - name = (*it)->asFuncInset()->name(); - } else { + name = (*it)->asUnknownInset()->name(); + } else { // is this a user defined function? // it it probably not, if it doesn't have a name. if (!extractString((*it).nucleus(), name)) @@ -403,7 +475,7 @@ void extractFunctions(MathArray & ar) // 'sin' '^2' 'x' -> 'sin(x)' '^2' MathArray exp; extractScript(exp, jt, ar.end()); - + // create a proper inset as replacement MathExFuncInset * p = new MathExFuncInset(name); @@ -412,15 +484,15 @@ void extractFunctions(MathArray & ar) // replace the function name by a real function inset (*it).reset(p); - + // remove the source of the argument from the array ar.erase(it + 1, st); // re-insert exponent ar.insert(i + 1, exp); - lyxerr << "\nFunctions to: " << ar << "\n"; + //lyxerr << "\nFunctions to: " << ar << "\n"; } -} +} // @@ -453,7 +525,7 @@ void extractIntegrals(MathArray & ar) if (ar.size() <= 2) return; - lyxerr << "\nIntegrals from: " << ar << "\n"; + //lyxerr << "\nIntegrals from: " << ar << "\n"; for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) { MathArray::iterator it = ar.begin() + i; @@ -475,7 +547,7 @@ void extractIntegrals(MathArray & ar) // collect subscript if any MathArray::iterator st = it + 1; if (st != ar.end()) - if (MathScriptInset * sub = (*st)->asScriptInset()) + if (MathScriptInset * sub = (*st)->asScriptInset()) if (sub->hasDown()) { p->cell(2) = sub->down().data_; ++st; @@ -483,7 +555,7 @@ void extractIntegrals(MathArray & ar) // collect superscript if any if (st != ar.end()) - if (MathScriptInset * sup = (*st)->asScriptInset()) + if (MathScriptInset * sup = (*st)->asScriptInset()) if (sup->hasUp()) { p->cell(3) = sup->up().data_; ++st; @@ -494,12 +566,12 @@ void extractIntegrals(MathArray & ar) // use the "thing" behind the 'd' as differential MathArray::iterator tt = extractArgument(p->cell(1), jt + 1, ar.end()); - + // remove used parts ar.erase(it + 1, tt); (*it).reset(p); } - lyxerr << "\nIntegrals to: " << ar << "\n"; + //lyxerr << "\nIntegrals to: " << ar << "\n"; } @@ -528,7 +600,7 @@ void extractSums(MathArray & ar) if (ar.size() <= 1) return; - lyxerr << "\nSums from: " << ar << "\n"; + //lyxerr << "\nSums from: " << ar << "\n"; for (MathArray::size_type i = 0; i + 1< ar.size(); ++i) { MathArray::iterator it = ar.begin() + i; @@ -547,7 +619,7 @@ void extractSums(MathArray & ar) // try to figure out the summation index from the subscript MathArray & ar = sub->down().data_; MathArray::iterator it = - std::find_if(ar.begin(), ar.end(), &testEqualSign); + find_if(ar.begin(), ar.end(), &testEqualSign); if (it != ar.end()) { // we found a '=', use everything in front of that as index, // and everything behind as lower index @@ -575,7 +647,7 @@ void extractSums(MathArray & ar) ar.erase(it + 1, tt); (*it).reset(p); } - lyxerr << "\nSums to: " << ar << "\n"; + //lyxerr << "\nSums to: " << ar << "\n"; } @@ -612,7 +684,7 @@ bool extractDiffExponent(MathArray::iterator it, int & i) string s; if (!extractString((*it).nucleus(), s)) return false; - std::istringstream is(s.c_str()); + istringstream is(s.c_str()); is >> i; return is; } @@ -620,14 +692,14 @@ bool extractDiffExponent(MathArray::iterator it, int & i) void extractDiff(MathArray & ar) { - lyxerr << "\nDiffs from: " << ar << "\n"; + //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->nucleus())) continue; - + MathFracInset * f = (*it)->asFracInset(); if (!f) { lyxerr << "should not happen\n"; @@ -638,20 +710,20 @@ void extractDiff(MathArray & ar) MathDiffInset * diff = new MathDiffInset; // collect function, let jt point behind last used item - MathArray::iterator jt = it + 1; - //int n = 1; + MathArray::iterator jt = it + 1; + //int n = 1; MathArray & numer = f->cell(0); - if (numer.size() > 1 && numer.at(1)->asScriptInset()) { + 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) + //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) + if (numer.size() > 1) diff->cell(0) = MathArray(numer.begin() + 1, numer.end()); else jt = extractArgument(diff->cell(0), jt, ar.end()); @@ -659,9 +731,9 @@ void extractDiff(MathArray & ar) // collect denominator parts MathArray & denom = f->cell(1); - for (MathArray::iterator dt = denom.begin(); dt != denom.end(); ) { + for (MathArray::iterator dt = denom.begin(); dt != denom.end();) { // find the next 'd' - MathArray::iterator et = std::find_if(dt + 1, denom.end(), &testDiffItem); + MathArray::iterator et = find_if(dt + 1, denom.end(), &testDiffItem); // point before this MathArray::iterator st = et - 1; @@ -670,7 +742,7 @@ void extractDiff(MathArray & ar) // things like d.../dx^n int mult = 1; if (extractNumber(script->up().data_, mult)) { - lyxerr << "mult: " << mult << std::endl; + //lyxerr << "mult: " << mult << endl; for (int i = 0; i < mult; ++i) diff->addDer(MathArray(dt + 1, st)); } @@ -685,7 +757,7 @@ void extractDiff(MathArray & ar) ar.erase(it + 1, jt); (*it).reset(diff); } - lyxerr << "\nDiffs to: " << ar << "\n"; + //lyxerr << "\nDiffs to: " << ar << "\n"; } @@ -697,9 +769,11 @@ void extractDiff(MathArray & ar) void extractStructure(MathArray & ar) { splitScripts(ar); + extractNumbers(ar); extractMatrices(ar); extractDelims(ar); extractFunctions(ar); + extractDets(ar); extractIntegrals(ar); extractSums(ar); extractDiff(ar); @@ -713,14 +787,14 @@ void write(MathArray const & dat, WriteStream & wi) MathArray ar = dat; extractStrings(ar); for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) { - wi.firstitem = (it == ar.begin()); + wi.firstitem() = (it == ar.begin()); MathInset const * p = it->nucleus(); if (it + 1 != ar.end()) { if (MathScriptInset const * q = asScript(it)) { q->write2(p, wi); ++it; continue; - } + } } p->write(wi); } @@ -743,7 +817,7 @@ void octavize(MathArray const & dat, OctaveStream & os) if (it + 1 != ar.end()) { if (MathScriptInset const * q = asScript(it)) { q->octavize2(p, os); - ++it; + ++it; continue; } } @@ -761,7 +835,7 @@ void maplize(MathArray const & dat, MapleStream & os) if (it + 1 != ar.end()) { if (MathScriptInset const * q = asScript(it)) { q->maplize2(p, os); - ++it; + ++it; continue; } } @@ -785,7 +859,7 @@ void mathmlize(MathArray const & dat, MathMLStream & os) if (it + 1 != ar.end()) { if (MathScriptInset const * q = asScript(it)) { q->mathmlize2(p, os); - ++it; + ++it; continue; } } @@ -795,3 +869,200 @@ void mathmlize(MathArray const & dat, MathMLStream & os) } } + + + +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; + } + + // 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, "*"); + } + + 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.push_back(mat->cell(0)); + else { + res.push_back(MathAtom(new MathDelimInset("(", ")"))); + res.back()->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; +}