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