]> git.lyx.org Git - lyx.git/blob - src/mathed/math_parser.C
fonts as insets
[lyx.git] / src / mathed / math_parser.C
1 /*
2  *  File:        math_parser.C
3  *  Purpose:     Parser for mathed
4  *  Author:      Alejandro Aguilar Sierra <asierra@servidor.unam.mx>
5  *  Created:     January 1996
6  *  Description: Parse LaTeX2e math mode code.
7  *
8  *  Dependencies: Xlib, XForms
9  *
10  *  Copyright: 1996, Alejandro Aguilar Sierra
11  *
12  *   Version: 0.8beta.
13  *
14  *   You are free to use and modify this code under the terms of
15  *   the GNU General Public Licence version 2 or later.
16  */
17
18 /*
19
20 If someone desperately needs partial "structures" (such as a few cells of
21 an array inset or similar) (s)he could uses the following hack as starting
22 point to write some macros:
23
24   \newif\ifcomment
25   \commentfalse
26   \ifcomment
27           \def\makeamptab{\catcode`\&=4\relax}
28           \def\makeampletter{\catcode`\&=11\relax}
29     \def\b{\makeampletter\expandafter\makeamptab\bi}
30     \long\def\bi#1\e{}
31   \else
32     \def\b{}\def\e{}
33   \fi
34
35   ...
36
37   \[\begin{array}{ccc}
38    1 & 2\b & 3^2\\
39    4 & 5\e & 6\\
40    7 & 8 & 9
41   \end{array}\]
42
43 */
44
45
46 #include <config.h>
47
48 #ifdef __GNUG__
49 #pragma implementation
50 #endif
51
52 #include "math_parser.h"
53 #include "math_inset.h"
54 #include "math_arrayinset.h"
55 #include "math_braceinset.h"
56 #include "math_boxinset.h"
57 #include "math_charinset.h"
58 #include "math_deliminset.h"
59 #include "math_factory.h"
60 #include "math_kerninset.h"
61 #include "math_macro.h"
62 #include "math_macrotable.h"
63 #include "math_macrotemplate.h"
64 #include "math_hullinset.h"
65 #include "math_rootinset.h"
66 #include "math_sizeinset.h"
67 #include "math_sqrtinset.h"
68 #include "math_scriptinset.h"
69 #include "math_specialcharinset.h"
70 #include "math_sqrtinset.h"
71 #include "math_support.h"
72 #include "math_xyarrowinset.h"
73
74 #include "lyxlex.h"
75 #include "debug.h"
76 #include "support/LAssert.h"
77 #include "support/lstrings.h"
78
79 #include <cctype>
80 #include <stack>
81 #include <algorithm>
82
83 using std::istream;
84 using std::ostream;
85 using std::ios;
86 using std::endl;
87 using std::stack;
88 using std::fill;
89 using std::vector;
90 using std::atoi;
91
92 //#define FILEDEBUG
93
94
95 namespace {
96
97 bool stared(string const & s)
98 {
99         string::size_type const n = s.size();
100         return n && s[n - 1] == '*';
101 }
102
103
104 void add(MathArray & ar, char c)
105 {
106         ar.push_back(MathAtom(new MathCharInset(c)));
107 }
108
109
110 // These are TeX's catcodes
111 enum CatCode {
112         catEscape,     // 0    backslash
113         catBegin,      // 1    {
114         catEnd,        // 2    }
115         catMath,       // 3    $
116         catAlign,      // 4    &
117         catNewline,    // 5    ^^M
118         catParameter,  // 6    #
119         catSuper,      // 7    ^
120         catSub,        // 8    _
121         catIgnore,     // 9
122         catSpace,      // 10   space
123         catLetter,     // 11   a-zA-Z
124         catOther,      // 12   none of the above
125         catActive,     // 13   ~
126         catComment,    // 14   %
127         catInvalid     // 15   <delete>
128 };
129
130 CatCode theCatcode[256];
131
132
133 inline CatCode catcode(unsigned char c)
134 {
135         return theCatcode[c];
136 }
137
138
139 enum {
140         FLAG_BRACE_LAST = 1 << 1,  //  last closing brace ends the parsing process
141         FLAG_RIGHT      = 1 << 2,  //  next \\right ends the parsing process
142         FLAG_END        = 1 << 3,  //  next \\end ends the parsing process
143         FLAG_BRACK_END  = 1 << 4,  //  next closing bracket ends the parsing process
144         FLAG_BOX        = 1 << 5,  //  we are in a box
145         FLAG_ITEM       = 1 << 6,  //  read a (possibly braced token)
146         FLAG_BLOCK      = 1 << 7,  //  next block ends the parsing process
147         FLAG_BLOCK2     = 1 << 8,  //  next block2 ends the parsing process
148         FLAG_LEAVE      = 1 << 9   //  leave the loop at the end
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'] = catSpace;
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;
198         ///
199         bool isCR() const;
200
201 private:
202         ///
203         string cs_;
204         ///
205         char char_;
206         ///
207         CatCode cat_;
208 };
209
210 bool Token::isCR() const
211 {
212         return cs_ == "\\" || cs_ == "cr" || cs_ == "crcr";
213 }
214
215 string Token::asString() const
216 {
217         return cs_.size() ? cs_ : string(1, char_);
218 }
219
220 // Angus' compiler says these are not needed
221 //bool operator==(Token const & s, Token const & t)
222 //{
223 //      return s.character() == t.character()
224 //              && s.cat() == t.cat() && s.cs() == t.cs();
225 //}
226 //
227 //bool operator!=(Token const & s, Token const & t)
228 //{
229 //      return !(s == t);
230 //}
231
232 ostream & operator<<(ostream & os, Token const & t)
233 {
234         if (t.cs().size())
235                 os << "\\" << t.cs();
236         else
237                 os << "[" << t.character() << "," << t.cat() << "]";
238         return os;
239 }
240
241
242 class Parser {
243
244 public:
245         ///
246         Parser(LyXLex & lex);
247         ///
248         Parser(istream & is);
249
250         ///
251         bool parse_macro(string & name);
252         ///
253         bool parse_normal(MathAtom &);
254         ///
255         void parse_into(MathArray & array, unsigned flags);
256         ///
257         int lineno() const { return lineno_; }
258         ///
259         void putback();
260
261 private:
262         ///
263         void parse_into1(MathArray & array, unsigned flags);
264         /// get arg delimited by 'left' and 'right'
265         string getArg(char left, char right);
266         ///
267         char getChar();
268         ///
269         void error(string const & msg);
270         ///
271         bool parse_lines(MathAtom & t, bool numbered, bool outmost);
272         /// parses {... & ... \\ ... & ... }
273         bool parse_lines2(MathAtom & t, bool braced);
274         /// dump contents to screen
275         void dump() const;
276
277 private:
278         ///
279         void tokenize(istream & is);
280         ///
281         void tokenize(string const & s);
282         ///
283         void skipSpaceTokens(istream & is, char c);
284         ///
285         void push_back(Token const & t);
286         ///
287         void pop_back();
288         ///
289         Token const & prevToken() const;
290         ///
291         Token const & nextToken() const;
292         ///
293         Token const & getToken();
294         /// skips spaces if any
295         void skipSpaces();
296         /// skips opening brace
297         void skipBegin();
298         /// skips closing brace
299         void skipEnd();
300         /// counts a sequence of hlines
301         int readHLines();
302         ///
303         void lex(string const & s);
304         ///
305         bool good() const;
306
307         ///
308         int lineno_;
309         ///
310         vector<Token> tokens_;
311         ///
312         unsigned pos_;
313         ///
314         bool   curr_num_;
315         ///
316         string curr_label_;
317         ///
318         string curr_skip_;
319 };
320
321
322 Parser::Parser(LyXLex & lexer)
323         : lineno_(lexer.getLineNo()), pos_(0), curr_num_(false)
324 {
325         tokenize(lexer.getStream());
326         lexer.eatLine();
327 }
328
329
330 Parser::Parser(istream & is)
331         : lineno_(0), pos_(0), curr_num_(false)
332 {
333         tokenize(is);
334 }
335
336
337 void Parser::push_back(Token const & t)
338 {
339         tokens_.push_back(t);
340 }
341
342
343 void Parser::pop_back()
344 {
345         tokens_.pop_back();
346 }
347
348
349 Token const & Parser::prevToken() const
350 {
351         static const Token dummy;
352         return pos_ > 0 ? tokens_[pos_ - 1] : dummy;
353 }
354
355
356 Token const & Parser::nextToken() const
357 {
358         static const Token dummy;
359         return good() ? tokens_[pos_] : dummy;
360 }
361
362
363 Token const & Parser::getToken()
364 {
365         static const Token dummy;
366         //lyxerr << "looking at token " << tokens_[pos_] << " pos: " << pos_ << '\n';
367         return good() ? tokens_[pos_++] : dummy;
368 }
369
370
371 void Parser::skipSpaces()
372 {
373         while (nextToken().cat() == catSpace)
374                 getToken();
375 }
376
377
378 void Parser::skipBegin()
379 {
380         if (nextToken().cat() == catBegin)
381                 getToken();
382         else
383                 lyxerr << "'{' expected\n";
384 }
385
386
387 void Parser::skipEnd()
388 {
389         if (nextToken().cat() == catEnd)
390                 getToken();
391         else
392                 lyxerr << "'}' expected\n";
393 }
394
395
396 int Parser::readHLines()
397 {
398         int num = 0;
399         skipSpaces();
400         while (nextToken().cs() == "hline") {
401                 getToken();
402                 ++num;
403                 skipSpaces();
404         }
405         return num;
406 }
407
408
409 void Parser::putback()
410 {
411         --pos_;
412 }
413
414
415 bool Parser::good() const
416 {
417         return pos_ < tokens_.size();
418 }
419
420
421 char Parser::getChar()
422 {
423         if (!good()) {
424                 lyxerr << "The input stream is not well..." << endl;
425                 dump();
426         }
427         return tokens_[pos_++].character();
428 }
429
430
431 string Parser::getArg(char left, char right)
432 {
433         skipSpaces();
434
435         string result;
436         char c = getChar();
437
438         if (c != left)
439                 putback();
440         else
441                 while ((c = getChar()) != right && good())
442                         result += c;
443
444         return result;
445 }
446
447
448 void Parser::tokenize(istream & is)
449 {
450         // eat everything up to the next \end_inset or end of stream
451         // and store it in s for further tokenization
452         string s;
453         char c;
454         while (is.get(c)) {
455                 s += c;
456                 if (s.size() >= 10 && s.substr(s.size() - 10) == "\\end_inset") {
457                         s = s.substr(0, s.size() - 10);
458                         break;
459                 }
460         }
461
462         // tokenize buffer
463         tokenize(s);
464 }
465
466
467 void Parser::skipSpaceTokens(istream & is, char c)
468 {
469         // skip trailing spaces
470         while (catcode(c) == catSpace || catcode(c) == catNewline)
471                 if (!is.get(c))
472                         break;
473         //lyxerr << "putting back: " << c << "\n";
474         is.putback(c);
475 }
476
477
478 void Parser::tokenize(string const & buffer)
479 {
480         static bool init_done = false;
481
482         if (!init_done) {
483                 catInit();
484                 init_done = true;
485         }
486
487         istringstream is(buffer.c_str(), ios::in | ios::binary);
488
489         char c;
490         while (is.get(c)) {
491                 //lyxerr << "reading c: " << c << "\n";
492
493                 switch (catcode(c)) {
494                         case catNewline: {
495                                 ++lineno_;
496                                 is.get(c);
497                                 if (catcode(c) == catNewline)
498                                         ; //push_back(Token("par"));
499                                 else {
500                                         push_back(Token(' ', catSpace));
501                                         is.putback(c);
502                                 }
503                                 break;
504                         }
505
506                         case catComment: {
507                                 while (is.get(c) && catcode(c) != catNewline)
508                                         ;
509                                 ++lineno_;
510                                 break;
511                         }
512
513                         case catEscape: {
514                                 is.get(c);
515                                 if (!is) {
516                                         error("unexpected end of input");
517                                 } else {
518                                         string s(1, c);
519                                         if (catcode(c) == catLetter) {
520                                                 // collect letters
521                                                 while (is.get(c) && catcode(c) == catLetter)
522                                                         s += c;
523                                                 skipSpaceTokens(is, c);
524                                         }
525                                         push_back(Token(s));
526                                 }
527                                 break;
528                         }
529
530                         case catSuper:
531                         case catSub: {
532                                 push_back(Token(c, catcode(c)));
533                                 is.get(c);
534                                 skipSpaceTokens(is, c);
535                                 break;
536                         }
537
538                         case catIgnore: {
539                                 lyxerr << "ignoring a char: " << int(c) << "\n";
540                                 break;
541                         }
542
543                         default:
544                                 push_back(Token(c, catcode(c)));
545                 }
546         }
547
548 #ifdef FILEDEBUG
549         dump();
550 #endif
551 }
552
553
554 void Parser::dump() const
555 {
556         lyxerr << "\nTokens: ";
557         for (unsigned i = 0; i < tokens_.size(); ++i) {
558                 if (i == pos_)
559                         lyxerr << " <#> ";
560                 lyxerr << tokens_[i];
561         }
562         lyxerr << "\n";
563 }
564
565
566 void Parser::error(string const & msg)
567 {
568         lyxerr << "Line ~" << lineno_ << ": Math parse error: " << msg << endl;
569         dump();
570         //exit(1);
571 }
572
573
574
575 bool Parser::parse_lines(MathAtom & t, bool numbered, bool outmost)
576 {
577         MathGridInset * p = t->asGridInset();
578         if (!p) {
579                 dump();
580                 lyxerr << "error in Parser::parse_lines() 1\n";
581                 return false;
582         }
583
584         // save global variables
585         bool   const saved_num   = curr_num_;
586         string const saved_label = curr_label_;
587
588         // read initial hlines
589         p->rowinfo(0).lines_ = readHLines();
590
591         for (int row = 0; true; ++row) {
592                 // reset global variables
593                 curr_num_   = numbered;
594                 curr_label_.erase();
595
596                 // reading a row
597                 for (MathInset::col_type col = 0; true; ++col) {
598                         //lyxerr << "reading cell " << row << " " << col << " "
599                         // << p->ncols() << "\n";
600                         //lyxerr << "ncols: " << p->ncols() << "\n";
601
602                         if (col >= p->ncols()) {
603                                 //lyxerr << "adding col " << col << "\n";
604                                 p->addCol(p->ncols());
605                         }
606
607                         MathArray & ar = p->cell(col + row * p->ncols());
608                         parse_into(ar, FLAG_BLOCK);
609                         // remove 'unnecessary' braces:
610                         if (ar.size() == 1 && ar.back()->asBraceInset())
611                                 ar = ar.back()->asBraceInset()->cell(0);
612                         //lyxerr << "ar: " << ar << "\n";
613
614                         // break if cell is not followed by an ampersand
615                         if (nextToken().cat() != catAlign) {
616                                 //lyxerr << "less cells read than normal in row/col: "
617                                 //      << row << " " << col << "\n";
618                                 break;
619                         }
620
621                         // skip the ampersand
622                         getToken();
623                 }
624
625                 if (outmost) {
626                         MathHullInset * m = t->asHullInset();
627                         if (!m) {
628                                 lyxerr << "error in Parser::parse_lines() 2\n";
629                                 return false;
630                         }
631                         m->numbered(row, curr_num_);
632                         m->label(row, curr_label_);
633                         if (curr_skip_.size()) {
634                                 m->vcrskip(LyXLength(curr_skip_), row);
635                                 curr_skip_.erase();
636                         }
637                 }
638
639                 // is a \\ coming?
640                 if (nextToken().isCR()) {
641                         // skip the cr-token
642                         getToken();
643
644                         // try to read a length
645                         //get
646
647                         // read hlines for next row
648                         p->rowinfo(row + 1).lines_ = readHLines();
649                 }
650
651                 // we are finished if the next token is an 'end'
652                 if (nextToken().cs() == "end") {
653                         // skip the end-token
654                         getToken();
655                         getArg('{','}');
656
657                         // leave the 'read a line'-loop
658                         break;
659                 }
660
661                 // otherwise, we have to start a new row
662                 p->appendRow();
663         }
664
665         // restore "global" variables
666         curr_num_   = saved_num;
667         curr_label_ = saved_label;
668
669         return true;
670 }
671
672
673 bool Parser::parse_lines2(MathAtom & t, bool braced)
674 {
675         MathGridInset * p = t->asGridInset();
676         if (!p) {
677                 lyxerr << "error in Parser::parse_lines() 1\n";
678                 return false;
679         }
680
681         for (int row = 0; true; ++row) {
682                 // reading a row
683                 for (MathInset::col_type col = 0; true; ++col) {
684                         //lyxerr << "reading cell " << row << " " << col << " " << p->ncols() << "\n";
685
686                         if (col >= p->ncols()) {
687                                 //lyxerr << "adding col " << col << "\n";
688                                 p->addCol(p->ncols());
689                         }
690
691                         parse_into(p->cell(col + row * p->ncols()), FLAG_BLOCK2);
692                         //lyxerr << "read cell: " << p->cell(col + row * p->ncols()) << "\n";
693
694                         // break if cell is not followed by an ampersand
695                         if (nextToken().cat() != catAlign) {
696                                 //lyxerr << "less cells read than normal in row/col: " << row << " " << col << "\n";
697                                 break;
698                         }
699
700                         // skip the ampersand
701                         getToken();
702                 }
703
704                 // is a \\ coming?
705                 if (nextToken().isCR()) {
706                         // skip the cr-token
707                         getToken();
708                 }
709
710                 // we are finished if the next token is the one we expected
711                 // skip the end-token
712                 // leave the 'read a line'-loop
713                 if (braced) {
714                         if (nextToken().cat() == catEnd) {
715                                 getToken();
716                                 break;
717                         }
718                 } else {
719                         if (nextToken().cs() == "end") {
720                                 getToken();
721                                 getArg('{','}');
722                                 break;
723                         }
724                 }
725
726                 // otherwise, we have to start a new row
727                 p->appendRow();
728         }
729
730         return true;
731 }
732
733  
734 bool Parser::parse_macro(string & name)
735 {
736         int nargs = 0;
737         name = "{error}";
738         skipSpaces();
739
740         if (nextToken().cs() == "def") {
741
742                 getToken();
743                 name = getToken().cs();
744
745                 string pars;
746                 while (good() && nextToken().cat() != catBegin)
747                         pars += getToken().cs();
748
749                 if (!good()) {
750                         lyxerr << "bad stream in parse_macro\n";
751                         dump();
752                         return false;
753                 }
754
755                 //lyxerr << "read \\def parameter list '" << pars << "'\n";
756                 if (!pars.empty()) {
757                         lyxerr << "can't handle non-empty parameter lists\n";
758                         dump();
759                         return false;
760                 }
761
762         } else if (nextToken().cs() == "newcommand") {
763
764                 getToken();
765
766                 if (getToken().cat() != catBegin) {
767                         lyxerr << "'{' in \\newcommand expected (1) \n";
768                         dump();
769                         return false;
770                 }
771
772                 name = getToken().cs();
773
774                 if (getToken().cat() != catEnd) {
775                         lyxerr << "'}' expected\n";
776                         return false;
777                 }
778
779                 string arg  = getArg('[', ']');
780                 if (!arg.empty())
781                         nargs = atoi(arg.c_str());
782
783         } else {
784                 lyxerr << "\\newcommand or \\def  expected\n";
785                 return false;
786         }
787
788
789         if (getToken().cat() != catBegin) {
790                 lyxerr << "'{' in macro definition expected (2)\n";
791                 return false;
792         }
793
794         MathArray ar1;
795         parse_into(ar1, FLAG_BRACE_LAST);
796
797         // we cannot handle recursive stuff at all
798         MathArray test;
799         test.push_back(createMathInset(name));
800         if (ar1.contains(test)) {
801                 lyxerr << "we cannot handle recursive macros at all.\n";
802                 return false;
803         }
804
805         // is a version for display attached?
806         MathArray ar2;
807         parse_into(ar2, FLAG_ITEM);
808
809         MathMacroTable::create(name, nargs, ar1, ar2);
810         return true;
811 }
812  
813
814 bool Parser::parse_normal(MathAtom & matrix)
815 {
816         skipSpaces();
817         Token const & t = getToken();
818
819         if (t.cs() == "(") {
820                 matrix = MathAtom(new MathHullInset(LM_OT_SIMPLE));
821                 parse_into(matrix->cell(0), 0);
822                 return true;
823         }
824
825         if (t.cat() == catMath) {
826                 Token const & n = getToken();
827                 if (n.cat() == catMath) {
828                         // TeX's $$...$$ syntax for displayed math
829                         matrix = MathAtom(new MathHullInset(LM_OT_EQUATION));
830                         MathHullInset * p = matrix->asHullInset();
831                         parse_into(p->cell(0), 0);
832                         p->numbered(0, curr_num_);
833                         p->label(0, curr_label_);
834                 } else {
835                         // simple $...$  stuff
836                         putback();
837                         matrix = MathAtom(new MathHullInset(LM_OT_SIMPLE));
838                         parse_into(matrix->cell(0), 0);
839                 }
840                 return true;
841         }
842
843         if (!t.cs().size()) {
844                 lyxerr << "start of math expected, got '" << t << "'\n";
845                 return false;
846         }
847
848         string const & cs = t.cs();
849
850         if (cs == "[") {
851                 curr_num_ = 0;
852                 curr_label_.erase();
853                 matrix = MathAtom(new MathHullInset(LM_OT_EQUATION));
854                 MathHullInset * p = matrix->asHullInset();
855                 parse_into(p->cell(0), 0);
856                 p->numbered(0, curr_num_);
857                 p->label(0, curr_label_);
858                 return true;
859         }
860
861         if (cs != "begin") {
862                 lyxerr[Debug::MATHED]
863                         << "'begin' of un-simple math expected, got '" << cs << "'\n";
864                 return false;
865         }
866
867         string const name = getArg('{', '}');
868
869         if (name == "math") {
870                 matrix = MathAtom(new MathHullInset(LM_OT_SIMPLE));
871                 parse_into(matrix->cell(0), 0);
872                 return true;
873         }
874
875         if (name == "equation" || name == "equation*" || name == "displaymath") {
876                 curr_num_ = (name == "equation");
877                 curr_label_.erase();
878                 matrix = MathAtom(new MathHullInset(LM_OT_EQUATION));
879                 MathHullInset * p = matrix->asHullInset();
880                 parse_into(p->cell(0), FLAG_END);
881                 p->numbered(0, curr_num_);
882                 p->label(0, curr_label_);
883                 return true;
884         }
885
886         if (name == "eqnarray" || name == "eqnarray*") {
887                 matrix = MathAtom(new MathHullInset(LM_OT_EQNARRAY));
888                 return parse_lines(matrix, !stared(name), true);
889         }
890
891         if (name == "align" || name == "align*") {
892                 matrix = MathAtom(new MathHullInset(LM_OT_ALIGN));
893                 return parse_lines(matrix, !stared(name), true);
894         }
895
896         if (name == "alignat" || name == "alignat*") {
897                 int nc = 2 * atoi(getArg('{', '}').c_str());
898                 matrix = MathAtom(new MathHullInset(LM_OT_ALIGNAT, nc));
899                 return parse_lines(matrix, !stared(name), true);
900         }
901
902         if (name == "xalignat" || name == "xalignat*") {
903                 int nc = 2 * atoi(getArg('{', '}').c_str());
904                 matrix = MathAtom(new MathHullInset(LM_OT_XALIGNAT, nc));
905                 return parse_lines(matrix, !stared(name), true);
906         }
907
908         if (name == "xxalignat") {
909                 int nc = 2 * atoi(getArg('{', '}').c_str());
910                 matrix = MathAtom(new MathHullInset(LM_OT_XXALIGNAT, nc));
911                 return parse_lines(matrix, !stared(name), true);
912         }
913
914         if (name == "multline" || name == "multline*") {
915                 matrix = MathAtom(new MathHullInset(LM_OT_MULTLINE));
916                 return parse_lines(matrix, !stared(name), true);
917         }
918
919         if (name == "gather" || name == "gather*") {
920                 matrix = MathAtom(new MathHullInset(LM_OT_GATHER));
921                 return parse_lines(matrix, !stared(name), true);
922         }
923
924         lyxerr[Debug::MATHED] << "1: unknown math environment: " << name << "\n";
925         lyxerr << "1: unknown math environment: " << name << "\n";
926         return false;
927 }
928
929
930 void Parser::parse_into(MathArray & array, unsigned flags)
931 {
932         parse_into1(array, flags);
933         // remove 'unnecessary' braces:
934         if (array.size() == 1 && array.back()->asBraceInset()) {
935                 lyxerr << "extra braces removed\n";
936                 array = array.back()->asBraceInset()->cell(0);
937         }
938 }
939
940
941 void Parser::parse_into1(MathArray & array, unsigned flags)
942 {
943         bool panic  = false;
944         int  limits = 0;
945
946         while (good()) {
947                 Token const & t = getToken();
948
949 #ifdef FILEDEBUG
950                 lyxerr << "t: " << t << " flags: " << flags << "\n";
951                 //array.dump();
952                 lyxerr << "\n";
953 #endif
954
955                 if (flags & FLAG_ITEM) {
956                         if (t.cat() == catSpace)
957                                 continue;
958
959                         flags &= ~FLAG_ITEM;
960                         if (t.cat() == catBegin) {
961                                 // skip the brace and collect everything to the next matching
962                                 // closing brace
963                                 flags |= FLAG_BRACE_LAST;
964                                 continue;
965                         }
966
967                         // handle only this single token, leave the loop if done
968                         flags |= FLAG_LEAVE;
969                 }
970
971                 if (flags & FLAG_BLOCK) {
972                         if (t.cat() == catAlign || t.isCR() || t.cs() == "end") {
973                                 putback();
974                                 return;
975                         }
976                 }
977
978                 if (flags & FLAG_BLOCK2) {
979                         if (t.cat() == catAlign || t.isCR() || t.cs() == "end"
980                                         || t.cat() == catEnd) {
981                                 putback();
982                                 return;
983                         }
984                 }
985
986                 //
987                 // cat codes
988                 //
989                 if (t.cat() == catMath) {
990                         if (flags & FLAG_BOX) {
991                                 // we are inside an mbox, so opening new math is allowed
992                                 array.push_back(MathAtom(new MathHullInset(LM_OT_SIMPLE)));
993                                 parse_into(array.back()->cell(0), 0);
994                         } else {
995                                 // otherwise this is the end of the formula
996                                 break;
997                         }
998                 }
999
1000                 else if (t.cat() == catLetter)
1001                         add(array, t.character());
1002
1003                 else if (t.cat() == catSpace) //&& code == LM_TC_TEXTRM
1004                         add(array, t.character());
1005
1006                 else if (t.cat() == catParameter) {
1007                         Token const & n = getToken();
1008                         array.push_back(MathAtom(new MathMacroArgument(n.character()-'0')));
1009                 }
1010
1011                 else if (t.cat() == catBegin) {
1012                         MathArray ar;
1013                         parse_into(ar, FLAG_BRACE_LAST);
1014 #ifndef WITH_WARNINGS
1015 #warning this might be wrong in general!
1016 #endif
1017                         // ignore braces around simple items
1018                         if ((ar.size() == 1 && !ar.front()->needsBraces()
1019        || (ar.size() == 2 && !ar.front()->needsBraces()
1020                                             && ar.back()->asScriptInset()))
1021        || (ar.size() == 0 && array.size() == 0))
1022                         {
1023                                 array.push_back(ar);
1024                         } else {
1025                                 array.push_back(MathAtom(new MathBraceInset));
1026                                 array.back()->cell(0).swap(ar);
1027                         }
1028                 }
1029
1030                 else if (t.cat() == catEnd) {
1031                         if (flags & FLAG_BRACE_LAST)
1032                                 return;
1033                         lyxerr << "found '}' unexpectedly, array: '" << array << "'\n";
1034                         dump();
1035                         //lyxerr << "found '}' unexpectedly\n";
1036                         //lyx::Assert(0);
1037                         //add(array, '}', LM_TC_TEX);
1038                 }
1039
1040                 else if (t.cat() == catAlign) {
1041                         lyxerr << "found tab unexpectedly, array: '" << array << "'\n";
1042                         dump();
1043                         //lyxerr << "found tab unexpectedly\n";
1044                         add(array, '&'); //, LM_TC_TEX;
1045                 }
1046
1047                 else if (t.cat() == catSuper || t.cat() == catSub) {
1048                         bool up = (t.cat() == catSuper);
1049                         MathScriptInset * p = 0;
1050                         if (array.size())
1051                                 p = array.back()->asScriptInset();
1052                         if (!p || p->has(up)) {
1053                                 array.push_back(MathAtom(new MathScriptInset(up)));
1054                                 p = array.back()->asScriptInset();
1055                         }
1056                         p->ensure(up);
1057                         parse_into(p->cell(up), FLAG_ITEM);
1058                         p->limits(limits);
1059                         limits = 0;
1060                 }
1061
1062                 else if (t.character() == ']' && (flags & FLAG_BRACK_END))
1063                         return;
1064
1065                 else if (t.cat() == catOther)
1066                         add(array, t.character());
1067
1068                 //
1069                 // control sequences
1070                 //
1071                 else if (t.cs() == "protect")
1072                         // ignore \\protect, will be re-added during output
1073                         ;
1074
1075                 else if (t.cs() == "end")
1076                         break;
1077
1078                 else if (t.cs() == ")")
1079                         break;
1080
1081                 else if (t.cs() == "]")
1082                         break;
1083
1084                 else if (t.cs() == "\\") {
1085                         curr_skip_ = getArg('[', ']');
1086                         //lyxerr << "found newline unexpectedly, array: '" << array << "'\n";
1087                         lyxerr << "found newline unexpectedly\n";
1088                         array.push_back(createMathInset("\\"));
1089                 }
1090
1091                 else if (t.cs() == "limits")
1092                         limits = 1;
1093
1094                 else if (t.cs() == "nolimits")
1095                         limits = -1;
1096
1097                 else if (t.cs() == "nonumber")
1098                         curr_num_ = false;
1099
1100                 else if (t.cs() == "number")
1101                         curr_num_ = true;
1102
1103                 else if (t.cs() == "sqrt") {
1104                         char c = getChar();
1105                         if (c == '[') {
1106                                 array.push_back(MathAtom(new MathRootInset));
1107                                 parse_into(array.back()->cell(0), FLAG_BRACK_END);
1108                                 parse_into(array.back()->cell(1), FLAG_ITEM);
1109                         } else {
1110                                 putback();
1111                                 array.push_back(MathAtom(new MathSqrtInset));
1112                                 parse_into(array.back()->cell(0), FLAG_ITEM);
1113                         }
1114                 }
1115
1116                 else if (t.cs() == "left") {
1117                         string l = getToken().asString();
1118                         MathArray ar;
1119                         parse_into(ar, FLAG_RIGHT);
1120                         string r = getToken().asString();
1121                         MathAtom dl(new MathDelimInset(l, r));
1122                         dl->cell(0) = ar;
1123                         array.push_back(dl);
1124                 }
1125
1126                 else if (t.cs() == "right") {
1127                         if (!(flags & FLAG_RIGHT)) {
1128                                 //lyxerr << "got so far: '" << array << "'\n";
1129                                 error("Unmatched right delimiter");
1130                         }
1131                         return;
1132                 }
1133
1134                 else if (t.cs() == "begin") {
1135                         string const name = getArg('{', '}');
1136                         if (name == "array" || name == "subarray") {
1137                                 string const valign = getArg('[', ']') + 'c';
1138                                 string const halign = getArg('{', '}');
1139                                 array.push_back(MathAtom(new MathArrayInset(name, valign[0], halign)));
1140                                 parse_lines(array.back(), false, false);
1141                         } else if (name == "split" || name == "cases" ||
1142                                          name == "gathered" || name == "aligned") {
1143                                 array.push_back(createMathInset(name));
1144                                 parse_lines(array.back(), false, false);
1145                         } else if (name == "matrix"  || name == "pmatrix" || name == "bmatrix" ||
1146                                          name == "vmatrix" || name == "Vmatrix") {
1147                                 array.push_back(createMathInset(name));
1148                                 parse_lines2(array.back(), false);
1149                         } else
1150                                 lyxerr << "unknow math inset begin '" << name << "'\n";
1151                 }
1152
1153                 else if (t.cs() == "kern") {
1154 #ifdef WITH_WARNINGS
1155 #warning A hack...
1156 #endif
1157                         string s;
1158                         while (1) {
1159                                 Token const & t = getToken();
1160                                 if (!good()) {
1161                                         putback();
1162                                         break;
1163                                 }
1164                                 s += t.character();
1165                                 if (isValidLength(s))
1166                                         break;
1167                         }
1168                         array.push_back(MathAtom(new MathKernInset(s)));
1169                 }
1170
1171                 else if (t.cs() == "label") {
1172                         curr_label_ = getArg('{', '}');
1173                 }
1174
1175                 else if (t.cs() == "choose" || t.cs() == "over" || t.cs() == "atop") {
1176                         MathAtom p = createMathInset(t.cs());
1177                         array.swap(p->cell(0));
1178                         parse_into(p->cell(1), flags);
1179                         array.push_back(p);
1180                         return;
1181                 }
1182
1183                 else if (t.cs() == "substack") {
1184                         array.push_back(createMathInset(t.cs()));
1185                         skipBegin();
1186                         parse_lines2(array.back(), true);
1187                 }
1188
1189                 else if (t.cs() == "xymatrix") {
1190                         array.push_back(createMathInset(t.cs()));
1191                         skipBegin();
1192                         parse_lines2(array.back(), true);
1193                 }
1194
1195 #if 0
1196                 // Disabled
1197                 else if (1 && t.cs() == "ar") {
1198                         MathXYArrowInset * p = new MathXYArrowInset;
1199
1200                         // try to read target
1201                         char c = getChar();
1202                         if (c == '[') {
1203                                 parse_into(p->cell(0), FLAG_BRACK_END);
1204                                 //lyxerr << "read target: " << p->cell(0) << "\n";
1205                         } else {
1206                                 putback();
1207                         }
1208
1209                         // try to read label
1210                         if (nextToken().cat() == catSuper || nextToken().cat() == catSub) {
1211                                 p->up_ = nextToken().cat() == catSuper;
1212                                 getToken();
1213                                 parse_into(p->cell(1), FLAG_ITEM);
1214                                 //lyxerr << "read label: " << p->cell(1) << "\n";
1215                         }
1216
1217                         array.push_back(MathAtom(p));
1218                         //lyxerr << "read array: " << array << "\n";
1219                 }
1220 #endif
1221
1222 #if 0
1223                 else if (t.cs() == "mbox" || t.cs() == "text") {
1224                         //array.push_back(createMathInset(t.cs()));
1225                         array.push_back(MathAtom(new MathBoxInset(t.cs())));
1226                         // slurp in the argument of mbox
1227
1228                         MathBoxInset * p = array.back()->asBoxInset();
1229                         //lyx::assert(p);
1230                 }
1231 #endif
1232
1233
1234                 else if (t.cs().size()) {
1235                         latexkeys const * l = in_word_set(t.cs());
1236                         if (l) {
1237                                 if (l->inset == "font") {
1238                                         lyxerr << "starting font " << t.cs() << "\n";
1239                                         //CatCode catSpaceSave = theCatcode[' '];
1240                                         //if (l->id == LM_TC_TEXTRM) {
1241                                         //      // temporarily change catcode
1242                                         //      theCatcode[' '] = catLetter;
1243                                         //}
1244
1245                                         MathAtom p = createMathInset(t.cs());
1246                                         parse_into(p->cell(0), FLAG_ITEM);
1247                                         array.push_back(p);
1248
1249                                         // undo catcode changes
1250                                         //theCatcode[' '] = catSpaceSave;
1251                                         //lyxerr << "ending font\n";
1252                                 }
1253
1254                                 else if (l->inset == "oldfont") {
1255                                         MathAtom p = createMathInset(t.cs());
1256                                         parse_into(p->cell(0), flags);
1257                                         array.push_back(p);
1258                                         return;
1259                                 }
1260
1261                                 else if (l->inset == "box") {
1262                                         MathAtom p = createMathInset(t.cs());
1263                                         parse_into(p->cell(0), FLAG_ITEM | FLAG_BOX);
1264                                         array.push_back(p);
1265                                 }
1266
1267                                 else if (l->inset == "style") {
1268                                         MathAtom p = createMathInset(t.cs());
1269                                         parse_into(p->cell(0), flags);
1270                                         array.push_back(p);
1271                                         return;
1272                                 }
1273
1274                                 else {
1275                                         MathAtom p = createMathInset(t.cs());
1276                                         for (MathInset::idx_type i = 0; i < p->nargs(); ++i)
1277                                                 parse_into(p->cell(i), FLAG_ITEM);
1278                                         array.push_back(p);
1279                                 }
1280                         }
1281
1282                         else {
1283                                 MathAtom p = createMathInset(t.cs());
1284                                 for (MathInset::idx_type i = 0; i < p->nargs(); ++i)
1285                                         parse_into(p->cell(i), FLAG_ITEM);
1286                                 array.push_back(p);
1287                         }
1288                 }
1289
1290
1291                 if (flags & FLAG_LEAVE) {
1292                         flags &= ~FLAG_LEAVE;
1293                         break;
1294                 }
1295         }
1296
1297         if (panic) {
1298                 lyxerr << " Math Panic, expect problems!\n";
1299                 //   Search for the end command.
1300                 Token t;
1301                 do {
1302                         t = getToken();
1303                 } while (good() && t.cs() != "end");
1304         }
1305 }
1306
1307
1308
1309 } // anonymous namespace
1310
1311
1312 void mathed_parse_cell(MathArray & ar, string const & str)
1313 {
1314         istringstream is(str.c_str());
1315         mathed_parse_cell(ar, is);
1316 }
1317
1318
1319 void mathed_parse_cell(MathArray & ar, istream & is)
1320 {
1321         Parser(is).parse_into(ar, 0);
1322 }
1323
1324
1325
1326 bool mathed_parse_macro(string & name, string const & str)
1327 {
1328         istringstream is(str.c_str());
1329         Parser parser(is);
1330         return parser.parse_macro(name);
1331 }
1332
1333 bool mathed_parse_macro(string & name, istream & is)
1334 {
1335         Parser parser(is);
1336         return parser.parse_macro(name);
1337 }
1338
1339 bool mathed_parse_macro(string & name, LyXLex & lex)
1340 {
1341         Parser parser(lex);
1342         return parser.parse_macro(name);
1343 }
1344
1345
1346
1347 bool mathed_parse_normal(MathAtom & t, string const & str)
1348 {
1349         istringstream is(str.c_str());
1350         Parser parser(is);
1351         return parser.parse_normal(t);
1352 }
1353
1354 bool mathed_parse_normal(MathAtom & t, istream & is)
1355 {
1356         Parser parser(is);
1357         return parser.parse_normal(t);
1358 }
1359
1360 bool mathed_parse_normal(MathAtom & t, LyXLex & lex)
1361 {
1362         Parser parser(lex);
1363         return parser.parse_normal(t);
1364 }