]> git.lyx.org Git - lyx.git/blob - src/mathed/math_parser.C
several smallish changes/bugfixes/left overs from Porto
[lyx.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, ar)));
885                 }
886
887                 else if (t.cs() == "right") {
888                         if (flags & FLAG_RIGHT)
889                                 return;
890                         //lyxerr << "got so far: '" << cell << "'\n";
891                         error("Unmatched right delimiter");
892                         return;
893                 }
894
895                 else if (t.cs() == "begin") {
896                         string const name = getArg('{', '}');
897                         if (name == "array" || name == "subarray") {
898                                 string const valign = getArg('[', ']') + 'c';
899                                 string const halign = getArg('{', '}');
900                                 cell->push_back(MathAtom(new MathArrayInset(name, valign[0], halign)));
901                                 parse_into2(cell->back(), FLAG_END, mathmode, false);
902                         }
903
904                         else if (name == "split" || name == "cases" ||
905                                          name == "gathered" || name == "aligned") {
906                                 cell->push_back(createMathInset(name));
907                                 parse_into2(cell->back(), FLAG_END, mathmode, false);
908                         }
909
910                         else if (name == "math") {
911                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_SIMPLE)));
912                                 parse_into2(cell->back(), FLAG_SIMPLE, true, true);
913                         }
914
915                         else if (name == "equation" || name == "equation*"
916                                         || name == "displaymath") {
917                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_EQUATION)));
918                                 parse_into2(cell->back(), FLAG_END, true, (name == "equation"));
919                         }
920
921                         else if (name == "eqnarray" || name == "eqnarray*") {
922                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_EQNARRAY)));
923                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
924                         }
925
926                         else if (name == "align" || name == "align*") {
927                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_ALIGN)));
928                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
929                         }
930
931                         else if (name == "alignat" || name == "alignat*") {
932                                 // ignore this for a while
933                                 getArg('{', '}');
934                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_ALIGNAT)));
935                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
936                         }
937
938                         else if (name == "xalignat" || name == "xalignat*") {
939                                 // ignore this for a while
940                                 getArg('{', '}');
941                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_XALIGNAT)));
942                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
943                         }
944
945                         else if (name == "xxalignat") {
946                                 // ignore this for a while
947                                 getArg('{', '}');
948                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_XXALIGNAT)));
949                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
950                         }
951
952                         else if (name == "multline" || name == "multline*") {
953                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_MULTLINE)));
954                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
955                         }
956
957                         else if (name == "gather" || name == "gather*") {
958                                 cell->push_back(MathAtom(new MathHullInset(LM_OT_GATHER)));
959                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
960                         }
961
962                         else {
963                                 latexkeys const * l = in_word_set(name);
964                                 if (l) {
965                                         if (l->inset == "matrix") {
966                                                 cell->push_back(createMathInset(name));
967                                                 parse_into2(cell->back(), FLAG_END, mathmode, false);
968                                         }
969                                 } else {
970                                         lyxerr << "unknow math inset begin '" << name << "'\n";
971                                 }
972                         }
973                 }
974
975                 else if (t.cs() == "kern") {
976 #ifdef WITH_WARNINGS
977 #warning A hack...
978 #endif
979                         string s;
980                         while (1) {
981                                 Token const & t = getToken();
982                                 if (!good()) {
983                                         putback();
984                                         break;
985                                 }
986                                 s += t.character();
987                                 if (isValidLength(s))
988                                         break;
989                         }
990                         cell->push_back(MathAtom(new MathKernInset(s)));
991                 }
992
993                 else if (t.cs() == "label") {
994                         if (grid.asHullInset())
995                                 grid.asHullInset()->label(cellrow, getArg('{', '}'));
996                 }
997
998                 else if (t.cs() == "choose" || t.cs() == "over" || t.cs() == "atop") {
999                         MathAtom p = createMathInset(t.cs());
1000                         cell->swap(p->cell(0));
1001                         parse_into(p->cell(1), flags, mathmode);
1002                         cell->push_back(p);
1003                         return;
1004                 }
1005
1006                 else if (t.cs() == "substack") {
1007                         cell->push_back(createMathInset(t.cs()));
1008                         parse_into2(cell->back(), FLAG_ITEM, mathmode, false);
1009                 }
1010
1011                 else if (t.cs() == "xymatrix") {
1012                         cell->push_back(createMathInset(t.cs()));
1013                         parse_into2(cell->back(), FLAG_ITEM, mathmode, false);
1014                 }
1015
1016 #if 0
1017                 // Disabled
1018                 else if (1 && t.cs() == "ar") {
1019                         MathXYArrowInset * p = new MathXYArrowInset;
1020
1021                         // try to read target
1022                         char c = getChar();
1023                         if (c == '[') {
1024                                 parse_into(p->cell(0), FLAG_BRACK_END, mathmode);
1025                                 //lyxerr << "read target: " << p->cell(0) << "\n";
1026                         } else {
1027                                 putback();
1028                         }
1029
1030                         // try to read label
1031                         if (nextToken().cat() == catSuper || nextToken().cat() == catSub) {
1032                                 p->up_ = nextToken().cat() == catSuper;
1033                                 getToken();
1034                                 parse_into(p->cell(1), FLAG_ITEM, mathmode);
1035                                 //lyxerr << "read label: " << p->cell(1) << "\n";
1036                         }
1037
1038                         cell->push_back(MathAtom(p));
1039                         //lyxerr << "read cell: " << cell << "\n";
1040                 }
1041 #endif
1042
1043                 else if (t.cs().size()) {
1044                         latexkeys const * l = in_word_set(t.cs());
1045                         if (l) {
1046                                 if (l->inset == "font") {
1047                                         lyxerr << "starting font " << t.cs() << "\n";
1048                                         MathAtom p = createMathInset(t.cs());
1049                                         bool textmode = (t.cs()[0] == 't');
1050                                         parse_into(p->cell(0), FLAG_ITEM, !textmode);
1051                                         cell->push_back(p);
1052                                         //lyxerr << "ending font\n";
1053                                 }
1054
1055                                 else if (l->inset == "oldfont") {
1056                                         cell->push_back(createMathInset(t.cs()));
1057                                         parse_into(cell->back()->cell(0), flags, l->extra == "mathmode");
1058                                         return;
1059                                 }
1060
1061                                 else if (l->inset == "box") {
1062                                         // switch to text mode
1063                                         cell->push_back(createMathInset(t.cs()));
1064                                         parse_into(cell->back()->cell(0), FLAG_ITEM, mathmode);
1065                                 }
1066
1067                                 else if (l->inset == "style") {
1068                                         cell->push_back(createMathInset(t.cs()));
1069                                         parse_into(cell->back()->cell(0), flags, mathmode);
1070                                         return;
1071                                 }
1072
1073                                 else {
1074                                         MathAtom p = createMathInset(t.cs());
1075                                         for (MathInset::idx_type i = 0; i < p->nargs(); ++i)
1076                                                 parse_into(p->cell(i), FLAG_ITEM, l->extra == "mathmode");
1077                                         cell->push_back(p);
1078                                 }
1079                         }
1080
1081                         else {
1082                                 MathAtom p = createMathInset(t.cs());
1083                                 for (MathInset::idx_type i = 0; i < p->nargs(); ++i)
1084                                         parse_into(p->cell(i), FLAG_ITEM, mathmode);
1085                                 cell->push_back(p);
1086                         }
1087                 }
1088
1089
1090                 if (flags & FLAG_LEAVE) {
1091                         flags &= ~FLAG_LEAVE;
1092                         break;
1093                 }
1094         }
1095 }
1096
1097
1098
1099 } // anonymous namespace
1100
1101
1102 void mathed_parse_cell(MathArray & ar, string const & str)
1103 {
1104         istringstream is(str.c_str());
1105         mathed_parse_cell(ar, is);
1106 }
1107
1108
1109 void mathed_parse_cell(MathArray & ar, istream & is)
1110 {
1111         Parser(is).parse_into(ar, 0, true);
1112 }
1113
1114
1115
1116 bool mathed_parse_macro(string & name, string const & str)
1117 {
1118         istringstream is(str.c_str());
1119         Parser parser(is);
1120         return parser.parse_macro(name);
1121 }
1122
1123 bool mathed_parse_macro(string & name, istream & is)
1124 {
1125         Parser parser(is);
1126         return parser.parse_macro(name);
1127 }
1128
1129 bool mathed_parse_macro(string & name, LyXLex & lex)
1130 {
1131         Parser parser(lex);
1132         return parser.parse_macro(name);
1133 }
1134
1135
1136
1137 bool mathed_parse_normal(MathAtom & t, string const & str)
1138 {
1139         istringstream is(str.c_str());
1140         Parser parser(is);
1141         return parser.parse_normal(t);
1142 }
1143
1144 bool mathed_parse_normal(MathAtom & t, istream & is)
1145 {
1146         Parser parser(is);
1147         return parser.parse_normal(t);
1148 }
1149
1150 bool mathed_parse_normal(MathAtom & t, LyXLex & lex)
1151 {
1152         Parser parser(lex);
1153         return parser.parse_normal(t);
1154 }