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