]> git.lyx.org Git - features.git/blob - src/mathed/math_parser.C
most of the so far unapplied stuff from porto including proper support for
[features.git] / src / mathed / math_parser.C
1 /** The math parser
2     \author André Pönitz (2001)
3  */
4
5 /*
6
7 If someone desperately needs partial "structures" (such as a few
8 cells of an array inset or similar) (s)he could uses the
9 following hack as starting point to write some macros:
10
11   \newif\ifcomment
12   \commentfalse
13   \ifcomment
14           \def\makeamptab{\catcode`\&=4\relax}
15           \def\makeampletter{\catcode`\&=11\relax}
16     \def\b{\makeampletter\expandafter\makeamptab\bi}
17     \long\def\bi#1\e{}
18   \else
19     \def\b{}\def\e{}
20   \fi
21
22   ...
23
24   \[\begin{array}{ccc}
25    1 & 2\b & 3^2\\
26    4 & 5\e & 6\\
27    7 & 8 & 9
28   \end{array}\]
29
30 */
31
32
33 #include <config.h>
34
35 #ifdef __GNUG__
36 #pragma implementation
37 #endif
38
39 #include "math_parser.h"
40 #include "math_inset.h"
41 #include "math_arrayinset.h"
42 #include "math_braceinset.h"
43 #include "math_boxinset.h"
44 #include "math_charinset.h"
45 #include "math_deliminset.h"
46 #include "math_extern.h"
47 #include "math_factory.h"
48 #include "math_kerninset.h"
49 #include "math_macro.h"
50 #include "math_macrotable.h"
51 #include "math_macrotemplate.h"
52 #include "math_hullinset.h"
53 #include "math_rootinset.h"
54 #include "math_sizeinset.h"
55 #include "math_sqrtinset.h"
56 #include "math_scriptinset.h"
57 #include "math_sqrtinset.h"
58 #include "math_support.h"
59 #include "math_xyarrowinset.h"
60
61 #include "lyxlex.h"
62 #include "debug.h"
63 #include "support/LAssert.h"
64 #include "support/lstrings.h"
65
66 #include <cctype>
67 #include <stack>
68 #include <algorithm>
69
70 using std::istream;
71 using std::ostream;
72 using std::ios;
73 using std::endl;
74 using std::stack;
75 using std::fill;
76 using std::vector;
77 using std::atoi;
78
79
80 //#define FILEDEBUG
81
82
83 namespace {
84
85 bool stared(string const & s)
86 {
87         string::size_type const n = s.size();
88         return n && s[n - 1] == '*';
89 }
90
91
92 // These are TeX's catcodes
93 enum CatCode {
94         catEscape,     // 0    backslash
95         catBegin,      // 1    {
96         catEnd,        // 2    }
97         catMath,       // 3    $
98         catAlign,      // 4    &
99         catNewline,    // 5    ^^M
100         catParameter,  // 6    #
101         catSuper,      // 7    ^
102         catSub,        // 8    _
103         catIgnore,     // 9
104         catSpace,      // 10   space
105         catLetter,     // 11   a-zA-Z
106         catOther,      // 12   none of the above
107         catActive,     // 13   ~
108         catComment,    // 14   %
109         catInvalid     // 15   <delete>
110 };
111
112 CatCode theCatcode[256];
113
114
115 inline CatCode catcode(unsigned char c)
116 {
117         return theCatcode[c];
118 }
119
120
121 enum {
122         FLAG_BRACE_LAST = 1 << 1,  //  last closing brace ends the parsing
123         FLAG_RIGHT      = 1 << 2,  //  next \\right ends the parsing process
124         FLAG_END        = 1 << 3,  //  next \\end ends the parsing process
125         FLAG_BRACK_END  = 1 << 4,  //  next closing bracket ends the parsing
126         FLAG_TEXTMODE   = 1 << 5,  //  we are in a box
127         FLAG_ITEM       = 1 << 6,  //  read a (possibly braced token)
128         FLAG_LEAVE      = 1 << 7,  //  leave the loop at the end
129         FLAG_SIMPLE     = 1 << 8,  //  next $ leaves the loop
130         FLAG_EQUATION   = 1 << 9,  //  next \] leaves the loop
131         FLAG_SIMPLE2    = 1 << 10  //  next \) leaves the loop
132 };
133
134
135 void catInit()
136 {
137         fill(theCatcode, theCatcode + 256, catOther);
138         fill(theCatcode + 'a', theCatcode + 'z' + 1, catLetter);
139         fill(theCatcode + 'A', theCatcode + 'Z' + 1, catLetter);
140
141         theCatcode['\\'] = catEscape;
142         theCatcode['{']  = catBegin;
143         theCatcode['}']  = catEnd;
144         theCatcode['$']  = catMath;
145         theCatcode['&']  = catAlign;
146         theCatcode['\n'] = catNewline;
147         theCatcode['#']  = catParameter;
148         theCatcode['^']  = catSuper;
149         theCatcode['_']  = catSub;
150         theCatcode['\7f'] = catIgnore;
151         theCatcode[' ']  = catSpace;
152         theCatcode['\t'] = catSpace;
153         theCatcode['\r'] = catSpace;
154         theCatcode['~']  = catActive;
155         theCatcode['%']  = catComment;
156 }
157
158
159
160 //
161 // Helper class for parsing
162 //
163
164 class Token {
165 public:
166         ///
167         Token() : cs_(), char_(0), cat_(catIgnore) {}
168         ///
169         Token(char c, CatCode cat) : cs_(), char_(c), cat_(cat) {}
170         ///
171         Token(string const & cs) : cs_(cs), char_(0), cat_(catIgnore) {}
172
173         ///
174         string const & cs() const { return cs_; }
175         ///
176         CatCode cat() const { return cat_; }
177         ///
178         char character() const { return char_; }
179         ///
180         string asString() const;
181         ///
182         bool isCR() const;
183
184 private:
185         ///
186         string cs_;
187         ///
188         char char_;
189         ///
190         CatCode cat_;
191 };
192
193 bool Token::isCR() const
194 {
195         return cs_ == "\\" || cs_ == "cr" || cs_ == "crcr";
196 }
197
198 string Token::asString() const
199 {
200         return cs_.size() ? cs_ : string(1, char_);
201 }
202
203 ostream & operator<<(ostream & os, Token const & t)
204 {
205         if (t.cs().size())
206                 os << "\\" << t.cs();
207         else
208                 os << "[" << t.character() << "," << t.cat() << "]";
209         return os;
210 }
211
212
213 class Parser {
214
215 public:
216         ///
217         Parser(LyXLex & lex);
218         ///
219         Parser(istream & is);
220
221         ///
222         bool parse_macro(string & name);
223         ///
224         bool parse_normal(MathAtom & at);
225         ///
226         void parse_into(MathArray & array, unsigned flags, bool mathmode);
227         ///
228         int lineno() const { return lineno_; }
229         ///
230         void putback();
231
232 private:
233         ///
234         void parse_into1(MathGridInset & grid, unsigned flags, bool mathmode, bool numbered);
235         ///
236         void parse_into2(MathAtom & at, unsigned flags, bool mathmode, bool numbered);
237         /// get arg delimited by 'left' and 'right'
238         string getArg(char left, char right);
239         ///
240         char getChar();
241         ///
242         void error(string const & msg);
243         /// dump contents to screen
244         void dump() const;
245
246 private:
247         ///
248         void tokenize(istream & is);
249         ///
250         void tokenize(string const & s);
251         ///
252         void skipSpaceTokens(istream & is, char c);
253         ///
254         void push_back(Token const & t);
255         ///
256         void pop_back();
257         ///
258         Token const & prevToken() const;
259         ///
260         Token const & nextToken() const;
261         ///
262         Token const & getToken();
263         /// skips spaces if any
264         void skipSpaces();
265         ///
266         void lex(string const & s);
267         ///
268         bool good() const;
269
270         ///
271         int lineno_;
272         ///
273         vector<Token> tokens_;
274         ///
275         unsigned pos_;
276 };
277
278
279 Parser::Parser(LyXLex & lexer)
280         : lineno_(lexer.getLineNo()), pos_(0)
281 {
282         tokenize(lexer.getStream());
283         lexer.eatLine();
284 }
285
286
287 Parser::Parser(istream & is)
288         : lineno_(0), pos_(0)
289 {
290         tokenize(is);
291 }
292
293
294 void Parser::push_back(Token const & t)
295 {
296         tokens_.push_back(t);
297 }
298
299
300 void Parser::pop_back()
301 {
302         tokens_.pop_back();
303 }
304
305
306 Token const & Parser::prevToken() const
307 {
308         static const Token dummy;
309         return pos_ > 0 ? tokens_[pos_ - 1] : dummy;
310 }
311
312
313 Token const & Parser::nextToken() const
314 {
315         static const Token dummy;
316         return good() ? tokens_[pos_] : dummy;
317 }
318
319
320 Token const & Parser::getToken()
321 {
322         static const Token dummy;
323         //lyxerr << "looking at token " << tokens_[pos_] << " pos: " << pos_ << '\n';
324         return good() ? tokens_[pos_++] : dummy;
325 }
326
327
328 void Parser::skipSpaces()
329 {
330         while (nextToken().cat() == catSpace)
331                 getToken();
332 }
333
334
335 void Parser::putback()
336 {
337         --pos_;
338 }
339
340
341 bool Parser::good() const
342 {
343         return pos_ < tokens_.size();
344 }
345
346
347 char Parser::getChar()
348 {
349         if (!good())
350                 error("The input stream is not well...");
351         return tokens_[pos_++].character();
352 }
353
354
355 string Parser::getArg(char left, char right)
356 {
357         skipSpaces();
358
359         string result;
360         char c = getChar();
361
362         if (c != left)
363                 putback();
364         else
365                 while ((c = getChar()) != right && good())
366                         result += c;
367
368         return result;
369 }
370
371
372 void Parser::tokenize(istream & is)
373 {
374         // eat everything up to the next \end_inset or end of stream
375         // and store it in s for further tokenization
376         string s;
377         char c;
378         while (is.get(c)) {
379                 s += c;
380                 if (s.size() >= 10 && s.substr(s.size() - 10) == "\\end_inset") {
381                         s = s.substr(0, s.size() - 10);
382                         break;
383                 }
384         }
385
386         // tokenize buffer
387         tokenize(s);
388 }
389
390
391 void Parser::skipSpaceTokens(istream & is, char c)
392 {
393         // skip trailing spaces
394         while (catcode(c) == catSpace || catcode(c) == catNewline)
395                 if (!is.get(c))
396                         break;
397         //lyxerr << "putting back: " << c << "\n";
398         is.putback(c);
399 }
400
401
402 void Parser::tokenize(string const & buffer)
403 {
404         static bool init_done = false;
405
406         if (!init_done) {
407                 catInit();
408                 init_done = true;
409         }
410
411         istringstream is(buffer.c_str(), ios::in | ios::binary);
412
413         char c;
414         while (is.get(c)) {
415                 //lyxerr << "reading c: " << c << "\n";
416
417                 switch (catcode(c)) {
418                         case catNewline: {
419                                 ++lineno_;
420                                 is.get(c);
421                                 if (catcode(c) == catNewline)
422                                         ; //push_back(Token("par"));
423                                 else {
424                                         push_back(Token(' ', catSpace));
425                                         is.putback(c);
426                                 }
427                                 break;
428                         }
429
430                         case catComment: {
431                                 while (is.get(c) && catcode(c) != catNewline)
432                                         ;
433                                 ++lineno_;
434                                 break;
435                         }
436
437                         case catEscape: {
438                                 is.get(c);
439                                 if (!is) {
440                                         error("unexpected end of input");
441                                 } else {
442                                         string s(1, c);
443                                         if (catcode(c) == catLetter) {
444                                                 // collect letters
445                                                 while (is.get(c) && catcode(c) == catLetter)
446                                                         s += c;
447                                                 skipSpaceTokens(is, c);
448                                         }
449                                         push_back(Token(s));
450                                 }
451                                 break;
452                         }
453
454                         case catSuper:
455                         case catSub: {
456                                 push_back(Token(c, catcode(c)));
457                                 is.get(c);
458                                 skipSpaceTokens(is, c);
459                                 break;
460                         }
461
462                         case catIgnore: {
463                                 lyxerr << "ignoring a char: " << int(c) << "\n";
464                                 break;
465                         }
466
467                         default:
468                                 push_back(Token(c, catcode(c)));
469                 }
470         }
471
472 #ifdef FILEDEBUG
473         dump();
474 #endif
475 }
476
477
478 void Parser::dump() const
479 {
480         lyxerr << "\nTokens: ";
481         for (unsigned i = 0; i < tokens_.size(); ++i) {
482                 if (i == pos_)
483                         lyxerr << " <#> ";
484                 lyxerr << tokens_[i];
485         }
486         lyxerr << " pos: " << pos_ << "\n";
487 }
488
489
490 void Parser::error(string const & msg)
491 {
492         lyxerr << "Line ~" << lineno_ << ": Math parse error: " << msg << endl;
493         dump();
494         //exit(1);
495 }
496
497
498 bool Parser::parse_macro(string & name)
499 {
500         int nargs = 0;
501         name = "{error}";
502         skipSpaces();
503
504         if (nextToken().cs() == "def") {
505
506                 getToken();
507                 name = getToken().cs();
508
509                 string pars;
510                 while (good() && nextToken().cat() != catBegin)
511                         pars += getToken().cs();
512
513                 if (!good()) {
514                         error("bad stream in parse_macro\n");
515                         return false;
516                 }
517
518                 //lyxerr << "read \\def parameter list '" << pars << "'\n";
519                 if (!pars.empty()) {
520                         error("can't handle non-empty parameter lists\n");
521                         return false;
522                 }
523
524         } else if (nextToken().cs() == "newcommand") {
525
526                 getToken();
527
528                 if (getToken().cat() != catBegin) {
529                         error("'{' in \\newcommand expected (1) \n");
530                         return false;
531                 }
532
533                 name = getToken().cs();
534
535                 if (getToken().cat() != catEnd) {
536                         error("'}' expected\n");
537                         return false;
538                 }
539
540                 string arg  = getArg('[', ']');
541                 if (!arg.empty())
542                         nargs = atoi(arg.c_str());
543
544         } else {
545                 lyxerr << "\\newcommand or \\def  expected\n";
546                 return false;
547         }
548
549
550         if (getToken().cat() != catBegin) {
551                 lyxerr << "'{' in macro definition expected (2)\n";
552                 return false;
553         }
554
555         MathArray ar1;
556         parse_into(ar1, FLAG_BRACE_LAST, true);
557
558         // we cannot handle recursive stuff at all
559         MathArray test;
560         test.push_back(createMathInset(name));
561         if (ar1.contains(test)) {
562                 lyxerr << "we cannot handle recursive macros at all.\n";
563                 return false;
564         }
565
566         // is a version for display attached?
567         MathArray ar2;
568         parse_into(ar2, FLAG_ITEM, true);
569
570         MathMacroTable::create(name, nargs, ar1, ar2);
571         return true;
572 }
573  
574
575 bool Parser::parse_normal(MathAtom & at)
576 {
577         MathArray ar;
578         parse_into(ar, false, false);
579         if (ar.size() != 1) {
580                 lyxerr << "Unusual contents found: " << ar << endl;
581                 at.reset(new MathParInset);
582                 at->cell(0) = ar;
583                 return true;
584         }
585         at = ar[0];
586         return true;
587 }
588
589
590 void Parser::parse_into(MathArray & array, unsigned flags, bool mathmode)
591 {
592         MathGridInset grid(1, 1);
593         parse_into1(grid, flags, mathmode, false);
594         array = grid.cell(0);
595         // remove 'unnecessary' braces:
596         if (array.size() == 1 && array.back()->asBraceInset()) {
597                 lyxerr << "extra braces removed\n";
598                 array = array.back()->asBraceInset()->cell(0);
599         }
600 }
601
602
603 void Parser::parse_into2(MathAtom & at, unsigned flags,
604         bool mathmode, bool numbered)
605 {
606         parse_into1(*(at->asGridInset()), flags, mathmode, numbered);
607 }
608
609
610 void Parser::parse_into1(MathGridInset & grid, unsigned flags,
611         bool mathmode, bool numbered)
612 {
613         int  limits = 0;
614         MathGridInset::row_type cellrow = 0;
615         MathGridInset::col_type cellcol = 0;
616         MathArray * cell = &grid.cell(grid.index(cellrow, cellcol));
617
618         if (grid.asHullInset())
619                 grid.asHullInset()->numbered(cellrow, numbered);
620
621         //dump();
622         //lyxerr << "grid: " << grid << endl;
623
624         while (good()) {
625                 Token const & t = getToken();
626
627 #ifdef FILEDEBUG
628                 lyxerr << "t: " << t << " flags: " << flags << "\n";
629                 //cell->dump();
630                 lyxerr << "\n";
631 #endif
632
633                 if (flags & FLAG_ITEM) {
634                         if (t.cat() == catSpace)
635                                 continue;
636
637                         flags &= ~FLAG_ITEM;
638                         if (t.cat() == catBegin) {
639                                 // skip the brace and collect everything to the next matching
640                                 // closing brace
641                                 flags |= FLAG_BRACE_LAST;
642                                 continue;
643                         }
644
645                         // handle only this single token, leave the loop if done
646                         flags |= FLAG_LEAVE;
647                 }
648
649                 //
650                 // cat codes
651                 //
652                 if (t.cat() == catMath) {
653                         if (!mathmode) {
654                                 // we are inside some text mode thingy, so opening new math is allowed
655                                 Token const & n = getToken();
656                                 if (n.cat() == catMath) {
657                                         // TeX's $$...$$ syntax for displayed math
658                                         cell->push_back(MathAtom(new MathHullInset(LM_OT_EQUATION)));
659                                         parse_into2(cell->back(), FLAG_SIMPLE, true, false);
660                                         getToken(); // skip the second '$' token
661                                 } else {
662                                         // simple $...$  stuff
663                                         putback();
664                                         cell->push_back(MathAtom(new MathHullInset(LM_OT_SIMPLE)));
665                                         parse_into2(cell->back(), FLAG_SIMPLE, true, false);
666                                 }
667                         }
668
669                         else if (flags & FLAG_SIMPLE) {
670                                 // this is the end of the formula
671                                 return;
672                         }
673
674                         else {
675                                 error("something strange in the parser\n");
676                                 break;
677                         }
678                 }
679
680                 else if (t.cat() == catLetter)
681                         cell->push_back(MathAtom(new MathCharInset(t.character())));
682
683                 else if (t.cat() == catSpace && !mathmode)
684                         cell->push_back(MathAtom(new MathCharInset(t.character())));
685
686                 else if (t.cat() == catParameter) {
687                         Token const & n = getToken();
688                         cell->push_back(MathAtom(new MathMacroArgument(n.character()-'0')));
689                 }
690
691                 else if (t.cat() == catBegin) {
692                         MathArray ar;
693                         parse_into(ar, FLAG_BRACE_LAST, mathmode);
694 #ifndef WITH_WARNINGS
695 #warning this might be wrong in general!
696 #endif
697                         // ignore braces around simple items
698                         if ((ar.size() == 1 && !ar.front()->needsBraces()
699                          || (ar.size() == 2 && !ar.front()->needsBraces()
700                                                         && ar.back()->asScriptInset()))
701                          || (ar.size() == 0 && cell->size() == 0))
702                         {
703                                 cell->push_back(ar);
704                         } else {
705                                 cell->push_back(MathAtom(new MathBraceInset));
706                                 cell->back()->cell(0).swap(ar);
707                         }
708                 }
709
710                 else if (t.cat() == catEnd) {
711                         if (flags & FLAG_BRACE_LAST)
712                                 return;
713                         lyxerr << "found '}' unexpectedly, cell: '" << cell << "'\n";
714                         dump();
715                         //lyxerr << "found '}' unexpectedly\n";
716                         //lyx::Assert(0);
717                         //add(cell, '}', LM_TC_TEX);
718                 }
719
720                 else if (t.cat() == catAlign) {
721                         ++cellcol;
722                         //lyxerr << " column now " << cellcol << " max: " << grid.ncols() << "\n";
723                         if (cellcol == grid.ncols()) { 
724                                 lyxerr << "adding column " << cellcol << "\n";
725                                 grid.addCol(cellcol - 1);
726                         }
727                         cell = &grid.cell(grid.index(cellrow, cellcol));
728                 }
729
730                 else if (t.cat() == catSuper || t.cat() == catSub) {
731                         bool up = (t.cat() == catSuper);
732                         MathScriptInset * p = 0;
733                         if (cell->size())
734                                 p = cell->back()->asScriptInset();
735                         if (!p || p->has(up)) {
736                                 cell->push_back(MathAtom(new MathScriptInset(up)));
737                                 p = cell->back()->asScriptInset();
738                         }
739                         p->ensure(up);
740                         parse_into(p->cell(up), FLAG_ITEM, mathmode);
741                         p->limits(limits);
742                         limits = 0;
743                 }
744
745                 else if (t.character() == ')' && (flags & FLAG_SIMPLE2))
746                         return;
747
748                 else if (t.character() == ']' && (flags & FLAG_BRACK_END))
749                         return;
750
751                 else if (t.cat() == catOther)
752                         cell->push_back(MathAtom(new MathCharInset(t.character())));
753
754                 //
755                 // control sequences
756                 //
757                 else if (t.cs() == "(") {
758                         cell->push_back(MathAtom(new MathHullInset(LM_OT_SIMPLE)));
759                         parse_into2(cell->back(), FLAG_SIMPLE2, true, true);
760                 }
761
762                 else if (t.cs() == "[") {
763                         cell->push_back(MathAtom(new MathHullInset(LM_OT_EQUATION)));
764                         parse_into2(cell->back(), FLAG_EQUATION, true, true);
765                 }
766
767                 else if (t.cs() == "protect")
768                         // ignore \\protect, will hopefully be re-added during output
769                         ;
770
771                 else if (t.cs() == "end") {
772                         if (flags & FLAG_END) {
773                                 // eat environment name
774                                 //string const name =
775                                 getArg('{', '}');
776                                 // FIXME: check that we ended the correct environment
777                                 return;
778                         }
779                         lyxerr << "found 'end' unexpectedly, cell: '" << cell << "'\n";
780                         dump();
781                 }
782
783                 else if (t.cs() == ")")
784                         break;
785
786                 else if (t.cs() == "]")
787                         break;
788
789                 else if (t.cs() == "\\") {
790                         grid.vcrskip(LyXLength(getArg('[', ']')), cellrow);
791                         ++cellrow;
792                         cellcol = 0;
793                         if (cellrow == grid.nrows())
794                                 grid.addRow(cellrow - 1);
795                         if (grid.asHullInset())
796                                 grid.asHullInset()->numbered(cellrow, numbered);
797                         cell = &grid.cell(grid.index(cellrow, cellcol));
798                 }
799
800 #if 1
801                 else if (t.cs() == "multicolumn") {
802                         // extract column count and insert dummy cells
803                         MathArray count;
804                         parse_into(count, FLAG_ITEM, mathmode);
805                         int cols = 1;
806                         if (!extractNumber(count, cols)) {
807                                 lyxerr << " can't extract number of cells from " << count << "\n";
808                         }
809                         // resize the table if necessary
810                         for (int i = 0; i < cols; ++i) {
811                                 ++cellcol;
812                                 if (cellcol == grid.ncols()) { 
813                                         lyxerr << "adding column " << cellcol << "\n";
814                                         grid.addCol(cellcol - 1);
815                                 }
816                                 cell = &grid.cell(grid.index(cellrow, cellcol));
817                                 // mark this as dummy
818                                 grid.cellinfo(grid.index(cellrow, cellcol)).dummy_ = true;
819                         }
820                         // the last cell is the real thng, not a dummy
821                         grid.cellinfo(grid.index(cellrow, cellcol)).dummy_ = false;
822
823                         // read special alignment
824                         MathArray align;
825                         parse_into(align, FLAG_ITEM, mathmode);
826                         //grid.cellinfo(grid.index(cellrow, cellcol)).align_ = extractString(align);
827
828                         // parse the remaining contents into the "real" cell
829                         parse_into(*cell, FLAG_ITEM, mathmode);
830                 }
831 #endif
832
833                 else if (t.cs() == "limits")
834                         limits = 1;
835
836                 else if (t.cs() == "nolimits")
837                         limits = -1;
838
839                 else if (t.cs() == "nonumber") {
840                         if (grid.asHullInset())
841                                 grid.asHullInset()->numbered(cellrow, false);
842                 }
843
844                 else if (t.cs() == "number") {
845                         if (grid.asHullInset())
846                                 grid.asHullInset()->numbered(cellrow, true);
847                 }
848
849                 else if (t.cs() == "hline") {
850                         if (grid.asHullInset())
851                                 grid.asHullInset()->rowinfo(cellrow + 1);
852                 }
853
854                 else if (t.cs() == "sqrt") {
855                         char c = getChar();
856                         if (c == '[') {
857                                 cell->push_back(MathAtom(new MathRootInset));
858                                 parse_into(cell->back()->cell(0), FLAG_BRACK_END, mathmode);
859                                 parse_into(cell->back()->cell(1), FLAG_ITEM, mathmode);
860                         } else {
861                                 putback();
862                                 cell->push_back(MathAtom(new MathSqrtInset));
863                                 parse_into(cell->back()->cell(0), FLAG_ITEM, mathmode);
864                         }
865                 }
866
867                 else if (t.cs() == "ref") {
868                         cell->push_back(MathAtom(new RefInset));
869                         char c = getChar();
870                         if (c == '[') 
871                                 parse_into(cell->back()->cell(1), FLAG_BRACK_END, mathmode);
872                         else 
873                                 putback();
874                         parse_into(cell->back()->cell(0), FLAG_ITEM, mathmode);
875                 }
876
877                 else if (t.cs() == "left") {
878                         string l = getToken().asString();
879                         MathArray ar;
880                         parse_into(ar, FLAG_RIGHT, mathmode);
881                         string r = getToken().asString();
882                         cell->push_back(MathAtom(new MathDelimInset(l, r)));
883                         cell->back()->cell(0) = ar;
884                 }
885
886                 else if (t.cs() == "right") {
887                         if (flags & FLAG_RIGHT)
888                                 return;
889                         //lyxerr << "got so far: '" << cell << "'\n";
890                         error("Unmatched right delimiter");
891                         return;
892                 }
893
894                 else if (t.cs() == "begin") {
895                         string const name = getArg('{', '}');
896                         if (name == "array" || name == "subarray") {
897                                 string const valign = getArg('[', ']') + 'c';
898                                 string const halign = getArg('{', '}');
899                                 cell->push_back(MathAtom(new MathArrayInset(name, valign[0], halign)));
900                                 parse_into2(cell->back(), FLAG_END, mathmode, false);
901                         }
902
903                         else if (name == "split" || name == "cases" ||
904                                          name == "gathered" || name == "aligned") {
905                                 cell->push_back(createMathInset(name));
906                                 parse_into2(cell->back(), FLAG_END, mathmode, false);
907                         }
908
909                         else if (name == "math") {
910                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_SIMPLE)));
911                                 parse_into2(cell->back(), FLAG_SIMPLE, true, true);
912                         }
913
914                         else if (name == "equation" || name == "equation*"
915                                         || name == "displaymath") {
916                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_EQUATION)));
917                                 parse_into2(cell->back(), FLAG_END, true, (name == "equation"));
918                         }
919
920                         else if (name == "eqnarray" || name == "eqnarray*") {
921                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_EQNARRAY)));
922                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
923                         }
924
925                         else if (name == "align" || name == "align*") {
926                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_ALIGN)));
927                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
928                         }
929
930                         else if (name == "alignat" || name == "alignat*") {
931                                 // ignore this for a while
932                                 getArg('{', '}');
933                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_ALIGNAT)));
934                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
935                         }
936
937                         else if (name == "xalignat" || name == "xalignat*") {
938                                 // ignore this for a while
939                                 getArg('{', '}');
940                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_XALIGNAT)));
941                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
942                         }
943
944                         else if (name == "xxalignat") {
945                                 // ignore this for a while
946                                 getArg('{', '}');
947                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_XXALIGNAT)));
948                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
949                         }
950
951                         else if (name == "multline" || name == "multline*") {
952                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_MULTLINE)));
953                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
954                         }
955
956                         else if (name == "gather" || name == "gather*") {
957                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_GATHER)));
958                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
959                         }
960
961                         else {
962                                 latexkeys const * l = in_word_set(name);
963                                 if (l) {
964                                         if (l->inset == "matrix") {
965                                                 cell->push_back(createMathInset(name));
966                                                 parse_into2(cell->back(), FLAG_END, mathmode, false);
967                                         }
968                                 } else {
969                                         lyxerr << "unknow math inset begin '" << name << "'\n";
970                                 }
971                         }
972                 }
973
974                 else if (t.cs() == "kern") {
975 #ifdef WITH_WARNINGS
976 #warning A hack...
977 #endif
978                         string s;
979                         while (1) {
980                                 Token const & t = getToken();
981                                 if (!good()) {
982                                         putback();
983                                         break;
984                                 }
985                                 s += t.character();
986                                 if (isValidLength(s))
987                                         break;
988                         }
989                         cell->push_back(MathAtom(new MathKernInset(s)));
990                 }
991
992                 else if (t.cs() == "label") {
993                         if (grid.asHullInset())
994                                 grid.asHullInset()->label(cellrow, getArg('{', '}'));
995                 }
996
997                 else if (t.cs() == "choose" || t.cs() == "over" || t.cs() == "atop") {
998                         MathAtom p = createMathInset(t.cs());
999                         cell->swap(p->cell(0));
1000                         parse_into(p->cell(1), flags, mathmode);
1001                         cell->push_back(p);
1002                         return;
1003                 }
1004
1005                 else if (t.cs() == "substack") {
1006                         cell->push_back(createMathInset(t.cs()));
1007                         parse_into2(cell->back(), FLAG_ITEM, mathmode, false);
1008                 }
1009
1010                 else if (t.cs() == "xymatrix") {
1011                         cell->push_back(createMathInset(t.cs()));
1012                         parse_into2(cell->back(), FLAG_ITEM, mathmode, false);
1013                 }
1014
1015 #if 0
1016                 // Disabled
1017                 else if (1 && t.cs() == "ar") {
1018                         MathXYArrowInset * p = new MathXYArrowInset;
1019
1020                         // try to read target
1021                         char c = getChar();
1022                         if (c == '[') {
1023                                 parse_into(p->cell(0), FLAG_BRACK_END, mathmode);
1024                                 //lyxerr << "read target: " << p->cell(0) << "\n";
1025                         } else {
1026                                 putback();
1027                         }
1028
1029                         // try to read label
1030                         if (nextToken().cat() == catSuper || nextToken().cat() == catSub) {
1031                                 p->up_ = nextToken().cat() == catSuper;
1032                                 getToken();
1033                                 parse_into(p->cell(1), FLAG_ITEM, mathmode);
1034                                 //lyxerr << "read label: " << p->cell(1) << "\n";
1035                         }
1036
1037                         cell->push_back(MathAtom(p));
1038                         //lyxerr << "read cell: " << cell << "\n";
1039                 }
1040 #endif
1041
1042                 else if (t.cs().size()) {
1043                         latexkeys const * l = in_word_set(t.cs());
1044                         if (l) {
1045                                 if (l->inset == "font") {
1046                                         lyxerr << "starting font " << t.cs() << "\n";
1047                                         MathAtom p = createMathInset(t.cs());
1048                                         bool textmode = (t.cs()[0] == 't');
1049                                         parse_into(p->cell(0), FLAG_ITEM, !textmode);
1050                                         cell->push_back(p);
1051                                         //lyxerr << "ending font\n";
1052                                 }
1053
1054                                 else if (l->inset == "oldfont") {
1055                                         cell->push_back(createMathInset(t.cs()));
1056                                         parse_into(cell->back()->cell(0), flags, l->extra == "mathmode");
1057                                         return;
1058                                 }
1059
1060                                 else if (l->inset == "box") {
1061                                         // switch to text mode
1062                                         cell->push_back(createMathInset(t.cs()));
1063                                         parse_into(cell->back()->cell(0), FLAG_ITEM, mathmode);
1064                                 }
1065
1066                                 else if (l->inset == "style") {
1067                                         cell->push_back(createMathInset(t.cs()));
1068                                         parse_into(cell->back()->cell(0), flags, mathmode);
1069                                         return;
1070                                 }
1071
1072                                 else {
1073                                         MathAtom p = createMathInset(t.cs());
1074                                         for (MathInset::idx_type i = 0; i < p->nargs(); ++i)
1075                                                 parse_into(p->cell(i), FLAG_ITEM, l->extra == "mathmode");
1076                                         cell->push_back(p);
1077                                 }
1078                         }
1079
1080                         else {
1081                                 MathAtom p = createMathInset(t.cs());
1082                                 for (MathInset::idx_type i = 0; i < p->nargs(); ++i)
1083                                         parse_into(p->cell(i), FLAG_ITEM, mathmode);
1084                                 cell->push_back(p);
1085                         }
1086                 }
1087
1088
1089                 if (flags & FLAG_LEAVE) {
1090                         flags &= ~FLAG_LEAVE;
1091                         break;
1092                 }
1093         }
1094 }
1095
1096
1097
1098 } // anonymous namespace
1099
1100
1101 void mathed_parse_cell(MathArray & ar, string const & str)
1102 {
1103         istringstream is(str.c_str());
1104         mathed_parse_cell(ar, is);
1105 }
1106
1107
1108 void mathed_parse_cell(MathArray & ar, istream & is)
1109 {
1110         Parser(is).parse_into(ar, 0, true);
1111 }
1112
1113
1114
1115 bool mathed_parse_macro(string & name, string const & str)
1116 {
1117         istringstream is(str.c_str());
1118         Parser parser(is);
1119         return parser.parse_macro(name);
1120 }
1121
1122 bool mathed_parse_macro(string & name, istream & is)
1123 {
1124         Parser parser(is);
1125         return parser.parse_macro(name);
1126 }
1127
1128 bool mathed_parse_macro(string & name, LyXLex & lex)
1129 {
1130         Parser parser(lex);
1131         return parser.parse_macro(name);
1132 }
1133
1134
1135
1136 bool mathed_parse_normal(MathAtom & t, string const & str)
1137 {
1138         istringstream is(str.c_str());
1139         Parser parser(is);
1140         return parser.parse_normal(t);
1141 }
1142
1143 bool mathed_parse_normal(MathAtom & t, istream & is)
1144 {
1145         Parser parser(is);
1146         return parser.parse_normal(t);
1147 }
1148
1149 bool mathed_parse_normal(MathAtom & t, LyXLex & lex)
1150 {
1151         Parser parser(lex);
1152         return parser.parse_normal(t);
1153 }