1 // This file contains most of the magic that extracts "context
2 // information" from the unstructered layout-oriented stuff in an
7 #include "math_amsarrayinset.h"
8 #include "math_charinset.h"
9 #include "math_deliminset.h"
10 #include "math_diffinset.h"
11 #include "math_exfuncinset.h"
12 #include "math_exintinset.h"
13 #include "math_funcinset.h"
14 #include "math_fracinset.h"
15 #include "math_matrixinset.h"
16 #include "math_mathmlstream.h"
17 #include "math_scriptinset.h"
18 #include "math_stringinset.h"
19 #include "math_symbolinset.h"
25 using std::istringstream;
29 ostream & operator<<(ostream & os, MathArray const & ar)
37 // define a function for tests
38 typedef bool TestItemFunc(MathInset *);
40 // define a function for replacing subexpressions
41 typedef MathInset * ReplaceArgumentFunc(const MathArray & ar);
45 // try to extract a super/subscript
46 // modify iterator position to point behind the thing
47 bool extractScript(MathArray & ar,
48 MathArray::iterator & pos, MathArray::iterator last)
50 // nothing to get here
54 // is this a scriptinset?
55 if (!(*pos)->asScriptInset())
58 // it is a scriptinset, use it.
65 // try to extract an "argument" to some function.
66 // returns position behind the argument
67 MathArray::iterator extractArgument(MathArray & ar,
68 MathArray::iterator pos, MathArray::iterator last, string const & = "")
70 // nothing to get here
74 // something deliminited _is_ an argument
75 if ((*pos)->asDelimInset()) {
80 // always take the first thing, no matter what it is
83 // go ahead if possible
88 // if the next item is a subscript, it most certainly belongs to the
90 extractScript(ar, pos, last);
94 // but it might be more than that.
95 // FIXME: not implemented
96 //for (MathArray::iterator it = pos + 1; it != last; ++it) {
97 // // always take the first thing, no matter
107 MathScriptInset const * asScript(MathArray::const_iterator it)
111 if (it->nucleus()->asScriptInset())
116 return it->nucleus()->asScriptInset();
121 // returns sequence of char with same code starting at it up to end
122 // it might be less, though...
123 MathArray::const_iterator charSequence(MathArray::const_iterator it,
124 MathArray::const_iterator end, string & s, MathTextCodes & c)
126 MathCharInset const * p = (*it)->asCharInset();
128 for (; it != end; ++it) {
129 p = (*it)->asCharInset();
130 if (!p || p->code() != c)
138 void extractStrings(MathArray & ar)
140 //lyxerr << "\nStrings from: " << ar << "\n";
141 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
142 MathArray::iterator it = ar.begin() + i;
143 if (!(*it)->asCharInset())
146 // create proper string inset
147 MathStringInset * p = new MathStringInset;
148 MathArray::const_iterator
149 jt = charSequence(it, ar.end(), p->str_, p->code_);
153 ar.erase(i + 1, jt - ar.begin());
155 //lyxerr << "\nStrings to: " << ar << "\n";
159 MathInset * singleItem(MathArray & ar)
161 return ar.size() == 1 ? ar.begin()->nucleus() : 0;
165 void extractMatrices(MathArray & ar)
167 //lyxerr << "\nMatrices from: " << ar << "\n";
168 // first pass for explicitly delimited stuff
169 for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it) {
170 MathDelimInset * del = (*it)->asDelimInset();
173 MathInset * arr = singleItem(del->cell(0));
174 if (!arr || !arr->asGridInset())
176 *it = MathAtom(new MathMatrixInset(*(arr->asGridInset())));
179 // second pass for AMS "pmatrix" etc
180 for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it) {
181 MathAMSArrayInset * ams = (*it)->asAMSArrayInset();
184 *it = MathAtom(new MathMatrixInset(*ams));
186 //lyxerr << "\nMatrices to: " << ar << "\n";
190 // convert this inset somehow to a string
191 bool extractString(MathInset * p, string & str)
196 str = string(1, p->getChar());
199 if (p->asStringInset()) {
200 str = p->asStringInset()->str();
207 // convert this inset somehow to a number
208 bool extractNumber(MathArray const & ar, int & i)
212 charSequence(ar.begin(), ar.end(), s, c);
213 istringstream is(s.c_str());
219 bool extractNumber(MathArray const & ar, double & i)
223 charSequence(ar.begin(), ar.end(), s, c);
224 istringstream is(s.c_str());
230 bool testString(MathInset * p, const string & str)
233 return extractString(p, s) && str == s;
237 // search end of nested sequence
238 MathArray::iterator endNestSearch(
239 MathArray::iterator it,
240 MathArray::iterator last,
241 TestItemFunc testOpen,
242 TestItemFunc testClose
245 for (int level = 0; it != last; ++it) {
246 if (testOpen(it->nucleus()))
248 if (testClose(it->nucleus()))
257 // replace nested sequences by a real Insets
260 TestItemFunc testOpen,
261 TestItemFunc testClose,
262 ReplaceArgumentFunc replaceArg
265 // use indices rather than iterators for the loop because we are going
266 // to modify the array.
267 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
268 // check whether this is the begin of the sequence
269 MathArray::iterator it = ar.begin() + i;
270 if (!testOpen(it->nucleus()))
273 // search end of sequence
274 MathArray::iterator jt = endNestSearch(it, ar.end(), testOpen, testClose);
278 // create a proper inset as replacement
279 MathInset * p = replaceArg(MathArray(it + 1, jt));
281 // replace the original stuff by the new inset
282 ar.erase(it + 1, jt + 1);
290 // split scripts into seperate super- and subscript insets. sub goes in
294 void splitScripts(MathArray & ar)
296 //lyxerr << "\nScripts from: " << ar << "\n";
297 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
298 MathArray::iterator it = ar.begin() + i;
300 // is this script inset?
301 MathScriptInset * p = (*it)->asScriptInset();
305 // no problem if we don't have both...
306 if (!p->hasUp() || !p->hasDown())
309 // create extra script inset and move superscript over
310 MathScriptInset * q = new MathScriptInset;
312 q->up().data_.swap(p->up().data_);
313 p->removeScript(true);
315 // insert new inset behind
317 ar.insert(i, MathAtom(q));
319 //lyxerr << "\nScripts to: " << ar << "\n";
327 void extractExps(MathArray & ar)
329 //lyxerr << "\nExps from: " << ar << "\n";
331 for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
332 MathArray::iterator it = ar.begin() + i;
335 MathCharInset const * p = (*it)->asCharInset();
336 if (!p || p->getChar() != 'e')
339 // we need an exponent but no subscript
340 MathScriptInset * sup = (*(it + 1))->asScriptInset();
341 if (!sup || sup->hasDown())
344 // create a proper exp-inset as replacement
345 MathExFuncInset * func = new MathExFuncInset("exp");
346 func->cell(0) = sup->cell(1);
352 //lyxerr << "\nExps to: " << ar << "\n";
357 // search deliminiters
360 bool testOpenParan(MathInset * p)
362 return testString(p, "(");
366 bool testCloseParan(MathInset * p)
368 return testString(p, ")");
372 MathInset * replaceDelims(const MathArray & ar)
374 MathDelimInset * del = new MathDelimInset("(", ")");
380 // replace '('...')' sequences by a real MathDelimInset
381 void extractDelims(MathArray & ar)
383 //lyxerr << "\nDelims from: " << ar << "\n";
384 replaceNested(ar, testOpenParan, testCloseParan, replaceDelims);
385 //lyxerr << "\nDelims to: " << ar << "\n";
391 // search well-known functions
395 // replace 'f' '(...)' and 'f' '^n' '(...)' sequences by a real MathExFuncInset
396 // assume 'extractDelims' ran before
397 void extractFunctions(MathArray & ar)
399 // we need at least two items...
403 //lyxerr << "\nFunctions from: " << ar << "\n";
404 for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
405 MathArray::iterator it = ar.begin() + i;
406 MathArray::iterator jt = it + 1;
410 if ((*it)->asFuncInset()) {
411 // it certainly is if it is well known...
412 name = (*it)->asFuncInset()->name();
414 // is this a user defined function?
415 // it it probably not, if it doesn't have a name.
416 if (!extractString((*it).nucleus(), name))
418 // it is not if it has no argument
421 // guess so, if this is followed by
422 // a DelimInset with a single item in the cell
423 MathDelimInset * del = (*jt)->asDelimInset();
424 if (!del || del->cell(0).size() != 1)
426 // fall trough into main branch
429 // do we have an exponent like in
430 // 'sin' '^2' 'x' -> 'sin(x)' '^2'
432 extractScript(exp, jt, ar.end());
434 // create a proper inset as replacement
435 MathExFuncInset * p = new MathExFuncInset(name);
437 // jt points to the "argument". Get hold of this.
438 MathArray::iterator st = extractArgument(p->cell(0), jt, ar.end());
440 // replace the function name by a real function inset
443 // remove the source of the argument from the array
444 ar.erase(it + 1, st);
446 // re-insert exponent
447 ar.insert(i + 1, exp);
448 //lyxerr << "\nFunctions to: " << ar << "\n";
457 bool testSymbol(MathInset * p, string const & name)
459 return p->asSymbolInset() && p->asSymbolInset()->name() == name;
463 bool testIntSymbol(MathInset * p)
465 return testSymbol(p, "int");
469 bool testIntDiff(MathInset * p)
471 return testString(p, "d");
475 // replace '\int' ['_^'] x 'd''x'(...)' sequences by a real MathExIntInset
476 // assume 'extractDelims' ran before
477 void extractIntegrals(MathArray & ar)
479 // we need at least three items...
483 //lyxerr << "\nIntegrals from: " << ar << "\n";
484 for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
485 MathArray::iterator it = ar.begin() + i;
487 // is this a integral name?
488 if (!testIntSymbol(it->nucleus()))
492 MathArray::iterator jt =
493 endNestSearch(it, ar.end(), testIntSymbol, testIntDiff);
495 // something sensible found?
499 // create a proper inset as replacement
500 MathExIntInset * p = new MathExIntInset("int");
502 // collect subscript if any
503 MathArray::iterator st = it + 1;
505 if (MathScriptInset * sub = (*st)->asScriptInset())
506 if (sub->hasDown()) {
507 p->cell(2) = sub->down().data_;
511 // collect superscript if any
513 if (MathScriptInset * sup = (*st)->asScriptInset())
515 p->cell(3) = sup->up().data_;
519 // core ist part from behind the scripts to the 'd'
520 p->cell(0) = MathArray(st, jt);
522 // use the "thing" behind the 'd' as differential
523 MathArray::iterator tt = extractArgument(p->cell(1), jt + 1, ar.end());
526 ar.erase(it + 1, tt);
529 //lyxerr << "\nIntegrals to: " << ar << "\n";
537 bool testSumSymbol(MathInset * p)
539 return testSymbol(p, "sum");
543 bool testEqualSign(MathAtom const & at)
545 return testString(at.nucleus(), "=");
550 // replace '\sum' ['_^'] f(x) sequences by a real MathExIntInset
551 // assume 'extractDelims' ran before
552 void extractSums(MathArray & ar)
554 // we need at least two items...
558 //lyxerr << "\nSums from: " << ar << "\n";
559 for (MathArray::size_type i = 0; i + 1< ar.size(); ++i) {
560 MathArray::iterator it = ar.begin() + i;
562 // is this a sum name?
563 if (!testSumSymbol(it->nucleus()))
566 // create a proper inset as replacement
567 MathExIntInset * p = new MathExIntInset("sum");
569 // collect lower bound and summation index
570 MathArray::iterator st = it + 1;
572 if (MathScriptInset * sub = (*st)->asScriptInset())
573 if (sub->hasDown()) {
574 // try to figure out the summation index from the subscript
575 MathArray & ar = sub->down().data_;
576 MathArray::iterator it =
577 find_if(ar.begin(), ar.end(), &testEqualSign);
578 if (it != ar.end()) {
579 // we found a '=', use everything in front of that as index,
580 // and everything behind as lower index
581 p->cell(1) = MathArray(ar.begin(), it);
582 p->cell(2) = MathArray(it + 1, ar.end());
584 // use everything as summation index, don't use scripts.
590 // collect upper bound
592 if (MathScriptInset * sup = (*st)->asScriptInset())
594 p->cell(3) = sup->up().data_;
598 // use some behind the script as core
599 MathArray::iterator tt = extractArgument(p->cell(0), st, ar.end());
602 ar.erase(it + 1, tt);
605 //lyxerr << "\nSums to: " << ar << "\n";
610 // search differential stuff
613 // tests for 'd' or '\partial'
614 bool testDiffItem(MathAtom const & at)
616 return testString(at.nucleus(), "d");
620 bool testDiffArray(MathArray const & ar)
622 return ar.size() && testDiffItem(ar.front());
626 bool testDiffFrac(MathInset * p)
628 MathFracInset * f = p->asFracInset();
629 return f && testDiffArray(f->cell(0)) && testDiffArray(f->cell(1));
633 // is this something like ^number?
634 bool extractDiffExponent(MathArray::iterator it, int & i)
636 if (!(*it)->asScriptInset())
640 if (!extractString((*it).nucleus(), s))
642 istringstream is(s.c_str());
648 void extractDiff(MathArray & ar)
650 //lyxerr << "\nDiffs from: " << ar << "\n";
651 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
652 MathArray::iterator it = ar.begin() + i;
654 // is this a "differential fraction"?
655 if (!testDiffFrac(it->nucleus()))
658 MathFracInset * f = (*it)->asFracInset();
660 lyxerr << "should not happen\n";
664 // create a proper diff inset
665 MathDiffInset * diff = new MathDiffInset;
667 // collect function, let jt point behind last used item
668 MathArray::iterator jt = it + 1;
670 MathArray & numer = f->cell(0);
671 if (numer.size() > 1 && numer.at(1)->asScriptInset()) {
672 // this is something like d^n f(x) / d... or d^n / d...
675 if (numer.size() > 2)
676 diff->cell(0) = MathArray(numer.begin() + 2, numer.end());
678 jt = extractArgument(diff->cell(0), jt, ar.end());
680 // simply d f(x) / d... or d/d...
681 if (numer.size() > 1)
682 diff->cell(0) = MathArray(numer.begin() + 1, numer.end());
684 jt = extractArgument(diff->cell(0), jt, ar.end());
687 // collect denominator parts
688 MathArray & denom = f->cell(1);
689 for (MathArray::iterator dt = denom.begin(); dt != denom.end();) {
691 MathArray::iterator et = find_if(dt + 1, denom.end(), &testDiffItem);
694 MathArray::iterator st = et - 1;
695 MathScriptInset * script = (*st)->asScriptInset();
696 if (script && script->hasUp()) {
697 // things like d.../dx^n
699 if (extractNumber(script->up().data_, mult)) {
700 //lyxerr << "mult: " << mult << endl;
701 for (int i = 0; i < mult; ++i)
702 diff->addDer(MathArray(dt + 1, st));
706 diff->addDer(MathArray(dt + 1, et));
712 ar.erase(it + 1, jt);
715 //lyxerr << "\nDiffs to: " << ar << "\n";
724 void extractStructure(MathArray & ar)
729 extractFunctions(ar);
730 extractIntegrals(ar);
738 void write(MathArray const & dat, WriteStream & wi)
742 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
743 wi.firstitem() = (it == ar.begin());
744 MathInset const * p = it->nucleus();
745 if (it + 1 != ar.end()) {
746 if (MathScriptInset const * q = asScript(it)) {
757 void normalize(MathArray const & ar, NormalStream & os)
759 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
760 (*it)->normalize(os);
764 void octavize(MathArray const & dat, OctaveStream & os)
767 extractStructure(ar);
768 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
769 MathInset const * p = it->nucleus();
770 if (it + 1 != ar.end()) {
771 if (MathScriptInset const * q = asScript(it)) {
782 void maplize(MathArray const & dat, MapleStream & os)
785 extractStructure(ar);
786 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
787 MathInset const * p = it->nucleus();
788 if (it + 1 != ar.end()) {
789 if (MathScriptInset const * q = asScript(it)) {
800 void mathmlize(MathArray const & dat, MathMLStream & os)
803 extractStructure(ar);
806 else if (ar.size() == 1)
807 os << ar.begin()->nucleus();
810 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
811 MathInset const * p = it->nucleus();
812 if (it + 1 != ar.end()) {
813 if (MathScriptInset const * q = asScript(it)) {
814 q->mathmlize2(p, os);