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