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