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