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