]> git.lyx.org Git - lyx.git/blob - src/mathed/math_extern.C
a34b045612489bc0d6283ab6102560dc9ecedd2d
[lyx.git] / src / mathed / math_extern.C
1
2 // This file contains most of the magic that extracts "context
3 // information" from the unstructered layout-oriented stuff in an
4 // MathArray.
5
6 #include <algorithm>
7
8 #include "math_charinset.h"
9 #include "math_deliminset.h"
10 #include "math_exfuncinset.h"
11 #include "math_exintinset.h"
12 #include "math_funcinset.h"
13 #include "math_matrixinset.h"
14 #include "math_mathmlstream.h"
15 #include "math_scriptinset.h"
16 #include "math_stringinset.h"
17 #include "math_symbolinset.h"
18 #include "debug.h"
19
20
21 std::ostream & operator<<(std::ostream & os, MathArray const & ar)
22 {
23         NormalStream ns(os);    
24         ns << ar;
25         return os;
26 }
27
28
29 MathScriptInset const * asScript(MathArray::const_iterator it)
30 {
31         if (it->nucleus()->asScriptInset())
32                 return 0;
33         ++it;
34         if (!it->nucleus())
35                 return 0;
36         return it->nucleus()->asScriptInset();
37 }
38
39
40
41 // returns sequence of char with same code starting at it up to end
42 // it might be less, though...
43 string charSequence(MathArray::const_iterator it, MathArray::const_iterator end)
44 {
45         string s;
46         MathCharInset const * p = it->nucleus()->asCharInset();
47         if (p) {
48                 for (MathTextCodes c = p->code(); it != end; ++it) {
49                         p = it->nucleus()->asCharInset();
50                         if (!p || p->code() != c)
51                                 break;
52                         s += p->getChar();
53                 }
54         }
55         return s;
56 }
57
58
59 void extractStrings(MathArray & dat)
60 {
61         //lyxerr << "\nStrings from: " << ar << "\n";
62         MathArray ar;
63         MathArray::const_iterator it = dat.begin();
64         while (it != dat.end()) {
65                 if (it->nucleus() && it->nucleus()->asCharInset()) {
66                         string s = charSequence(it, dat.end());
67                         MathTextCodes c = it->nucleus()->asCharInset()->code();
68                         ar.push_back(MathAtom(new MathStringInset(s, c)));
69                         it += s.size();
70                 } else {
71                         ar.push_back(*it);
72                         ++it;
73                 }
74         }
75         ar.swap(dat);
76         //lyxerr << "\nStrings to: " << ar << "\n";
77 }
78
79
80 MathInset * singleItem(MathArray & ar)
81 {
82         return ar.size() == 1 ? ar.begin()->nucleus() : 0;
83 }
84
85
86 void extractMatrices(MathArray & ar)
87 {
88         lyxerr << "\nMatrices from: " << ar << "\n";
89         for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it) {
90                 MathDelimInset * del = (*it)->asDelimInset();
91                 if (!del)
92                         continue;
93                 MathInset * arr = singleItem(del->cell(0));
94                 if (!arr || !arr->asArrayInset())
95                         continue;
96                 *it = MathAtom(new MathMatrixInset(*(arr->asArrayInset())));
97                 lyxerr << "\nMatrices to: " << ar << "\n";
98         }
99
100
101 // convert this inset somehow to a string
102 string extractString(MathInset * p)
103 {
104         if (p && p->getChar())
105                 return string(1, p->getChar());
106         if (p && p->asStringInset())
107                 return p->asStringInset()->str();
108         return string();
109 }
110
111
112 // define a function for tests
113 typedef bool TestItemFunc(MathInset *);
114
115 // define a function for replacing subexpressions
116 typedef MathInset * ReplaceArgumentFunc(const MathArray & ar);
117
118
119 // search end of nested sequence
120 MathArray::iterator endNestSearch(
121         MathArray::iterator it,
122         MathArray::iterator last,
123         TestItemFunc testOpen,
124         TestItemFunc testClose
125 )
126 {
127         for (int level = 0; it != last; ++it) {
128                 if (testOpen(it->nucleus()))
129                         ++level;
130                 if (testClose(it->nucleus()))
131                         --level;
132                 if (level == 0)
133                         break;
134         }
135         return it;
136 }
137
138
139 // replace nested sequences by a real Insets
140 void replaceNested(
141         MathArray & ar,
142         TestItemFunc testOpen,
143         TestItemFunc testClose,
144         ReplaceArgumentFunc replaceArg
145 )
146 {
147         // use indices rather than iterators for the loop  because we are going
148         // to modify the array.
149         for (MathArray::size_type i = 0; i < ar.size(); ++i) {
150                 // check whether this is the begin of the sequence
151                 MathArray::iterator it = ar.begin() + i;
152                 if (!testOpen(it->nucleus()))
153                         continue;
154
155                 // search end of sequence
156                 MathArray::iterator jt = endNestSearch(it, ar.end(), testOpen, testClose);
157                 if (jt == ar.end())
158                         continue;
159
160                 // create a proper inset as replacement
161                 MathInset * p = replaceArg(MathArray(it + 1, jt));
162
163                 // replace the original stuff by the new inset
164                 ar.erase(it + 1, jt + 1);
165                 (*it).reset(p);
166         }
167
168
169
170 //
171 // search deliminiters
172 //
173
174 bool openParanTest(MathInset * p)
175 {
176         return extractString(p) == "(";
177 }
178
179
180 bool closeParanTest(MathInset * p)
181 {
182         return extractString(p) == ")";
183 }
184
185
186 MathInset * delimReplacement(const MathArray & ar)
187 {
188         MathDelimInset * del = new MathDelimInset("(", ")");
189         del->cell(0) = ar;
190         return del;
191 }
192
193
194 // replace '('...')' sequences by a real MathDelimInset
195 void extractDelims(MathArray & ar) {
196         lyxerr << "\nDelims from: " << ar << "\n";
197         replaceNested(ar, openParanTest, closeParanTest, delimReplacement);
198         lyxerr << "\nDelims to: " << ar << "\n";
199 }
200
201
202
203 //
204 // search well-known functions
205 //
206
207
208 // replace 'f' '(...)' and 'f' '^n' '(...)' sequences by a real MathExFuncInset
209 // assume 'extractDelims' ran before
210 void extractFunctions(MathArray & ar)
211 {
212         // we need at least two items...
213         if (ar.size() <= 1)
214                 return;
215
216         lyxerr << "\nFunctions from: " << ar << "\n";
217         for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
218                 MathArray::iterator it = ar.begin() + i;
219
220                 // is this a function name?
221                 MathFuncInset * func = (*it)->asFuncInset();
222                 if (!func)
223                         continue;
224
225                 // do we have an exponent?
226                 // simply skippping the postion does the right thing:
227                 // 'sin' '^2' 'x' -> 'sin(x)' '^2'
228                 MathArray::iterator jt = it + 1;
229                 if (MathScriptInset * script = (*jt)->asScriptInset()) {
230                         // allow superscripts only
231                         if (script->hasDown())
232                                 continue;
233                         ++jt;
234                         if (jt == ar.end())
235                                 continue;
236                 }
237
238                 // jt points now to the "argument". Since we had run "extractDelims"
239                 // before, this could be a single argument only. Get hold of this.
240                 MathArray arg;
241                 MathDelimInset * del = (*jt)->asDelimInset();
242                 if (del && del->isParanthesis()) 
243                         arg = del->cell(0);
244                 else
245                         arg.push_back(*jt);
246
247                 // replace the function name by a real function inset
248                 (*it).reset(new MathExFuncInset(func->name(), arg));
249                 
250                 // remove the source of the argument from the array
251                 ar.erase(jt);
252                 lyxerr << "\nFunctions to: " << ar << "\n";
253         }
254
255
256
257 //
258 // search integrals
259 //
260
261 bool intSymbolTest(MathInset * p)
262 {
263         return p->asSymbolInset() && p->asSymbolInset()->name() == "int";
264 }
265
266
267 bool differentialTest(MathInset * p)
268 {
269         return extractString(p) == "d";
270 }
271
272
273 // replace '\int' ['_^'] x 'd''x'(...)' sequences by a real MathExIntInset
274 // assume 'extractDelims' ran before
275 void extractIntegrals(MathArray & ar)
276 {
277         // we need at least three items...
278         if (ar.size() <= 2)
279                 return;
280
281         lyxerr << "\nIntegrals from: " << ar << "\n";
282         for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
283                 MathArray::iterator it = ar.begin() + i;
284
285                 // is this a integral name?
286                 if (!intSymbolTest(it->nucleus()))
287                         continue;
288
289                 // search 'd'
290                 MathArray::iterator jt =
291                         endNestSearch(it, ar.end(), intSymbolTest, differentialTest);
292
293                 // something sensible found?
294                 if (jt == ar.end())
295                         continue;
296
297                 // create a proper inset as replacement
298                 MathExIntInset * p = new MathExIntInset("int");
299
300                 // collect scripts
301                 MathArray::iterator st = it + 1;
302                 if ((*st)->asScriptInset()) {
303                         p->scripts(*st);
304                         p->core(MathArray(st + 1, jt));
305                 } else {
306                         p->core(MathArray(st, jt));
307                 }
308
309                 // use the atom behind the 'd' as differential
310                 MathArray ind;
311                 if (jt + 1 != ar.end()) {
312                         ind.push_back(*(jt + 1));
313                         ++jt;
314                 }
315                 ar.erase(it + 1, jt + 1);
316
317                 p->index(ind);
318                 (*it).reset(p);
319         }
320         lyxerr << "\nIntegrals to: " << ar << "\n";
321 }
322
323
324 //
325 // search sums
326 //
327
328 bool sumSymbolTest(MathInset * p)
329 {
330         return p->asSymbolInset() && p->asSymbolInset()->name() == "sum";
331 }
332
333
334 bool equalSign(MathInset * p)
335 {
336         return extractString(p) == "=";
337 }
338
339
340 bool equalSign1(MathAtom const & at)
341 {
342         return equalSign(at.nucleus());
343 }
344
345
346
347 // replace '\sum' ['_^'] f(x) sequences by a real MathExIntInset
348 // assume 'extractDelims' ran before
349 void extractSums(MathArray & ar)
350 {
351         // we need at least two items...
352         if (ar.size() <= 1)
353                 return;
354
355         lyxerr << "\nSums from: " << ar << "\n";
356         for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
357                 MathArray::iterator it = ar.begin() + i;
358
359                 // is this a sum name?
360                 if (!sumSymbolTest(it->nucleus()))
361                         continue;
362
363                 // create a proper inset as replacement
364                 MathExIntInset * p = new MathExIntInset("sum");
365
366                 // collect scripts
367                 MathArray::iterator st = it + 1;
368                 if (st != ar.end() && (*st)->asScriptInset()) {
369                         p->scripts(*st);
370                         ++st;
371
372                         // try to figure out the summation index from the subscript
373                         MathScriptInset * script = p->scripts()->asScriptInset();
374                         if (script->hasDown()) {
375                                 MathArray & ar = script->down().data_;
376                                 MathArray::iterator it =
377                                         std::find_if(ar.begin(), ar.end(), &equalSign1);
378                                 if (it != ar.end()) {
379                                         // we found a '=', use everything in front of that as index,
380                                         // and everything behind as start value
381                                         p->index(MathArray(ar.begin(), it));
382                                         ar.erase(ar.begin(), it + 1);
383                                 } else {
384                                         // use everything as summation index
385                                         p->index(ar);
386                                         p->scripts().reset(0);
387                                 }
388                         }
389                 }
390
391                 // use the atom behind the script as core
392                 MathArray ind;
393                 if (st != ar.end()) {
394                         MathArray core;
395                         core.push_back(*st);
396                         p->core(core);
397                         ++st;
398                 }
399                 ar.erase(it + 1, st);
400                 (*it).reset(p);
401         }
402         lyxerr << "\nSums to: " << ar << "\n";
403 }
404
405
406 //
407 // search differential stuff
408 //
409
410 void extractDiff(MathArray & ar)
411 {
412         lyxerr << "\nDiffs from: " << ar << "\n";
413         lyxerr << "\nDiffs to: " << ar << "\n";
414 }
415
416 //
417 // combine searches
418 //
419
420 void extractStructure(MathArray & ar)
421 {
422         extractMatrices(ar);
423         extractDelims(ar);
424         extractFunctions(ar);
425         extractIntegrals(ar);
426         extractSums(ar);
427         extractDiff(ar);
428         extractStrings(ar);
429 }
430
431
432 void write(MathArray const & dat, WriteStream & wi)
433 {
434         MathArray ar = dat;
435         extractStrings(ar);
436         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
437                 wi.firstitem = (it == ar.begin());
438                 MathInset const * p = it->nucleus();
439                 if (it + 1 != ar.end()) {
440                         if (MathScriptInset const * q = asScript(it)) {
441                                 q->write(p, wi);
442                                 ++it;
443                                 continue;
444                         } 
445                 }
446                 p->write(wi);
447         }
448 }
449
450
451 void normalize(MathArray const & ar, NormalStream & os)
452 {
453         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
454                 (*it)->normalize(os);
455 }
456
457
458 void octavize(MathArray const & dat, OctaveStream & os)
459 {
460         MathArray ar = dat;
461         extractStructure(ar);
462         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
463                 MathInset const * p = it->nucleus();
464                 if (it + 1 != ar.end()) {
465                         if (MathScriptInset const * q = asScript(it)) {
466                                 q->octavize(p, os);
467                                 ++it;   
468                                 continue;
469                         }
470                 }
471                 p->octavize(os);
472         }
473 }
474
475
476 void maplize(MathArray const & dat, MapleStream & os)
477 {
478         MathArray ar = dat;
479         extractStructure(ar);
480         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
481                 MathInset const * p = it->nucleus();
482                 if (it + 1 != ar.end()) {
483                         if (MathScriptInset const * q = asScript(it)) {
484                                 q->maplize(p, os);
485                                 ++it;   
486                                 continue;
487                         }
488                 }
489                 p->maplize(os);
490         }
491 }
492
493
494 void mathmlize(MathArray const & dat, MathMLStream & os)
495 {
496         MathArray ar = dat;
497         extractStructure(ar);
498         if (ar.size() == 0)
499                 os << "<mrow/>";
500         else if (ar.size() == 1)
501                 os << ar.begin()->nucleus();
502         else {
503                 os << MTag("mrow");
504                 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
505                         MathInset const * p = it->nucleus();
506                         if (it + 1 != ar.end()) {
507                                 if (MathScriptInset const * q = asScript(it)) {
508                                         q->mathmlize(p, os);
509                                         ++it;   
510                                         continue;
511                                 }
512                         }
513                         p->mathmlize(os);
514                 }
515                 os << ETag("mrow");
516         }
517 }
518