1 // This file contains most of the magic that extracts "context
2 // information" from the unstructered layout-oriented stuff in an
7 #include "math_charinset.h"
8 #include "math_deliminset.h"
9 #include "math_diffinset.h"
10 #include "math_exfuncinset.h"
11 #include "math_exintinset.h"
12 #include "math_funcinset.h"
13 #include "math_fracinset.h"
14 #include "math_matrixinset.h"
15 #include "math_mathmlstream.h"
16 #include "math_scriptinset.h"
17 #include "math_stringinset.h"
18 #include "math_symbolinset.h"
24 using std::istringstream;
28 ostream & operator<<(ostream & os, MathArray const & ar)
36 // define a function for tests
37 typedef bool TestItemFunc(MathInset *);
39 // define a function for replacing subexpressions
40 typedef MathInset * ReplaceArgumentFunc(const MathArray & ar);
44 // try to extract a super/subscript
45 // modify iterator position to point behind the thing
46 bool extractScript(MathArray & ar,
47 MathArray::iterator & pos, MathArray::iterator last)
49 // nothing to get here
53 // is this a scriptinset?
54 if (!(*pos)->asScriptInset())
57 // it is a scriptinset, use it.
64 // try to extract an "argument" to some function.
65 // returns position behind the argument
66 MathArray::iterator extractArgument(MathArray & ar,
67 MathArray::iterator pos, MathArray::iterator last, string const & = "")
69 // nothing to get here
73 // something deliminited _is_ an argument
74 if ((*pos)->asDelimInset()) {
79 // always take the first thing, no matter what it is
82 // go ahead if possible
87 // if the next item is a subscript, it most certainly belongs to the
89 extractScript(ar, pos, last);
93 // but it might be more than that.
94 // FIXME: not implemented
95 //for (MathArray::iterator it = pos + 1; it != last; ++it) {
96 // // always take the first thing, no matter
106 MathScriptInset const * asScript(MathArray::const_iterator it)
108 if (it->nucleus()->asScriptInset())
113 return it->nucleus()->asScriptInset();
118 // returns sequence of char with same code starting at it up to end
119 // it might be less, though...
120 MathArray::const_iterator charSequence(MathArray::const_iterator it,
121 MathArray::const_iterator end, string & s, MathTextCodes & c)
123 MathCharInset const * p = (*it)->asCharInset();
125 for (; it != end; ++it) {
126 p = (*it)->asCharInset();
127 if (!p || p->code() != c)
135 void extractStrings(MathArray & ar)
137 //lyxerr << "\nStrings from: " << ar << "\n";
138 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
139 MathArray::iterator it = ar.begin() + i;
140 if (!(*it)->asCharInset())
143 // create proper string inset
144 MathStringInset * p = new MathStringInset;
145 MathArray::const_iterator
146 jt = charSequence(it, ar.end(), p->str_, p->code_);
150 ar.erase(i + 1, jt - ar.begin());
152 //lyxerr << "\nStrings to: " << ar << "\n";
156 MathInset * singleItem(MathArray & ar)
158 return ar.size() == 1 ? ar.begin()->nucleus() : 0;
162 void extractMatrices(MathArray & ar)
164 //lyxerr << "\nMatrices from: " << ar << "\n";
165 for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it) {
166 MathDelimInset * del = (*it)->asDelimInset();
169 MathInset * arr = singleItem(del->cell(0));
170 if (!arr || !arr->asArrayInset())
172 *it = MathAtom(new MathMatrixInset(*(arr->asArrayInset())));
174 //lyxerr << "\nMatrices to: " << ar << "\n";
178 // convert this inset somehow to a string
179 bool extractString(MathInset * p, string & str)
184 str = string(1, p->getChar());
187 if (p->asStringInset()) {
188 str = p->asStringInset()->str();
195 // convert this inset somehow to a number
196 bool extractNumber(MathArray const & ar, int & i)
200 charSequence(ar.begin(), ar.end(), s, c);
201 istringstream is(s.c_str());
207 bool extractNumber(MathArray const & ar, double & i)
211 charSequence(ar.begin(), ar.end(), s, c);
212 istringstream is(s.c_str());
218 bool testString(MathInset * p, const string & str)
221 return extractString(p, s) && str == s;
225 // search end of nested sequence
226 MathArray::iterator endNestSearch(
227 MathArray::iterator it,
228 MathArray::iterator last,
229 TestItemFunc testOpen,
230 TestItemFunc testClose
233 for (int level = 0; it != last; ++it) {
234 if (testOpen(it->nucleus()))
236 if (testClose(it->nucleus()))
245 // replace nested sequences by a real Insets
248 TestItemFunc testOpen,
249 TestItemFunc testClose,
250 ReplaceArgumentFunc replaceArg
253 // use indices rather than iterators for the loop because we are going
254 // to modify the array.
255 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
256 // check whether this is the begin of the sequence
257 MathArray::iterator it = ar.begin() + i;
258 if (!testOpen(it->nucleus()))
261 // search end of sequence
262 MathArray::iterator jt = endNestSearch(it, ar.end(), testOpen, testClose);
266 // create a proper inset as replacement
267 MathInset * p = replaceArg(MathArray(it + 1, jt));
269 // replace the original stuff by the new inset
270 ar.erase(it + 1, jt + 1);
278 // split scripts into seperate super- and subscript insets. sub goes in
282 void splitScripts(MathArray & ar)
284 //lyxerr << "\nScripts from: " << ar << "\n";
285 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
286 MathArray::iterator it = ar.begin() + i;
288 // is this script inset?
289 MathScriptInset * p = (*it)->asScriptInset();
293 // no problem if we don't have both...
294 if (!p->hasUp() || !p->hasDown())
297 // create extra script inset and move superscript over
298 MathScriptInset * q = new MathScriptInset;
300 q->up().data_.swap(p->up().data_);
301 p->removeScript(true);
303 // insert new inset behind
305 ar.insert(i, MathAtom(q));
307 //lyxerr << "\nScripts to: " << ar << "\n";
315 void extractExps(MathArray & ar)
317 //lyxerr << "\nExps from: " << ar << "\n";
319 for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
320 MathArray::iterator it = ar.begin() + i;
323 MathCharInset const * p = (*it)->asCharInset();
324 if (!p || p->getChar() != 'e')
327 // we need an exponent but no subscript
328 MathScriptInset * sup = (*(it + 1))->asScriptInset();
329 if (!sup || sup->hasDown())
332 // create a proper exp-inset as replacement
333 MathExFuncInset * func = new MathExFuncInset("exp");
334 func->cell(0) = sup->cell(1);
340 //lyxerr << "\nExps to: " << ar << "\n";
345 // search deliminiters
348 bool testOpenParan(MathInset * p)
350 return testString(p, "(");
354 bool testCloseParan(MathInset * p)
356 return testString(p, ")");
360 MathInset * replaceDelims(const MathArray & ar)
362 MathDelimInset * del = new MathDelimInset("(", ")");
368 // replace '('...')' sequences by a real MathDelimInset
369 void extractDelims(MathArray & ar)
371 //lyxerr << "\nDelims from: " << ar << "\n";
372 replaceNested(ar, testOpenParan, testCloseParan, replaceDelims);
373 //lyxerr << "\nDelims to: " << ar << "\n";
379 // search well-known functions
383 // replace 'f' '(...)' and 'f' '^n' '(...)' sequences by a real MathExFuncInset
384 // assume 'extractDelims' ran before
385 void extractFunctions(MathArray & ar)
387 // we need at least two items...
391 //lyxerr << "\nFunctions from: " << ar << "\n";
392 for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
393 MathArray::iterator it = ar.begin() + i;
394 MathArray::iterator jt = it + 1;
398 if ((*it)->asFuncInset()) {
399 // it certainly is if it is well known...
400 name = (*it)->asFuncInset()->name();
402 // is this a user defined function?
403 // it it probably not, if it doesn't have a name.
404 if (!extractString((*it).nucleus(), name))
406 // it is not if it has no argument
409 // guess so, if this is followed by
410 // a DelimInset with a single item in the cell
411 MathDelimInset * del = (*jt)->asDelimInset();
412 if (!del || del->cell(0).size() != 1)
414 // fall trough into main branch
417 // do we have an exponent like in
418 // 'sin' '^2' 'x' -> 'sin(x)' '^2'
420 extractScript(exp, jt, ar.end());
422 // create a proper inset as replacement
423 MathExFuncInset * p = new MathExFuncInset(name);
425 // jt points to the "argument". Get hold of this.
426 MathArray::iterator st = extractArgument(p->cell(0), jt, ar.end());
428 // replace the function name by a real function inset
431 // remove the source of the argument from the array
432 ar.erase(it + 1, st);
434 // re-insert exponent
435 ar.insert(i + 1, exp);
436 //lyxerr << "\nFunctions to: " << ar << "\n";
445 bool testSymbol(MathInset * p, string const & name)
447 return p->asSymbolInset() && p->asSymbolInset()->name() == name;
451 bool testIntSymbol(MathInset * p)
453 return testSymbol(p, "int");
457 bool testIntDiff(MathInset * p)
459 return testString(p, "d");
463 // replace '\int' ['_^'] x 'd''x'(...)' sequences by a real MathExIntInset
464 // assume 'extractDelims' ran before
465 void extractIntegrals(MathArray & ar)
467 // we need at least three items...
471 //lyxerr << "\nIntegrals from: " << ar << "\n";
472 for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
473 MathArray::iterator it = ar.begin() + i;
475 // is this a integral name?
476 if (!testIntSymbol(it->nucleus()))
480 MathArray::iterator jt =
481 endNestSearch(it, ar.end(), testIntSymbol, testIntDiff);
483 // something sensible found?
487 // create a proper inset as replacement
488 MathExIntInset * p = new MathExIntInset("int");
490 // collect subscript if any
491 MathArray::iterator st = it + 1;
493 if (MathScriptInset * sub = (*st)->asScriptInset())
494 if (sub->hasDown()) {
495 p->cell(2) = sub->down().data_;
499 // collect superscript if any
501 if (MathScriptInset * sup = (*st)->asScriptInset())
503 p->cell(3) = sup->up().data_;
507 // core ist part from behind the scripts to the 'd'
508 p->cell(0) = MathArray(st, jt);
510 // use the "thing" behind the 'd' as differential
511 MathArray::iterator tt = extractArgument(p->cell(1), jt + 1, ar.end());
514 ar.erase(it + 1, tt);
517 //lyxerr << "\nIntegrals to: " << ar << "\n";
525 bool testSumSymbol(MathInset * p)
527 return testSymbol(p, "sum");
531 bool testEqualSign(MathAtom const & at)
533 return testString(at.nucleus(), "=");
538 // replace '\sum' ['_^'] f(x) sequences by a real MathExIntInset
539 // assume 'extractDelims' ran before
540 void extractSums(MathArray & ar)
542 // we need at least two items...
546 //lyxerr << "\nSums from: " << ar << "\n";
547 for (MathArray::size_type i = 0; i + 1< ar.size(); ++i) {
548 MathArray::iterator it = ar.begin() + i;
550 // is this a sum name?
551 if (!testSumSymbol(it->nucleus()))
554 // create a proper inset as replacement
555 MathExIntInset * p = new MathExIntInset("sum");
557 // collect lower bound and summation index
558 MathArray::iterator st = it + 1;
560 if (MathScriptInset * sub = (*st)->asScriptInset())
561 if (sub->hasDown()) {
562 // try to figure out the summation index from the subscript
563 MathArray & ar = sub->down().data_;
564 MathArray::iterator it =
565 find_if(ar.begin(), ar.end(), &testEqualSign);
566 if (it != ar.end()) {
567 // we found a '=', use everything in front of that as index,
568 // and everything behind as lower index
569 p->cell(1) = MathArray(ar.begin(), it);
570 p->cell(2) = MathArray(it + 1, ar.end());
572 // use everything as summation index, don't use scripts.
578 // collect upper bound
580 if (MathScriptInset * sup = (*st)->asScriptInset())
582 p->cell(3) = sup->up().data_;
586 // use some behind the script as core
587 MathArray::iterator tt = extractArgument(p->cell(0), st, ar.end());
590 ar.erase(it + 1, tt);
593 //lyxerr << "\nSums to: " << ar << "\n";
598 // search differential stuff
601 // tests for 'd' or '\partial'
602 bool testDiffItem(MathAtom const & at)
604 return testString(at.nucleus(), "d");
608 bool testDiffArray(MathArray const & ar)
610 return ar.size() && testDiffItem(ar.front());
614 bool testDiffFrac(MathInset * p)
616 MathFracInset * f = p->asFracInset();
617 return f && testDiffArray(f->cell(0)) && testDiffArray(f->cell(1));
621 // is this something like ^number?
622 bool extractDiffExponent(MathArray::iterator it, int & i)
624 if (!(*it)->asScriptInset())
628 if (!extractString((*it).nucleus(), s))
630 istringstream is(s.c_str());
636 void extractDiff(MathArray & ar)
638 //lyxerr << "\nDiffs from: " << ar << "\n";
639 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
640 MathArray::iterator it = ar.begin() + i;
642 // is this a "differential fraction"?
643 if (!testDiffFrac(it->nucleus()))
646 MathFracInset * f = (*it)->asFracInset();
648 lyxerr << "should not happen\n";
652 // create a proper diff inset
653 MathDiffInset * diff = new MathDiffInset;
655 // collect function, let jt point behind last used item
656 MathArray::iterator jt = it + 1;
658 MathArray & numer = f->cell(0);
659 if (numer.size() > 1 && numer.at(1)->asScriptInset()) {
660 // this is something like d^n f(x) / d... or d^n / d...
663 if (numer.size() > 2)
664 diff->cell(0) = MathArray(numer.begin() + 2, numer.end());
666 jt = extractArgument(diff->cell(0), jt, ar.end());
668 // simply d f(x) / d... or d/d...
669 if (numer.size() > 1)
670 diff->cell(0) = MathArray(numer.begin() + 1, numer.end());
672 jt = extractArgument(diff->cell(0), jt, ar.end());
675 // collect denominator parts
676 MathArray & denom = f->cell(1);
677 for (MathArray::iterator dt = denom.begin(); dt != denom.end();) {
679 MathArray::iterator et = find_if(dt + 1, denom.end(), &testDiffItem);
682 MathArray::iterator st = et - 1;
683 MathScriptInset * script = (*st)->asScriptInset();
684 if (script && script->hasUp()) {
685 // things like d.../dx^n
687 if (extractNumber(script->up().data_, mult)) {
688 //lyxerr << "mult: " << mult << endl;
689 for (int i = 0; i < mult; ++i)
690 diff->addDer(MathArray(dt + 1, st));
694 diff->addDer(MathArray(dt + 1, et));
700 ar.erase(it + 1, jt);
703 //lyxerr << "\nDiffs to: " << ar << "\n";
712 void extractStructure(MathArray & ar)
717 extractFunctions(ar);
718 extractIntegrals(ar);
726 void write(MathArray const & dat, WriteStream & wi)
730 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
731 wi.firstitem() = (it == ar.begin());
732 MathInset const * p = it->nucleus();
733 if (it + 1 != ar.end()) {
734 if (MathScriptInset const * q = asScript(it)) {
745 void normalize(MathArray const & ar, NormalStream & os)
747 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
748 (*it)->normalize(os);
752 void octavize(MathArray const & dat, OctaveStream & os)
755 extractStructure(ar);
756 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
757 MathInset const * p = it->nucleus();
758 if (it + 1 != ar.end()) {
759 if (MathScriptInset const * q = asScript(it)) {
770 void maplize(MathArray const & dat, MapleStream & os)
773 extractStructure(ar);
774 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
775 MathInset const * p = it->nucleus();
776 if (it + 1 != ar.end()) {
777 if (MathScriptInset const * q = asScript(it)) {
788 void mathmlize(MathArray const & dat, MathMLStream & os)
791 extractStructure(ar);
794 else if (ar.size() == 1)
795 os << ar.begin()->nucleus();
798 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
799 MathInset const * p = it->nucleus();
800 if (it + 1 != ar.end()) {
801 if (MathScriptInset const * q = asScript(it)) {
802 q->mathmlize2(p, os);