]> git.lyx.org Git - features.git/blob - src/mathed/math_parser.C
remove unneeded member
[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         void tokenize(istream & is);
254         ///
255         void tokenize(string const & s);
256         ///
257         void skipSpaceTokens(istream & is, char c);
258         ///
259         void push_back(Token const & t);
260         ///
261         void pop_back();
262         ///
263         Token const & prevToken() const;
264         ///
265         Token const & nextToken() const;
266         ///
267         Token const & getToken();
268         /// skips spaces if any
269         void skipSpaces();
270         ///
271         void lex(string const & s);
272         ///
273         bool good() const;
274
275         ///
276         int lineno_;
277         ///
278         vector<Token> tokens_;
279         ///
280         unsigned pos_;
281 };
282
283
284 Parser::Parser(LyXLex & lexer)
285         : lineno_(lexer.getLineNo()), pos_(0)
286 {
287         tokenize(lexer.getStream());
288         lexer.eatLine();
289 }
290
291
292 Parser::Parser(istream & is)
293         : lineno_(0), pos_(0)
294 {
295         tokenize(is);
296 }
297
298
299 void Parser::push_back(Token const & t)
300 {
301         tokens_.push_back(t);
302 }
303
304
305 void Parser::pop_back()
306 {
307         tokens_.pop_back();
308 }
309
310
311 Token const & Parser::prevToken() const
312 {
313         static const Token dummy;
314         return pos_ > 0 ? tokens_[pos_ - 1] : dummy;
315 }
316
317
318 Token const & Parser::nextToken() const
319 {
320         static const Token dummy;
321         return good() ? tokens_[pos_] : dummy;
322 }
323
324
325 Token const & Parser::getToken()
326 {
327         static const Token dummy;
328         //lyxerr << "looking at token " << tokens_[pos_] << " pos: " << pos_ << '\n';
329         return good() ? tokens_[pos_++] : dummy;
330 }
331
332
333 void Parser::skipSpaces()
334 {
335         while (nextToken().cat() == catSpace)
336                 getToken();
337 }
338
339
340 void Parser::putback()
341 {
342         --pos_;
343 }
344
345
346 bool Parser::good() const
347 {
348         return pos_ < tokens_.size();
349 }
350
351
352 char Parser::getChar()
353 {
354         if (!good())
355                 error("The input stream is not well...");
356         return tokens_[pos_++].character();
357 }
358
359
360 string Parser::getArg(char left, char right)
361 {
362         skipSpaces();
363
364         string result;
365         char c = getChar();
366
367         if (c != left)
368                 putback();
369         else
370                 while ((c = getChar()) != right && good())
371                         result += c;
372
373         return result;
374 }
375
376
377 void Parser::tokenize(istream & is)
378 {
379         // eat everything up to the next \end_inset or end of stream
380         // and store it in s for further tokenization
381         string s;
382         char c;
383         while (is.get(c)) {
384                 s += c;
385                 if (s.size() >= 10 && s.substr(s.size() - 10) == "\\end_inset") {
386                         s = s.substr(0, s.size() - 10);
387                         break;
388                 }
389         }
390
391         // tokenize buffer
392         tokenize(s);
393 }
394
395
396 void Parser::skipSpaceTokens(istream & is, char c)
397 {
398         // skip trailing spaces
399         while (catcode(c) == catSpace || catcode(c) == catNewline)
400                 if (!is.get(c))
401                         break;
402         //lyxerr << "putting back: " << c << "\n";
403         is.putback(c);
404 }
405
406
407 void Parser::tokenize(string const & buffer)
408 {
409         static bool init_done = false;
410
411         if (!init_done) {
412                 catInit();
413                 init_done = true;
414         }
415
416         istringstream is(buffer.c_str(), ios::in | ios::binary);
417
418         char c;
419         while (is.get(c)) {
420                 //lyxerr << "reading c: " << c << "\n";
421
422                 switch (catcode(c)) {
423                         case catNewline: {
424                                 ++lineno_;
425                                 is.get(c);
426                                 if (catcode(c) == catNewline)
427                                         ; //push_back(Token("par"));
428                                 else {
429                                         push_back(Token(' ', catSpace));
430                                         is.putback(c);
431                                 }
432                                 break;
433                         }
434
435                         case catComment: {
436                                 while (is.get(c) && catcode(c) != catNewline)
437                                         ;
438                                 ++lineno_;
439                                 break;
440                         }
441
442                         case catEscape: {
443                                 is.get(c);
444                                 if (!is) {
445                                         error("unexpected end of input");
446                                 } else {
447                                         string s(1, c);
448                                         if (catcode(c) == catLetter) {
449                                                 // collect letters
450                                                 while (is.get(c) && catcode(c) == catLetter)
451                                                         s += c;
452                                                 skipSpaceTokens(is, c);
453                                         }
454                                         push_back(Token(s));
455                                 }
456                                 break;
457                         }
458
459                         case catSuper:
460                         case catSub: {
461                                 push_back(Token(c, catcode(c)));
462                                 is.get(c);
463                                 skipSpaceTokens(is, c);
464                                 break;
465                         }
466
467                         case catIgnore: {
468                                 lyxerr << "ignoring a char: " << int(c) << "\n";
469                                 break;
470                         }
471
472                         default:
473                                 push_back(Token(c, catcode(c)));
474                 }
475         }
476
477 #ifdef FILEDEBUG
478         dump();
479 #endif
480 }
481
482
483 void Parser::dump() const
484 {
485         lyxerr << "\nTokens: ";
486         for (unsigned i = 0; i < tokens_.size(); ++i) {
487                 if (i == pos_)
488                         lyxerr << " <#> ";
489                 lyxerr << tokens_[i];
490         }
491         lyxerr << " pos: " << pos_ << "\n";
492 }
493
494
495 void Parser::error(string const & msg)
496 {
497         lyxerr << "Line ~" << lineno_ << ": Math parse error: " << msg << endl;
498         dump();
499         //exit(1);
500 }
501
502
503 bool Parser::parse_macro(string & name)
504 {
505         int nargs = 0;
506         name = "{error}";
507         skipSpaces();
508
509         if (nextToken().cs() == "def") {
510
511                 getToken();
512                 name = getToken().cs();
513
514                 string pars;
515                 while (good() && nextToken().cat() != catBegin)
516                         pars += getToken().cs();
517
518                 if (!good()) {
519                         error("bad stream in parse_macro\n");
520                         return false;
521                 }
522
523                 //lyxerr << "read \\def parameter list '" << pars << "'\n";
524                 if (!pars.empty()) {
525                         error("can't handle non-empty parameter lists\n");
526                         return false;
527                 }
528
529         } else if (nextToken().cs() == "newcommand") {
530
531                 // skip the 'newcommand'
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("'}' in \\newcommand 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 closing 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                         // do not create a BraceInset if they were written by LyX
713                         // this helps to keep the annoyance of  "a choose b"  to a minimum
714                         if (ar.size() == 1 && ar[0]->extraBraces())
715                                 cell->push_back(ar);
716                         else
717                                 cell->push_back(MathAtom(new MathBraceInset(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 if (latexkeys const * l = in_word_set(name)) {
967                                 if (l->inset == "matrix") {
968                                         cell->push_back(createMathInset(name));
969                                         parse_into2(cell->back(), FLAG_END, mathmode, false);
970                                 }
971                         }
972
973                         else {
974                                 // lyxerr << "unknow math inset begin '" << name << "'\n";
975                                 // create generic environment inset
976                                 cell->push_back(MathAtom(new MathEnvInset(name)));
977                                 parse_into(cell->back()->cell(0), FLAG_END, mathmode);
978                         }
979                 }
980
981                 else if (t.cs() == "kern") {
982 #ifdef WITH_WARNINGS
983 #warning A hack...
984 #endif
985                         string s;
986                         while (1) {
987                                 Token const & t = getToken();
988                                 if (!good()) {
989                                         putback();
990                                         break;
991                                 }
992                                 s += t.character();
993                                 if (isValidLength(s))
994                                         break;
995                         }
996                         cell->push_back(MathAtom(new MathKernInset(s)));
997                 }
998
999                 else if (t.cs() == "label") {
1000                         MathArray ar;
1001                         parse_into(ar, FLAG_ITEM, false);
1002                         if (grid.asHullInset()) {
1003                                 grid.asHullInset()->label(cellrow, asString(ar));
1004                         } else {
1005                                 cell->push_back(createMathInset(t.cs()));
1006                                 cell->push_back(MathAtom(new MathBraceInset(ar)));
1007                         }
1008                 }
1009
1010                 else if (t.cs() == "choose" || t.cs() == "over" || t.cs() == "atop") {
1011                         MathAtom p = createMathInset(t.cs());
1012                         cell->swap(p->cell(0));
1013                         parse_into(p->cell(1), flags, mathmode);
1014                         cell->push_back(p);
1015                         return;
1016                 }
1017
1018                 else if (t.cs() == "substack") {
1019                         cell->push_back(createMathInset(t.cs()));
1020                         parse_into2(cell->back(), FLAG_ITEM, mathmode, false);
1021                 }
1022
1023                 else if (t.cs() == "xymatrix") {
1024                         cell->push_back(createMathInset(t.cs()));
1025                         parse_into2(cell->back(), FLAG_ITEM, mathmode, false);
1026                 }
1027
1028 #if 0
1029                 // Disabled
1030                 else if (1 && t.cs() == "ar") {
1031                         MathXYArrowInset * p = new MathXYArrowInset;
1032                         // try to read target
1033                         parse_into(p->cell(0), FLAG_OTPTION, mathmode);
1034                         // try to read label
1035                         if (nextToken().cat() == catSuper || nextToken().cat() == catSub) {
1036                                 p->up_ = nextToken().cat() == catSuper;
1037                                 getToken();
1038                                 parse_into(p->cell(1), FLAG_ITEM, mathmode);
1039                                 //lyxerr << "read label: " << p->cell(1) << "\n";
1040                         }
1041
1042                         cell->push_back(MathAtom(p));
1043                         //lyxerr << "read cell: " << cell << "\n";
1044                 }
1045 #endif
1046
1047                 else if (t.cs().size()) {
1048                         latexkeys const * l = in_word_set(t.cs());
1049                         if (l) {
1050                                 if (l->inset == "font") {
1051                                         lyxerr << "starting font " << t.cs() << "\n";
1052                                         MathAtom p = createMathInset(t.cs());
1053                                         bool textmode = (t.cs()[0] == 't');
1054                                         parse_into(p->cell(0), FLAG_ITEM, !textmode);
1055                                         cell->push_back(p);
1056                                         //lyxerr << "ending font\n";
1057                                 }
1058
1059                                 else if (l->inset == "oldfont") {
1060                                         cell->push_back(createMathInset(t.cs()));
1061                                         parse_into(cell->back()->cell(0), flags, l->extra == "mathmode");
1062                                         return;
1063                                 }
1064
1065                                 else if (l->inset == "style") {
1066                                         cell->push_back(createMathInset(t.cs()));
1067                                         parse_into(cell->back()->cell(0), flags, mathmode);
1068                                         return;
1069                                 }
1070
1071                                 else if (l->inset == "parbox") {
1072                                         // read optional positioning and width
1073                                         MathArray pos, width;
1074                                         parse_into(pos, FLAG_OPTION, false);
1075                                         parse_into(width, FLAG_ITEM, false);
1076                                         cell->push_back(createMathInset(t.cs()));
1077                                         parse_into(cell->back()->cell(0), FLAG_ITEM, false);
1078                                         cell->back()->asParboxInset()->setPosition(asString(pos));
1079                                         cell->back()->asParboxInset()->setWidth(asString(width));
1080                                 }
1081
1082                                 else {
1083                                         MathAtom p = createMathInset(t.cs());
1084                                         for (MathInset::idx_type i = 0; i < p->nargs(); ++i)
1085                                                 parse_into(p->cell(i), FLAG_ITEM, l->extra != "forcetext");
1086                                         cell->push_back(p);
1087                                 }
1088                         }
1089
1090                         else {
1091                                 MathAtom p = createMathInset(t.cs());
1092                                 for (MathInset::idx_type i = 0; i < p->nargs(); ++i)
1093                                         parse_into(p->cell(i), FLAG_ITEM, mathmode);
1094                                 cell->push_back(p);
1095                         }
1096                 }
1097
1098
1099                 if (flags & FLAG_LEAVE) {
1100                         flags &= ~FLAG_LEAVE;
1101                         break;
1102                 }
1103         }
1104 }
1105
1106
1107
1108 } // anonymous namespace
1109
1110
1111 void mathed_parse_cell(MathArray & ar, string const & str)
1112 {
1113         istringstream is(str.c_str());
1114         mathed_parse_cell(ar, is);
1115 }
1116
1117
1118 void mathed_parse_cell(MathArray & ar, istream & is)
1119 {
1120         Parser(is).parse_into(ar, 0, true);
1121 }
1122
1123
1124
1125 bool mathed_parse_macro(string & name, string const & str)
1126 {
1127         istringstream is(str.c_str());
1128         return Parser(is).parse_macro(name);
1129 }
1130
1131
1132 bool mathed_parse_macro(string & name, istream & is)
1133 {
1134         return Parser(is).parse_macro(name);
1135 }
1136
1137
1138 bool mathed_parse_macro(string & name, LyXLex & lex)
1139 {
1140         return Parser(lex).parse_macro(name);
1141 }
1142
1143
1144
1145 bool mathed_parse_normal(MathAtom & t, string const & str)
1146 {
1147         istringstream is(str.c_str());
1148         return Parser(is).parse_normal(t);
1149 }
1150
1151
1152 bool mathed_parse_normal(MathAtom & t, istream & is)
1153 {
1154         return Parser(is).parse_normal(t);
1155 }
1156
1157
1158 bool mathed_parse_normal(MathAtom & t, LyXLex & lex)
1159 {
1160         return Parser(lex).parse_normal(t);
1161 }