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