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"
23 std::ostream & operator<<(std::ostream & os, MathArray const & ar)
31 // define a function for tests
32 typedef bool TestItemFunc(MathInset *);
34 // define a function for replacing subexpressions
35 typedef MathInset * ReplaceArgumentFunc(const MathArray & ar);
39 // try to extract an "argument" to some function.
40 // returns position behind the argument
41 MathArray::iterator extractArgument(MathArray & ar,
42 MathArray::iterator pos, MathArray::iterator last, string const & = "")
44 // nothing to get here
48 // something deliminited _is_ an argument
49 if ((*pos)->asDelimInset()) {
54 // always take the first thing, no matter what it is
57 // go ahead if possible
62 // if the next item is a subscript, it most certainly belongs to the
64 if ((*pos)->asScriptInset()) {
66 // go ahead if possible
72 // but it might be more than that.
73 // FIXME: not implemented
74 //for (MathArray::iterator it = pos + 1; it != last; ++it) {
75 // // always take the first thing, no matter
85 MathScriptInset const * asScript(MathArray::const_iterator it)
87 if (it->nucleus()->asScriptInset())
92 return it->nucleus()->asScriptInset();
97 // returns sequence of char with same code starting at it up to end
98 // it might be less, though...
99 string charSequence(MathArray::const_iterator it, MathArray::const_iterator end)
102 MathCharInset const * p = it->nucleus()->asCharInset();
104 for (MathTextCodes c = p->code(); it != end; ++it) {
105 p = it->nucleus()->asCharInset();
106 if (!p || p->code() != c)
115 void extractStrings(MathArray & dat)
117 //lyxerr << "\nStrings from: " << ar << "\n";
119 MathArray::const_iterator it = dat.begin();
120 while (it != dat.end()) {
121 if (it->nucleus() && it->nucleus()->asCharInset()) {
122 string s = charSequence(it, dat.end());
123 MathTextCodes c = it->nucleus()->asCharInset()->code();
124 ar.push_back(MathAtom(new MathStringInset(s, c)));
132 //lyxerr << "\nStrings to: " << ar << "\n";
136 MathInset * singleItem(MathArray & ar)
138 return ar.size() == 1 ? ar.begin()->nucleus() : 0;
142 void extractMatrices(MathArray & ar)
144 lyxerr << "\nMatrices from: " << ar << "\n";
145 for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it) {
146 MathDelimInset * del = (*it)->asDelimInset();
149 MathInset * arr = singleItem(del->cell(0));
150 if (!arr || !arr->asArrayInset())
152 *it = MathAtom(new MathMatrixInset(*(arr->asArrayInset())));
154 lyxerr << "\nMatrices to: " << ar << "\n";
158 // convert this inset somehow to a string
159 string extractString(MathInset * p)
161 if (p && p->getChar())
162 return string(1, p->getChar());
163 if (p && p->asStringInset())
164 return p->asStringInset()->str();
169 bool stringTest(MathInset * p, const string & str)
171 return extractString(p) == str;
175 // search end of nested sequence
176 MathArray::iterator endNestSearch(
177 MathArray::iterator it,
178 MathArray::iterator last,
179 TestItemFunc testOpen,
180 TestItemFunc testClose
183 for (int level = 0; it != last; ++it) {
184 if (testOpen(it->nucleus()))
186 if (testClose(it->nucleus()))
195 // replace nested sequences by a real Insets
198 TestItemFunc testOpen,
199 TestItemFunc testClose,
200 ReplaceArgumentFunc replaceArg
203 // use indices rather than iterators for the loop because we are going
204 // to modify the array.
205 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
206 // check whether this is the begin of the sequence
207 MathArray::iterator it = ar.begin() + i;
208 if (!testOpen(it->nucleus()))
211 // search end of sequence
212 MathArray::iterator jt = endNestSearch(it, ar.end(), testOpen, testClose);
216 // create a proper inset as replacement
217 MathInset * p = replaceArg(MathArray(it + 1, jt));
219 // replace the original stuff by the new inset
220 ar.erase(it + 1, jt + 1);
227 // search deliminiters
230 bool openParanTest(MathInset * p)
232 return stringTest(p, "(");
236 bool closeParanTest(MathInset * p)
238 return stringTest(p, ")");
242 MathInset * delimReplacement(const MathArray & ar)
244 MathDelimInset * del = new MathDelimInset("(", ")");
250 // replace '('...')' sequences by a real MathDelimInset
251 void extractDelims(MathArray & ar)
253 lyxerr << "\nDelims from: " << ar << "\n";
254 replaceNested(ar, openParanTest, closeParanTest, delimReplacement);
255 lyxerr << "\nDelims to: " << ar << "\n";
261 // search well-known functions
265 // replace 'f' '(...)' and 'f' '^n' '(...)' sequences by a real MathExFuncInset
266 // assume 'extractDelims' ran before
267 void extractFunctions(MathArray & ar)
269 // we need at least two items...
273 lyxerr << "\nFunctions from: " << ar << "\n";
274 for (MathArray::size_type i = 0; i + 1 < ar.size(); ++i) {
275 MathArray::iterator it = ar.begin() + i;
277 // is this a well known function name?
278 MathFuncInset * func = (*it)->asFuncInset();
283 // is this a user defined function?
284 // guess so, if this is a "string" and it is followed by
286 //name = extractString((*it)->nucleus());
287 //if (name.size() && it + 1
293 // do we have an exponent?
294 // simply skippping the postion does the right thing:
295 // 'sin' '^2' 'x' -> 'sin(x)' '^2'
296 MathArray::iterator jt = it + 1;
297 if (MathScriptInset * script = (*jt)->asScriptInset()) {
298 // allow superscripts only
299 if (script->hasDown())
306 // create a proper inset as replacement
307 MathExFuncInset * p = new MathExFuncInset(name);
309 // jt points to the "argument". Get hold of this.
310 MathArray::iterator st = extractArgument(p->cell(0), jt, ar.end());
312 // replace the function name by a real function inset
315 // remove the source of the argument from the array
317 lyxerr << "\nFunctions to: " << ar << "\n";
326 bool symbolTest(MathInset * p, string const & name)
328 return p->asSymbolInset() && p->asSymbolInset()->name() == name;
332 bool intSymbolTest(MathInset * p)
334 return symbolTest(p, "int");
338 bool intDiffTest(MathInset * p)
340 return stringTest(p, "d");
344 // replace '\int' ['_^'] x 'd''x'(...)' sequences by a real MathExIntInset
345 // assume 'extractDelims' ran before
346 void extractIntegrals(MathArray & ar)
348 // we need at least three items...
352 lyxerr << "\nIntegrals from: " << ar << "\n";
353 for (MathArray::size_type i = 0; i + 1< ar.size(); ++i) {
354 MathArray::iterator it = ar.begin() + i;
356 // is this a integral name?
357 if (!intSymbolTest(it->nucleus()))
361 MathArray::iterator jt =
362 endNestSearch(it, ar.end(), intSymbolTest, intDiffTest);
364 // something sensible found?
368 // create a proper inset as replacement
369 MathExIntInset * p = new MathExIntInset("int");
372 MathArray::iterator st = it + 1;
373 if ((*st)->asScriptInset()) {
375 p->cell(0) = MathArray(st + 1, jt);
377 p->cell(0) = MathArray(st, jt);
380 // use the atom behind the 'd' as differential
381 MathArray::iterator tt = extractArgument(p->cell(1), jt + 1, ar.end());
384 ar.erase(it + 1, tt);
387 lyxerr << "\nIntegrals to: " << ar << "\n";
395 bool sumSymbolTest(MathInset * p)
397 return p->asSymbolInset() && p->asSymbolInset()->name() == "sum";
401 bool equalSign(MathInset * p)
403 return stringTest(p, "=");
407 bool equalSign1(MathAtom const & at)
409 return equalSign(at.nucleus());
414 // replace '\sum' ['_^'] f(x) sequences by a real MathExIntInset
415 // assume 'extractDelims' ran before
416 void extractSums(MathArray & ar)
418 // we need at least two items...
422 lyxerr << "\nSums from: " << ar << "\n";
423 for (MathArray::size_type i = 0; i + 1< ar.size(); ++i) {
424 MathArray::iterator it = ar.begin() + i;
426 // is this a sum name?
427 if (!sumSymbolTest(it->nucleus()))
430 // create a proper inset as replacement
431 MathExIntInset * p = new MathExIntInset("sum");
434 MathArray::iterator st = it + 1;
435 if (st != ar.end() && (*st)->asScriptInset()) {
439 // try to figure out the summation index from the subscript
440 MathScriptInset * script = p->scripts()->asScriptInset();
441 if (script->hasDown()) {
442 MathArray & ar = script->down().data_;
443 MathArray::iterator it =
444 std::find_if(ar.begin(), ar.end(), &equalSign1);
445 if (it != ar.end()) {
446 // we found a '=', use everything in front of that as index,
447 // and everything behind as start value
448 p->cell(1) = MathArray(ar.begin(), it);
449 ar.erase(ar.begin(), it + 1);
451 // use everything as summation index, don't use scripts.
457 // use some behind the script as core
458 MathArray::iterator tt = extractArgument(p->cell(0), st, ar.end());
461 ar.erase(it + 1, tt);
464 lyxerr << "\nSums to: " << ar << "\n";
469 // search differential stuff
472 // tests for 'd' or '\partial'
473 bool diffItemTest(MathInset * p)
475 return stringTest(p, "d");
479 bool diffItemTest(MathArray const & ar)
481 return ar.size() && diffItemTest(ar.front().nucleus());
485 bool diffFracTest(MathInset * p)
489 diffItemTest(p->asFracInset()->cell(0)) &&
490 diffItemTest(p->asFracInset()->cell(1));
493 void extractDiff(MathArray & ar)
495 lyxerr << "\nDiffs from: " << ar << "\n";
496 for (MathArray::size_type i = 0; i < ar.size(); ++i) {
497 MathArray::iterator it = ar.begin() + i;
499 // is this a "differential fraction"?
500 if (!diffFracTest(it->nucleus()))
503 MathFracInset * f = (*it)->asFracInset();
505 lyxerr << "should not happen\n";
509 // create a proper diff inset
510 MathDiffInset * p = new MathDiffInset;
512 // collect function, let jt point behind last used item
513 MathArray::iterator jt = it + 1;
515 MathArray & numer = f->cell(0);
516 if (numer.size() > 1 && numer.at(1)->asScriptInset()) {
517 // this is something like d^n f(x) / d... or d^n / d...
519 if (numer.size() > 2)
520 p->cell(0) = MathArray(numer.begin() + 2, numer.end());
522 jt = extractArgument(p->cell(0), jt, ar.end());
524 // simply d f(x) / d... or d/d...
525 if (numer.size() > 1)
526 p->cell(0) = MathArray(numer.begin() + 1, numer.end());
528 jt = extractArgument(p->cell(0), jt, ar.end());
531 // collect denominator
532 MathArray & denom = f->cell(1);
533 for (MathArray::iterator dt = denom.begin(); dt + 1 != denom.end(); ) {
534 if (!diffItemTest((*dt).nucleus())) {
535 lyxerr << "extractDiff: should not happen 2\n";
539 dt = extractArgument(diff, dt + 1, denom.end());
542 if (dt == denom.end())
547 ar.erase(it + 1, jt);
550 lyxerr << "\nDiffs to: " << ar << "\n";
557 void extractStructure(MathArray & ar)
561 extractFunctions(ar);
562 extractIntegrals(ar);
569 void write(MathArray const & dat, WriteStream & wi)
573 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
574 wi.firstitem = (it == ar.begin());
575 MathInset const * p = it->nucleus();
576 if (it + 1 != ar.end()) {
577 if (MathScriptInset const * q = asScript(it)) {
588 void normalize(MathArray const & ar, NormalStream & os)
590 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
591 (*it)->normalize(os);
595 void octavize(MathArray const & dat, OctaveStream & os)
598 extractStructure(ar);
599 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
600 MathInset const * p = it->nucleus();
601 if (it + 1 != ar.end()) {
602 if (MathScriptInset const * q = asScript(it)) {
613 void maplize(MathArray const & dat, MapleStream & os)
616 extractStructure(ar);
617 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
618 MathInset const * p = it->nucleus();
619 if (it + 1 != ar.end()) {
620 if (MathScriptInset const * q = asScript(it)) {
631 void mathmlize(MathArray const & dat, MathMLStream & os)
634 extractStructure(ar);
637 else if (ar.size() == 1)
638 os << ar.begin()->nucleus();
641 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
642 MathInset const * p = it->nucleus();
643 if (it + 1 != ar.end()) {
644 if (MathScriptInset const * q = asScript(it)) {