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