]> git.lyx.org Git - features.git/blob - src/mathed/math_parser.C
New support for \begin{...}...\end{...} style environments
[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_envinset.h"
47 #include "math_extern.h"
48 #include "math_factory.h"
49 #include "math_kerninset.h"
50 #include "math_macro.h"
51 #include "math_macrotable.h"
52 #include "math_macrotemplate.h"
53 #include "math_hullinset.h"
54 #include "math_parboxinset.h"
55 #include "math_parinset.h"
56 #include "math_rootinset.h"
57 #include "math_sizeinset.h"
58 #include "math_sqrtinset.h"
59 #include "math_scriptinset.h"
60 #include "math_sqrtinset.h"
61 #include "math_support.h"
62 #include "math_xyarrowinset.h"
63
64 //#include "insets/insetref.h"
65 #include "ref_inset.h"
66
67 #include "lyxlex.h"
68 #include "debug.h"
69 #include "support/LAssert.h"
70 #include "support/lstrings.h"
71
72 #include <cctype>
73 #include <stack>
74 #include <algorithm>
75
76 using std::istream;
77 using std::ostream;
78 using std::ios;
79 using std::endl;
80 using std::stack;
81 using std::fill;
82 using std::vector;
83 using std::atoi;
84
85
86 //#define FILEDEBUG
87
88
89 namespace {
90
91 bool stared(string const & s)
92 {
93         string::size_type const n = s.size();
94         return n && s[n - 1] == '*';
95 }
96
97
98 // These are TeX's catcodes
99 enum CatCode {
100         catEscape,     // 0    backslash
101         catBegin,      // 1    {
102         catEnd,        // 2    }
103         catMath,       // 3    $
104         catAlign,      // 4    &
105         catNewline,    // 5    ^^M
106         catParameter,  // 6    #
107         catSuper,      // 7    ^
108         catSub,        // 8    _
109         catIgnore,     // 9
110         catSpace,      // 10   space
111         catLetter,     // 11   a-zA-Z
112         catOther,      // 12   none of the above
113         catActive,     // 13   ~
114         catComment,    // 14   %
115         catInvalid     // 15   <delete>
116 };
117
118 CatCode theCatcode[256];
119
120
121 inline CatCode catcode(unsigned char c)
122 {
123         return theCatcode[c];
124 }
125
126
127 enum {
128         FLAG_BRACE_LAST = 1 << 1,  //  last closing brace ends the parsing
129         FLAG_RIGHT      = 1 << 2,  //  next \\right ends the parsing process
130         FLAG_END        = 1 << 3,  //  next \\end ends the parsing process
131         FLAG_BRACK_LAST = 1 << 4,  //  next closing bracket ends the parsing
132         FLAG_TEXTMODE   = 1 << 5,  //  we are in a box
133         FLAG_ITEM       = 1 << 6,  //  read a (possibly braced token)
134         FLAG_LEAVE      = 1 << 7,  //  leave the loop at the end
135         FLAG_SIMPLE     = 1 << 8,  //  next $ leaves the loop
136         FLAG_EQUATION   = 1 << 9,  //  next \] leaves the loop
137         FLAG_SIMPLE2    = 1 << 10, //  next \) leaves the loop
138         FLAG_OPTION     = 1 << 11  //  read [...] style option
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 & at);
232         ///
233         void parse_into(MathArray & array, unsigned flags, bool mathmode);
234         ///
235         int lineno() const { return lineno_; }
236         ///
237         void putback();
238
239 private:
240         ///
241         void parse_into1(MathGridInset & grid, unsigned flags, bool mathmode, bool numbered);
242         ///
243         void parse_into2(MathAtom & at, unsigned flags, bool mathmode, 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                 error("The input stream is not well...");
358         return tokens_[pos_++].character();
359 }
360
361
362 string Parser::getArg(char left, char right)
363 {
364         skipSpaces();
365
366         string result;
367         char c = getChar();
368
369         if (c != left)
370                 putback();
371         else
372                 while ((c = getChar()) != right && good())
373                         result += c;
374
375         return result;
376 }
377
378
379 void Parser::tokenize(istream & is)
380 {
381         // eat everything up to the next \end_inset or end of stream
382         // and store it in s for further tokenization
383         string s;
384         char c;
385         while (is.get(c)) {
386                 s += c;
387                 if (s.size() >= 10 && s.substr(s.size() - 10) == "\\end_inset") {
388                         s = s.substr(0, s.size() - 10);
389                         break;
390                 }
391         }
392
393         // tokenize buffer
394         tokenize(s);
395 }
396
397
398 void Parser::skipSpaceTokens(istream & is, char c)
399 {
400         // skip trailing spaces
401         while (catcode(c) == catSpace || catcode(c) == catNewline)
402                 if (!is.get(c))
403                         break;
404         //lyxerr << "putting back: " << c << "\n";
405         is.putback(c);
406 }
407
408
409 void Parser::tokenize(string const & buffer)
410 {
411         static bool init_done = false;
412
413         if (!init_done) {
414                 catInit();
415                 init_done = true;
416         }
417
418         istringstream is(buffer.c_str(), ios::in | ios::binary);
419
420         char c;
421         while (is.get(c)) {
422                 //lyxerr << "reading c: " << c << "\n";
423
424                 switch (catcode(c)) {
425                         case catNewline: {
426                                 ++lineno_;
427                                 is.get(c);
428                                 if (catcode(c) == catNewline)
429                                         ; //push_back(Token("par"));
430                                 else {
431                                         push_back(Token(' ', catSpace));
432                                         is.putback(c);
433                                 }
434                                 break;
435                         }
436
437                         case catComment: {
438                                 while (is.get(c) && catcode(c) != catNewline)
439                                         ;
440                                 ++lineno_;
441                                 break;
442                         }
443
444                         case catEscape: {
445                                 is.get(c);
446                                 if (!is) {
447                                         error("unexpected end of input");
448                                 } else {
449                                         string s(1, c);
450                                         if (catcode(c) == catLetter) {
451                                                 // collect letters
452                                                 while (is.get(c) && catcode(c) == catLetter)
453                                                         s += c;
454                                                 skipSpaceTokens(is, c);
455                                         }
456                                         push_back(Token(s));
457                                 }
458                                 break;
459                         }
460
461                         case catSuper:
462                         case catSub: {
463                                 push_back(Token(c, catcode(c)));
464                                 is.get(c);
465                                 skipSpaceTokens(is, c);
466                                 break;
467                         }
468
469                         case catIgnore: {
470                                 lyxerr << "ignoring a char: " << int(c) << "\n";
471                                 break;
472                         }
473
474                         default:
475                                 push_back(Token(c, catcode(c)));
476                 }
477         }
478
479 #ifdef FILEDEBUG
480         dump();
481 #endif
482 }
483
484
485 void Parser::dump() const
486 {
487         lyxerr << "\nTokens: ";
488         for (unsigned i = 0; i < tokens_.size(); ++i) {
489                 if (i == pos_)
490                         lyxerr << " <#> ";
491                 lyxerr << tokens_[i];
492         }
493         lyxerr << " pos: " << pos_ << "\n";
494 }
495
496
497 void Parser::error(string const & msg)
498 {
499         lyxerr << "Line ~" << lineno_ << ": Math parse error: " << msg << endl;
500         dump();
501         //exit(1);
502 }
503
504
505 bool Parser::parse_macro(string & name)
506 {
507         int nargs = 0;
508         name = "{error}";
509         skipSpaces();
510
511         if (nextToken().cs() == "def") {
512
513                 getToken();
514                 name = getToken().cs();
515
516                 string pars;
517                 while (good() && nextToken().cat() != catBegin)
518                         pars += getToken().cs();
519
520                 if (!good()) {
521                         error("bad stream in parse_macro\n");
522                         return false;
523                 }
524
525                 //lyxerr << "read \\def parameter list '" << pars << "'\n";
526                 if (!pars.empty()) {
527                         error("can't handle non-empty parameter lists\n");
528                         return false;
529                 }
530
531         } else if (nextToken().cs() == "newcommand") {
532
533                 // skip the 'newcommand'
534                 getToken();
535
536                 if (getToken().cat() != catBegin) {
537                         error("'{' in \\newcommand expected (1) \n");
538                         return false;
539                 }
540
541                 name = getToken().cs();
542
543                 if (getToken().cat() != catEnd) {
544                         error("'}' in \\newcommand expected\n");
545                         return false;
546                 }
547
548                 string arg  = getArg('[', ']');
549                 if (!arg.empty())
550                         nargs = atoi(arg.c_str());
551
552         } else {
553                 lyxerr << "\\newcommand or \\def  expected\n";
554                 return false;
555         }
556
557
558         if (getToken().cat() != catBegin) {
559                 error("'{' in macro definition expected (2)\n");
560                 return false;
561         }
562
563         MathArray ar1;
564         parse_into(ar1, FLAG_BRACE_LAST, true);
565
566         // we cannot handle recursive stuff at all
567         MathArray test;
568         test.push_back(createMathInset(name));
569         if (ar1.contains(test)) {
570                 error("we cannot handle recursive macros at all.\n");
571                 return false;
572         }
573
574         // is a version for display attached?
575         MathArray ar2;
576         parse_into(ar2, FLAG_ITEM, true);
577
578         MathMacroTable::create(name, nargs, ar1, ar2);
579         return true;
580 }
581
582
583 bool Parser::parse_normal(MathAtom & at)
584 {
585         skipSpaces();
586         MathArray ar;
587         parse_into(ar, false, false);
588         if (ar.size() != 1 || ar.front()->getType() == "none") {
589                 lyxerr << "unusual contents found: " << ar << endl;
590                 at.reset(new MathParInset);
591                 if (at->nargs() > 0)
592                         at->cell(0) = ar;
593                 else
594                         lyxerr << "unusual contents found: " << ar << endl;
595                 return true;
596         }
597         at = ar[0];
598         return true;
599 }
600
601
602 void Parser::parse_into(MathArray & array, unsigned flags, bool mathmode)
603 {
604         MathGridInset grid(1, 1);
605         parse_into1(grid, flags, mathmode, false);
606         array = grid.cell(0);
607 }
608
609
610 void Parser::parse_into2(MathAtom & at, unsigned flags,
611         bool mathmode, bool numbered)
612 {
613         parse_into1(*(at->asGridInset()), flags, mathmode, numbered);
614 }
615
616
617 void Parser::parse_into1(MathGridInset & grid, unsigned flags,
618         bool mathmode, bool numbered)
619 {
620         int  limits = 0;
621         MathGridInset::row_type cellrow = 0;
622         MathGridInset::col_type cellcol = 0;
623         MathArray * cell = &grid.cell(grid.index(cellrow, cellcol));
624
625         if (grid.asHullInset())
626                 grid.asHullInset()->numbered(cellrow, numbered);
627
628         //dump();
629         //lyxerr << "grid: " << grid << endl;
630
631         while (good()) {
632                 Token const & t = getToken();
633
634 #ifdef FILEDEBUG
635                 lyxerr << "t: " << t << " flags: " << flags << "\n";
636                 //cell->dump();
637                 lyxerr << "\n";
638 #endif
639
640                 if (flags & FLAG_ITEM) {
641                         if (t.cat() == catSpace)
642                                 continue;
643
644                         flags &= ~FLAG_ITEM;
645                         if (t.cat() == catBegin) {
646                                 // skip the brace and collect everything to the next matching
647                                 // closing brace
648                                 flags |= FLAG_BRACE_LAST;
649                                 continue;
650                         }
651
652                         // handle only this single token, leave the loop if done
653                         flags |= FLAG_LEAVE;
654                 }
655
656
657                 if (flags & FLAG_OPTION) {
658                         if (t.cat() == catOther && t.character() == '[') {
659                                 // skip the bracket and collect everything to the clsing bracket
660                                 flags |= FLAG_BRACK_LAST;
661                                 continue;
662                         }
663
664                         // no option found, put back token and we are done
665                         putback();
666                         return;
667                 }
668
669                 //
670                 // cat codes
671                 //
672                 if (t.cat() == catMath) {
673                         if (!mathmode) {
674                                 // we are inside some text mode thingy, so opening new math is allowed
675                                 Token const & n = getToken();
676                                 if (n.cat() == catMath) {
677                                         // TeX's $$...$$ syntax for displayed math
678                                         cell->push_back(MathAtom(new MathHullInset("equation")));
679                                         parse_into2(cell->back(), FLAG_SIMPLE, true, false);
680                                         getToken(); // skip the second '$' token
681                                 } else {
682                                         // simple $...$  stuff
683                                         putback();
684                                         cell->push_back(MathAtom(new MathHullInset("simple")));
685                                         parse_into2(cell->back(), FLAG_SIMPLE, true, false);
686                                 }
687                         }
688
689                         else if (flags & FLAG_SIMPLE) {
690                                 // this is the end of the formula
691                                 return;
692                         }
693
694                         else {
695                                 error("something strange in the parser\n");
696                                 break;
697                         }
698                 }
699
700                 else if (t.cat() == catLetter)
701                         cell->push_back(MathAtom(new MathCharInset(t.character())));
702
703                 else if (t.cat() == catSpace && !mathmode)
704                         cell->push_back(MathAtom(new MathCharInset(t.character())));
705
706                 else if (t.cat() == catParameter) {
707                         Token const & n = getToken();
708                         cell->push_back(MathAtom(new MathMacroArgument(n.character()-'0')));
709                 }
710
711                 else if (t.cat() == catBegin) {
712                         MathArray ar;
713                         parse_into(ar, FLAG_BRACE_LAST, mathmode);
714                         // reduce multiple nesting levels to a single one
715                         // this helps to keep the annoyance of  "a choose b"  to a minimum
716                         if (ar.size() && ar.front()->asBraceInset())
717                                 ar = ar.front()->asBraceInset()->cell(0);
718                         cell->push_back(MathAtom(new MathBraceInset));
719                         cell->back()->cell(0).swap(ar);
720                 }
721
722                 else if (t.cat() == catEnd) {
723                         if (flags & FLAG_BRACE_LAST)
724                                 return;
725                         error("found '}' unexpectedly");
726                         //lyx::Assert(0);
727                         //add(cell, '}', LM_TC_TEX);
728                 }
729
730                 else if (t.cat() == catAlign) {
731                         ++cellcol;
732                         //lyxerr << " column now " << cellcol << " max: " << grid.ncols() << "\n";
733                         if (cellcol == grid.ncols()) {
734                                 lyxerr << "adding column " << cellcol << "\n";
735                                 grid.addCol(cellcol - 1);
736                         }
737                         cell = &grid.cell(grid.index(cellrow, cellcol));
738                 }
739
740                 else if (t.cat() == catSuper || t.cat() == catSub) {
741                         bool up = (t.cat() == catSuper);
742                         MathScriptInset * p = 0;
743                         if (cell->size())
744                                 p = cell->back()->asScriptInset();
745                         if (!p || p->has(up)) {
746                                 cell->push_back(MathAtom(new MathScriptInset(up)));
747                                 p = cell->back()->asScriptInset();
748                         }
749                         p->ensure(up);
750                         parse_into(p->cell(up), FLAG_ITEM, mathmode);
751                         p->limits(limits);
752                         limits = 0;
753                 }
754
755                 else if (t.character() == ']' && (flags & FLAG_BRACK_LAST))
756                         return;
757
758                 else if (t.cat() == catOther)
759                         cell->push_back(MathAtom(new MathCharInset(t.character())));
760
761                 //
762                 // control sequences
763                 //
764                 else if (t.cs() == "(") {
765                         cell->push_back(MathAtom(new MathHullInset("simple")));
766                         parse_into2(cell->back(), FLAG_SIMPLE2, true, false);
767                 }
768
769                 else if (t.cs() == "[") {
770                         cell->push_back(MathAtom(new MathHullInset("equation")));
771                         parse_into2(cell->back(), FLAG_EQUATION, true, false);
772                 }
773
774                 else if (t.cs() == "protect")
775                         // ignore \\protect, will hopefully be re-added during output
776                         ;
777
778                 else if (t.cs() == "end") {
779                         if (flags & FLAG_END) {
780                                 // eat environment name
781                                 //string const name =
782                                 getArg('{', '}');
783                                 // FIXME: check that we ended the correct environment
784                                 return;
785                         }
786                         error("found 'end' unexpectedly");
787                 }
788
789                 else if (t.cs() == ")") {
790                         if (flags & FLAG_SIMPLE2)
791                                 return;
792                         error("found '\\)' unexpectedly");
793                 }
794
795                 else if (t.cs() == "]") {
796                         if (flags & FLAG_EQUATION)
797                                 return;
798                         error("found '\\]' unexpectedly");
799                 }
800
801                 else if (t.cs() == "\\") {
802                         grid.vcrskip(LyXLength(getArg('[', ']')), cellrow);
803                         ++cellrow;
804                         cellcol = 0;
805                         if (cellrow == grid.nrows())
806                                 grid.addRow(cellrow - 1);
807                         if (grid.asHullInset())
808                                 grid.asHullInset()->numbered(cellrow, numbered);
809                         cell = &grid.cell(grid.index(cellrow, cellcol));
810                 }
811
812 #if 1
813                 else if (t.cs() == "multicolumn") {
814                         // extract column count and insert dummy cells
815                         MathArray count;
816                         parse_into(count, FLAG_ITEM, mathmode);
817                         int cols = 1;
818                         if (!extractNumber(count, cols)) {
819                                 lyxerr << " can't extract number of cells from " << count << "\n";
820                         }
821                         // resize the table if necessary
822                         for (int i = 0; i < cols; ++i) {
823                                 ++cellcol;
824                                 if (cellcol == grid.ncols()) {
825                                         lyxerr << "adding column " << cellcol << "\n";
826                                         grid.addCol(cellcol - 1);
827                                 }
828                                 cell = &grid.cell(grid.index(cellrow, cellcol));
829                                 // mark this as dummy
830                                 grid.cellinfo(grid.index(cellrow, cellcol)).dummy_ = true;
831                         }
832                         // the last cell is the real thng, not a dummy
833                         grid.cellinfo(grid.index(cellrow, cellcol)).dummy_ = false;
834
835                         // read special alignment
836                         MathArray align;
837                         parse_into(align, FLAG_ITEM, mathmode);
838                         //grid.cellinfo(grid.index(cellrow, cellcol)).align_ = extractString(align);
839
840                         // parse the remaining contents into the "real" cell
841                         parse_into(*cell, FLAG_ITEM, mathmode);
842                 }
843 #endif
844
845                 else if (t.cs() == "limits")
846                         limits = 1;
847
848                 else if (t.cs() == "nolimits")
849                         limits = -1;
850
851                 else if (t.cs() == "nonumber") {
852                         if (grid.asHullInset())
853                                 grid.asHullInset()->numbered(cellrow, false);
854                 }
855
856                 else if (t.cs() == "number") {
857                         if (grid.asHullInset())
858                                 grid.asHullInset()->numbered(cellrow, true);
859                 }
860
861                 else if (t.cs() == "hline") {
862                         if (grid.asHullInset())
863                                 grid.asHullInset()->rowinfo(cellrow + 1);
864                 }
865
866                 else if (t.cs() == "sqrt") {
867                         MathArray ar;
868                         parse_into(ar, FLAG_OPTION, mathmode);
869                         if (ar.size()) {
870                                 cell->push_back(MathAtom(new MathRootInset));
871                                 cell->back()->cell(0) = ar;
872                                 parse_into(cell->back()->cell(1), FLAG_ITEM, mathmode);
873                         } else {
874                                 cell->push_back(MathAtom(new MathSqrtInset));
875                                 parse_into(cell->back()->cell(0), FLAG_ITEM, mathmode);
876                         }
877                 }
878
879                 else if (t.cs() == "ref") {
880                         cell->push_back(MathAtom(new RefInset));
881                         parse_into(cell->back()->cell(1), FLAG_OPTION, mathmode);
882                         parse_into(cell->back()->cell(0), FLAG_ITEM, mathmode);
883                 }
884
885                 else if (t.cs() == "left") {
886                         string l = getToken().asString();
887                         MathArray ar;
888                         parse_into(ar, FLAG_RIGHT, mathmode);
889                         string r = getToken().asString();
890                         cell->push_back(MathAtom(new MathDelimInset(l, r, ar)));
891                 }
892
893                 else if (t.cs() == "right") {
894                         if (flags & FLAG_RIGHT)
895                                 return;
896                         //lyxerr << "got so far: '" << cell << "'\n";
897                         error("Unmatched right delimiter");
898                         return;
899                 }
900
901                 else if (t.cs() == "begin") {
902                         string const name = getArg('{', '}');
903                         if (name == "array" || name == "subarray") {
904                                 string const valign = getArg('[', ']') + 'c';
905                                 string const halign = getArg('{', '}');
906                                 cell->push_back(MathAtom(new MathArrayInset(name, valign[0], halign)));
907                                 parse_into2(cell->back(), FLAG_END, mathmode, false);
908                         }
909
910                         else if (name == "split" || name == "cases" ||
911                                          name == "gathered" || name == "aligned") {
912                                 cell->push_back(createMathInset(name));
913                                 parse_into2(cell->back(), FLAG_END, mathmode, false);
914                         }
915
916                         else if (name == "math") {
917                                 cell->push_back(MathAtom(new MathHullInset("simple")));
918                                 parse_into2(cell->back(), FLAG_SIMPLE, true, true);
919                         }
920
921                         else if (name == "equation" || name == "equation*"
922                                         || name == "displaymath") {
923                                 cell->push_back(MathAtom(new MathHullInset("equation")));
924                                 parse_into2(cell->back(), FLAG_END, true, (name == "equation"));
925                         }
926
927                         else if (name == "eqnarray" || name == "eqnarray*") {
928                                 cell->push_back(MathAtom(new MathHullInset("eqnarray")));
929                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
930                         }
931
932                         else if (name == "align" || name == "align*") {
933                                 cell->push_back(MathAtom(new MathHullInset("align")));
934                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
935                         }
936
937                         else if (name == "alignat" || name == "alignat*") {
938                                 // ignore this for a while
939                                 getArg('{', '}');
940                                 cell->push_back(MathAtom(new MathHullInset("alignat")));
941                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
942                         }
943
944                         else if (name == "xalignat" || name == "xalignat*") {
945                                 // ignore this for a while
946                                 getArg('{', '}');
947                                 cell->push_back(MathAtom(new MathHullInset("xalignat")));
948                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
949                         }
950
951                         else if (name == "xxalignat") {
952                                 // ignore this for a while
953                                 getArg('{', '}');
954                                 cell->push_back(MathAtom(new MathHullInset("xxalignat")));
955                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
956                         }
957
958                         else if (name == "multline" || name == "multline*") {
959                                 cell->push_back(MathAtom(new MathHullInset("multline")));
960                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
961                         }
962
963                         else if (name == "gather" || name == "gather*") {
964                                 cell->push_back(MathAtom(new MathHullInset("gather")));
965                                 parse_into2(cell->back(), FLAG_END, true, !stared(name));
966                         }
967
968                         else if (latexkeys const * l = in_word_set(name)) {
969                                 if (l->inset == "matrix") {
970                                         cell->push_back(createMathInset(name));
971                                         parse_into2(cell->back(), FLAG_END, mathmode, false);
972                                 }
973                         }
974
975                         else {
976                                 // lyxerr << "unknow math inset begin '" << name << "'\n";
977                                 // create generic environment inset
978                                 cell->push_back(MathAtom(new MathEnvInset(name)));
979                                 parse_into(cell->back()->cell(0), FLAG_END, mathmode);
980                         }
981                 }
982
983                 else if (t.cs() == "kern") {
984 #ifdef WITH_WARNINGS
985 #warning A hack...
986 #endif
987                         string s;
988                         while (1) {
989                                 Token const & t = getToken();
990                                 if (!good()) {
991                                         putback();
992                                         break;
993                                 }
994                                 s += t.character();
995                                 if (isValidLength(s))
996                                         break;
997                         }
998                         cell->push_back(MathAtom(new MathKernInset(s)));
999                 }
1000
1001                 else if (t.cs() == "label") {
1002                         if (grid.asHullInset())
1003                                 grid.asHullInset()->label(cellrow, getArg('{', '}'));
1004                 }
1005
1006                 else if (t.cs() == "choose" || t.cs() == "over" || t.cs() == "atop") {
1007                         MathAtom p = createMathInset(t.cs());
1008                         cell->swap(p->cell(0));
1009                         parse_into(p->cell(1), flags, mathmode);
1010                         cell->push_back(p);
1011                         return;
1012                 }
1013
1014                 else if (t.cs() == "substack") {
1015                         cell->push_back(createMathInset(t.cs()));
1016                         parse_into2(cell->back(), FLAG_ITEM, mathmode, false);
1017                 }
1018
1019                 else if (t.cs() == "xymatrix") {
1020                         cell->push_back(createMathInset(t.cs()));
1021                         parse_into2(cell->back(), FLAG_ITEM, mathmode, false);
1022                 }
1023
1024 #if 0
1025                 // Disabled
1026                 else if (1 && t.cs() == "ar") {
1027                         MathXYArrowInset * p = new MathXYArrowInset;
1028                         // try to read target
1029                         parse_into(p->cell(0), FLAG_OTPTION, mathmode);
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 == "mbox") {
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 if (l->inset == "parbox") {
1074                                         // read optional positioning and width
1075                                         MathArray pos, width;
1076                                         parse_into(pos, FLAG_OPTION, false);
1077                                         parse_into(width, FLAG_ITEM, false);
1078                                         cell->push_back(createMathInset(t.cs()));
1079                                         parse_into(cell->back()->cell(0), FLAG_ITEM, false);
1080                                         cell->back()->asParboxInset()->setPosition(asString(pos));
1081                                         cell->back()->asParboxInset()->setWidth(asString(width));
1082                                 }
1083
1084                                 else {
1085                                         MathAtom p = createMathInset(t.cs());
1086                                         for (MathInset::idx_type i = 0; i < p->nargs(); ++i)
1087                                                 parse_into(p->cell(i), FLAG_ITEM, l->extra == "mathmode");
1088                                         cell->push_back(p);
1089                                 }
1090                         }
1091
1092                         else {
1093                                 MathAtom p = createMathInset(t.cs());
1094                                 for (MathInset::idx_type i = 0; i < p->nargs(); ++i)
1095                                         parse_into(p->cell(i), FLAG_ITEM, mathmode);
1096                                 cell->push_back(p);
1097                         }
1098                 }
1099
1100
1101                 if (flags & FLAG_LEAVE) {
1102                         flags &= ~FLAG_LEAVE;
1103                         break;
1104                 }
1105         }
1106 }
1107
1108
1109
1110 } // anonymous namespace
1111
1112
1113 void mathed_parse_cell(MathArray & ar, string const & str)
1114 {
1115         istringstream is(str.c_str());
1116         mathed_parse_cell(ar, is);
1117 }
1118
1119
1120 void mathed_parse_cell(MathArray & ar, istream & is)
1121 {
1122         Parser(is).parse_into(ar, 0, true);
1123 }
1124
1125
1126
1127 bool mathed_parse_macro(string & name, string const & str)
1128 {
1129         istringstream is(str.c_str());
1130         return Parser(is).parse_macro(name);
1131 }
1132
1133
1134 bool mathed_parse_macro(string & name, istream & is)
1135 {
1136         return Parser(is).parse_macro(name);
1137 }
1138
1139
1140 bool mathed_parse_macro(string & name, LyXLex & lex)
1141 {
1142         return Parser(lex).parse_macro(name);
1143 }
1144
1145
1146
1147 bool mathed_parse_normal(MathAtom & t, string const & str)
1148 {
1149         istringstream is(str.c_str());
1150         return Parser(is).parse_normal(t);
1151 }
1152
1153
1154 bool mathed_parse_normal(MathAtom & t, istream & is)
1155 {
1156         return Parser(is).parse_normal(t);
1157 }
1158
1159
1160 bool mathed_parse_normal(MathAtom & t, LyXLex & lex)
1161 {
1162         return Parser(lex).parse_normal(t);
1163 }