]> git.lyx.org Git - features.git/blob - src/mathed/math_parser.C
read support for \begin{displaymath}...\end{displaymath}
[features.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 #include <cctype>
49 #include <stack>
50
51 #ifdef __GNUG__
52 #pragma implementation
53 #endif
54
55 #include "math_parser.h"
56 #include "array.h"
57 #include "math_inset.h"
58 #include "math_arrayinset.h"
59 #include "math_braceinset.h"
60 #include "math_charinset.h"
61 #include "math_deliminset.h"
62 #include "math_factory.h"
63 #include "math_funcinset.h"
64 #include "math_kerninset.h"
65 #include "math_macro.h"
66 #include "math_macrotable.h"
67 #include "math_macrotemplate.h"
68 #include "math_matrixinset.h"
69 #include "math_rootinset.h"
70 #include "math_sqrtinset.h"
71 #include "math_scriptinset.h"
72 #include "math_specialcharinset.h"
73 #include "math_splitinset.h"
74 #include "math_sqrtinset.h"
75 #include "debug.h"
76 #include "support.h"
77 #include "lyxlex.h"
78 #include "support/lstrings.h"
79
80 using std::istream;
81 using std::ostream;
82 using std::ios;
83 using std::endl;
84 using std::stack;
85
86
87 namespace {
88
89 bool stared(string const & s)
90 {
91         unsigned n = s.size();
92         return n && s[n - 1] == '*';
93 }
94
95
96 void add(MathArray & ar, char c, MathTextCodes code)
97 {
98         ar.push_back(MathAtom(new MathCharInset(c, code)));
99 }
100
101
102 // These are TeX's catcodes
103 enum CatCode {
104         catEscape,     // 0    backslash 
105         catBegin,      // 1    {
106         catEnd,        // 2    }
107         catMath,       // 3    $
108         catAlign,      // 4    &
109         catNewline,    // 5    ^^M
110         catParameter,  // 6    #
111         catSuper,      // 7    ^
112         catSub,        // 8    _
113         catIgnore,     // 9       
114         catSpace,      // 10   space
115         catLetter,     // 11   a-zA-Z
116         catOther,      // 12   none of the above
117         catActive,     // 13   ~
118         catComment,    // 14   %
119         catInvalid     // 15   <delete>
120 };
121
122 CatCode theCatcode[256];  
123
124
125 inline CatCode catcode(unsigned char c)
126 {
127         return theCatcode[c];
128 }
129
130
131 enum {
132         FLAG_BRACE      = 1 << 0,  //  an opening brace needed
133         FLAG_BRACE_LAST = 1 << 1,  //  last closing brace ends the parsing process
134         FLAG_RIGHT      = 1 << 2,  //  next \\right ends the parsing process
135         FLAG_END        = 1 << 3,  //  next \\end ends the parsing process
136         FLAG_BRACK_END  = 1 << 4,  //  next closing bracket ends the parsing process
137         FLAG_NEWLINE    = 1 << 6,  //  next \\\\ ends the parsing process
138         FLAG_ITEM       = 1 << 7,  //  read a (possibly braced token)
139         FLAG_BLOCK      = 1 << 8,  //  next block ends the parsing process
140         FLAG_LEAVE      = 1 << 9   //  leave the loop at the end
141 };
142
143
144 void catInit()
145 {
146         for (int i = 0; i <= 255; ++i) 
147                 theCatcode[i] = catOther;
148         for (int i = 'a'; i <= 'z'; ++i) 
149                 theCatcode[i] = catLetter;
150         for (int i = 'A'; i <= 'Z'; ++i) 
151                 theCatcode[i] = catLetter;
152
153         theCatcode['\\'] = catEscape;   
154         theCatcode['{']  = catBegin;    
155         theCatcode['}']  = catEnd;      
156         theCatcode['$']  = catMath;     
157         theCatcode['&']  = catAlign;    
158         theCatcode['\n'] = catNewline;  
159         theCatcode['#']  = catParameter;        
160         theCatcode['^']  = catSuper;    
161         theCatcode['_']  = catSub;      
162         theCatcode['\7f'] = catIgnore;    
163         theCatcode[' ']  = catSpace;    
164         theCatcode['\t'] = catSpace;    
165         theCatcode['\r'] = catSpace;    
166         theCatcode['~']  = catActive;   
167         theCatcode['%']  = catComment;  
168 }
169
170
171
172 //
173 // Helper class for parsing
174 //
175
176 class Token {
177 public:
178         ///
179         Token() : cs_(), char_(0), cat_(catIgnore) {}
180         ///
181         Token(char c, CatCode cat) : cs_(), char_(c), cat_(cat) {}
182         ///
183         Token(const string & cs) : cs_(cs), char_(0), cat_(catIgnore) {}
184
185         ///
186         string const & cs() const { return cs_; }
187         ///
188         CatCode cat() const { return cat_; }
189         ///
190         char character() const { return char_; }
191         ///
192         string asString() const;
193
194 private:        
195         ///
196         string cs_;
197         ///
198         char char_;
199         ///
200         CatCode cat_;
201 };
202
203 string Token::asString() const
204 {
205         return cs_.size() ? cs_ : string(1, char_);
206 }
207
208 bool operator==(Token const & s, Token const & t)
209 {
210         return s.character() == t.character()
211                 && s.cat() == t.cat() && s.cs() == t.cs(); 
212 }
213
214 bool operator!=(Token const & s, Token const & t)
215 {
216         return !(s == t);
217 }
218
219 ostream & operator<<(ostream & os, Token const & t)
220 {
221         if (t.cs().size())
222                 os << "\\" << t.cs();
223         else
224                 os << "[" << t.character() << "," << t.cat() << "]";
225         return os;
226 }
227
228
229 class Parser {
230
231 public:
232         ///
233         Parser(LyXLex & lex);
234         ///
235         Parser(istream & is);
236
237         ///
238         string parse_macro();
239         ///
240         bool parse_normal(MathAtom &);
241         ///
242         void parse_into(MathArray & array, unsigned flags, MathTextCodes = LM_TC_MIN);
243         ///
244         int lineno() const { return lineno_; }
245         ///
246         void putback();
247
248 private:
249         ///
250         string getArg(char lf, char rf);
251         ///
252         char getChar();
253         ///
254         void error(string const & msg);
255         ///
256         bool parse_lines(MathAtom & t, bool numbered, bool outmost);
257
258 private:
259         ///
260         void tokenize(istream & is);
261         ///
262         void tokenize(string const & s);
263         ///
264         void push_back(Token const & t);
265         ///
266         void pop_back();
267         ///
268         Token const & prevToken() const;
269         ///
270         Token const & nextToken() const;
271         ///
272         Token const & getToken();
273         ///
274         void lex(string const & s);
275         ///
276         bool good() const;
277
278         ///
279         int lineno_;
280         ///
281         std::vector<Token> tokens_;
282         ///
283         unsigned pos_;
284         ///
285         bool   curr_num_;
286         ///
287         string curr_label_;
288         ///
289         string curr_skip_;
290 };
291
292
293 Parser::Parser(LyXLex & lexer)
294         : lineno_(lexer.getLineNo()), pos_(0), curr_num_(false)
295 {
296         tokenize(lexer.getStream());
297         lexer.eatLine();
298 }
299
300
301 Parser::Parser(istream & is)
302         : lineno_(0), pos_(0), curr_num_(false)
303 {
304         tokenize(is);
305 }
306
307
308 void Parser::push_back(Token const & t)
309 {
310         tokens_.push_back(t);
311 }
312
313
314 void Parser::pop_back()
315 {
316         tokens_.pop_back();
317 }
318
319
320 Token const & Parser::prevToken() const
321 {
322         static const Token dummy;
323         return pos_ > 0 ? tokens_[pos_ - 1] : dummy;
324 }
325
326
327 Token const & Parser::nextToken() const
328 {
329         static const Token dummy;
330         return good() ? tokens_[pos_] : dummy;
331 }
332
333
334 Token const & Parser::getToken()
335 {
336         static const Token dummy;
337         return good() ? tokens_[pos_++] : dummy;
338 }
339
340
341 void Parser::putback()
342 {
343         --pos_;
344 }
345
346
347 bool Parser::good() const
348 {
349         return pos_ < tokens_.size();
350 }
351
352
353 char Parser::getChar()
354 {
355         if (!good())
356                 lyxerr << "The input stream is not well..." << endl;
357         return tokens_[pos_++].character();
358 }
359
360
361 string Parser::getArg(char lf, char rg)
362 {
363         string result;
364         char c = getChar();
365
366         if (c != lf)  
367                 putback();
368         else 
369                 while ((c = getChar()) != rg && good())
370                         result += c;
371
372         return result;
373 }
374
375
376 void Parser::tokenize(istream & is)
377 {
378         // eat everything up to the next \end_inset or end of stream
379         // and store it in s for further tokenization
380         string s;
381         char c;
382         while (is.get(c)) {
383                 s += c;
384                 if (s.size() >= 10 && s.substr(s.size() - 10) == "\\end_inset") {
385                         s = s.substr(0, s.size() - 10);
386                         break;
387                 }
388         }
389
390         // tokenize buffer
391         tokenize(s);
392 }
393
394
395 void Parser::tokenize(string const & buffer)
396 {
397         static bool init_done = false;
398         
399         if (!init_done) {
400                 catInit();
401                 init_done = true;
402         }
403
404         istringstream is(buffer.c_str(), ios::in | ios::binary);
405
406         char c;
407         while (is.get(c)) {
408
409                 switch (catcode(c)) {
410                         case catNewline: {
411                                 ++lineno_; 
412                                 is.get(c);
413                                 if (catcode(c) == catNewline)
414                                         ; //push_back(Token("par"));
415                                 else {
416                                         push_back(Token(' ', catSpace));
417                                         is.putback(c);  
418                                 }
419                                 break;
420                         }
421
422                         case catComment: {
423                                 while (is.get(c) && catcode(c) != catNewline)
424                                         ;
425                                 ++lineno_; 
426                                 break;
427                         }
428
429                         case catEscape: {
430                                 is.get(c);
431                                 string s(1, c);
432                                 if (catcode(c) == catLetter) {
433                                         while (is.get(c) && catcode(c) == catLetter)
434                                                 s += c;
435                                         if (catcode(c) == catSpace)
436                                                 while (is.get(c) && catcode(c) == catSpace)
437                                                         ;
438                                         is.putback(c);
439                                 }       
440                                 push_back(Token(s));
441                                 break;
442                         }
443
444                         default:
445                                 push_back(Token(c, catcode(c)));
446                 }
447         }
448
449 #if 0
450         lyxerr << "\nTokens: ";
451         for (unsigned i = 0; i < tokens_.size(); ++i)
452                 lyxerr << tokens_[i];
453         lyxerr << "\n";
454 #endif
455 }
456
457
458 void Parser::error(string const & msg) 
459 {
460         lyxerr << "Line ~" << lineno_ << ": Math parse error: " << msg << endl;
461         //exit(1);
462 }
463
464
465 bool Parser::parse_lines(MathAtom & t, bool numbered, bool outmost)
466 {       
467         MathGridInset * p = t->asGridInset();
468         if (!p) {
469                 lyxerr << "error in Parser::parse_lines() 1\n";
470                 return false;
471         }
472
473         const int cols = p->ncols();
474
475         // save global variables
476         bool   const saved_num   = curr_num_;
477         string const saved_label = curr_label_;
478
479         for (int row = 0; true; ++row) {
480                 // reset global variables
481                 curr_num_   = numbered;
482                 curr_label_.erase();
483
484                 // reading a row
485                 for (int col = 0; col < cols; ++col) {
486                         //lyxerr << "reading cell " << row << " " << col << "\n";
487                         parse_into(p->cell(col + row * cols), FLAG_BLOCK);
488
489                         // no ampersand
490                         if (prevToken().cat() != catAlign) {
491                                 //lyxerr << "less cells read than normal in row/col: "
492                                 //      << row << " " << col << "\n";
493                                 break;
494                         }
495                 }
496
497                 if (outmost) {
498                         MathMatrixInset * m = t->asMatrixInset();
499                         if (!m) {
500                                 lyxerr << "error in Parser::parse_lines() 2\n";
501                                 return false;
502                         }
503                         m->numbered(row, curr_num_);
504                         m->label(row, curr_label_);
505                         if (curr_skip_.size()) {
506                                 m->vskip(LyXLength(curr_skip_), row);
507                                 curr_skip_.erase();
508                         }
509                 }
510
511                 // no newline?
512                 if (prevToken() != Token("\\")) {
513                         //lyxerr << "no newline here\n";
514                         break;
515                 }
516
517                 p->appendRow();
518         }
519
520         // restore "global" variables
521         curr_num_   = saved_num;
522         curr_label_ = saved_label;
523
524         return true;
525 }
526
527
528 string Parser::parse_macro()
529 {
530         string name = "{error}";
531
532         while (nextToken().cat() == catSpace)
533                 getToken();
534
535         if (getToken().cs() != "newcommand") {
536                 lyxerr << "\\newcommand expected\n";
537                 return name;
538         }
539
540         if (getToken().cat() != catBegin) {
541                 lyxerr << "'{' expected\n";
542                 return name;
543         }
544
545         name = getToken().cs();
546
547         if (getToken().cat() != catEnd) {
548                 lyxerr << "'}' expected\n";
549                 return name;
550         }
551
552         string    arg  = getArg('[', ']');
553         int       narg = arg.empty() ? 0 : atoi(arg.c_str()); 
554         MathArray ar;
555         parse_into(ar, FLAG_BRACE | FLAG_BRACE_LAST);
556         MathMacroTable::create(name, narg, ar);
557         
558         return name;
559 }
560
561
562 bool Parser::parse_normal(MathAtom & matrix)
563 {
564         while (nextToken().cat() == catSpace)
565                 getToken();
566
567         Token const & t = getToken();
568
569         if (t.cs() == "(") {
570                 matrix = MathAtom(new MathMatrixInset(LM_OT_SIMPLE));
571                 parse_into(matrix->cell(0), 0);
572                 return true;
573         }
574
575         if (t.cat() == catMath) {
576                 Token const & n = getToken();
577                 if (n.cat() == catMath) {
578                         // TeX's $$...$$ syntax for displayed math
579                         matrix = MathAtom(new MathMatrixInset(LM_OT_EQUATION));
580                         MathMatrixInset * p = matrix->asMatrixInset();
581                         parse_into(p->cell(0), 0);
582                         p->numbered(0, curr_num_);
583                         p->label(0, curr_label_);
584                 } else {
585                         // simple $...$  stuff
586                         putback();
587                         matrix = MathAtom(new MathMatrixInset(LM_OT_SIMPLE));
588                         parse_into(matrix->cell(0), 0);
589                 }
590                 return true;
591         }
592
593         if (!t.cs().size()) {
594                 lyxerr << "start of math expected, got '" << t << "'\n";
595                 return false;
596         }
597
598         string const & cs = t.cs();
599
600         if (cs == "[") {
601                 curr_num_ = 0;
602                 curr_label_.erase();
603                 matrix = MathAtom(new MathMatrixInset(LM_OT_EQUATION));
604                 MathMatrixInset * p = matrix->asMatrixInset();
605                 parse_into(p->cell(0), 0);
606                 p->numbered(0, curr_num_);
607                 p->label(0, curr_label_);
608                 return true;
609         }
610
611         if (cs != "begin") {
612                 lyxerr << "'begin' of un-simple math expected, got '" << cs << "'\n";
613                 return false;
614         }
615
616         string const name = getArg('{', '}');
617
618         if (name == "equation" || name == "equation*" || name == "displaymath") {
619                 curr_num_ = (name == "equation");
620                 curr_label_.erase();
621                 matrix = MathAtom(new MathMatrixInset(LM_OT_EQUATION));
622                 MathMatrixInset * p = matrix->asMatrixInset();
623                 parse_into(p->cell(0), FLAG_END);
624                 p->numbered(0, curr_num_);
625                 p->label(0, curr_label_);
626                 return true;
627         }
628
629         if (name == "eqnarray" || name == "eqnarray*") {
630                 matrix = MathAtom(new MathMatrixInset(LM_OT_EQNARRAY));
631                 return parse_lines(matrix, !stared(name), true);
632         }
633
634         if (name == "align" || name == "align*") {
635                 matrix = MathAtom(new MathMatrixInset(LM_OT_ALIGN));
636                 return parse_lines(matrix, !stared(name), true);
637         }
638
639         if (name == "alignat" || name == "alignat*") {
640                 int nc = 2 * atoi(getArg('{', '}').c_str());
641                 matrix = MathAtom(new MathMatrixInset(LM_OT_ALIGNAT, nc));
642                 return parse_lines(matrix, !stared(name), true);
643         }
644
645         if (name == "xalignat" || name == "xalignat*") {
646                 int nc = 2 * atoi(getArg('{', '}').c_str());
647                 matrix = MathAtom(new MathMatrixInset(LM_OT_XALIGNAT, nc));
648                 return parse_lines(matrix, !stared(name), true);
649         }
650
651         if (name == "xxalignat") {
652                 int nc = 2 * atoi(getArg('{', '}').c_str());
653                 matrix = MathAtom(new MathMatrixInset(LM_OT_XXALIGNAT, nc));
654                 return parse_lines(matrix, !stared(name), true);
655         }
656
657         if (name == "multline" || name == "multline*") {
658                 matrix = MathAtom(new MathMatrixInset(LM_OT_MULTLINE));
659                 return parse_lines(matrix, !stared(name), true);
660         }
661
662         if (name == "gather" || name == "gather*") {
663                 matrix = MathAtom(new MathMatrixInset(LM_OT_GATHER));
664                 return parse_lines(matrix, !stared(name), true);
665         }
666
667         lyxerr[Debug::MATHED] << "1: unknown math environment: " << name << "\n";
668         return false;
669 }
670
671
672 void Parser::parse_into(MathArray & array, unsigned flags, MathTextCodes code)
673 {
674         bool panic  = false;
675         int  limits = 0;
676
677         while (good()) {
678                 Token const & t = getToken();
679         
680                 //lyxerr << "t: " << t << " flags: " << flags << "'\n";
681                 //array.dump(lyxerr);
682                 //lyxerr << "\n";
683
684                 if (flags & FLAG_ITEM) {
685                         flags &= ~FLAG_ITEM;
686                         if (t.cat() == catBegin) { 
687                                 // skip the brace and collect everything to the next matching
688                                 // closing brace
689                                 flags |= FLAG_BRACE_LAST;
690                                 continue;
691                         } else {
692                                 // handle only this single token, leave the loop if done
693                                 flags |= FLAG_LEAVE;
694                         }
695                 }
696
697                 if (flags & FLAG_BRACE) {
698                         if (t.cat() != catBegin) {
699                                 error("Expected {. Maybe you forgot to enclose an argument in {}");
700                                 panic = true;
701                                 break;
702                         } else {
703                                 flags &= ~FLAG_BRACE;
704                                 continue;
705                         }
706                 }
707
708                 if (flags & FLAG_BLOCK) {
709                         if (t.cat() == catAlign || t.cs() == "\\")
710                                 return;
711                         if (t.cs() == "end") {
712                                 getArg('{', '}');
713                                 return;
714                         }
715                 }
716
717                 //
718                 // cat codes
719                 //
720                 if (t.cat() == catMath)
721                         break;
722
723                 else if (t.cat() == catLetter)
724                         add(array, t.character(), code);
725
726                 else if (t.cat() == catSpace && code == LM_TC_TEXTRM)
727                         add(array, t.character(), code);
728
729                 else if (t.cat() == catParameter) {
730                         Token const & n = getToken();
731                         array.push_back(MathAtom(new MathMacroArgument(n.character() - '0')));
732                 }
733
734                 else if (t.cat() == catBegin) {
735                         array.push_back(MathAtom(new MathBraceInset));
736                         parse_into(array.back()->cell(0), FLAG_BRACE_LAST, LM_TC_MIN);
737                 }
738
739                 else if (t.cat() == catEnd) {
740                         if (flags & FLAG_BRACE_LAST)
741                                 return;
742                         lyxerr << "found '}' unexpectedly, array: '" << array << "'\n";
743                         add(array, '}', LM_TC_TEX);
744                 }
745                 
746                 else if (t.cat() == catAlign) {
747                         lyxerr << "found tab unexpectedly, array: '" << array << "'\n";
748                         add(array, '&', LM_TC_TEX);
749                 }
750                 
751                 else if (t.cat() == catSuper || t.cat() == catSub) {
752                         bool up = (t.cat() == catSuper);
753                         MathScriptInset * p = 0; 
754                         if (array.size()) 
755                                 p = array.back()->asScriptInset();
756                         if (!p || p->has(up)) {
757                                 array.push_back(MathAtom(new MathScriptInset(up)));
758                                 p = array.back()->asScriptInset();
759                         }
760                         p->ensure(up);
761                         parse_into(p->cell(up), FLAG_ITEM);
762                         p->limits(limits);
763                         limits = 0;
764                 }
765
766                 else if (t.character() == ']' && (flags & FLAG_BRACK_END))
767                         return;
768
769                 else if (t.cat() == catOther)
770                         add(array, t.character(), code);
771                 
772                 //
773                 // codesequences
774                 //      
775                 else if (t.cs() == "protect") 
776                         ;
777
778                 else if (t.cs() == "end")
779                         break;
780
781                 else if (t.cs() == ")")
782                         break;
783
784                 else if (t.cs() == "]")
785                         break;
786
787                 else if (t.cs() == "\\") {
788                         curr_skip_ = getArg('[', ']');
789                         if (flags & FLAG_NEWLINE)
790                                 return;
791                         lyxerr[Debug::MATHED]
792                                         << "found newline unexpectedly, array: '" << array << "'\n";
793                         array.push_back(createMathInset("\\"));
794                 }
795         
796                 else if (t.cs() == "limits")
797                         limits = 1;
798                 
799                 else if (t.cs() == "nolimits")
800                         limits = -1;
801                 
802                 else if (t.cs() == "nonumber")
803                         curr_num_ = false;
804
805                 else if (t.cs() == "number")
806                         curr_num_ = true;
807
808                 else if (t.cs() == "sqrt") {
809                         char c = getChar();
810                         if (c == '[') {
811                                 array.push_back(MathAtom(new MathRootInset));
812                                 parse_into(array.back()->cell(0), FLAG_BRACK_END);
813                                 parse_into(array.back()->cell(1), FLAG_ITEM);
814                         } else {
815                                 putback();
816                                 array.push_back(MathAtom(new MathSqrtInset));
817                                 parse_into(array.back()->cell(0), FLAG_ITEM);
818                         }
819                 }
820                 
821                 else if (t.cs() == "left") {
822                         string l = getToken().asString();
823                         MathArray ar;
824                         parse_into(ar, FLAG_RIGHT);
825                         string r = getToken().asString();
826                         MathAtom dl(new MathDelimInset(l, r));
827                         dl->cell(0) = ar;
828                         array.push_back(dl);
829                 }
830                 
831                 else if (t.cs() == "right") {
832                         if (!(flags & FLAG_RIGHT)) {
833                                 lyxerr << "got so far: '" << array << "'\n";
834                                 error("Unmatched right delimiter");
835                         }
836                         return;
837                 }
838
839 /*              
840                 case LM_TK_STY:
841                 {
842                         lyxerr[Debug::MATHED] << "LM_TK_STY not implemented\n";
843                         //MathArray tmp = array;
844                         //MathSizeInset * p = new MathSizeInset(MathStyles(lval_->id));
845                         //array.push_back(p);
846                         //parse_into(p->cell(0), FLAG_BRACE_FONT);
847                         break; 
848                 }
849
850 */
851                 
852                 else if (t.cs() == "begin") {
853                         string const name = getArg('{', '}');   
854                         if (name == "array") {
855                                 string const valign = getArg('[', ']') + 'c';
856                                 string const halign = getArg('{', '}');
857                                 array.push_back(
858                                         MathAtom(new MathArrayInset(halign.size(), 1, valign[0], halign)));
859                                 parse_lines(array.back(), false, false);
860                         } else if (name == "split") {
861                                 array.push_back(MathAtom(new MathSplitInset(1)));
862                                 parse_lines(array.back(), false, false);
863                         } else 
864                                 lyxerr[Debug::MATHED] << "unknow math inset begin '" << name << "'\n";  
865                 }
866         
867                 else if (t.cs() == "kern") {
868 #ifdef WITH_WARNINGS
869 #warning A hack...
870 #endif
871                         string s;
872                         while (1) {
873                                 Token const & t = getToken();
874                                 if (!good()) {
875                                         putback();      
876                                         break;
877                                 }
878                                 s += t.character();
879                                 if (isValidLength(s))
880                                         break;
881                         }
882                         array.push_back(MathAtom(new MathKernInset(s)));
883                 }
884
885                 else if (t.cs() == "label") {
886                         //MathArray ar;
887                         //parse_into(ar, FLAG_ITEM);
888                         //ostringstream os;
889                         //ar.write(os, true);
890                         //curr_label_ = os.str();
891                         // was: 
892                         curr_label_ = getArg('{', '}');
893                 }
894
895                 else if (t.cs() == "choose" || t.cs() == "over" || t.cs() == "atop") {
896                         MathAtom p = createMathInset(t.cs());
897                         // search backward for position of last '{' if any
898                         int pos;
899                         for (pos = array.size() - 1; pos >= 0; --pos)
900                                 if (array.at(pos)->getChar() == '{')
901                                         break;
902                         if (pos >= 0) {
903                                 // found it -> use the part after '{' as "numerator"
904                                 p->cell(0) = MathArray(array, pos + 1, array.size());
905                                 parse_into(p->cell(1), FLAG_BRACE_LAST);
906                                 // delete denominator and the '{'
907                                 array.erase(pos, array.size());
908                         } else if (flags & FLAG_RIGHT) {
909                                 // we are inside a \left ... \right block
910                                 //lyxerr << "found '" << t.cs() << "' enclosed by \\left .. \\right\n";
911                                 p->cell(0).swap(array);
912                                 parse_into(p->cell(1), FLAG_RIGHT);
913                                 // handle the right delimiter properly
914                                 putback();
915                         } else {
916                                 // not found -> use everything as "numerator"
917                                 p->cell(0).swap(array);
918                                 parse_into(p->cell(1), FLAG_BLOCK);
919                         }
920                         array.push_back(MathAtom(p));
921                 }
922
923 /*
924                 // Disabled
925                 else if (t.cs() == "mbox") {
926                         array.push_back(createMathInset(t.cs()));
927                         // slurp in the argument of mbox
928         
929                         MathBoxInset * p = array.back()->asBoxInset();
930                         //lyx::assert(p);
931                 }
932 */
933         
934                 else if (t.cs().size()) {
935                         latexkeys const * l = in_word_set(t.cs());
936                         if (l) {
937                                 if (l->token == LM_TK_FONT) {
938                                         //lyxerr << "starting font\n";
939                                         //CatCode catSpaceSave = theCatcode[' '];
940                                         //if (l->id == LM_TC_TEXTRM) {
941                                         //      // temporarily change catcode   
942                                         //      theCatcode[' '] = catLetter;    
943                                         //}
944
945                                         MathTextCodes t = static_cast<MathTextCodes>(l->id);
946                                         MathArray ar;
947                                         parse_into(ar, FLAG_ITEM, t);
948                                         for (MathArray::iterator it = ar.begin(); it != ar.end(); ++it)
949                                                 (*it)->handleFont(t);
950                                         array.push_back(ar);
951
952                                         // undo catcode changes
953                                         ////theCatcode[' '] = catSpaceSave;
954                                         //lyxerr << "ending font\n";
955                                 }
956
957                                 else if (l->token == LM_TK_OLDFONT) {
958                                         code = static_cast<MathTextCodes>(l->id);
959                                 }
960
961                                 else {
962                                         MathAtom p = createMathInset(t.cs());
963                                         for (MathInset::idx_type i = 0; i < p->nargs(); ++i) 
964                                                 parse_into(p->cell(i), FLAG_ITEM);
965                                         array.push_back(p);
966                                 }
967                         }
968
969                         else {
970                                 MathAtom p = createMathInset(t.cs());
971                                 for (MathInset::idx_type i = 0; i < p->nargs(); ++i)
972                                         parse_into(p->cell(i), FLAG_ITEM);
973                                 array.push_back(p);
974                         }
975                 }
976
977
978                 if (flags & FLAG_LEAVE) {
979                         flags &= ~FLAG_LEAVE;
980                         break;
981                 }
982         }
983
984         if (panic) {
985                 lyxerr << " Math Panic, expect problems!\n";
986                 //   Search for the end command. 
987                 Token t;
988                 do {
989                         t = getToken();
990                 } while (good() && t.cs() != "end");
991         }
992 }
993
994
995
996 } // anonymous namespace
997
998
999 void mathed_parse_cell(MathArray & ar, string const & str)
1000 {
1001         istringstream is(str.c_str());
1002         mathed_parse_cell(ar, is);
1003 }
1004
1005
1006 void mathed_parse_cell(MathArray & ar, istream & is)
1007 {
1008         Parser(is).parse_into(ar, 0);
1009 }
1010
1011
1012
1013 string mathed_parse_macro(string const & str)
1014 {
1015         istringstream is(str.c_str());
1016         Parser parser(is);
1017         return parser.parse_macro();
1018 }
1019
1020 string mathed_parse_macro(istream & is)
1021 {
1022         Parser parser(is);
1023         return parser.parse_macro();
1024 }
1025
1026 string mathed_parse_macro(LyXLex & lex)
1027 {
1028         Parser parser(lex);
1029         return parser.parse_macro();
1030 }
1031
1032
1033
1034 bool mathed_parse_normal(MathAtom & t, string const & str)
1035 {
1036         istringstream is(str.c_str());
1037         Parser parser(is);
1038         return parser.parse_normal(t);
1039 }
1040
1041 bool mathed_parse_normal(MathAtom & t, istream & is)
1042 {
1043         Parser parser(is);
1044         return parser.parse_normal(t);
1045 }
1046
1047 bool mathed_parse_normal(MathAtom & t, LyXLex & lex)
1048 {
1049         Parser parser(lex);
1050         return parser.parse_normal(t);
1051 }