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