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