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