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