2 // This file contains most of the magic that extracts "context
3 // information" from the unstructered layout-oriented stuff in an
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"
24 std::ostream & operator<<(std::ostream & os, MathArray const & ar)
32 // define a function for tests
33 typedef bool TestItemFunc(MathInset *);
35 // define a function for replacing subexpressions
36 typedef MathInset * ReplaceArgumentFunc(const MathArray & ar);
40 // try to extract a super/subscript
41 // modify iterator position to point behind the thing
42 bool extractScript(MathArray & ar,
43 MathArray::iterator & pos, MathArray::iterator last)
45 // nothing to get here
49 // is this a scriptinset?
50 if (!(*pos)->asScriptInset())
53 // it is a scriptinset, use it.
60 // try to extract an "argument" to some function.
61 // returns position behind the argument
62 MathArray::iterator extractArgument(MathArray & ar,
63 MathArray::iterator pos, MathArray::iterator last, string const & = "")
65 // nothing to get here
69 // something deliminited _is_ an argument
70 if ((*pos)->asDelimInset()) {
75 // always take the first thing, no matter what it is
78 // go ahead if possible
83 // if the next item is a subscript, it most certainly belongs to the
85 extractScript(ar, pos, last);
89 // but it might be more than that.
90 // FIXME: not implemented
91 //for (MathArray::iterator it = pos + 1; it != last; ++it) {
92 // // always take the first thing, no matter
102 MathScriptInset const * asScript(MathArray::const_iterator it)
104 if (it->nucleus()->asScriptInset())
109 return it->nucleus()->asScriptInset();
114 // returns sequence of char with same code starting at it up to end
115 // it might be less, though...
116 MathArray::const_iterator charSequence(MathArray::const_iterator it,
117 MathArray::const_iterator end, string & s, MathTextCodes & c)
119 MathCharInset const * p = (*it)->asCharInset();
121 for (; it != end; ++it) {
122 p = (*it)->asCharInset();
123 if (!p || p->code() != c)
131 void extractStrings(MathArray & ar)
133 //lyxerr << "\nStrings from: " << ar << "\n";
134 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
135 MathArray::iterator it = ar.begin() + i;
136 if (!(*it)->asCharInset())
139 // create proper string inset
140 MathStringInset * p = new MathStringInset;
141 MathArray::const_iterator
142 jt = charSequence(it, ar.end(), p->str_, p->code_);
146 ar.erase(i + 1, jt - ar.begin());
148 //lyxerr << "\nStrings to: " << ar << "\n";
152 MathInset * singleItem(MathArray & ar)
154 return ar.size() == 1 ? ar.begin()->nucleus() : 0;
158 void extractMatrices(MathArray & ar)
160 //lyxerr << "\nMatrices from: " << ar << "\n";
161 for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it) {
162 MathDelimInset * del = (*it)->asDelimInset();
165 MathInset * arr = singleItem(del->cell(0));
166 if (!arr || !arr->asArrayInset())
168 *it = MathAtom(new MathMatrixInset(*(arr->asArrayInset())));
170 //lyxerr << "\nMatrices to: " << ar << "\n";
174 // convert this inset somehow to a string
175 bool extractString(MathInset * p, string & str)
180 str = string(1, p->getChar());
183 if (p->asStringInset()) {
184 str = p->asStringInset()->str();
191 // convert this inset somehow to a number
192 bool extractNumber(MathArray const & ar, int & i)
196 charSequence(ar.begin(), ar.end(), s, c);
197 std::istringstream is(s.c_str());
203 bool testString(MathInset * p, const string & str)
206 return extractString(p, s) && str == s;
210 // search end of nested sequence
211 MathArray::iterator endNestSearch(
212 MathArray::iterator it,
213 MathArray::iterator last,
214 TestItemFunc testOpen,
215 TestItemFunc testClose
218 for (int level = 0; it != last; ++it) {
219 if (testOpen(it->nucleus()))
221 if (testClose(it->nucleus()))
230 // replace nested sequences by a real Insets
233 TestItemFunc testOpen,
234 TestItemFunc testClose,
235 ReplaceArgumentFunc replaceArg
238 // use indices rather than iterators for the loop because we are going
239 // to modify the array.
240 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
241 // check whether this is the begin of the sequence
242 MathArray::iterator it = ar.begin() + i;
243 if (!testOpen(it->nucleus()))
246 // search end of sequence
247 MathArray::iterator jt = endNestSearch(it, ar.end(), testOpen, testClose);
251 // create a proper inset as replacement
252 MathInset * p = replaceArg(MathArray(it + 1, jt));
254 // replace the original stuff by the new inset
255 ar.erase(it + 1, jt + 1);
263 // split scripts into seperate super- and subscript insets. sub goes in
267 void splitScripts(MathArray & ar)
269 //lyxerr << "\nScripts from: " << ar << "\n";
270 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
271 MathArray::iterator it = ar.begin() + i;
273 // is this script inset?
274 MathScriptInset * p = (*it)->asScriptInset();
278 // no problem if we don't have both...
279 if (!p->hasUp() || !p->hasDown())
282 // create extra script inset and move superscript over
283 MathScriptInset * q = new MathScriptInset;
285 q->up().data_.swap(p->up().data_);
286 p->removeScript(true);
288 // insert new inset behind
290 ar.insert(i, MathAtom(q));
292 //lyxerr << "\nScripts to: " << ar << "\n";
300 void extractExps(MathArray & ar)
302 //lyxerr << "\nExps from: " << ar << "\n";
304 for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
305 MathArray::iterator it = ar.begin() + i;
308 MathCharInset const * p = (*it)->asCharInset();
309 if (!p || p->getChar() != 'e')
312 // we need an exponent but no subscript
313 MathScriptInset * sup = (*(it + 1))->asScriptInset();
314 if (!sup || sup->hasDown())
317 // create a proper exp-inset as replacement
318 MathExFuncInset * func = new MathExFuncInset("exp");
319 func->cell(0) = sup->cell(1);
325 //lyxerr << "\nExps to: " << ar << "\n";
330 // search deliminiters
333 bool testOpenParan(MathInset * p)
335 return testString(p, "(");
339 bool testCloseParan(MathInset * p)
341 return testString(p, ")");
345 MathInset * replaceDelims(const MathArray & ar)
347 MathDelimInset * del = new MathDelimInset("(", ")");
353 // replace '('...')' sequences by a real MathDelimInset
354 void extractDelims(MathArray & ar)
356 //lyxerr << "\nDelims from: " << ar << "\n";
357 replaceNested(ar, testOpenParan, testCloseParan, replaceDelims);
358 //lyxerr << "\nDelims to: " << ar << "\n";
364 // search well-known functions
368 // replace 'f' '(...)' and 'f' '^n' '(...)' sequences by a real MathExFuncInset
369 // assume 'extractDelims' ran before
370 void extractFunctions(MathArray & ar)
372 // we need at least two items...
376 //lyxerr << "\nFunctions from: " << ar << "\n";
377 for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
378 MathArray::iterator it = ar.begin() + i;
379 MathArray::iterator jt = it + 1;
383 if ((*it)->asFuncInset()) {
384 // it certainly is if it is well known...
385 name = (*it)->asFuncInset()->name();
387 // is this a user defined function?
388 // it it probably not, if it doesn't have a name.
389 if (!extractString((*it).nucleus(), name))
391 // it is not if it has no argument
394 // guess so, if this is followed by
395 // a DelimInset with a single item in the cell
396 MathDelimInset * del = (*jt)->asDelimInset();
397 if (!del || del->cell(0).size() != 1)
399 // fall trough into main branch
402 // do we have an exponent like in
403 // 'sin' '^2' 'x' -> 'sin(x)' '^2'
405 extractScript(exp, jt, ar.end());
407 // create a proper inset as replacement
408 MathExFuncInset * p = new MathExFuncInset(name);
410 // jt points to the "argument". Get hold of this.
411 MathArray::iterator st = extractArgument(p->cell(0), jt, ar.end());
413 // replace the function name by a real function inset
416 // remove the source of the argument from the array
417 ar.erase(it + 1, st);
419 // re-insert exponent
420 ar.insert(i + 1, exp);
421 //lyxerr << "\nFunctions to: " << ar << "\n";
430 bool testSymbol(MathInset * p, string const & name)
432 return p->asSymbolInset() && p->asSymbolInset()->name() == name;
436 bool testIntSymbol(MathInset * p)
438 return testSymbol(p, "int");
442 bool testIntDiff(MathInset * p)
444 return testString(p, "d");
448 // replace '\int' ['_^'] x 'd''x'(...)' sequences by a real MathExIntInset
449 // assume 'extractDelims' ran before
450 void extractIntegrals(MathArray & ar)
452 // we need at least three items...
456 //lyxerr << "\nIntegrals from: " << ar << "\n";
457 for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
458 MathArray::iterator it = ar.begin() + i;
460 // is this a integral name?
461 if (!testIntSymbol(it->nucleus()))
465 MathArray::iterator jt =
466 endNestSearch(it, ar.end(), testIntSymbol, testIntDiff);
468 // something sensible found?
472 // create a proper inset as replacement
473 MathExIntInset * p = new MathExIntInset("int");
475 // collect subscript if any
476 MathArray::iterator st = it + 1;
478 if (MathScriptInset * sub = (*st)->asScriptInset())
479 if (sub->hasDown()) {
480 p->cell(2) = sub->down().data_;
484 // collect superscript if any
486 if (MathScriptInset * sup = (*st)->asScriptInset())
488 p->cell(3) = sup->up().data_;
492 // core ist part from behind the scripts to the 'd'
493 p->cell(0) = MathArray(st, jt);
495 // use the "thing" behind the 'd' as differential
496 MathArray::iterator tt = extractArgument(p->cell(1), jt + 1, ar.end());
499 ar.erase(it + 1, tt);
502 //lyxerr << "\nIntegrals to: " << ar << "\n";
510 bool testSumSymbol(MathInset * p)
512 return testSymbol(p, "sum");
516 bool testEqualSign(MathAtom const & at)
518 return testString(at.nucleus(), "=");
523 // replace '\sum' ['_^'] f(x) sequences by a real MathExIntInset
524 // assume 'extractDelims' ran before
525 void extractSums(MathArray & ar)
527 // we need at least two items...
531 //lyxerr << "\nSums from: " << ar << "\n";
532 for (MathArray::size_type i = 0; i + 1< ar.size(); ++i) {
533 MathArray::iterator it = ar.begin() + i;
535 // is this a sum name?
536 if (!testSumSymbol(it->nucleus()))
539 // create a proper inset as replacement
540 MathExIntInset * p = new MathExIntInset("sum");
542 // collect lower bound and summation index
543 MathArray::iterator st = it + 1;
545 if (MathScriptInset * sub = (*st)->asScriptInset())
546 if (sub->hasDown()) {
547 // try to figure out the summation index from the subscript
548 MathArray & ar = sub->down().data_;
549 MathArray::iterator it =
550 std::find_if(ar.begin(), ar.end(), &testEqualSign);
551 if (it != ar.end()) {
552 // we found a '=', use everything in front of that as index,
553 // and everything behind as lower index
554 p->cell(1) = MathArray(ar.begin(), it);
555 p->cell(2) = MathArray(it + 1, ar.end());
557 // use everything as summation index, don't use scripts.
563 // collect upper bound
565 if (MathScriptInset * sup = (*st)->asScriptInset())
567 p->cell(3) = sup->up().data_;
571 // use some behind the script as core
572 MathArray::iterator tt = extractArgument(p->cell(0), st, ar.end());
575 ar.erase(it + 1, tt);
578 //lyxerr << "\nSums to: " << ar << "\n";
583 // search differential stuff
586 // tests for 'd' or '\partial'
587 bool testDiffItem(MathAtom const & at)
589 return testString(at.nucleus(), "d");
593 bool testDiffArray(MathArray const & ar)
595 return ar.size() && testDiffItem(ar.front());
599 bool testDiffFrac(MathInset * p)
601 MathFracInset * f = p->asFracInset();
602 return f && testDiffArray(f->cell(0)) && testDiffArray(f->cell(1));
606 // is this something like ^number?
607 bool extractDiffExponent(MathArray::iterator it, int & i)
609 if (!(*it)->asScriptInset())
613 if (!extractString((*it).nucleus(), s))
615 std::istringstream is(s.c_str());
621 void extractDiff(MathArray & ar)
623 //lyxerr << "\nDiffs from: " << ar << "\n";
624 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
625 MathArray::iterator it = ar.begin() + i;
627 // is this a "differential fraction"?
628 if (!testDiffFrac(it->nucleus()))
631 MathFracInset * f = (*it)->asFracInset();
633 lyxerr << "should not happen\n";
637 // create a proper diff inset
638 MathDiffInset * diff = new MathDiffInset;
640 // collect function, let jt point behind last used item
641 MathArray::iterator jt = it + 1;
643 MathArray & numer = f->cell(0);
644 if (numer.size() > 1 && numer.at(1)->asScriptInset()) {
645 // this is something like d^n f(x) / d... or d^n / d...
648 if (numer.size() > 2)
649 diff->cell(0) = MathArray(numer.begin() + 2, numer.end());
651 jt = extractArgument(diff->cell(0), jt, ar.end());
653 // simply d f(x) / d... or d/d...
654 if (numer.size() > 1)
655 diff->cell(0) = MathArray(numer.begin() + 1, numer.end());
657 jt = extractArgument(diff->cell(0), jt, ar.end());
660 // collect denominator parts
661 MathArray & denom = f->cell(1);
662 for (MathArray::iterator dt = denom.begin(); dt != denom.end(); ) {
664 MathArray::iterator et = std::find_if(dt + 1, denom.end(), &testDiffItem);
667 MathArray::iterator st = et - 1;
668 MathScriptInset * script = (*st)->asScriptInset();
669 if (script && script->hasUp()) {
670 // things like d.../dx^n
672 if (extractNumber(script->up().data_, mult)) {
673 //lyxerr << "mult: " << mult << std::endl;
674 for (int i = 0; i < mult; ++i)
675 diff->addDer(MathArray(dt + 1, st));
679 diff->addDer(MathArray(dt + 1, et));
685 ar.erase(it + 1, jt);
688 //lyxerr << "\nDiffs to: " << ar << "\n";
697 void extractStructure(MathArray & ar)
702 extractFunctions(ar);
703 extractIntegrals(ar);
711 void write(MathArray const & dat, WriteStream & wi)
715 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
716 wi.firstitem() = (it == ar.begin());
717 MathInset const * p = it->nucleus();
718 if (it + 1 != ar.end()) {
719 if (MathScriptInset const * q = asScript(it)) {
730 void normalize(MathArray const & ar, NormalStream & os)
732 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
733 (*it)->normalize(os);
737 void octavize(MathArray const & dat, OctaveStream & os)
740 extractStructure(ar);
741 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
742 MathInset const * p = it->nucleus();
743 if (it + 1 != ar.end()) {
744 if (MathScriptInset const * q = asScript(it)) {
755 void maplize(MathArray const & dat, MapleStream & os)
758 extractStructure(ar);
759 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
760 MathInset const * p = it->nucleus();
761 if (it + 1 != ar.end()) {
762 if (MathScriptInset const * q = asScript(it)) {
773 void mathmlize(MathArray const & dat, MathMLStream & os)
776 extractStructure(ar);
779 else if (ar.size() == 1)
780 os << ar.begin()->nucleus();
783 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
784 MathInset const * p = it->nucleus();
785 if (it + 1 != ar.end()) {
786 if (MathScriptInset const * q = asScript(it)) {
787 q->mathmlize2(p, os);