]> git.lyx.org Git - features.git/blob - src/mathed/math_parser.C
small bugfixes, new inset for comments and the main part is moving lfun
[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_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         MathArray parse(unsigned flags, mode_type mode);
236         ///
237         int lineno() const { return lineno_; }
238         ///
239         void putback();
240
241 private:
242         ///
243         void parse1(MathGridInset & grid, unsigned flags, mode_type mode,
244                 bool numbered);
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);
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                         cell->push_back(MathAtom(new MathCharInset(t.character())));
653
654                 else if (t.cat() == catNewline && mode != MathInset::MATH_MODE)
655                         cell->push_back(MathAtom(new MathCharInset(t.character())));
656
657                 else if (t.cat() == catParameter) {
658                         Token const & n = getToken();
659                         cell->push_back(MathAtom(new MathMacroArgument(n.character()-'0')));
660                 }
661
662                 else if (t.cat() == catActive)
663                         cell->push_back(MathAtom(new MathCharInset(t.character())));
664
665                 else if (t.cat() == catBegin) {
666                         MathArray ar;
667                         parse(ar, FLAG_BRACE_LAST, mode);
668                         // do not create a BraceInset if they were written by LyX
669                         // this helps to keep the annoyance of  "a choose b"  to a minimum
670                         if (ar.size() == 1 && ar[0]->extraBraces())
671                                 cell->append(ar);
672                         else
673                                 cell->push_back(MathAtom(new MathBraceInset(ar)));
674                 }
675
676                 else if (t.cat() == catEnd) {
677                         if (flags & FLAG_BRACE_LAST)
678                                 return;
679                         error("found '}' unexpectedly");
680                         //lyx::Assert(0);
681                         //add(cell, '}', LM_TC_TEX);
682                 }
683
684                 else if (t.cat() == catAlign) {
685                         ++cellcol;
686                         //lyxerr << " column now " << cellcol << " max: " << grid.ncols() << "\n";
687                         if (cellcol == grid.ncols()) {
688                                 lyxerr << "adding column " << cellcol << "\n";
689                                 grid.addCol(cellcol - 1);
690                         }
691                         cell = &grid.cell(grid.index(cellrow, cellcol));
692                 }
693
694                 else if (t.cat() == catSuper || t.cat() == catSub) {
695                         if (mode == MathInset::VERBATIM_MODE)
696                                 cell->push_back(MathAtom(new MathStringInset(t.asString())));
697                         else {
698                                 bool up = (t.cat() == catSuper);
699                                 // we need no new script inset if the last thing was a scriptinset,
700                                 // which has that script already not the same script already
701                                 if (!cell->size())
702                                         cell->push_back(MathAtom(new MathScriptInset(up)));
703                                 else if (cell->back()->asScriptInset() &&
704                                                 !cell->back()->asScriptInset()->has(up))
705                                         cell->back().nucleus()->asScriptInset()->ensure(up);
706                                 else if (cell->back()->asScriptInset())
707                                         cell->push_back(MathAtom(new MathScriptInset(up)));
708                                 else
709                                         cell->back() = MathAtom(new MathScriptInset(cell->back(), up));
710                                 MathScriptInset * p = cell->back().nucleus()->asScriptInset();
711                                 parse(p->cell(up), FLAG_ITEM, mode);
712                                 p->limits(limits);
713                                 limits = 0;
714                                 // special handling of {}-bases
715                                 // is this always correct?
716                                 if (p->nuc().size() == 1 && p->nuc().back()->asNestInset() &&
717                                                 p->nuc().back()->extraBraces())
718                                         p->nuc() = p->nuc().back()->asNestInset()->cell(0);
719                         }
720                 }
721
722                 else if (t.character() == ']' && (flags & FLAG_BRACK_LAST)) {
723                         lyxerr << "finished reading option\n";
724                         return;
725                 }
726
727                 else if (t.cat() == catOther)
728                         cell->push_back(MathAtom(new MathCharInset(t.character())));
729
730                 else if (t.cat() == catComment) {
731                         string s;
732                         while (good()) {
733                                 Token const & t = getToken();
734                                 if (t.cat() == catNewline)
735                                         break;
736                                 s += t.asString();
737                         }
738                         cell->push_back(MathAtom(new MathCommentInset(s)));
739                         skipSpaces();
740                 }
741
742                 //
743                 // control sequences
744                 //
745
746                 else if (t.cs() == "lyxlock") {
747                         if (cell->size())
748                                 cell->back().nucleus()->lock(true);
749                 }
750
751                 else if (t.cs() == "def" || t.cs() == "newcommand") {
752                         string name;
753                         int nargs = 0;
754                         if (t.cs() == "def") {
755                                 // get name
756                                 name = getToken().cs();
757
758                                 // read parameter
759                                 string pars;
760                                 while (good() && nextToken().cat() != catBegin) {
761                                         pars += getToken().cs();
762                                         ++nargs;
763                                 }
764                                 nargs /= 2;
765                                 //lyxerr << "read \\def parameter list '" << pars << "'\n";
766
767                         } else { // t.cs() == "newcommand"
768
769                                 if (getToken().cat() != catBegin) {
770                                         error("'{' in \\newcommand expected (1) \n");
771                                         return;
772                                 }
773
774                                 name = getToken().cs();
775
776                                 if (getToken().cat() != catEnd) {
777                                         error("'}' in \\newcommand expected\n");
778                                         return;
779                                 }
780
781                                 string arg  = getArg('[', ']');
782                                 if (!arg.empty())
783                                         nargs = atoi(arg.c_str());
784
785                         }
786
787                         MathArray ar1;
788                         parse(ar1, FLAG_ITEM, MathInset::UNDECIDED_MODE);
789
790                         // we cannot handle recursive stuff at all
791                         //MathArray test;
792                         //test.push_back(createMathInset(name));
793                         //if (ar1.contains(test)) {
794                         //      error("we cannot handle recursive macros at all.\n");
795                         //      return;
796                         //}
797
798                         // is a version for display attached?
799                         skipSpaces();
800                         MathArray ar2;
801                         if (nextToken().cat() == catBegin) {
802                                 parse(ar2, FLAG_ITEM, MathInset::MATH_MODE);
803                         }
804
805                         cell->push_back(MathAtom(new MathMacroTemplate(name, nargs, ar1, ar2)));
806                 }
807
808                 else if (t.cs() == "(") {
809                         cell->push_back(MathAtom(new MathHullInset("simple")));
810                         parse2(cell->back(), FLAG_SIMPLE2, MathInset::MATH_MODE, false);
811                 }
812
813                 else if (t.cs() == "[") {
814                         cell->push_back(MathAtom(new MathHullInset("equation")));
815                         parse2(cell->back(), FLAG_EQUATION, MathInset::MATH_MODE, false);
816                 }
817
818                 else if (t.cs() == "protect")
819                         // ignore \\protect, will hopefully be re-added during output
820                         ;
821
822                 else if (t.cs() == "end") {
823                         if (flags & FLAG_END) {
824                                 // eat environment name
825                                 //string const name =
826                                 getArg('{', '}');
827                                 // FIXME: check that we ended the correct environment
828                                 return;
829                         }
830                         error("found 'end' unexpectedly");
831                 }
832
833                 else if (t.cs() == ")") {
834                         if (flags & FLAG_SIMPLE2)
835                                 return;
836                         error("found '\\)' unexpectedly");
837                 }
838
839                 else if (t.cs() == "]") {
840                         if (flags & FLAG_EQUATION)
841                                 return;
842                         error("found '\\]' unexpectedly");
843                 }
844
845                 else if (t.cs() == "\\") {
846                         grid.vcrskip(LyXLength(getArg('[', ']')), cellrow);
847                         ++cellrow;
848                         cellcol = 0;
849                         if (cellrow == grid.nrows())
850                                 grid.addRow(cellrow - 1);
851                         if (grid.asHullInset())
852                                 grid.asHullInset()->numbered(cellrow, numbered);
853                         cell = &grid.cell(grid.index(cellrow, cellcol));
854                 }
855
856 #if 1
857                 else if (t.cs() == "multicolumn") {
858                         // extract column count and insert dummy cells
859                         MathArray count;
860                         parse(count, FLAG_ITEM, mode);
861                         int cols = 1;
862                         if (!extractNumber(count, cols)) {
863                                 lyxerr << " can't extract number of cells from " << count << "\n";
864                         }
865                         // resize the table if necessary
866                         for (int i = 0; i < cols; ++i) {
867                                 ++cellcol;
868                                 if (cellcol == grid.ncols()) {
869                                         lyxerr << "adding column " << cellcol << "\n";
870                                         grid.addCol(cellcol - 1);
871                                 }
872                                 cell = &grid.cell(grid.index(cellrow, cellcol));
873                                 // mark this as dummy
874                                 grid.cellinfo(grid.index(cellrow, cellcol)).dummy_ = true;
875                         }
876                         // the last cell is the real thng, not a dummy
877                         grid.cellinfo(grid.index(cellrow, cellcol)).dummy_ = false;
878
879                         // read special alignment
880                         MathArray align;
881                         parse(align, FLAG_ITEM, mode);
882                         //grid.cellinfo(grid.index(cellrow, cellcol)).align_ = extractString(align);
883
884                         // parse the remaining contents into the "real" cell
885                         parse(*cell, FLAG_ITEM, mode);
886                 }
887 #endif
888
889                 else if (t.cs() == "limits")
890                         limits = 1;
891
892                 else if (t.cs() == "nolimits")
893                         limits = -1;
894
895                 else if (t.cs() == "nonumber") {
896                         if (grid.asHullInset())
897                                 grid.asHullInset()->numbered(cellrow, false);
898                 }
899
900                 else if (t.cs() == "number") {
901                         if (grid.asHullInset())
902                                 grid.asHullInset()->numbered(cellrow, true);
903                 }
904
905                 else if (t.cs() == "hline") {
906                         if (grid.asHullInset())
907                                 grid.asHullInset()->rowinfo(cellrow + 1);
908                 }
909
910                 else if (t.cs() == "sqrt") {
911                         MathArray ar;
912                         parse(ar, FLAG_OPTION, mode);
913                         if (ar.size()) {
914                                 cell->push_back(MathAtom(new MathRootInset));
915                                 cell->back().nucleus()->cell(0) = ar;
916                                 parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode);
917                         } else {
918                                 cell->push_back(MathAtom(new MathSqrtInset));
919                                 parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
920                         }
921                 }
922
923                 else if (t.cs() == "ref") {
924                         cell->push_back(MathAtom(new RefInset));
925                         parse(cell->back().nucleus()->cell(1), FLAG_OPTION, mode);
926                         parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
927                 }
928
929                 else if (t.cs() == "left") {
930                         string l = getToken().asString();
931                         MathArray ar;
932                         parse(ar, FLAG_RIGHT, mode);
933                         string r = getToken().asString();
934                         cell->push_back(MathAtom(new MathDelimInset(l, r, ar)));
935                 }
936
937                 else if (t.cs() == "right") {
938                         if (flags & FLAG_RIGHT)
939                                 return;
940                         //lyxerr << "got so far: '" << cell << "'\n";
941                         error("Unmatched right delimiter");
942                         return;
943                 }
944
945                 else if (t.cs() == "begin") {
946                         string const name = getArg('{', '}');
947
948                         if (name == "array" || name == "subarray") {
949                                 string const valign =
950                                         asString(parse(FLAG_OPTION, MathInset::VERBATIM_MODE)) + 'c';
951                                 string const halign =
952                                         asString(parse(FLAG_ITEM, MathInset::VERBATIM_MODE));
953                                 cell->push_back(MathAtom(new MathArrayInset(name, valign[0], halign)));
954                                 parse2(cell->back(), FLAG_END, mode, false);
955                         }
956
957                         else if (name == "split" || name == "cases" ||
958                                          name == "gathered" || name == "aligned") {
959                                 cell->push_back(createMathInset(name));
960                                 parse2(cell->back(), FLAG_END, mode, false);
961                         }
962
963                         else if (name == "math") {
964                                 cell->push_back(MathAtom(new MathHullInset("simple")));
965                                 parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, true);
966                         }
967
968                         else if (name == "equation" || name == "equation*"
969                                         || name == "displaymath") {
970                                 cell->push_back(MathAtom(new MathHullInset("equation")));
971                                 parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, (name == "equation"));
972                         }
973
974                         else if (name == "eqnarray" || name == "eqnarray*") {
975                                 cell->push_back(MathAtom(new MathHullInset("eqnarray")));
976                                 parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name));
977                         }
978
979                         else if (name == "align" || name == "align*") {
980                                 cell->push_back(MathAtom(new MathHullInset("align")));
981                                 parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name));
982                         }
983
984                         else if (name == "alignat" || name == "alignat*") {
985                                 // ignore this for a while
986                                 getArg('{', '}');
987                                 cell->push_back(MathAtom(new MathHullInset("alignat")));
988                                 parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name));
989                         }
990
991                         else if (name == "xalignat" || name == "xalignat*") {
992                                 // ignore this for a while
993                                 getArg('{', '}');
994                                 cell->push_back(MathAtom(new MathHullInset("xalignat")));
995                                 parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name));
996                         }
997
998                         else if (name == "xxalignat") {
999                                 // ignore this for a while
1000                                 getArg('{', '}');
1001                                 cell->push_back(MathAtom(new MathHullInset("xxalignat")));
1002                                 parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name));
1003                         }
1004
1005                         else if (name == "multline" || name == "multline*") {
1006                                 cell->push_back(MathAtom(new MathHullInset("multline")));
1007                                 parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name));
1008                         }
1009
1010                         else if (name == "gather" || name == "gather*") {
1011                                 cell->push_back(MathAtom(new MathHullInset("gather")));
1012                                 parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name));
1013                         }
1014
1015                         else if (latexkeys const * l = in_word_set(name)) {
1016                                 if (l->inset == "matrix") {
1017                                         cell->push_back(createMathInset(name));
1018                                         parse2(cell->back(), FLAG_END, mode, false);
1019                                 }
1020                         }
1021
1022                         else {
1023                                 // lyxerr << "unknow math inset begin '" << name << "'\n";
1024                                 // create generic environment inset
1025                                 cell->push_back(MathAtom(new MathEnvInset(name)));
1026                                 parse(cell->back().nucleus()->cell(0), FLAG_END, mode);
1027                         }
1028                 }
1029
1030                 else if (t.cs() == "kern") {
1031 #ifdef WITH_WARNINGS
1032 #warning A hack...
1033 #endif
1034                         string s;
1035                         while (1) {
1036                                 Token const & t = getToken();
1037                                 if (!good()) {
1038                                         putback();
1039                                         break;
1040                                 }
1041                                 s += t.character();
1042                                 if (isValidLength(s))
1043                                         break;
1044                         }
1045                         cell->push_back(MathAtom(new MathKernInset(s)));
1046                 }
1047
1048                 else if (t.cs() == "label") {
1049                         MathArray ar = parse(FLAG_ITEM, MathInset::VERBATIM_MODE);
1050                         if (grid.asHullInset()) {
1051                                 grid.asHullInset()->label(cellrow, asString(ar));
1052                         } else {
1053                                 cell->push_back(createMathInset(t.cs()));
1054                                 cell->push_back(MathAtom(new MathBraceInset(ar)));
1055                         }
1056                 }
1057
1058                 else if (t.cs() == "choose" || t.cs() == "over" || t.cs() == "atop") {
1059                         MathAtom at = createMathInset(t.cs());
1060                         at.nucleus()->cell(0) = *cell;
1061                         cell->clear();
1062                         parse(at.nucleus()->cell(1), flags, mode);
1063                         cell->push_back(at);
1064                         return;
1065                 }
1066
1067                 else if (t.cs() == "substack") {
1068                         cell->push_back(createMathInset(t.cs()));
1069                         parse2(cell->back(), FLAG_ITEM, mode, false);
1070                 }
1071
1072                 else if (t.cs() == "xymatrix") {
1073                         cell->push_back(createMathInset(t.cs()));
1074                         parse2(cell->back(), FLAG_ITEM, mode, false);
1075                 }
1076
1077 #if 0
1078                 // Disabled
1079                 else if (1 && t.cs() == "ar") {
1080                         MathXYArrowInset * p = new MathXYArrowInset;
1081                         // try to read target
1082                         parse(p->cell(0), FLAG_OTPTION, mode);
1083                         // try to read label
1084                         if (nextToken().cat() == catSuper || nextToken().cat() == catSub) {
1085                                 p->up_ = nextToken().cat() == catSuper;
1086                                 getToken();
1087                                 parse(p->cell(1), FLAG_ITEM, mode);
1088                                 //lyxerr << "read label: " << p->cell(1) << "\n";
1089                         }
1090
1091                         cell->push_back(MathAtom(p));
1092                         //lyxerr << "read cell: " << cell << "\n";
1093                 }
1094 #endif
1095
1096                 else if (t.cs().size()) {
1097                         latexkeys const * l = in_word_set(t.cs());
1098                         if (l) {
1099                                 if (l->inset == "font") {
1100                                         cell->push_back(createMathInset(t.cs()));
1101                                         parse(cell->back().nucleus()->cell(0), FLAG_ITEM, asMode(l->extra));
1102                                 }
1103
1104                                 else if (l->inset == "oldfont") {
1105                                         cell->push_back(createMathInset(t.cs()));
1106                                         parse(cell->back().nucleus()->cell(0), flags, asMode(l->extra));
1107                                         return;
1108                                 }
1109
1110                                 else if (l->inset == "style") {
1111                                         cell->push_back(createMathInset(t.cs()));
1112                                         parse(cell->back().nucleus()->cell(0), flags, mode);
1113                                         return;
1114                                 }
1115
1116                                 else if (l->inset == "parbox") {
1117                                         // read optional positioning and width
1118                                         MathArray pos, width;
1119                                         parse(pos, FLAG_OPTION, MathInset::VERBATIM_MODE);
1120
1121                                         parse(width, FLAG_ITEM, MathInset::VERBATIM_MODE);
1122                                         cell->push_back(createMathInset(t.cs()));
1123                                         parse(cell->back().nucleus()->cell(0), FLAG_ITEM, MathInset::TEXT_MODE);
1124                                         cell->back().nucleus()->asParboxInset()->setPosition(asString(pos));
1125                                         cell->back().nucleus()->asParboxInset()->setWidth(asString(width));
1126                                 }
1127
1128                                 else {
1129                                         MathAtom at = createMathInset(t.cs());
1130                                         for (MathInset::idx_type i = 0; i < at->nargs(); ++i)
1131                                                 parse(at.nucleus()->cell(i), FLAG_ITEM, asMode(l->extra));
1132                                         cell->push_back(at);
1133                                 }
1134                         }
1135
1136                         else {
1137                                 MathAtom at = createMathInset(t.cs());
1138                                 MathInset::mode_type m = mode;
1139                                 if (m == MathInset::UNDECIDED_MODE)
1140                                         m = at->currentMode();
1141                                 for (MathInset::idx_type i = 0; i < at->nargs(); ++i)
1142                                         parse(at.nucleus()->cell(i), FLAG_ITEM, m);
1143                                 cell->push_back(at);
1144                         }
1145                 }
1146
1147
1148                 if (flags & FLAG_LEAVE) {
1149                         flags &= ~FLAG_LEAVE;
1150                         break;
1151                 }
1152         }
1153 }
1154
1155
1156
1157 } // anonymous namespace
1158
1159
1160 void mathed_parse_cell(MathArray & ar, string const & str)
1161 {
1162         istringstream is(str.c_str());
1163         mathed_parse_cell(ar, is);
1164 }
1165
1166
1167 void mathed_parse_cell(MathArray & ar, istream & is)
1168 {
1169         Parser(is).parse(ar, 0, MathInset::MATH_MODE);
1170 }
1171
1172
1173 bool mathed_parse_normal(MathAtom & t, string const & str)
1174 {
1175         istringstream is(str.c_str());
1176         return Parser(is).parse(t);
1177 }
1178
1179
1180 bool mathed_parse_normal(MathAtom & t, istream & is)
1181 {
1182         return Parser(is).parse(t);
1183 }
1184
1185
1186 bool mathed_parse_normal(MathAtom & t, LyXLex & lex)
1187 {
1188         return Parser(lex).parse(t);
1189 }