3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
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:
20 \def\makeamptab{\catcode`\&=4\relax}
21 \def\makeampletter{\catcode`\&=11\relax}
22 \def\b{\makeampletter\expandafter\makeamptab\bi}
41 #include "MathParser.h"
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"
68 #include "support/convert.h"
81 using std::istringstream;
91 InsetMath::mode_type asMode(InsetMath::mode_type oldmode, docstring const & str)
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;
102 bool stared(docstring const & s)
104 size_t const n = s.size();
105 return n && s[n - 1] == '*';
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.
114 bool addRow(InsetMathGrid & grid, InsetMathGrid::row_type & cellrow,
115 docstring const & vskip)
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
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.
129 lyxerr << "ignoring extra row";
131 lyxerr << " with extra space " << to_utf8(vskip);
132 lyxerr << '.' << endl;
136 grid.vcrskip(LyXLength(to_utf8(vskip)), cellrow - 1);
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.
146 bool addCol(InsetMathGrid & grid, InsetMathGrid::col_type & 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
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.
160 lyxerr << "ignoring extra column." << endl;
169 * Check wether the last row is empty and remove it if yes.
170 * Otherwise the following code
178 * will result in a grid with 3 rows (+ the dummy row that is always present),
179 * because the last '\\' opens a new row.
181 void delEmptyLastRow(InsetMathGrid & grid)
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())
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
191 grid.rowinfo(row + 1) = grid.rowinfo(row);
196 // These are TeX's catcodes
198 catEscape, // 0 backslash
208 catSpace, // 10 space
209 catLetter, // 11 a-zA-Z
210 catOther, // 12 none of the above
213 catInvalid // 15 <delete>
216 CatCode theCatcode[128];
219 inline CatCode catcode(lyx::char_type c)
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.
229 return theCatcode[c];
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
251 // Helper class for parsing
257 Token() : cs_(), char_(0), cat_(catIgnore) {}
259 Token(char_type c, CatCode cat) : cs_(), char_(c), cat_(cat) {}
261 Token(docstring const & cs) : cs_(cs), char_(0), cat_(catIgnore) {}
264 docstring const & cs() const { return cs_; }
266 CatCode cat() const { return cat_; }
268 char_type character() const { return char_; }
270 docstring asString() const { return cs_.size() ? cs_ : docstring(1, char_); }
272 docstring asInput() const { return cs_.size() ? '\\' + cs_ : docstring(1, char_); }
283 ostream & operator<<(ostream & os, Token const & t)
286 os << '\\' << t.cs();
287 else if (t.cat() == catLetter)
290 os << '[' << t.character() << ',' << t.cat() << ']';
298 typedef InsetMath::mode_type mode_type;
301 Parser(LyXLex & lex);
303 Parser(istream & is);
306 bool parse(MathAtom & at);
308 void parse(MathArray & array, unsigned flags, mode_type mode);
310 void parse1(InsetMathGrid & grid, unsigned flags, mode_type mode,
313 MathArray parse(unsigned flags, mode_type mode);
315 int lineno() const { return lineno_; }
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);
327 void error(string const & msg);
328 void error(docstring const & msg) { error(to_utf8(msg)); }
329 /// dump contents to screen
332 void tokenize(istream & is);
334 void tokenize(docstring const & s);
336 void skipSpaceTokens(idocstream & is, char_type c);
338 void push_back(Token const & t);
342 Token const & prevToken() const;
344 Token const & nextToken() const;
346 Token const & getToken();
347 /// skips spaces if any
350 void lex(docstring const & s);
354 docstring parse_verbatim_item();
356 docstring parse_verbatim_option();
361 vector<Token> tokens_;
364 /// Stack of active environments
365 vector<docstring> environments_;
369 Parser::Parser(LyXLex & lexer)
370 : lineno_(lexer.getLineNo()), pos_(0)
372 tokenize(lexer.getStream());
377 Parser::Parser(istream & is)
378 : lineno_(0), pos_(0)
384 void Parser::push_back(Token const & t)
386 tokens_.push_back(t);
390 void Parser::pop_back()
396 Token const & Parser::prevToken() const
398 static const Token dummy;
399 return pos_ > 0 ? tokens_[pos_ - 1] : dummy;
403 Token const & Parser::nextToken() const
405 static const Token dummy;
406 return good() ? tokens_[pos_] : dummy;
410 Token const & Parser::getToken()
412 static const Token dummy;
413 //lyxerr << "looking at token " << tokens_[pos_] << " pos: " << pos_ << endl;
414 return good() ? tokens_[pos_++] : dummy;
418 void Parser::skipSpaces()
420 while (nextToken().cat() == catSpace || nextToken().cat() == catNewline)
425 void Parser::putback()
431 bool Parser::good() const
433 return pos_ < tokens_.size();
437 char_type Parser::getChar()
440 error("The input stream is not well...");
441 return tokens_[pos_++].character();
445 docstring Parser::getArg(char_type left, char_type right)
450 char_type c = getChar();
455 while ((c = getChar()) != right && good())
462 void Parser::skipSpaceTokens(idocstream & is, char_type c)
464 // skip trailing spaces
465 while (catcode(c) == catSpace || catcode(c) == catNewline)
468 //lyxerr << "putting back: " << c << endl;
473 void Parser::tokenize(istream & is)
475 // eat everything up to the next \end_inset or end of stream
476 // and store it in s for further tokenization
481 if (s.size() >= 10 && s.substr(s.size() - 10) == "\\end_inset") {
482 s = s.substr(0, s.size() - 10);
486 // Remove the space after \end_inset
487 if (is.get(c) && c != ' ')
491 tokenize(from_utf8(s));
495 void Parser::tokenize(docstring const & buffer)
497 idocstringstream is(buffer, ios::in | ios::binary);
501 //lyxerr << "reading c: " << c << endl;
503 switch (catcode(c)) {
507 if (catcode(c) == catNewline)
508 ; //push_back(Token("par"));
510 push_back(Token('\n', catNewline));
518 while (is.get(c) && catcode(c) != catNewline)
528 error("unexpected end of input");
531 if (catcode(c) == catLetter) {
533 while (is.get(c) && catcode(c) == catLetter)
535 skipSpaceTokens(is, c);
544 push_back(Token(c, catcode(c)));
546 skipSpaceTokens(is, c);
551 lyxerr << "ignoring a char: " << int(c) << endl;
556 push_back(Token(c, catcode(c)));
566 void Parser::dump() const
568 lyxerr << "\nTokens: ";
569 for (unsigned i = 0; i < tokens_.size(); ++i) {
572 lyxerr << tokens_[i];
574 lyxerr << " pos: " << pos_ << endl;
578 void Parser::error(string const & msg)
580 lyxerr << "Line ~" << lineno_ << ": Math parse error: " << msg << endl;
586 bool Parser::parse(MathAtom & at)
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;
597 // lyxerr << "unusual contents found: " << ar << endl;
605 docstring Parser::parse_verbatim_option()
609 if (nextToken().character() == '[') {
610 Token t = getToken();
611 for (Token t = getToken(); t.character() != ']' && good(); t = getToken()) {
612 if (t.cat() == catBegin) {
614 res += '{' + parse_verbatim_item() + '}';
623 docstring Parser::parse_verbatim_item()
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) {
632 res += '{' + parse_verbatim_item() + '}';
642 MathArray Parser::parse(unsigned flags, mode_type mode)
645 parse(ar, flags, mode);
650 void Parser::parse(MathArray & array, unsigned flags, mode_type mode)
652 InsetMathGrid grid(1, 1);
653 parse1(grid, flags, mode, false);
654 array = grid.cell(0);
658 void Parser::parse2(MathAtom & at, const unsigned flags, const mode_type mode,
661 parse1(*(at.nucleus()->asGridInset()), flags, mode, numbered);
665 void Parser::parse1(InsetMathGrid & grid, unsigned flags,
666 const mode_type mode, const bool numbered)
669 InsetMathGrid::row_type cellrow = 0;
670 InsetMathGrid::col_type cellcol = 0;
671 MathArray * cell = &grid.cell(grid.index(cellrow, cellcol));
673 if (grid.asHullInset())
674 grid.asHullInset()->numbered(cellrow, numbered);
677 //lyxerr << " flags: " << flags << endl;
678 //lyxerr << " mode: " << mode << endl;
679 //lyxerr << "grid: " << grid << endl;
682 Token const & t = getToken();
685 lyxerr << "t: " << t << " flags: " << flags << endl;
686 lyxerr << "mode: " << mode << endl;
691 if (flags & FLAG_ITEM) {
693 if (t.cat() == catBegin) {
694 // skip the brace and collect everything to the next matching
696 parse1(grid, FLAG_BRACE_LAST, mode, numbered);
700 // handle only this single token, leave the loop if done
705 if (flags & FLAG_BRACED) {
706 if (t.cat() == catSpace)
709 if (t.cat() != catBegin) {
710 error("opening brace expected");
714 // skip the brace and collect everything to the next matching
716 flags = FLAG_BRACE_LAST;
720 if (flags & FLAG_OPTION) {
721 if (t.cat() == catOther && t.character() == '[') {
723 parse(ar, FLAG_BRACK_LAST, mode);
726 // no option found, put back token and we are done
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
745 // simple $...$ stuff
747 cell->push_back(MathAtom(new InsetMathHull(hullSimple)));
748 parse2(cell->back(), FLAG_SIMPLE, InsetMath::MATH_MODE, false);
752 else if (flags & FLAG_SIMPLE) {
753 // this is the end of the formula
758 error("something strange in the parser");
763 else if (t.cat() == catLetter)
764 cell->push_back(MathAtom(new InsetMathChar(t.character())));
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())));
771 else if (t.cat() == catNewline && mode != InsetMath::MATH_MODE) {
772 if (cell->empty() || cell->back()->getChar() != ' ')
773 cell->push_back(MathAtom(new InsetMathChar(' ')));
776 else if (t.cat() == catParameter) {
777 Token const & n = getToken();
778 cell->push_back(MathAtom(new MathMacroArgument(n.character()-'0')));
781 else if (t.cat() == catActive)
782 cell->push_back(MathAtom(new InsetMathChar(t.character())));
784 else if (t.cat() == catBegin) {
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())
792 cell->push_back(MathAtom(new InsetMathBrace(ar)));
795 else if (t.cat() == catEnd) {
796 if (flags & FLAG_BRACE_LAST)
798 error("found '}' unexpectedly");
799 //BOOST_ASSERT(false);
800 //add(cell, '}', LM_TC_TEX);
803 else if (t.cat() == catAlign) {
804 //lyxerr << " column now " << (cellcol + 1)
805 // << " max: " << grid.ncols() << endl;
806 if (flags & FLAG_ALIGN)
808 if (addCol(grid, cellcol))
809 cell = &grid.cell(grid.index(cellrow, cellcol));
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
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)));
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);
842 else if (t.character() == ']' && (flags & FLAG_BRACK_LAST)) {
843 //lyxerr << "finished reading option" << endl;
847 else if (t.cat() == catOther)
848 cell->push_back(MathAtom(new InsetMathChar(t.character())));
850 else if (t.cat() == catComment) {
853 Token const & t = getToken();
854 if (t.cat() == catNewline)
858 cell->push_back(MathAtom(new InsetMathComment(s)));
866 else if (t.cs() == "lyxlock") {
868 cell->back().nucleus()->lock(true);
871 else if (t.cs() == "def" ||
872 t.cs() == "newcommand" ||
873 t.cs() == "renewcommand")
875 docstring const type = t.cs();
878 if (t.cs() == "def") {
880 name = getToken().cs();
884 while (good() && nextToken().cat() != catBegin) {
885 pars += getToken().cs();
889 //lyxerr << "read \\def parameter list '" << pars << "'" << endl;
891 } else { // t.cs() == "newcommand" || t.cs() == "renewcommand"
893 if (getToken().cat() != catBegin) {
894 error("'{' in \\newcommand expected (1) ");
898 name = getToken().cs();
900 if (getToken().cat() != catEnd) {
901 error("'}' in \\newcommand expected");
905 docstring const arg = getArg('[', ']');
907 nargs = convert<int>(arg);
912 parse(ar1, FLAG_ITEM, InsetMath::UNDECIDED_MODE);
914 // we cannot handle recursive stuff at all
916 //test.push_back(createInsetMath(name));
917 //if (ar1.contains(test)) {
918 // error("we cannot handle recursive macros at all.");
922 // is a version for display attached?
925 if (nextToken().cat() == catBegin)
926 parse(ar2, FLAG_ITEM, InsetMath::MATH_MODE);
928 cell->push_back(MathAtom(new MathMacroTemplate(name, nargs, type,
932 else if (t.cs() == "(") {
933 cell->push_back(MathAtom(new InsetMathHull(hullSimple)));
934 parse2(cell->back(), FLAG_SIMPLE2, InsetMath::MATH_MODE, false);
937 else if (t.cs() == "[") {
938 cell->push_back(MathAtom(new InsetMathHull(hullEquation)));
939 parse2(cell->back(), FLAG_EQUATION, InsetMath::MATH_MODE, false);
942 else if (t.cs() == "protect")
943 // ignore \\protect, will hopefully be re-added during output
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{" +
954 else if (name != environments_.back())
955 error("'\\end{" + name +
956 "}' does not match '\\begin{" +
957 environments_.back() + "}'");
959 environments_.pop_back();
960 // Delete empty last row in matrix
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);
972 error("found 'end' unexpectedly");
975 else if (t.cs() == ")") {
976 if (flags & FLAG_SIMPLE2)
978 error("found '\\)' unexpectedly");
981 else if (t.cs() == "]") {
982 if (flags & FLAG_EQUATION)
984 error("found '\\]' unexpectedly");
987 else if (t.cs() == "\\") {
988 if (flags & FLAG_ALIGN)
990 if (addRow(grid, cellrow, getArg('[', ']'))) {
992 if (grid.asHullInset())
993 grid.asHullInset()->numbered(
995 cell = &grid.cell(grid.index(cellrow,
1001 else if (t.cs() == "multicolumn") {
1002 // extract column count and insert dummy cells
1004 parse(count, FLAG_ITEM, mode);
1006 if (!extractNumber(count, cols)) {
1007 lyxerr << " can't extract number of cells from " << count << endl;
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(
1014 // mark this as dummy
1015 grid.cellinfo(grid.index(
1016 cellrow, cellcol)).dummy_ = true;
1019 // the last cell is the real thing, not a dummy
1020 grid.cellinfo(grid.index(cellrow, cellcol)).dummy_ = false;
1022 // read special alignment
1024 parse(align, FLAG_ITEM, mode);
1025 //grid.cellinfo(grid.index(cellrow, cellcol)).align_ = extractString(align);
1027 // parse the remaining contents into the "real" cell
1028 parse(*cell, FLAG_ITEM, mode);
1032 else if (t.cs() == "limits")
1035 else if (t.cs() == "nolimits")
1038 else if (t.cs() == "nonumber") {
1039 if (grid.asHullInset())
1040 grid.asHullInset()->numbered(cellrow, false);
1043 else if (t.cs() == "number") {
1044 if (grid.asHullInset())
1045 grid.asHullInset()->numbered(cellrow, true);
1048 else if (t.cs() == "hline") {
1049 grid.rowinfo(cellrow).lines_ ++;
1052 else if (t.cs() == "sqrt") {
1054 parse(ar, FLAG_OPTION, mode);
1056 cell->push_back(MathAtom(new InsetMathRoot));
1057 cell->back().nucleus()->cell(0) = ar;
1058 parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode);
1060 cell->push_back(MathAtom(new InsetMathSqrt));
1061 parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode);
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);
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);
1078 else if (t.cs() == "left") {
1080 Token const & tl = getToken();
1081 // \| and \Vert are equivalent, and InsetMathDelim
1083 // FIXME: fix this in InsetMathDelim itself!
1084 docstring const l = tl.cs() == "|" ? from_ascii("Vert") : tl.asString();
1086 parse(ar, FLAG_RIGHT, mode);
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)));
1093 else if (t.cs() == "right") {
1094 if (flags & FLAG_RIGHT)
1096 //lyxerr << "got so far: '" << cell << "'" << endl;
1097 error("Unmatched right delimiter");
1101 else if (t.cs() == "begin") {
1102 docstring const name = getArg('{', '}');
1103 environments_.push_back(name);
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);
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);
1119 else if (name == "split" || name == "cases") {
1120 cell->push_back(createInsetMath(name));
1121 parse2(cell->back(), FLAG_END, mode, false);
1124 else if (name == "alignedat") {
1125 docstring const valign = parse_verbatim_option() + 'c';
1126 // ignore this for a while
1128 cell->push_back(MathAtom(new InsetMathSplit(name, (char)valign[0])));
1129 parse2(cell->back(), FLAG_END, mode, false);
1132 else if (name == "math") {
1133 cell->push_back(MathAtom(new InsetMathHull(hullSimple)));
1134 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, true);
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"));
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));
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));
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));
1158 else if (name == "alignat" || name == "alignat*") {
1159 // ignore this for a while
1161 cell->push_back(MathAtom(new InsetMathHull(hullAlignAt)));
1162 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
1165 else if (name == "xalignat" || name == "xalignat*") {
1166 // ignore this for a while
1168 cell->push_back(MathAtom(new InsetMathHull(hullXAlignAt)));
1169 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
1172 else if (name == "xxalignat") {
1173 // ignore this for a while
1175 cell->push_back(MathAtom(new InsetMathHull(hullXXAlignAt)));
1176 parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name));
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));
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));
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);
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);
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);
1217 else if (t.cs() == "kern") {
1218 #ifdef WITH_WARNINGS
1223 Token const & t = getToken();
1229 if (isValidLength(to_utf8(s)))
1232 cell->push_back(MathAtom(new InsetMathKern(s)));
1235 else if (t.cs() == "label") {
1236 // FIXME: This is swallowed in inline formulas
1237 docstring label = parse_verbatim_item();
1240 if (grid.asHullInset()) {
1241 grid.asHullInset()->label(cellrow, label);
1243 cell->push_back(createInsetMath(t.cs()));
1244 cell->push_back(MathAtom(new InsetMathBrace(ar)));
1248 else if (t.cs() == "choose" || t.cs() == "over" || t.cs() == "atop") {
1249 MathAtom at = createInsetMath(t.cs());
1250 at.nucleus()->cell(0) = *cell;
1252 parse(at.nucleus()->cell(1), flags, mode);
1253 cell->push_back(at);
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);
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);
1270 else if (t.cs() == "normalcolor") {
1271 cell->push_back(createInsetMath(t.cs()));
1272 parse(cell->back().nucleus()->cell(0), flags, mode);
1276 else if (t.cs() == "substack") {
1277 cell->push_back(createInsetMath(t.cs()));
1278 parse2(cell->back(), FLAG_ITEM, mode, false);
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);
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);
1296 else if (t.cs() == "tag") {
1297 if (nextToken().character() == '*') {
1299 cell->push_back(createInsetMath(t.cs() + '*'));
1301 cell->push_back(createInsetMath(t.cs()));
1302 parse(cell->back().nucleus()->cell(0), FLAG_ITEM, InsetMath::TEXT_MODE);
1306 else if (t.cs() == "infer") {
1308 parse(ar, FLAG_OPTION, mode);
1309 cell->push_back(createInsetMath(t.cs()));
1310 parse2(cell->back(), FLAG_ITEM, mode, false);
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;
1322 parse(p->cell(1), FLAG_ITEM, mode);
1323 //lyxerr << "read label: " << p->cell(1) << endl;
1326 cell->push_back(MathAtom(p.release()));
1327 //lyxerr << "read cell: " << cell << endl;
1331 else if (t.cs().size()) {
1332 latexkeys const * l = in_word_set(t.cs());
1334 if (l->inset == "big") {
1336 docstring const delim = getToken().asInput();
1337 if (InsetMathBig::isBigInsetDelim(delim))
1338 cell->push_back(MathAtom(
1339 new InsetMathBig(t.cs(), delim)));
1341 cell->push_back(createInsetMath(t.cs()));
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));
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() != "\\")
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() != "\\")
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);
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]
1392 //parse(opt, FLAG_OPTION, InsetMath::VERBATIM_MODE);
1395 // at.nucleus()->cell(0) = opt;
1397 for (InsetMath::idx_type i = start; i < at->nargs(); ++i) {
1398 parse(at.nucleus()->cell(i), FLAG_ITEM, m);
1401 cell->push_back(at);
1406 if (flags & FLAG_LEAVE) {
1407 flags &= ~FLAG_LEAVE;
1415 } // anonymous namespace
1418 void mathed_parse_cell(MathArray & ar, docstring const & str)
1420 istringstream is(to_utf8(str));
1421 mathed_parse_cell(ar, is);
1425 void mathed_parse_cell(MathArray & ar, istream & is)
1427 Parser(is).parse(ar, 0, InsetMath::MATH_MODE);
1431 bool mathed_parse_normal(MathAtom & t, string const & str)
1433 istringstream is(str);
1434 return Parser(is).parse(t);
1438 bool mathed_parse_normal(MathAtom & t, istream & is)
1440 return Parser(is).parse(t);
1444 bool mathed_parse_normal(MathAtom & t, LyXLex & lex)
1446 return Parser(lex).parse(t);
1450 void mathed_parse_normal(InsetMathGrid & grid, string const & str)
1452 istringstream is(str);
1453 Parser(is).parse1(grid, 0, InsetMath::MATH_MODE, false);
1459 fill(theCatcode, theCatcode + 128, catOther);
1460 fill(theCatcode + 'a', theCatcode + 'z' + 1, catLetter);
1461 fill(theCatcode + 'A', theCatcode + 'Z' + 1, catLetter);
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;