]> git.lyx.org Git - lyx.git/blob - src/mathed/math_extern.C
64eb18b3f3d04947df988ed0f3b186d0fc884530
[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
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         return extractString(p) == "d";
268 }
269
270
271 // replace '\int' ['_^'] x 'd''x'(...)' sequences by a real MathExIntInset
272 // assume 'extractDelims' ran before
273 void extractIntegrals(MathArray & ar)
274 {
275         // we need at least three items...
276         if (ar.size() <= 2)
277                 return;
278
279         lyxerr << "\nIntegrals from: " << ar << "\n";
280         for (MathArray::size_type i = 0; i < ar.size() - 1; ++i) {
281                 MathArray::iterator it = ar.begin() + i;
282
283                 // is this a integral name?
284                 if (!intSymbolTest(it->nucleus()))
285                         continue;
286
287                 // search 'd'
288                 MathArray::iterator jt =
289                         endNestSearch(it, ar.end(), intSymbolTest, differentialTest);
290
291                 // something sensible found?
292                 if (jt == ar.end())
293                         continue;
294
295                 // create a proper inset as replacement
296                 MathExIntInset * p = new MathExIntInset;
297
298                 // collect scripts
299                 MathArray::iterator st = it + 1;
300                 if ((*st)->asScriptInset()) {
301                         p->scripts(*st);
302                         p->core(MathArray(st + 1, jt));
303                 } else {
304                         p->core(MathArray(st, jt));
305                 }
306
307                 // use the atom behind the 'd' as differential
308                 MathArray diff;
309                 if (jt + 1 != ar.end()) {
310                         diff.push_back(*(jt + 1));
311                         ar.erase(it + 1, jt + 2);
312                 } else {
313                         ar.erase(it + 1, jt + 1);
314                 }
315                 p->differential(diff);
316                 (*it).reset(p);
317         }
318         lyxerr << "\nIntegrals to: " << ar << "\n";
319 }
320
321
322 void extractStructure(MathArray & ar)
323 {
324         extractMatrices(ar);
325         extractDelims(ar);
326         extractFunctions(ar);
327         extractIntegrals(ar);
328         extractStrings(ar);
329 }
330
331
332 void write(MathArray const & dat, WriteStream & wi)
333 {
334         MathArray ar = dat;
335         extractStrings(ar);
336         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
337                 MathInset const * p = it->nucleus();
338                 if (it + 1 != ar.end()) {
339                         if (MathScriptInset const * q = asScript(it)) {
340                                 q->write(p, wi);
341                                 ++it;
342                                 continue;
343                         } 
344                 }
345                 p->write(wi);
346         }
347 }
348
349
350 void normalize(MathArray const & ar, NormalStream & os)
351 {
352         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
353                 (*it)->normalize(os);
354 }
355
356
357 void octavize(MathArray const & dat, OctaveStream & os)
358 {
359         MathArray ar = dat;
360         extractStructure(ar);
361         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
362                 MathInset const * p = it->nucleus();
363                 if (it + 1 != ar.end()) {
364                         if (MathScriptInset const * q = asScript(it)) {
365                                 q->octavize(p, os);
366                                 ++it;   
367                                 continue;
368                         }
369                 }
370                 p->octavize(os);
371         }
372 }
373
374
375 void maplize(MathArray const & dat, MapleStream & os)
376 {
377         MathArray ar = dat;
378         extractStructure(ar);
379         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
380                 MathInset const * p = it->nucleus();
381                 if (it + 1 != ar.end()) {
382                         if (MathScriptInset const * q = asScript(it)) {
383                                 q->maplize(p, os);
384                                 ++it;   
385                                 continue;
386                         }
387                 }
388                 p->maplize(os);
389         }
390 }
391
392
393 void mathmlize(MathArray const & dat, MathMLStream & os)
394 {
395         MathArray ar = dat;
396         extractStructure(ar);
397         if (ar.size() == 0)
398                 os << "<mrow/>";
399         else if (ar.size() == 1)
400                 os << ar.begin()->nucleus();
401         else {
402                 os << MTag("mrow");
403                 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it) {
404                         MathInset const * p = it->nucleus();
405                         if (it + 1 != ar.end()) {
406                                 if (MathScriptInset const * q = asScript(it)) {
407                                         q->mathmlize(p, os);
408                                         ++it;   
409                                         continue;
410                                 }
411                         }
412                         p->mathmlize(os);
413                 }
414                 os << ETag("mrow");
415         }
416 }
417