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