]> git.lyx.org Git - lyx.git/blob - src/mathed/MathParser.C
Convert to unicode.
[lyx.git] / src / mathed / MathParser.C
1 /**
2  * \file MathParser.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 /*
12
13 If someone desperately needs partial "structures" (such as a few
14 cells of an array inset or similar) (s)he could uses the
15 following hack as starting point to write some macros:
16
17   \newif\ifcomment
18   \commentfalse
19   \ifcomment
20           \def\makeamptab{\catcode`\&=4\relax}
21           \def\makeampletter{\catcode`\&=11\relax}
22     \def\b{\makeampletter\expandafter\makeamptab\bi}
23     \long\def\bi#1\e{}
24   \else
25     \def\b{}\def\e{}
26   \fi
27
28   ...
29
30   \[\begin{array}{ccc}
31 1
32 &
33
34   \end{array}\]
35
36 */
37
38
39 #include <config.h>
40
41 #include "MathParser.h"
42
43 #include "InsetMathArray.h"
44 #include "InsetMathBig.h"
45 #include "InsetMathBrace.h"
46 #include "InsetMathChar.h"
47 #include "InsetMathColor.h"
48 #include "InsetMathComment.h"
49 #include "InsetMathDelim.h"
50 #include "InsetMathEnv.h"
51 #include "InsetMathKern.h"
52 #include "InsetMathMacro.h"
53 #include "InsetMathPar.h"
54 #include "InsetMathRef.h"
55 #include "InsetMathRoot.h"
56 #include "InsetMathScript.h"
57 #include "InsetMathSplit.h"
58 #include "InsetMathSqrt.h"
59 #include "InsetMathTabular.h"
60 #include "MathMacroTemplate.h"
61 #include "MathFactory.h"
62 #include "MathMacroArgument.h"
63 #include "MathSupport.h"
64
65 #include "lyxlex.h"
66 #include "debug.h"
67
68 #include "support/convert.h"
69
70 #include <sstream>
71
72
73 namespace lyx {
74
75 using std::endl;
76 using std::fill;
77
78 using std::string;
79 using std::ios;
80 using std::istream;
81 using std::istringstream;
82 using std::ostream;
83 using std::vector;
84
85
86 //#define FILEDEBUG
87
88
89 namespace {
90
91 InsetMath::mode_type asMode(InsetMath::mode_type oldmode, docstring const & str)
92 {
93         //lyxerr << "handling mode: '" << str << "'" << endl;
94         if (str == "mathmode")
95                 return InsetMath::MATH_MODE;
96         if (str == "textmode" || str == "forcetext")
97                 return InsetMath::TEXT_MODE;
98         return oldmode;
99 }
100
101
102 bool stared(docstring const & s)
103 {
104         size_t const n = s.size();
105         return n && s[n - 1] == '*';
106 }
107
108
109 /*!
110  * Add the row \p cellrow to \p grid.
111  * \returns wether the row could be added. Adding a row can fail for
112  * environments like "equation" that have a fixed number of rows.
113  */
114 bool addRow(InsetMathGrid & grid, InsetMathGrid::row_type & cellrow,
115             docstring const & vskip)
116 {
117         ++cellrow;
118         if (cellrow == grid.nrows()) {
119                 //lyxerr << "adding row " << cellrow << endl;
120                 grid.addRow(cellrow - 1);
121                 if (cellrow == grid.nrows()) {
122                         // We can't add a row to this grid, so let's
123                         // append the content of this cell to the previous
124                         // one.
125                         // This does not happen in well formed .lyx files,
126                         // but LyX versions 1.3.x and older could create
127                         // such files and tex2lyx can still do that.
128                         --cellrow;
129                         lyxerr << "ignoring extra row";
130                         if (!vskip.empty())
131                                 lyxerr << " with extra space " << to_utf8(vskip);
132                         lyxerr << '.' << endl;
133                         return false;
134                 }
135         }
136         grid.vcrskip(LyXLength(to_utf8(vskip)), cellrow - 1);
137         return true;
138 }
139
140
141 /*!
142  * Add the column \p cellcol to \p grid.
143  * \returns wether the column could be added. Adding a column can fail for
144  * environments like "eqnarray" that have a fixed number of columns.
145  */
146 bool addCol(InsetMathGrid & grid, InsetMathGrid::col_type & cellcol)
147 {
148         ++cellcol;
149         if (cellcol == grid.ncols()) {
150                 //lyxerr << "adding column " << cellcol << endl;
151                 grid.addCol(cellcol - 1);
152                 if (cellcol == grid.ncols()) {
153                         // We can't add a column to this grid, so let's
154                         // append the content of this cell to the previous
155                         // one.
156                         // This does not happen in well formed .lyx files,
157                         // but LyX versions 1.3.x and older could create
158                         // such files and tex2lyx can still do that.
159                         --cellcol;
160                         lyxerr << "ignoring extra column." << endl;
161                         return false;
162                 }
163         }
164         return true;
165 }
166
167
168 /*!
169  * Check wether the last row is empty and remove it if yes.
170  * Otherwise the following code
171  * \verbatim
172 \begin{array}{|c|c|}
173 \hline
174 1 & 2 \\ \hline
175 3 & 4 \\ \hline
176 \end{array}
177  * \endverbatim
178  * will result in a grid with 3 rows (+ the dummy row that is always present),
179  * because the last '\\' opens a new row.
180  */
181 void delEmptyLastRow(InsetMathGrid & grid)
182 {
183         InsetMathGrid::row_type const row = grid.nrows() - 1;
184         for (InsetMathGrid::col_type col = 0; col < grid.ncols(); ++col) {
185                 if (!grid.cell(grid.index(row, col)).empty())
186                         return;
187         }
188         // Copy the row information of the empty row (which would contain the
189         // last hline in the example above) to the dummy row and delete the
190         // empty row.
191         grid.rowinfo(row + 1) = grid.rowinfo(row);
192         grid.delRow(row);
193 }
194
195
196 // These are TeX's catcodes
197 enum CatCode {
198         catEscape,     // 0    backslash
199         catBegin,      // 1    {
200         catEnd,        // 2    }
201         catMath,       // 3    $
202         catAlign,      // 4    &
203         catNewline,    // 5    ^^M
204         catParameter,  // 6    #
205         catSuper,      // 7    ^
206         catSub,        // 8    _
207         catIgnore,     // 9
208         catSpace,      // 10   space
209         catLetter,     // 11   a-zA-Z
210         catOther,      // 12   none of the above
211         catActive,     // 13   ~
212         catComment,    // 14   %
213         catInvalid     // 15   <delete>
214 };
215
216 CatCode theCatcode[128];
217
218
219 inline CatCode catcode(lyx::char_type c)
220 {
221         /* The fact that we use unicode internally does not change Knuth's TeX
222         engine. It is still 7bit only, not even latin1 or something like that.
223         Therefore, the catcode table needs only to have 128 entries.
224         Everything not in that range is catOther.
225         */
226         if (c >= 128)
227                 return catOther;
228
229         return theCatcode[c];
230 }
231
232
233 enum {
234         FLAG_ALIGN      = 1 << 0,  //  next & or \\ ends the parsing process
235         FLAG_BRACE_LAST = 1 << 1,  //  next closing brace ends the parsing
236         FLAG_RIGHT      = 1 << 2,  //  next \\right ends the parsing process
237         FLAG_END        = 1 << 3,  //  next \\end ends the parsing process
238         FLAG_BRACK_LAST = 1 << 4,  //  next closing bracket ends the parsing
239         FLAG_TEXTMODE   = 1 << 5,  //  we are in a box
240         FLAG_ITEM       = 1 << 6,  //  read a (possibly braced token)
241         FLAG_LEAVE      = 1 << 7,  //  leave the loop at the end
242         FLAG_SIMPLE     = 1 << 8,  //  next $ leaves the loop
243         FLAG_EQUATION   = 1 << 9,  //  next \] leaves the loop
244         FLAG_SIMPLE2    = 1 << 10, //  next \) leaves the loop
245         FLAG_OPTION     = 1 << 11, //  read [...] style option
246         FLAG_BRACED     = 1 << 12  //  read {...} style argument
247 };
248
249
250 //
251 // Helper class for parsing
252 //
253
254 class Token {
255 public:
256         ///
257         Token() : cs_(), char_(0), cat_(catIgnore) {}
258         ///
259         Token(char_type c, CatCode cat) : cs_(), char_(c), cat_(cat) {}
260         ///
261         Token(docstring const & cs) : cs_(cs), char_(0), cat_(catIgnore) {}
262
263         ///
264         docstring const & cs() const { return cs_; }
265         ///
266         CatCode cat() const { return cat_; }
267         ///
268         char_type character() const { return char_; }
269         ///
270         docstring asString() const { return cs_.size() ? cs_ : docstring(1, char_); }
271         ///
272         docstring asInput() const { return cs_.size() ? '\\' + cs_ : docstring(1, char_); }
273
274 private:
275         ///
276         docstring cs_;
277         ///
278         char_type char_;
279         ///
280         CatCode cat_;
281 };
282
283 ostream & operator<<(ostream & os, Token const & t)
284 {
285         if (t.cs().size())
286                 os << '\\' << t.cs();
287         else if (t.cat() == catLetter)
288                 os << t.character();
289         else
290                 os << '[' << t.character() << ',' << t.cat() << ']';
291         return os;
292 }
293
294
295 class Parser {
296 public:
297         ///
298         typedef  InsetMath::mode_type mode_type;
299
300         ///
301         Parser(LyXLex & lex);
302         ///
303         Parser(istream & is);
304
305         ///
306         bool parse(MathAtom & at);
307         ///
308         void parse(MathArray & array, unsigned flags, mode_type mode);
309         ///
310         void parse1(InsetMathGrid & grid, unsigned flags, mode_type mode,
311                 bool numbered);
312         ///
313         MathArray parse(unsigned flags, mode_type mode);
314         ///
315         int lineno() const { return lineno_; }
316         ///
317         void putback();
318
319 private:
320         ///
321         void parse2(MathAtom & at, unsigned flags, mode_type mode, bool numbered);
322         /// get arg delimited by 'left' and 'right'
323         docstring getArg(char_type left, char_type right);
324         ///
325         char_type getChar();
326         ///
327         void error(string const & msg);
328         void error(docstring const & msg) { error(to_utf8(msg)); }
329         /// dump contents to screen
330         void dump() const;
331         ///
332         void tokenize(istream & is);
333         ///
334         void tokenize(docstring const & s);
335         ///
336         void skipSpaceTokens(idocstream & is, char_type c);
337         ///
338         void push_back(Token const & t);
339         ///
340         void pop_back();
341         ///
342         Token const & prevToken() const;
343         ///
344         Token const & nextToken() const;
345         ///
346         Token const & getToken();
347         /// skips spaces if any
348         void skipSpaces();
349         ///
350         void lex(docstring const & s);
351         ///
352         bool good() const;
353         ///
354         docstring parse_verbatim_item();
355         ///
356         docstring parse_verbatim_option();
357
358         ///
359         int lineno_;
360         ///
361         vector<Token> tokens_;
362         ///
363         unsigned pos_;
364         /// Stack of active environments
365         vector<docstring> environments_;
366 };
367
368
369 Parser::Parser(LyXLex & lexer)
370         : lineno_(lexer.getLineNo()), pos_(0)
371 {
372         tokenize(lexer.getStream());
373         lexer.eatLine();
374 }
375
376
377 Parser::Parser(istream & is)
378         : lineno_(0), pos_(0)
379 {
380         tokenize(is);
381 }
382
383
384 void Parser::push_back(Token const & t)
385 {
386         tokens_.push_back(t);
387 }
388
389
390 void Parser::pop_back()
391 {
392         tokens_.pop_back();
393 }
394
395
396 Token const & Parser::prevToken() const
397 {
398         static const Token dummy;
399         return pos_ > 0 ? tokens_[pos_ - 1] : dummy;
400 }
401
402
403 Token const & Parser::nextToken() const
404 {
405         static const Token dummy;
406         return good() ? tokens_[pos_] : dummy;
407 }
408
409
410 Token const & Parser::getToken()
411 {
412         static const Token dummy;
413         //lyxerr << "looking at token " << tokens_[pos_] << " pos: " << pos_ << endl;
414         return good() ? tokens_[pos_++] : dummy;
415 }
416
417
418 void Parser::skipSpaces()
419 {
420         while (nextToken().cat() == catSpace || nextToken().cat() == catNewline)
421                 getToken();
422 }
423
424
425 void Parser::putback()
426 {
427         --pos_;
428 }
429
430
431 bool Parser::good() const
432 {
433         return pos_ < tokens_.size();
434 }
435
436
437 char_type Parser::getChar()
438 {
439         if (!good())
440                 error("The input stream is not well...");
441         return tokens_[pos_++].character();
442 }
443
444
445 docstring Parser::getArg(char_type left, char_type right)
446 {
447         skipSpaces();
448
449         docstring result;
450         char_type c = getChar();
451
452         if (c != left)
453                 putback();
454         else
455                 while ((c = getChar()) != right && good())
456                         result += c;
457
458         return result;
459 }
460
461
462 void Parser::skipSpaceTokens(idocstream & is, char_type c)
463 {
464         // skip trailing spaces
465         while (catcode(c) == catSpace || catcode(c) == catNewline)
466                 if (!is.get(c))
467                         break;
468         //lyxerr << "putting back: " << c << endl;
469         is.putback(c);
470 }
471
472
473 void Parser::tokenize(istream & is)
474 {
475         // eat everything up to the next \end_inset or end of stream
476         // and store it in s for further tokenization
477         std::string s;
478         char c;
479         while (is.get(c)) {
480                 s += c;
481                 if (s.size() >= 10 && s.substr(s.size() - 10) == "\\end_inset") {
482                         s = s.substr(0, s.size() - 10);
483                         break;
484                 }
485         }
486         // Remove the space after \end_inset
487         if (is.get(c) && c != ' ')
488                 is.unget();
489
490         // tokenize buffer
491         tokenize(from_utf8(s));
492 }
493
494
495 void Parser::tokenize(docstring const & buffer)
496 {
497         idocstringstream is(buffer, ios::in | ios::binary);
498
499         char_type c;
500         while (is.get(c)) {
501                 //lyxerr << "reading c: " << c << endl;
502
503                 switch (catcode(c)) {
504                         case catNewline: {
505                                 ++lineno_;
506                                 is.get(c);
507                                 if (catcode(c) == catNewline)
508                                         ; //push_back(Token("par"));
509                                 else {
510                                         push_back(Token('\n', catNewline));
511                                         is.putback(c);
512                                 }
513                                 break;
514                         }
515
516 /*
517                         case catComment: {
518                                 while (is.get(c) && catcode(c) != catNewline)
519                                         ;
520                                 ++lineno_;
521                                 break;
522                         }
523 */
524
525                         case catEscape: {
526                                 is.get(c);
527                                 if (!is) {
528                                         error("unexpected end of input");
529                                 } else {
530                                         docstring s(1, c);
531                                         if (catcode(c) == catLetter) {
532                                                 // collect letters
533                                                 while (is.get(c) && catcode(c) == catLetter)
534                                                         s += c;
535                                                 skipSpaceTokens(is, c);
536                                         }
537                                         push_back(Token(s));
538                                 }
539                                 break;
540                         }
541
542                         case catSuper:
543                         case catSub: {
544                                 push_back(Token(c, catcode(c)));
545                                 is.get(c);
546                                 skipSpaceTokens(is, c);
547                                 break;
548                         }
549
550                         case catIgnore: {
551                                 lyxerr << "ignoring a char: " << int(c) << endl;
552                                 break;
553                         }
554
555                         default:
556                                 push_back(Token(c, catcode(c)));
557                 }
558         }
559
560 #ifdef FILEDEBUG
561         dump();
562 #endif
563 }
564
565
566 void Parser::dump() const
567 {
568         lyxerr << "\nTokens: ";
569         for (unsigned i = 0; i < tokens_.size(); ++i) {
570                 if (i == pos_)
571                         lyxerr << " <#> ";
572                 lyxerr << tokens_[i];
573         }
574         lyxerr << " pos: " << pos_ << endl;
575 }
576
577
578 void Parser::error(string const & msg)
579 {
580         lyxerr << "Line ~" << lineno_ << ": Math parse error: " << msg << endl;
581         dump();
582         //exit(1);
583 }
584
585
586 bool Parser::parse(MathAtom & at)
587 {
588         skipSpaces();
589         MathArray ar;
590         parse(ar, false, InsetMath::UNDECIDED_MODE);
591         if (ar.size() != 1 || ar.front()->getType() == hullNone) {
592                 lyxerr << "unusual contents found: " << ar << endl;
593                 at = MathAtom(new InsetMathPar(ar));
594                 //if (at->nargs() > 0)
595                 //      at.nucleus()->cell(0) = ar;
596                 //else
597                 //      lyxerr << "unusual contents found: " << ar << endl;
598                 return true;
599         }
600         at = ar[0];
601         return true;
602 }
603
604
605 docstring Parser::parse_verbatim_option()
606 {
607         skipSpaces();
608         docstring res;
609         if (nextToken().character() == '[') {
610                 Token t = getToken();
611                 for (Token t = getToken(); t.character() != ']' && good(); t = getToken()) {
612                         if (t.cat() == catBegin) {
613                                 putback();
614                                 res += '{' + parse_verbatim_item() + '}';
615                         } else
616                                 res += t.asString();
617                 }
618         }
619         return res;
620 }
621
622
623 docstring Parser::parse_verbatim_item()
624 {
625         skipSpaces();
626         docstring res;
627         if (nextToken().cat() == catBegin) {
628                 Token t = getToken();
629                 for (Token t = getToken(); t.cat() != catEnd && good(); t = getToken()) {
630                         if (t.cat() == catBegin) {
631                                 putback();
632                                 res += '{' + parse_verbatim_item() + '}';
633                         }
634                         else
635                                 res += t.asString();
636                 }
637         }
638         return res;
639 }
640
641
642 MathArray Parser::parse(unsigned flags, mode_type mode)
643 {
644         MathArray ar;
645         parse(ar, flags, mode);
646         return ar;
647 }
648
649
650 void Parser::parse(MathArray & array, unsigned flags, mode_type mode)
651 {
652         InsetMathGrid grid(1, 1);
653         parse1(grid, flags, mode, false);
654         array = grid.cell(0);
655 }
656
657
658 void Parser::parse2(MathAtom & at, const unsigned flags, const mode_type mode,
659         const bool numbered)
660 {
661         parse1(*(at.nucleus()->asGridInset()), flags, mode, numbered);
662 }
663
664
665 void Parser::parse1(InsetMathGrid & grid, unsigned flags,
666         const mode_type mode, const bool numbered)
667 {
668         int limits = 0;
669         InsetMathGrid::row_type cellrow = 0;
670         InsetMathGrid::col_type cellcol = 0;
671         MathArray * cell = &grid.cell(grid.index(cellrow, cellcol));
672
673         if (grid.asHullInset())
674                 grid.asHullInset()->numbered(cellrow, numbered);
675
676         //dump();
677         //lyxerr << " flags: " << flags << endl;
678         //lyxerr << " mode: " << mode  << endl;
679         //lyxerr << "grid: " << grid << endl;
680
681         while (good()) {
682                 Token const & t = getToken();
683
684 #ifdef FILEDEBUG
685                 lyxerr << "t: " << t << " flags: " << flags << endl;
686                 lyxerr << "mode: " << mode  << endl;
687                 cell->dump();
688                 lyxerr << endl;
689 #endif
690
691                 if (flags & FLAG_ITEM) {
692
693                         if (t.cat() == catBegin) {
694                                 // skip the brace and collect everything to the next matching
695                                 // closing brace
696                                 parse1(grid, FLAG_BRACE_LAST, mode, numbered);
697                                 return;
698                         }
699
700                         // handle only this single token, leave the loop if done
701                         flags = FLAG_LEAVE;
702                 }
703
704
705                 if (flags & FLAG_BRACED) {
706                         if (t.cat() == catSpace)
707                                 continue;
708
709                         if (t.cat() != catBegin) {
710                                 error("opening brace expected");
711                                 return;
712                         }
713
714                         // skip the brace and collect everything to the next matching
715                         // closing brace
716                         flags = FLAG_BRACE_LAST;
717                 }
718
719
720                 if (flags & FLAG_OPTION) {
721                         if (t.cat() == catOther && t.character() == '[') {
722                                 MathArray ar;
723                                 parse(ar, FLAG_BRACK_LAST, mode);
724                                 cell->append(ar);
725                         } else {
726                                 // no option found, put back token and we are done
727                                 putback();
728                         }
729                         return;
730                 }
731
732                 //
733                 // cat codes
734                 //
735                 if (t.cat() == catMath) {
736                         if (mode != InsetMath::MATH_MODE) {
737                                 // we are inside some text mode thingy, so opening new math is allowed
738                                 Token const & n = getToken();
739                                 if (n.cat() == catMath) {
740                                         // TeX's $$...$$ syntax for displayed math
741                                         cell->push_back(MathAtom(new InsetMathHull(hullEquation)));
742                                         parse2(cell->back(), FLAG_SIMPLE, InsetMath::MATH_MODE, false);
743                                         getToken(); // skip the second '$' token
744                                 } else {
745                                         // simple $...$  stuff
746                                         putback();
747                                         cell->push_back(MathAtom(new InsetMathHull(hullSimple)));
748                                         parse2(cell->back(), FLAG_SIMPLE, InsetMath::MATH_MODE, false);
749                                 }
750                         }
751
752                         else if (flags & FLAG_SIMPLE) {
753                                 // this is the end of the formula
754                                 return;
755                         }
756
757                         else {
758                                 error("something strange in the parser");
759                                 break;
760                         }
761                 }
762
763                 else if (t.cat() == catLetter)
764                         cell->push_back(MathAtom(new InsetMathChar(t.character())));
765
766                 else if (t.cat() == catSpace && mode != InsetMath::MATH_MODE) {
767                         if (cell->empty() || cell->back()->getChar() != ' ')
768                                 cell->push_back(MathAtom(new InsetMathChar(t.character())));
769                 }
770
771                 else if (t.cat() == catNewline && mode != InsetMath::MATH_MODE) {
772                         if (cell->empty() || cell->back()->getChar() != ' ')
773                                 cell->push_back(MathAtom(new InsetMathChar(' ')));
774                 }
775
776                 else if (t.cat() == catParameter) {
777                         Token const & n = getToken();
778                         cell->push_back(MathAtom(new MathMacroArgument(n.character()-'0')));
779                 }
780
781                 else if (t.cat() == catActive)
782                         cell->push_back(MathAtom(new InsetMathChar(t.character())));
783
784                 else if (t.cat() == catBegin) {
785                         MathArray ar;
786                         parse(ar, FLAG_BRACE_LAST, mode);
787                         // do not create a BraceInset if they were written by LyX
788                         // this helps to keep the annoyance of  "a choose b"  to a minimum
789                         if (ar.size() == 1 && ar[0]->extraBraces())
790                                 cell->append(ar);
791                         else
792                                 cell->push_back(MathAtom(new InsetMathBrace(ar)));
793                 }
794
795                 else if (t.cat() == catEnd) {
796                         if (flags & FLAG_BRACE_LAST)
797                                 return;
798                         error("found '}' unexpectedly");
799                         //BOOST_ASSERT(false);
800                         //add(cell, '}', LM_TC_TEX);
801                 }
802
803                 else if (t.cat() == catAlign) {
804                         //lyxerr << " column now " << (cellcol + 1)
805                         //       << " max: " << grid.ncols() << endl;
806                         if (flags & FLAG_ALIGN)
807                                 return;
808                         if (addCol(grid, cellcol))
809                                 cell = &grid.cell(grid.index(cellrow, cellcol));
810                 }
811
812                 else if (t.cat() == catSuper || t.cat() == catSub) {
813                         bool up = (t.cat() == catSuper);
814                         // we need no new script inset if the last thing was a scriptinset,
815                         // which has that script already not the same script already
816                         if (!cell->size())
817                                 cell->push_back(MathAtom(new InsetMathScript(up)));
818                         else if (cell->back()->asScriptInset() &&
819                                         !cell->back()->asScriptInset()->has(up))
820                                 cell->back().nucleus()->asScriptInset()->ensure(up);
821                         else if (cell->back()->asScriptInset())
822                                 cell->push_back(MathAtom(new InsetMathScript(up)));
823                         else
824                                 cell->back() = MathAtom(new InsetMathScript(cell->back(), up));
825                         InsetMathScript * p = cell->back().nucleus()->asScriptInset();
826                         // special handling of {}-bases
827                         // Test for empty brace inset, otherwise \xxx{\vec{H}}_{0}
828                         // where \xxx is an unknown command gets misparsed to
829                         // \xxx\vec{H}_{0}, and that is invalid LaTeX.
830                         // is this always correct?
831                         if (p->nuc().size() == 1 &&
832                             p->nuc().back()->asBraceInset() &&
833                             p->nuc().back()->asBraceInset()->cell(0).empty())
834                                 p->nuc() = p->nuc().back()->asNestInset()->cell(0);
835                         parse(p->cell(p->idxOfScript(up)), FLAG_ITEM, mode);
836                         if (limits) {
837                                 p->limits(limits);
838                                 limits = 0;
839                         }
840                 }
841
842                 else if (t.character() == ']' && (flags & FLAG_BRACK_LAST)) {
843                         //lyxerr << "finished reading option" << endl;
844                         return;
845                 }
846
847                 else if (t.cat() == catOther)
848                         cell->push_back(MathAtom(new InsetMathChar(t.character())));
849
850                 else if (t.cat() == catComment) {
851                         docstring s;
852                         while (good()) {
853                                 Token const & t = getToken();
854                                 if (t.cat() == catNewline)
855                                         break;
856                                 s += t.asString();
857                         }
858                         cell->push_back(MathAtom(new InsetMathComment(s)));
859                         skipSpaces();
860                 }
861
862                 //
863                 // control sequences
864                 //
865
866                 else if (t.cs() == "lyxlock") {
867                         if (cell->size())
868                                 cell->back().nucleus()->lock(true);
869                 }
870
871                 else if (t.cs() == "def" ||
872                         t.cs() == "newcommand" ||
873                         t.cs() == "renewcommand")
874                 {
875                         docstring const type = t.cs();
876                         docstring name;
877                         int nargs = 0;
878                         if (t.cs() == "def") {
879                                 // get name
880                                 name = getToken().cs();
881
882                                 // read parameter
883                                 docstring pars;
884                                 while (good() && nextToken().cat() != catBegin) {
885                                         pars += getToken().cs();
886                                         ++nargs;
887                                 }
888                                 nargs /= 2;
889                                 //lyxerr << "read \\def parameter list '" << pars << "'" << endl;
890
891                         } else { // t.cs() == "newcommand" || t.cs() == "renewcommand"
892
893                                 if (getToken().cat() != catBegin) {
894                                         error("'{' in \\newcommand expected (1) ");
895                                         return;
896                                 }
897
898                                 name = getToken().cs();
899
900                                 if (getToken().cat() != catEnd) {
901                                         error("'}' in \\newcommand expected");
902                                         return;
903                                 }
904
905                                 docstring const arg  = getArg('[', ']');
906                                 if (!arg.empty())
907                                         nargs = convert<int>(arg);
908
909                         }
910
911                         MathArray ar1;
912                         parse(ar1, FLAG_ITEM, InsetMath::UNDECIDED_MODE);
913
914                         // we cannot handle recursive stuff at all
915                         //MathArray test;
916                         //test.push_back(createInsetMath(name));
917                         //if (ar1.contains(test)) {
918                         //      error("we cannot handle recursive macros at all.");
919                         //      return;
920                         //}
921
922                         // is a version for display attached?
923                         skipSpaces();
924                         MathArray ar2;
925                         if (nextToken().cat() == catBegin)
926                                 parse(ar2, FLAG_ITEM, InsetMath::MATH_MODE);
927
928                         cell->push_back(MathAtom(new MathMacroTemplate(name, nargs, type,
929                                 ar1, ar2)));
930                 }
931
932                 else if (t.cs() == "(") {
933                         cell->push_back(MathAtom(new InsetMathHull(hullSimple)));
934                         parse2(cell->back(), FLAG_SIMPLE2, InsetMath::MATH_MODE, false);
935                 }
936
937                 else if (t.cs() == "[") {
938                         cell->push_back(MathAtom(new InsetMathHull(hullEquation)));
939                         parse2(cell->back(), FLAG_EQUATION, InsetMath::MATH_MODE, false);
940                 }
941
942                 else if (t.cs() == "protect")
943                         // ignore \\protect, will hopefully be re-added during output
944                         ;
945
946                 else if (t.cs() == "end") {
947                         if (flags & FLAG_END) {
948                                 // eat environment name
949                                 docstring const name = getArg('{', '}');
950                                 if (environments_.empty())
951                                         error("'found \\end{" + name +
952                                               "}' without matching '\\begin{" +
953                                               name + "}'");
954                                 else if (name != environments_.back())
955                                         error("'\\end{" + name +
956                                               "}' does not match '\\begin{" +
957                                               environments_.back() + "}'");
958                                 else {
959                                         environments_.pop_back();
960                                         // Delete empty last row in matrix
961                                         // like insets.
962                                         // If you abuse InsetMathGrid for
963                                         // non-matrix like structures you
964                                         // probably need to refine this test.
965                                         // Right now we only have to test for
966                                         // single line hull insets.
967                                         if (grid.nrows() > 1)
968                                                 delEmptyLastRow(grid);
969                                         return;
970                                 }
971                         } else
972                                 error("found 'end' unexpectedly");
973                 }
974
975                 else if (t.cs() == ")") {
976                         if (flags & FLAG_SIMPLE2)
977                                 return;
978                         error("found '\\)' unexpectedly");
979                 }
980
981                 else if (t.cs() == "]") {
982                         if (flags & FLAG_EQUATION)
983                                 return;
984                         error("found '\\]' unexpectedly");
985                 }
986
987                 else if (t.cs() == "\\") {
988                         if (flags & FLAG_ALIGN)
989                                 return;
990                         if (addRow(grid, cellrow, getArg('[', ']'))) {
991                                 cellcol = 0;
992                                 if (grid.asHullInset())
993                                         grid.asHullInset()->numbered(
994                                                         cellrow, numbered);
995                                 cell = &grid.cell(grid.index(cellrow,
996                                                              cellcol));
997                         }
998                 }
999
1000 #if 0
1001                 else if (t.cs() == "multicolumn") {
1002                         // extract column count and insert dummy cells
1003                         MathArray count;
1004                         parse(count, FLAG_ITEM, mode);
1005                         int cols = 1;
1006                         if (!extractNumber(count, cols)) {
1007                                 lyxerr << " can't extract number of cells from " << count << endl;
1008                         }
1009                         // resize the table if necessary
1010                         for (int i = 0; i < cols; ++i) {
1011                                 if (addCol(grid, cellcol)) {
1012                                         cell = &grid.cell(grid.index(
1013                                                         cellrow, cellcol));
1014                                         // mark this as dummy
1015                                         grid.cellinfo(grid.index(
1016                                                 cellrow, cellcol)).dummy_ = true;
1017                                 }
1018                         }
1019                         // the last cell is the real thing, not a dummy
1020                         grid.cellinfo(grid.index(cellrow, cellcol)).dummy_ = false;
1021
1022                         // read special alignment
1023                         MathArray align;
1024                         parse(align, FLAG_ITEM, mode);
1025                         //grid.cellinfo(grid.index(cellrow, cellcol)).align_ = extractString(align);
1026
1027                         // parse the remaining contents into the "real" cell
1028                         parse(*cell, FLAG_ITEM, mode);
1029                 }
1030 #endif
1031
1032                 else if (t.cs() == "limits")
1033                         limits = 1;
1034
1035                 else if (t.cs() == "nolimits")
1036                         limits = -1;
1037
1038                 else if (t.cs() == "nonumber") {
1039                         if (grid.asHullInset())
1040                                 grid.asHullInset()->numbered(cellrow, false);
1041                 }
1042
1043                 else if (t.cs() == "number") {
1044                         if (grid.asHullInset())
1045                                 grid.asHullInset()->numbered(cellrow, true);
1046                 }
1047
1048                 else if (t.cs() == "hline") {
1049                         grid.rowinfo(cellrow).lines_ ++;
1050                 }
1051
1052                 else if (t.cs() == "sqrt") {
1053                         MathArray ar;
1054                         parse(ar, FLAG_OPTION, mode);
1055                         if (ar.size()) {
1056                                 cell->push_back(MathAtom(new InsetMathRoot));
1057                                 cell->back().nucleus()->cell(0) = ar;
1058                                 parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode);
1059                         } else {
1060                                 cell->push_back(MathAtom(new InsetMathSqrt));
1061                                 parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
1062                         }
1063                 }
1064
1065                 else if (t.cs() == "xrightarrow" || t.cs() == "xleftarrow") {
1066                         cell->push_back(createInsetMath(t.cs()));
1067                         parse(cell->back().nucleus()->cell(1), FLAG_OPTION, mode);
1068                         parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
1069                 }
1070
1071                 else if (t.cs() == "ref" || t.cs() == "prettyref" ||
1072                                 t.cs() == "pageref" || t.cs() == "vpageref" || t.cs() == "vref") {
1073                         cell->push_back(MathAtom(new RefInset(t.cs())));
1074                         parse(cell->back().nucleus()->cell(1), FLAG_OPTION, mode);
1075                         parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
1076                 }
1077
1078                 else if (t.cs() == "left") {
1079                         skipSpaces();
1080                         Token const & tl = getToken();
1081                         // \| and \Vert are equivalent, and InsetMathDelim
1082                         // can't handle \|
1083                         // FIXME: fix this in InsetMathDelim itself!
1084                         docstring const l = tl.cs() == "|" ? from_ascii("Vert") : tl.asString();
1085                         MathArray ar;
1086                         parse(ar, FLAG_RIGHT, mode);
1087                         skipSpaces();
1088                         Token const & tr = getToken();
1089                         docstring const r = tr.cs() == "|" ? from_ascii("Vert") : tr.asString();
1090                         cell->push_back(MathAtom(new InsetMathDelim(l, r, ar)));
1091                 }
1092
1093                 else if (t.cs() == "right") {
1094                         if (flags & FLAG_RIGHT)
1095                                 return;
1096                         //lyxerr << "got so far: '" << cell << "'" << endl;
1097                         error("Unmatched right delimiter");
1098                         return;
1099                 }
1100
1101                 else if (t.cs() == "begin") {
1102                         docstring const name = getArg('{', '}');
1103                         environments_.push_back(name);
1104
1105                         if (name == "array" || name == "subarray") {
1106                                 docstring const valign = parse_verbatim_option() + 'c';
1107                                 docstring const halign = parse_verbatim_item();
1108                                 cell->push_back(MathAtom(new InsetMathArray(name, (char)valign[0], halign)));
1109                                 parse2(cell->back(), FLAG_END, mode, false);
1110                         }
1111
1112                         else if (name == "tabular") {
1113                                 docstring const valign = parse_verbatim_option() + 'c';
1114                                 docstring const halign = parse_verbatim_item();
1115                                 cell->push_back(MathAtom(new InsetMathTabular(name, (char)valign[0], halign)));
1116                                 parse2(cell->back(), FLAG_END, InsetMath::TEXT_MODE, false);
1117                         }
1118
1119                         else if (name == "split" || name == "cases") {
1120                                 cell->push_back(createInsetMath(name));
1121                                 parse2(cell->back(), FLAG_END, mode, false);
1122                         }
1123
1124                         else if (name == "alignedat") {
1125                                 docstring const valign = parse_verbatim_option() + 'c';
1126                                 // ignore this for a while
1127                                 getArg('{', '}');
1128                                 cell->push_back(MathAtom(new InsetMathSplit(name, (char)valign[0])));
1129                                 parse2(cell->back(), FLAG_END, mode, false);
1130                         }
1131
1132                         else if (name == "math") {
1133                                 cell->push_back(MathAtom(new InsetMathHull(hullSimple)));
1134                                 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, true);
1135                         }
1136
1137                         else if (name == "equation" || name == "equation*"
1138                                         || name == "displaymath") {
1139                                 cell->push_back(MathAtom(new InsetMathHull(hullEquation)));
1140                                 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, (name == "equation"));
1141                         }
1142
1143                         else if (name == "eqnarray" || name == "eqnarray*") {
1144                                 cell->push_back(MathAtom(new InsetMathHull(hullEqnArray)));
1145                                 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
1146                         }
1147
1148                         else if (name == "align" || name == "align*") {
1149                                 cell->push_back(MathAtom(new InsetMathHull(hullAlign)));
1150                                 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
1151                         }
1152
1153                         else if (name == "flalign" || name == "flalign*") {
1154                                 cell->push_back(MathAtom(new InsetMathHull(hullFlAlign)));
1155                                 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
1156                         }
1157
1158                         else if (name == "alignat" || name == "alignat*") {
1159                                 // ignore this for a while
1160                                 getArg('{', '}');
1161                                 cell->push_back(MathAtom(new InsetMathHull(hullAlignAt)));
1162                                 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
1163                         }
1164
1165                         else if (name == "xalignat" || name == "xalignat*") {
1166                                 // ignore this for a while
1167                                 getArg('{', '}');
1168                                 cell->push_back(MathAtom(new InsetMathHull(hullXAlignAt)));
1169                                 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
1170                         }
1171
1172                         else if (name == "xxalignat") {
1173                                 // ignore this for a while
1174                                 getArg('{', '}');
1175                                 cell->push_back(MathAtom(new InsetMathHull(hullXXAlignAt)));
1176                                 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
1177                         }
1178
1179                         else if (name == "multline" || name == "multline*") {
1180                                 cell->push_back(MathAtom(new InsetMathHull(hullMultline)));
1181                                 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
1182                         }
1183
1184                         else if (name == "gather" || name == "gather*") {
1185                                 cell->push_back(MathAtom(new InsetMathHull(hullGather)));
1186                                 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
1187                         }
1188
1189                         else if (latexkeys const * l = in_word_set(name)) {
1190                                 if (l->inset == "matrix") {
1191                                         cell->push_back(createInsetMath(name));
1192                                         parse2(cell->back(), FLAG_END, mode, false);
1193                                 } else if (l->inset == "split") {
1194                                         docstring const valign = parse_verbatim_option() + 'c';
1195                                         cell->push_back(MathAtom(new InsetMathSplit(name, (char)valign[0])));
1196                                         parse2(cell->back(), FLAG_END, mode, false);
1197                                 } else {
1198                                         dump();
1199                                         lyxerr << "found math environment `" << name
1200                                                << "' in symbols file with unsupported inset `"
1201                                                << l->inset << "'." << endl;
1202                                         // create generic environment inset
1203                                         cell->push_back(MathAtom(new InsetMathEnv(name)));
1204                                         parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
1205                                 }
1206                         }
1207
1208                         else {
1209                                 dump();
1210                                 lyxerr << "found unknown math environment '" << name << "'" << endl;
1211                                 // create generic environment inset
1212                                 cell->push_back(MathAtom(new InsetMathEnv(name)));
1213                                 parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
1214                         }
1215                 }
1216
1217                 else if (t.cs() == "kern") {
1218 #ifdef WITH_WARNINGS
1219 #warning A hack...
1220 #endif
1221                         docstring s;
1222                         while (true) {
1223                                 Token const & t = getToken();
1224                                 if (!good()) {
1225                                         putback();
1226                                         break;
1227                                 }
1228                                 s += t.character();
1229                                 if (isValidLength(to_utf8(s)))
1230                                         break;
1231                         }
1232                         cell->push_back(MathAtom(new InsetMathKern(s)));
1233                 }
1234
1235                 else if (t.cs() == "label") {
1236                         // FIXME: This is swallowed in inline formulas
1237                         docstring label = parse_verbatim_item();
1238                         MathArray ar;
1239                         asArray(label, ar);
1240                         if (grid.asHullInset()) {
1241                                 grid.asHullInset()->label(cellrow, label);
1242                         } else {
1243                                 cell->push_back(createInsetMath(t.cs()));
1244                                 cell->push_back(MathAtom(new InsetMathBrace(ar)));
1245                         }
1246                 }
1247
1248                 else if (t.cs() == "choose" || t.cs() == "over" || t.cs() == "atop") {
1249                         MathAtom at = createInsetMath(t.cs());
1250                         at.nucleus()->cell(0) = *cell;
1251                         cell->clear();
1252                         parse(at.nucleus()->cell(1), flags, mode);
1253                         cell->push_back(at);
1254                         return;
1255                 }
1256
1257                 else if (t.cs() == "color") {
1258                         docstring const color = parse_verbatim_item();
1259                         cell->push_back(MathAtom(new InsetMathColor(true, color)));
1260                         parse(cell->back().nucleus()->cell(0), flags, mode);
1261                         return;
1262                 }
1263
1264                 else if (t.cs() == "textcolor") {
1265                         docstring const color = parse_verbatim_item();
1266                         cell->push_back(MathAtom(new InsetMathColor(false, color)));
1267                         parse(cell->back().nucleus()->cell(0), FLAG_ITEM, InsetMath::TEXT_MODE);
1268                 }
1269
1270                 else if (t.cs() == "normalcolor") {
1271                         cell->push_back(createInsetMath(t.cs()));
1272                         parse(cell->back().nucleus()->cell(0), flags, mode);
1273                         return;
1274                 }
1275
1276                 else if (t.cs() == "substack") {
1277                         cell->push_back(createInsetMath(t.cs()));
1278                         parse2(cell->back(), FLAG_ITEM, mode, false);
1279                 }
1280
1281                 else if (t.cs() == "xymatrix") {
1282                         odocstringstream os;
1283                         while (good() && nextToken().cat() != catBegin)
1284                                 os << getToken().asInput();
1285                         cell->push_back(createInsetMath(t.cs() + os.str()));
1286                         parse2(cell->back(), FLAG_ITEM, mode, false);
1287                 }
1288
1289                 else if (t.cs() == "framebox" || t.cs() == "makebox") {
1290                         cell->push_back(createInsetMath(t.cs()));
1291                         parse(cell->back().nucleus()->cell(0), FLAG_OPTION, InsetMath::TEXT_MODE);
1292                         parse(cell->back().nucleus()->cell(1), FLAG_OPTION, InsetMath::TEXT_MODE);
1293                         parse(cell->back().nucleus()->cell(2), FLAG_ITEM, InsetMath::TEXT_MODE);
1294                 }
1295
1296                 else if (t.cs() == "tag") {
1297                         if (nextToken().character() == '*') {
1298                                 getToken();
1299                                 cell->push_back(createInsetMath(t.cs() + '*'));
1300                         } else
1301                                 cell->push_back(createInsetMath(t.cs()));
1302                         parse(cell->back().nucleus()->cell(0), FLAG_ITEM, InsetMath::TEXT_MODE);
1303                 }
1304
1305 #if 0
1306                 else if (t.cs() == "infer") {
1307                         MathArray ar;
1308                         parse(ar, FLAG_OPTION, mode);
1309                         cell->push_back(createInsetMath(t.cs()));
1310                         parse2(cell->back(), FLAG_ITEM, mode, false);
1311                 }
1312
1313                 // Disabled
1314                 else if (1 && t.cs() == "ar") {
1315                         auto_ptr<InsetMathXYArrow> p(new InsetMathXYArrow);
1316                         // try to read target
1317                         parse(p->cell(0), FLAG_OTPTION, mode);
1318                         // try to read label
1319                         if (nextToken().cat() == catSuper || nextToken().cat() == catSub) {
1320                                 p->up_ = nextToken().cat() == catSuper;
1321                                 getToken();
1322                                 parse(p->cell(1), FLAG_ITEM, mode);
1323                                 //lyxerr << "read label: " << p->cell(1) << endl;
1324                         }
1325
1326                         cell->push_back(MathAtom(p.release()));
1327                         //lyxerr << "read cell: " << cell << endl;
1328                 }
1329 #endif
1330
1331                 else if (t.cs().size()) {
1332                         latexkeys const * l = in_word_set(t.cs());
1333                         if (l) {
1334                                 if (l->inset == "big") {
1335                                         skipSpaces();
1336                                         docstring const delim = getToken().asInput();
1337                                         if (InsetMathBig::isBigInsetDelim(delim))
1338                                                 cell->push_back(MathAtom(
1339                                                         new InsetMathBig(t.cs(), delim)));
1340                                         else {
1341                                                 cell->push_back(createInsetMath(t.cs()));
1342                                                 putback();
1343                                         }
1344                                 }
1345
1346                                 else if (l->inset == "font") {
1347                                         cell->push_back(createInsetMath(t.cs()));
1348                                         parse(cell->back().nucleus()->cell(0),
1349                                                 FLAG_ITEM, asMode(mode, l->extra));
1350                                 }
1351
1352                                 else if (l->inset == "oldfont") {
1353                                         cell->push_back(createInsetMath(t.cs()));
1354                                         parse(cell->back().nucleus()->cell(0),
1355                                                 flags | FLAG_ALIGN, asMode(mode, l->extra));
1356                                         if (prevToken().cat() != catAlign &&
1357                                             prevToken().cs() != "\\")
1358                                                 return;
1359                                         putback();
1360                                 }
1361
1362                                 else if (l->inset == "style") {
1363                                         cell->push_back(createInsetMath(t.cs()));
1364                                         parse(cell->back().nucleus()->cell(0),
1365                                                 flags | FLAG_ALIGN, mode);
1366                                         if (prevToken().cat() != catAlign &&
1367                                             prevToken().cs() != "\\")
1368                                                 return;
1369                                         putback();
1370                                 }
1371
1372                                 else {
1373                                         MathAtom at = createInsetMath(t.cs());
1374                                         for (InsetMath::idx_type i = 0; i < at->nargs(); ++i)
1375                                                 parse(at.nucleus()->cell(i),
1376                                                         FLAG_ITEM, asMode(mode, l->extra));
1377                                         cell->push_back(at);
1378                                 }
1379                         }
1380
1381                         else {
1382                                 MathAtom at = createInsetMath(t.cs());
1383                                 InsetMath::mode_type m = mode;
1384                                 //if (m == InsetMath::UNDECIDED_MODE)
1385                                 //lyxerr << "default creation: m1: " << m << endl;
1386                                 if (at->currentMode() != InsetMath::UNDECIDED_MODE)
1387                                         m = at->currentMode();
1388                                 //lyxerr << "default creation: m2: " << m << endl;
1389                                 InsetMath::idx_type start = 0;
1390                                 // this fails on \bigg[...\bigg]
1391                                 //MathArray opt;
1392                                 //parse(opt, FLAG_OPTION, InsetMath::VERBATIM_MODE);
1393                                 //if (opt.size()) {
1394                                 //      start = 1;
1395                                 //      at.nucleus()->cell(0) = opt;
1396                                 //}
1397                                 for (InsetMath::idx_type i = start; i < at->nargs(); ++i) {
1398                                         parse(at.nucleus()->cell(i), FLAG_ITEM, m);
1399                                         skipSpaces();
1400                                 }
1401                                 cell->push_back(at);
1402                         }
1403                 }
1404
1405
1406                 if (flags & FLAG_LEAVE) {
1407                         flags &= ~FLAG_LEAVE;
1408                         break;
1409                 }
1410         }
1411 }
1412
1413
1414
1415 } // anonymous namespace
1416
1417
1418 void mathed_parse_cell(MathArray & ar, docstring const & str)
1419 {
1420         istringstream is(to_utf8(str));
1421         mathed_parse_cell(ar, is);
1422 }
1423
1424
1425 void mathed_parse_cell(MathArray & ar, istream & is)
1426 {
1427         Parser(is).parse(ar, 0, InsetMath::MATH_MODE);
1428 }
1429
1430
1431 bool mathed_parse_normal(MathAtom & t, string const & str)
1432 {
1433         istringstream is(str);
1434         return Parser(is).parse(t);
1435 }
1436
1437
1438 bool mathed_parse_normal(MathAtom & t, istream & is)
1439 {
1440         return Parser(is).parse(t);
1441 }
1442
1443
1444 bool mathed_parse_normal(MathAtom & t, LyXLex & lex)
1445 {
1446         return Parser(lex).parse(t);
1447 }
1448
1449
1450 void mathed_parse_normal(InsetMathGrid & grid, string const & str)
1451 {
1452         istringstream is(str);
1453         Parser(is).parse1(grid, 0, InsetMath::MATH_MODE, false);
1454 }
1455
1456
1457 void initParser()
1458 {
1459         fill(theCatcode, theCatcode + 128, catOther);
1460         fill(theCatcode + 'a', theCatcode + 'z' + 1, catLetter);
1461         fill(theCatcode + 'A', theCatcode + 'Z' + 1, catLetter);
1462
1463         theCatcode[int('\\')] = catEscape;
1464         theCatcode[int('{')]  = catBegin;
1465         theCatcode[int('}')]  = catEnd;
1466         theCatcode[int('$')]  = catMath;
1467         theCatcode[int('&')]  = catAlign;
1468         theCatcode[int('\n')] = catNewline;
1469         theCatcode[int('#')]  = catParameter;
1470         theCatcode[int('^')]  = catSuper;
1471         theCatcode[int('_')]  = catSub;
1472         theCatcode[int(0x7f)] = catIgnore;
1473         theCatcode[int(' ')]  = catSpace;
1474         theCatcode[int('\t')] = catSpace;
1475         theCatcode[int('\r')] = catNewline;
1476         theCatcode[int('~')]  = catActive;
1477         theCatcode[int('%')]  = catComment;
1478 }
1479
1480
1481 } // namespace lyx