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}
40 #include "MathParser.h"
42 #include "InsetMathArray.h"
43 #include "InsetMathBig.h"
44 #include "InsetMathBrace.h"
45 #include "InsetMathChar.h"
46 #include "InsetMathColor.h"
47 #include "InsetMathComment.h"
48 #include "InsetMathDelim.h"
49 #include "InsetMathEnsureMath.h"
50 #include "InsetMathEnv.h"
51 #include "InsetMathFrac.h"
52 #include "InsetMathKern.h"
53 #include "MathMacro.h"
54 #include "InsetMathPar.h"
55 #include "InsetMathRef.h"
56 #include "InsetMathRoot.h"
57 #include "InsetMathScript.h"
58 #include "InsetMathSpace.h"
59 #include "InsetMathSplit.h"
60 #include "InsetMathSqrt.h"
61 #include "InsetMathTabular.h"
62 #include "MathMacroTemplate.h"
63 #include "MathFactory.h"
64 #include "MathMacroArgument.h"
65 #include "MathSupport.h"
71 #include "support/debug.h"
72 #include "support/convert.h"
73 #include "support/docstream.h"
85 InsetMath::mode_type
asMode(InsetMath::mode_type oldmode
, docstring
const & str
)
87 //lyxerr << "handling mode: '" << str << "'" << endl;
88 if (str
== "mathmode")
89 return InsetMath::MATH_MODE
;
90 if (str
== "textmode" || str
== "forcetext")
91 return InsetMath::TEXT_MODE
;
96 bool stared(docstring
const & s
)
98 size_t const n
= s
.size();
99 return n
&& s
[n
- 1] == '*';
103 docstring
const repl(docstring
const & oldstr
, char_type
const c
,
104 docstring
const & macro
, bool textmode
= false)
110 for (i
= 0, j
= 0; i
< oldstr
.size(); ++i
) {
111 if (c
== oldstr
[i
]) {
112 newstr
.append(oldstr
, j
, i
- j
);
113 newstr
.append(macro
);
115 if (macro
.size() > 2 && j
< oldstr
.size())
116 newstr
+= (textmode
&& oldstr
[j
] == ' ' ? '\\' : ' ');
124 newstr
.append(oldstr
, j
, i
- j
);
129 docstring
escapeSpecialChars(docstring
const & str
, bool textmode
)
131 docstring
const backslash
= textmode
? from_ascii("\\textbackslash")
132 : from_ascii("\\backslash");
133 docstring
const caret
= textmode
? from_ascii("\\textasciicircum")
134 : from_ascii("\\mathcircumflex");
135 docstring
const tilde
= textmode
? from_ascii("\\textasciitilde")
136 : from_ascii("\\sim");
138 return repl(repl(repl(repl(repl(repl(repl(repl(repl(repl(str
,
139 '\\', backslash
, textmode
),
140 '^', caret
, textmode
),
141 '~', tilde
, textmode
),
142 '_', from_ascii("\\_")),
143 '$', from_ascii("\\$")),
144 '#', from_ascii("\\#")),
145 '&', from_ascii("\\&")),
146 '%', from_ascii("\\%")),
147 '{', from_ascii("\\{")),
148 '}', from_ascii("\\}"));
153 * Add the row \p cellrow to \p grid.
154 * \returns wether the row could be added. Adding a row can fail for
155 * environments like "equation" that have a fixed number of rows.
157 bool addRow(InsetMathGrid
& grid
, InsetMathGrid::row_type
& cellrow
,
158 docstring
const & vskip
, bool allow_newpage_
= true)
161 if (cellrow
== grid
.nrows()) {
162 //lyxerr << "adding row " << cellrow << endl;
163 grid
.addRow(cellrow
- 1);
164 if (cellrow
== grid
.nrows()) {
165 // We can't add a row to this grid, so let's
166 // append the content of this cell to the previous
168 // This does not happen in well formed .lyx files,
169 // but LyX versions 1.3.x and older could create
170 // such files and tex2lyx can still do that.
172 lyxerr
<< "ignoring extra row";
174 lyxerr
<< " with extra space " << to_utf8(vskip
);
176 lyxerr
<< " with no page break allowed";
177 lyxerr
<< '.' << endl
;
181 grid
.vcrskip(Length(to_utf8(vskip
)), cellrow
- 1);
182 grid
.rowinfo(cellrow
- 1).allow_newpage_
= allow_newpage_
;
188 * Add the column \p cellcol to \p grid.
189 * \returns wether the column could be added. Adding a column can fail for
190 * environments like "eqnarray" that have a fixed number of columns.
192 bool addCol(InsetMathGrid
& grid
, InsetMathGrid::col_type
& cellcol
)
195 if (cellcol
== grid
.ncols()) {
196 //lyxerr << "adding column " << cellcol << endl;
197 grid
.addCol(cellcol
);
198 if (cellcol
== grid
.ncols()) {
199 // We can't add a column to this grid, so let's
200 // append the content of this cell to the previous
202 // This does not happen in well formed .lyx files,
203 // but LyX versions 1.3.x and older could create
204 // such files and tex2lyx can still do that.
206 lyxerr
<< "ignoring extra column." << endl
;
215 * Check wether the last row is empty and remove it if yes.
216 * Otherwise the following code
224 * will result in a grid with 3 rows (+ the dummy row that is always present),
225 * because the last '\\' opens a new row.
227 void delEmptyLastRow(InsetMathGrid
& grid
)
229 InsetMathGrid::row_type
const row
= grid
.nrows() - 1;
230 for (InsetMathGrid::col_type col
= 0; col
< grid
.ncols(); ++col
) {
231 if (!grid
.cell(grid
.index(row
, col
)).empty())
234 // Copy the row information of the empty row (which would contain the
235 // last hline in the example above) to the dummy row and delete the
237 grid
.rowinfo(row
+ 1) = grid
.rowinfo(row
);
242 // These are TeX's catcodes
244 catEscape
, // 0 backslash
254 catSpace
, // 10 space
255 catLetter
, // 11 a-zA-Z
256 catOther
, // 12 none of the above
259 catInvalid
// 15 <delete>
262 CatCode theCatcode
[128];
265 inline CatCode
catcode(char_type c
)
267 /* The only characters that are not catOther lie in the pure ASCII
268 * range. Therefore theCatcode has only 128 entries.
269 * TeX itself deals with 8bit characters, so if needed this table
270 * could be enlarged to 256 entries.
271 * Any larger value does not make sense, since the fact that we use
272 * unicode internally does not change Knuth's TeX engine.
273 * Apart from that a table for the full 21bit UCS4 range would waste
274 * too much memory. */
278 return theCatcode
[c
];
283 FLAG_ALIGN
= 1 << 0, // next & or \\ ends the parsing process
284 FLAG_BRACE_LAST
= 1 << 1, // next closing brace ends the parsing
285 FLAG_RIGHT
= 1 << 2, // next \\right ends the parsing process
286 FLAG_END
= 1 << 3, // next \\end ends the parsing process
287 FLAG_BRACK_LAST
= 1 << 4, // next closing bracket ends the parsing
288 FLAG_TEXTMODE
= 1 << 5, // we are in a box
289 FLAG_ITEM
= 1 << 6, // read a (possibly braced) token
290 FLAG_LEAVE
= 1 << 7, // leave the loop at the end
291 FLAG_SIMPLE
= 1 << 8, // next $ leaves the loop
292 FLAG_EQUATION
= 1 << 9, // next \] leaves the loop
293 FLAG_SIMPLE2
= 1 << 10, // next \) leaves the loop
294 FLAG_OPTION
= 1 << 11, // read [...] style option
295 FLAG_BRACED
= 1 << 12 // read {...} style argument
300 // Helper class for parsing
306 Token() : cs_(), char_(0), cat_(catIgnore
) {}
308 Token(char_type c
, CatCode cat
) : cs_(), char_(c
), cat_(cat
) {}
310 explicit Token(docstring
const & cs
) : cs_(cs
), char_(0), cat_(catIgnore
) {}
313 docstring
const & cs() const { return cs_
; }
315 CatCode
cat() const { return cat_
; }
317 char_type
character() const { return char_
; }
319 docstring
asString() const { return cs_
.size() ? cs_
: docstring(1, char_
); }
321 docstring
asInput() const { return cs_
.size() ? '\\' + cs_
: docstring(1, char_
); }
333 ostream
& operator<<(ostream
& os
, Token
const & t
)
336 docstring
const & cs
= t
.cs();
337 // FIXME: For some strange reason, the stream operator instanciate
338 // a new Token before outputting the contents of t.cs().
339 // Because of this the line
341 // below becomes recursive.
342 // In order to avoid that we return early:
345 os
<< '\\' << to_utf8(cs
);
347 else if (t
.cat() == catLetter
)
350 os
<< '[' << t
.character() << ',' << t
.cat() << ']';
358 typedef InsetMath::mode_type mode_type
;
360 typedef Parse::flags parse_mode
;
363 Parser(Lexer
& lex
, parse_mode mode
, Buffer
* buf
);
364 /// Only use this for reading from .lyx file format, for the reason
365 /// see Parser::tokenize(istream &).
366 Parser(istream
& is
, parse_mode mode
, Buffer
* buf
);
368 Parser(docstring
const & str
, parse_mode mode
, Buffer
* buf
);
371 bool parse(MathAtom
& at
);
373 bool parse(MathData
& array
, unsigned flags
, mode_type mode
);
375 bool parse1(InsetMathGrid
& grid
, unsigned flags
, mode_type mode
,
378 MathData
parse(unsigned flags
, mode_type mode
);
380 int lineno() const { return lineno_
; }
386 void parse2(MathAtom
& at
, unsigned flags
, mode_type mode
, bool numbered
);
387 /// get arg delimited by 'left' and 'right'
388 docstring
getArg(char_type left
, char_type right
);
392 void error(string
const & msg
);
393 void error(docstring
const & msg
) { error(to_utf8(msg
)); }
394 /// dump contents to screen
396 /// Only use this for reading from .lyx file format (see
397 /// implementation for reason)
398 void tokenize(istream
& is
);
400 void tokenize(docstring
const & s
);
402 void skipSpaceTokens(idocstream
& is
, char_type c
);
404 void push_back(Token
const & t
);
408 Token
const & prevToken() const;
410 Token
const & nextToken() const;
412 Token
const & getToken();
413 /// skips spaces if any
416 void lex(docstring
const & s
);
420 docstring
parse_verbatim_item();
422 docstring
parse_verbatim_option();
427 vector
<Token
> tokens_
;
430 /// Stack of active environments
431 vector
<docstring
> environments_
;
441 Parser::Parser(Lexer
& lexer
, parse_mode mode
, Buffer
* buf
)
442 : lineno_(lexer
.lineNumber()), pos_(0), mode_(mode
), success_(true),
445 tokenize(lexer
.getStream());
450 Parser::Parser(istream
& is
, parse_mode mode
, Buffer
* buf
)
451 : lineno_(0), pos_(0), mode_(mode
), success_(true), buffer_(buf
)
457 Parser::Parser(docstring
const & str
, parse_mode mode
, Buffer
* buf
)
458 : lineno_(0), pos_(0), mode_(mode
), success_(true), buffer_(buf
)
464 void Parser::push_back(Token
const & t
)
466 tokens_
.push_back(t
);
470 void Parser::pop_back()
476 Token
const & Parser::prevToken() const
478 static const Token dummy
;
479 return pos_
> 0 ? tokens_
[pos_
- 1] : dummy
;
483 Token
const & Parser::nextToken() const
485 static const Token dummy
;
486 return good() ? tokens_
[pos_
] : dummy
;
490 Token
const & Parser::getToken()
492 static const Token dummy
;
493 //lyxerr << "looking at token " << tokens_[pos_] << " pos: " << pos_ << endl;
494 return good() ? tokens_
[pos_
++] : dummy
;
498 void Parser::skipSpaces()
500 while (nextToken().cat() == catSpace
|| nextToken().cat() == catNewline
)
505 void Parser::putback()
511 bool Parser::good() const
513 return pos_
< tokens_
.size();
517 char_type
Parser::getChar()
520 error("The input stream is not well...");
524 return tokens_
[pos_
++].character();
528 docstring
Parser::getArg(char_type left
, char_type right
)
533 char_type c
= getChar();
538 while ((c
= getChar()) != right
&& good())
545 void Parser::skipSpaceTokens(idocstream
& is
, char_type c
)
547 // skip trailing spaces
548 while (catcode(c
) == catSpace
|| catcode(c
) == catNewline
)
551 //lyxerr << "putting back: " << c << endl;
556 void Parser::tokenize(istream
& is
)
558 // eat everything up to the next \end_inset or end of stream
559 // and store it in s for further tokenization
564 if (s
.size() >= 10 && s
.substr(s
.size() - 10) == "\\end_inset") {
565 s
= s
.substr(0, s
.size() - 10);
569 // Remove the space after \end_inset
570 if (is
.get(c
) && c
!= ' ')
574 tokenize(from_utf8(s
));
578 void Parser::tokenize(docstring
const & buffer
)
580 idocstringstream
is(mode_
& Parse::VERBATIM
581 ? escapeSpecialChars(buffer
, mode_
& Parse::TEXTMODE
)
582 : buffer
, ios::in
| ios::binary
);
586 //lyxerr << "reading c: " << c << endl;
588 switch (catcode(c
)) {
592 if (catcode(c
) == catNewline
)
593 ; //push_back(Token("par"));
595 push_back(Token('\n', catNewline
));
603 while (is.get(c) && catcode(c) != catNewline)
613 error("unexpected end of input");
616 if (catcode(c
) == catLetter
) {
618 while (is
.get(c
) && catcode(c
) == catLetter
)
620 skipSpaceTokens(is
, c
);
629 push_back(Token(c
, catcode(c
)));
631 skipSpaceTokens(is
, c
);
636 if (!(mode_
& Parse::QUIET
))
637 lyxerr
<< "ignoring a char: " << int(c
) << endl
;
642 push_back(Token(c
, catcode(c
)));
652 void Parser::dump() const
654 lyxerr
<< "\nTokens: ";
655 for (unsigned i
= 0; i
< tokens_
.size(); ++i
) {
658 lyxerr
<< tokens_
[i
];
660 lyxerr
<< " pos: " << pos_
<< endl
;
664 void Parser::error(string
const & msg
)
667 if (!(mode_
& Parse::QUIET
)) {
668 lyxerr
<< "Line ~" << lineno_
<< ": Math parse error: "
675 bool Parser::parse(MathAtom
& at
)
678 MathData
ar(buffer_
);
679 parse(ar
, false, InsetMath::UNDECIDED_MODE
);
680 if (ar
.size() != 1 || ar
.front()->getType() == hullNone
) {
681 if (!(mode_
& Parse::QUIET
))
682 lyxerr
<< "unusual contents found: " << ar
<< endl
;
683 at
= MathAtom(new InsetMathPar(buffer_
, ar
));
684 //if (at->nargs() > 0)
685 // at.nucleus()->cell(0) = ar;
687 // lyxerr << "unusual contents found: " << ar << endl;
695 docstring
Parser::parse_verbatim_option()
699 if (nextToken().character() == '[') {
700 Token t
= getToken();
701 for (Token t
= getToken(); t
.character() != ']' && good(); t
= getToken()) {
702 if (t
.cat() == catBegin
) {
704 res
+= '{' + parse_verbatim_item() + '}';
713 docstring
Parser::parse_verbatim_item()
717 if (nextToken().cat() == catBegin
) {
718 Token t
= getToken();
719 for (Token t
= getToken(); t
.cat() != catEnd
&& good(); t
= getToken()) {
720 if (t
.cat() == catBegin
) {
722 res
+= '{' + parse_verbatim_item() + '}';
732 MathData
Parser::parse(unsigned flags
, mode_type mode
)
734 MathData
ar(buffer_
);
735 parse(ar
, flags
, mode
);
740 bool Parser::parse(MathData
& array
, unsigned flags
, mode_type mode
)
742 InsetMathGrid
grid(buffer_
, 1, 1);
743 parse1(grid
, flags
, mode
, false);
744 array
= grid
.cell(0);
749 void Parser::parse2(MathAtom
& at
, const unsigned flags
, const mode_type mode
,
752 parse1(*(at
.nucleus()->asGridInset()), flags
, mode
, numbered
);
756 bool Parser::parse1(InsetMathGrid
& grid
, unsigned flags
,
757 const mode_type mode
, const bool numbered
)
760 InsetMathGrid::row_type cellrow
= 0;
761 InsetMathGrid::col_type cellcol
= 0;
762 MathData
* cell
= &grid
.cell(grid
.index(cellrow
, cellcol
));
763 Buffer
* buf
= buffer_
;
765 if (grid
.asHullInset())
766 grid
.asHullInset()->numbered(cellrow
, numbered
);
769 //lyxerr << " flags: " << flags << endl;
770 //lyxerr << " mode: " << mode << endl;
771 //lyxerr << "grid: " << grid << endl;
774 Token
const & t
= getToken();
777 lyxerr
<< "t: " << t
<< " flags: " << flags
<< endl
;
778 lyxerr
<< "mode: " << mode
<< endl
;
783 if (flags
& FLAG_ITEM
) {
785 if (t
.cat() == catBegin
) {
786 // skip the brace and collect everything to the next matching
788 parse1(grid
, FLAG_BRACE_LAST
, mode
, numbered
);
792 // handle only this single token, leave the loop if done
797 if (flags
& FLAG_BRACED
) {
798 if (t
.cat() == catSpace
)
801 if (t
.cat() != catBegin
) {
802 error("opening brace expected");
806 // skip the brace and collect everything to the next matching
808 flags
= FLAG_BRACE_LAST
;
812 if (flags
& FLAG_OPTION
) {
813 if (t
.cat() == catOther
&& t
.character() == '[') {
815 parse(ar
, FLAG_BRACK_LAST
, mode
);
818 // no option found, put back token and we are done
827 if (t
.cat() == catMath
) {
828 if (mode
!= InsetMath::MATH_MODE
) {
829 // we are inside some text mode thingy, so opening new math is allowed
830 Token
const & n
= getToken();
831 if (n
.cat() == catMath
) {
832 // TeX's $$...$$ syntax for displayed math
833 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullEquation
)));
834 parse2(cell
->back(), FLAG_SIMPLE
, InsetMath::MATH_MODE
, false);
835 getToken(); // skip the second '$' token
837 // simple $...$ stuff
839 if (mode
== InsetMath::UNDECIDED_MODE
) {
840 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullSimple
)));
841 parse2(cell
->back(), FLAG_SIMPLE
, InsetMath::MATH_MODE
, false);
843 // Don't create nested math hulls (bug #5392)
844 cell
->push_back(MathAtom(new InsetMathEnsureMath(buf
)));
845 parse(cell
->back().nucleus()->cell(0), FLAG_SIMPLE
, InsetMath::MATH_MODE
);
850 else if (flags
& FLAG_SIMPLE
) {
851 // this is the end of the formula
856 error("something strange in the parser");
861 else if (t
.cat() == catLetter
)
862 cell
->push_back(MathAtom(new InsetMathChar(t
.character())));
864 else if (t
.cat() == catSpace
&& mode
!= InsetMath::MATH_MODE
) {
865 if (cell
->empty() || cell
->back()->getChar() != ' ')
866 cell
->push_back(MathAtom(new InsetMathChar(t
.character())));
869 else if (t
.cat() == catNewline
&& mode
!= InsetMath::MATH_MODE
) {
870 if (cell
->empty() || cell
->back()->getChar() != ' ')
871 cell
->push_back(MathAtom(new InsetMathChar(' ')));
874 else if (t
.cat() == catParameter
) {
875 Token
const & n
= getToken();
876 cell
->push_back(MathAtom(new MathMacroArgument(n
.character()-'0')));
879 else if (t
.cat() == catActive
)
880 cell
->push_back(MathAtom(new InsetMathChar(t
.character())));
882 else if (t
.cat() == catBegin
) {
884 parse(ar
, FLAG_BRACE_LAST
, mode
);
885 // do not create a BraceInset if they were written by LyX
886 // this helps to keep the annoyance of "a choose b" to a minimum
887 if (ar
.size() == 1 && ar
[0]->extraBraces())
890 cell
->push_back(MathAtom(new InsetMathBrace(ar
)));
893 else if (t
.cat() == catEnd
) {
894 if (flags
& FLAG_BRACE_LAST
)
896 error("found '}' unexpectedly");
897 //LASSERT(false, /**/);
898 //add(cell, '}', LM_TC_TEX);
901 else if (t
.cat() == catAlign
) {
902 //lyxerr << " column now " << (cellcol + 1)
903 // << " max: " << grid.ncols() << endl;
904 if (flags
& FLAG_ALIGN
)
906 if (addCol(grid
, cellcol
))
907 cell
= &grid
.cell(grid
.index(cellrow
, cellcol
));
910 else if (t
.cat() == catSuper
|| t
.cat() == catSub
) {
911 bool up
= (t
.cat() == catSuper
);
912 // we need no new script inset if the last thing was a scriptinset,
913 // which has that script already not the same script already
915 cell
->push_back(MathAtom(new InsetMathScript(buf
, up
)));
916 else if (cell
->back()->asScriptInset() &&
917 !cell
->back()->asScriptInset()->has(up
))
918 cell
->back().nucleus()->asScriptInset()->ensure(up
);
919 else if (cell
->back()->asScriptInset())
920 cell
->push_back(MathAtom(new InsetMathScript(buf
, up
)));
922 cell
->back() = MathAtom(new InsetMathScript(buf
, cell
->back(), up
));
923 InsetMathScript
* p
= cell
->back().nucleus()->asScriptInset();
924 // special handling of {}-bases
925 // Here we could remove the brace inset for things
926 // like {a'}^2 and add the braces back in
927 // InsetMathScript::write().
928 // We do not do it, since it is not possible to detect
929 // reliably whether the braces are needed because the
930 // nucleus contains more than one symbol, or whether
931 // they are needed for unknown commands like \xx{a}_0
932 // or \yy{a}{b}_0. This was done in revision 14819
933 // in an unreliable way. See this thread
934 // http://www.mail-archive.com/lyx-devel%40lists.lyx.org/msg104917.html
936 parse(p
->cell(p
->idxOfScript(up
)), FLAG_ITEM
, mode
);
943 else if (t
.character() == ']' && (flags
& FLAG_BRACK_LAST
)) {
944 //lyxerr << "finished reading option" << endl;
948 else if (t
.cat() == catOther
) {
949 char_type c
= t
.character();
950 if (isAsciiOrMathAlpha(c
)
951 || mode_
& Parse::VERBATIM
952 || !(mode_
& Parse::USETEXT
)
953 || mode
== InsetMath::TEXT_MODE
) {
954 cell
->push_back(MathAtom(new InsetMathChar(c
)));
956 MathAtom at
= createInsetMath("text", buf
);
957 at
.nucleus()->cell(0).push_back(MathAtom(new InsetMathChar(c
)));
958 while (nextToken().cat() == catOther
959 && !isAsciiOrMathAlpha(nextToken().character())) {
960 c
= getToken().character();
961 at
.nucleus()->cell(0).push_back(MathAtom(new InsetMathChar(c
)));
967 else if (t
.cat() == catComment
) {
970 Token
const & t
= getToken();
971 if (t
.cat() == catNewline
)
975 cell
->push_back(MathAtom(new InsetMathComment(buf
, s
)));
983 else if (t
.cs() == "lyxlock") {
985 cell
->back().nucleus()->lock(true);
988 else if ((t
.cs() == "global" && nextToken().cs() == "def") ||
990 if (t
.cs() == "global")
994 docstring name
= getToken().cs();
999 while (good() && nextToken().cat() != catBegin
) {
1000 pars
+= getToken().cs();
1007 parse(def
, FLAG_ITEM
, InsetMath::UNDECIDED_MODE
);
1009 // is a version for display attached?
1012 if (nextToken().cat() == catBegin
)
1013 parse(display
, FLAG_ITEM
, InsetMath::MATH_MODE
);
1015 cell
->push_back(MathAtom(new MathMacroTemplate(buf
,
1016 name
, nargs
, 0, MacroTypeDef
,
1017 vector
<MathData
>(), def
, display
)));
1019 if (buf
&& (mode_
& Parse::TRACKMACRO
))
1020 buf
->usermacros
.insert(name
);
1023 else if (t
.cs() == "newcommand" ||
1024 t
.cs() == "renewcommand" ||
1025 t
.cs() == "newlyxcommand") {
1027 if (getToken().cat() != catBegin
) {
1028 error("'{' in \\newcommand expected (1) ");
1031 docstring name
= getToken().cs();
1032 if (getToken().cat() != catEnd
) {
1033 error("'}' in \\newcommand expected");
1038 docstring
const arg
= getArg('[', ']');
1041 nargs
= convert
<int>(arg
);
1043 // optional argument given?
1046 vector
<MathData
> optionalValues
;
1047 while (nextToken().character() == '[') {
1049 optionalValues
.push_back(MathData());
1050 parse(optionalValues
[optionals
], FLAG_BRACK_LAST
, mode
);
1055 parse(def
, FLAG_ITEM
, InsetMath::UNDECIDED_MODE
);
1057 // is a version for display attached?
1060 if (nextToken().cat() == catBegin
)
1061 parse(display
, FLAG_ITEM
, InsetMath::MATH_MODE
);
1063 cell
->push_back(MathAtom(new MathMacroTemplate(buf
,
1064 name
, nargs
, optionals
, MacroTypeNewcommand
,
1065 optionalValues
, def
, display
)));
1067 if (buf
&& (mode_
& Parse::TRACKMACRO
))
1068 buf
->usermacros
.insert(name
);
1071 else if (t
.cs() == "newcommandx" ||
1072 t
.cs() == "renewcommandx") {
1073 // \newcommandx{\foo}[2][usedefault, addprefix=\global,1=default]{#1,#2}
1076 if (nextToken().cat() == catBegin
) {
1078 name
= getToken().cs();
1079 if (getToken().cat() != catEnd
) {
1080 error("'}' in \\newcommandx expected");
1084 name
= getToken().cs();
1087 docstring
const arg
= getArg('[', ']');
1089 error("[num] in \\newcommandx expected");
1092 int nargs
= convert
<int>(arg
);
1096 vector
<MathData
> optionalValues
;
1097 if (nextToken().character() == '[') {
1101 // handle 'opt=value' options, separated by ','.
1103 while (nextToken().character() != ']' && good()) {
1104 if (nextToken().character() >= '1'
1105 && nextToken().character() <= '9') {
1106 // optional value -> get parameter number
1107 int n
= getChar() - '0';
1109 error("Arity of \\newcommandx too low "
1110 "for given optional parameter.");
1115 if (getToken().character() != '=') {
1116 error("'=' and optional parameter value "
1117 "expected for \\newcommandx");
1122 int optNum
= max(size_t(n
), optionalValues
.size());
1123 optionalValues
.resize(optNum
);
1124 optionalValues
[n
- 1].clear();
1125 while (nextToken().character() != ']'
1126 && nextToken().character() != ',') {
1128 parse(data
, FLAG_ITEM
, InsetMath::UNDECIDED_MODE
);
1129 optionalValues
[n
- 1].append(data
);
1131 optionals
= max(n
, optionals
);
1132 } else if (nextToken().cat() == catLetter
) {
1133 // we in fact ignore every non-optional
1138 while (nextToken().cat() == catLetter
)
1144 if (nextToken().character() == '=') {
1146 while (nextToken().character() != ']'
1147 && nextToken().character() != ',')
1148 parse(value
, FLAG_ITEM
,
1149 InsetMath::UNDECIDED_MODE
);
1152 error("option for \\newcommandx expected");
1158 if (nextToken().character() == ',') {
1161 } else if (nextToken().character() != ']') {
1162 error("Expecting ',' or ']' in options "
1163 "of \\newcommandx");
1176 parse(def
, FLAG_ITEM
, InsetMath::UNDECIDED_MODE
);
1178 // is a version for display attached?
1181 if (nextToken().cat() == catBegin
)
1182 parse(display
, FLAG_ITEM
, InsetMath::MATH_MODE
);
1184 cell
->push_back(MathAtom(new MathMacroTemplate(buf
,
1185 name
, nargs
, optionals
, MacroTypeNewcommandx
,
1186 optionalValues
, def
, display
)));
1188 if (buf
&& (mode_
& Parse::TRACKMACRO
))
1189 buf
->usermacros
.insert(name
);
1192 else if (t
.cs() == "(") {
1193 if (mode
== InsetMath::MATH_MODE
) {
1194 error("bad math environment");
1197 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullSimple
)));
1198 parse2(cell
->back(), FLAG_SIMPLE2
, InsetMath::MATH_MODE
, false);
1201 else if (t
.cs() == "[") {
1202 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1203 error("bad math environment");
1206 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullEquation
)));
1207 parse2(cell
->back(), FLAG_EQUATION
, InsetMath::MATH_MODE
, false);
1210 else if (t
.cs() == "protect")
1211 // ignore \\protect, will hopefully be re-added during output
1214 else if (t
.cs() == "end") {
1215 if (flags
& FLAG_END
) {
1216 // eat environment name
1217 docstring
const name
= getArg('{', '}');
1218 if (environments_
.empty())
1219 error("'found \\end{" + name
+
1220 "}' without matching '\\begin{" +
1222 else if (name
!= environments_
.back())
1223 error("'\\end{" + name
+
1224 "}' does not match '\\begin{" +
1225 environments_
.back() + "}'");
1227 environments_
.pop_back();
1228 // Delete empty last row in matrix
1230 // If you abuse InsetMathGrid for
1231 // non-matrix like structures you
1232 // probably need to refine this test.
1233 // Right now we only have to test for
1234 // single line hull insets.
1235 if (grid
.nrows() > 1)
1236 delEmptyLastRow(grid
);
1240 error("found 'end' unexpectedly");
1243 else if (t
.cs() == ")") {
1244 if (flags
& FLAG_SIMPLE2
)
1246 error("found '\\)' unexpectedly");
1249 else if (t
.cs() == "]") {
1250 if (flags
& FLAG_EQUATION
)
1252 error("found '\\]' unexpectedly");
1255 else if (t
.cs() == "\\") {
1256 if (flags
& FLAG_ALIGN
)
1259 if (nextToken().asInput() == "*") {
1261 added
= addRow(grid
, cellrow
, docstring(), false);
1263 added
= addRow(grid
, cellrow
, getArg('[', ']'));
1265 error("missing token after \\\\");
1268 if (grid
.asHullInset())
1269 grid
.asHullInset()->numbered(
1271 cell
= &grid
.cell(grid
.index(cellrow
,
1277 else if (t
.cs() == "multicolumn") {
1278 // extract column count and insert dummy cells
1280 parse(count
, FLAG_ITEM
, mode
);
1282 if (!extractNumber(count
, cols
)) {
1284 lyxerr
<< " can't extract number of cells from " << count
<< endl
;
1286 // resize the table if necessary
1287 for (int i
= 0; i
< cols
; ++i
) {
1288 if (addCol(grid
, cellcol
)) {
1289 cell
= &grid
.cell(grid
.index(
1291 // mark this as dummy
1292 grid
.cellinfo(grid
.index(
1293 cellrow
, cellcol
)).dummy_
= true;
1296 // the last cell is the real thing, not a dummy
1297 grid
.cellinfo(grid
.index(cellrow
, cellcol
)).dummy_
= false;
1299 // read special alignment
1301 parse(align
, FLAG_ITEM
, mode
);
1302 //grid.cellinfo(grid.index(cellrow, cellcol)).align_ = extractString(align);
1304 // parse the remaining contents into the "real" cell
1305 parse(*cell
, FLAG_ITEM
, mode
);
1309 else if (t
.cs() == "limits")
1312 else if (t
.cs() == "nolimits")
1315 else if (t
.cs() == "nonumber") {
1316 if (grid
.asHullInset())
1317 grid
.asHullInset()->numbered(cellrow
, false);
1320 else if (t
.cs() == "number") {
1321 if (grid
.asHullInset())
1322 grid
.asHullInset()->numbered(cellrow
, true);
1325 else if (t
.cs() == "hline") {
1326 grid
.rowinfo(cellrow
).lines_
++;
1329 else if (t
.cs() == "sqrt") {
1331 parse(ar
, FLAG_OPTION
, mode
);
1333 cell
->push_back(MathAtom(new InsetMathRoot(buf
)));
1334 cell
->back().nucleus()->cell(0) = ar
;
1335 parse(cell
->back().nucleus()->cell(1), FLAG_ITEM
, mode
);
1337 cell
->push_back(MathAtom(new InsetMathSqrt(buf
)));
1338 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, mode
);
1342 else if (t
.cs() == "unit") {
1343 // Allowed formats \unit[val]{unit}
1345 parse(ar
, FLAG_OPTION
, mode
);
1347 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::UNIT
)));
1348 cell
->back().nucleus()->cell(0) = ar
;
1349 parse(cell
->back().nucleus()->cell(1), FLAG_ITEM
, mode
);
1351 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::UNIT
, 1)));
1352 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, mode
);
1356 else if (t
.cs() == "unitfrac") {
1357 // Here allowed formats are \unitfrac[val]{num}{denom}
1359 parse(ar
, FLAG_OPTION
, mode
);
1361 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::UNITFRAC
, 3)));
1362 cell
->back().nucleus()->cell(2) = ar
;
1364 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::UNITFRAC
)));
1366 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, mode
);
1367 parse(cell
->back().nucleus()->cell(1), FLAG_ITEM
, mode
);
1370 else if (t
.cs() == "cfrac") {
1371 // allowed formats are \cfrac[pos]{num}{denom}
1372 docstring
const arg
= getArg('[', ']');
1373 //lyxerr << "got so far: '" << arg << "'" << endl;
1375 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::CFRACLEFT
)));
1376 else if (arg
== "r")
1377 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::CFRACRIGHT
)));
1378 else if (arg
.empty() || arg
== "c")
1379 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::CFRAC
)));
1381 error("found invalid optional argument");
1384 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, mode
);
1385 parse(cell
->back().nucleus()->cell(1), FLAG_ITEM
, mode
);
1388 else if (t
.cs() == "xrightarrow" || t
.cs() == "xleftarrow") {
1389 cell
->push_back(createInsetMath(t
.cs(), buf
));
1390 parse(cell
->back().nucleus()->cell(1), FLAG_OPTION
, mode
);
1391 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, mode
);
1394 else if (t
.cs() == "ref" || t
.cs() == "eqref" || t
.cs() == "prettyref"
1395 || t
.cs() == "pageref" || t
.cs() == "vpageref" || t
.cs() == "vref") {
1396 cell
->push_back(MathAtom(new InsetMathRef(buf
, t
.cs())));
1397 parse(cell
->back().nucleus()->cell(1), FLAG_OPTION
, mode
);
1398 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, mode
);
1401 else if (t
.cs() == "left") {
1403 Token
const & tl
= getToken();
1404 // \| and \Vert are equivalent, and InsetMathDelim
1406 // FIXME: fix this in InsetMathDelim itself!
1407 docstring
const l
= tl
.cs() == "|" ? from_ascii("Vert") : tl
.asString();
1409 parse(ar
, FLAG_RIGHT
, mode
);
1413 Token
const & tr
= getToken();
1414 docstring
const r
= tr
.cs() == "|" ? from_ascii("Vert") : tr
.asString();
1415 cell
->push_back(MathAtom(new InsetMathDelim(buf
, l
, r
, ar
)));
1418 else if (t
.cs() == "right") {
1419 if (flags
& FLAG_RIGHT
)
1421 //lyxerr << "got so far: '" << cell << "'" << endl;
1422 error("Unmatched right delimiter");
1426 else if (t
.cs() == "begin") {
1427 docstring
const name
= getArg('{', '}');
1428 environments_
.push_back(name
);
1430 if (name
== "array" || name
== "subarray") {
1431 docstring
const valign
= parse_verbatim_option() + 'c';
1432 docstring
const halign
= parse_verbatim_item();
1433 cell
->push_back(MathAtom(new InsetMathArray(buf
, name
,
1434 InsetMathGrid::guessColumns(halign
), 1, (char)valign
[0], halign
)));
1435 parse2(cell
->back(), FLAG_END
, mode
, false);
1438 else if (name
== "tabular") {
1439 docstring
const valign
= parse_verbatim_option() + 'c';
1440 docstring
const halign
= parse_verbatim_item();
1441 cell
->push_back(MathAtom(new InsetMathTabular(buf
, name
,
1442 InsetMathGrid::guessColumns(halign
), 1, (char)valign
[0], halign
)));
1443 parse2(cell
->back(), FLAG_END
, InsetMath::TEXT_MODE
, false);
1446 else if (name
== "split" || name
== "cases") {
1447 cell
->push_back(createInsetMath(name
, buf
));
1448 parse2(cell
->back(), FLAG_END
, mode
, false);
1451 else if (name
== "alignedat") {
1452 docstring
const valign
= parse_verbatim_option() + 'c';
1453 // ignore this for a while
1455 cell
->push_back(MathAtom(new InsetMathSplit(buf
, name
, (char)valign
[0])));
1456 parse2(cell
->back(), FLAG_END
, mode
, false);
1459 else if (name
== "math") {
1460 if (mode
== InsetMath::MATH_MODE
) {
1461 error("bad math environment");
1464 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullSimple
)));
1465 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, true);
1468 else if (name
== "equation" || name
== "equation*"
1469 || name
== "displaymath") {
1470 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1471 error("bad math environment");
1474 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullEquation
)));
1475 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, (name
== "equation"));
1478 else if (name
== "eqnarray" || name
== "eqnarray*") {
1479 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1480 error("bad math environment");
1483 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullEqnArray
)));
1484 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1487 else if (name
== "align" || name
== "align*") {
1488 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1489 error("bad math environment");
1492 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullAlign
)));
1493 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1496 else if (name
== "flalign" || name
== "flalign*") {
1497 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1498 error("bad math environment");
1501 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullFlAlign
)));
1502 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1505 else if (name
== "alignat" || name
== "alignat*") {
1506 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1507 error("bad math environment");
1510 // ignore this for a while
1512 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullAlignAt
)));
1513 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1516 else if (name
== "xalignat" || name
== "xalignat*") {
1517 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1518 error("bad math environment");
1521 // ignore this for a while
1523 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullXAlignAt
)));
1524 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1527 else if (name
== "xxalignat") {
1528 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1529 error("bad math environment");
1532 // ignore this for a while
1534 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullXXAlignAt
)));
1535 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1538 else if (name
== "multline" || name
== "multline*") {
1539 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1540 error("bad math environment");
1543 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullMultline
)));
1544 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1547 else if (name
== "gather" || name
== "gather*") {
1548 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1549 error("bad math environment");
1552 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullGather
)));
1553 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1556 else if (latexkeys
const * l
= in_word_set(name
)) {
1557 if (l
->inset
== "matrix") {
1558 cell
->push_back(createInsetMath(name
, buf
));
1559 parse2(cell
->back(), FLAG_END
, mode
, false);
1560 } else if (l
->inset
== "split") {
1561 docstring
const valign
= parse_verbatim_option() + 'c';
1562 cell
->push_back(MathAtom(
1563 new InsetMathSplit(buf
, name
, (char)valign
[0])));
1564 parse2(cell
->back(), FLAG_END
, mode
, false);
1567 if (!(mode_
& Parse::QUIET
)) {
1569 lyxerr
<< "found math environment `"
1571 << "' in symbols file with unsupported inset `"
1572 << to_utf8(l
->inset
)
1575 // create generic environment inset
1576 cell
->push_back(MathAtom(new InsetMathEnv(buf
, name
)));
1577 parse(cell
->back().nucleus()->cell(0), FLAG_END
, mode
);
1583 if (!(mode_
& Parse::QUIET
)) {
1585 lyxerr
<< "found unknown math environment '"
1586 << to_utf8(name
) << "'" << endl
;
1588 // create generic environment inset
1589 cell
->push_back(MathAtom(new InsetMathEnv(buf
, name
)));
1590 parse(cell
->back().nucleus()->cell(0), FLAG_END
, mode
);
1594 else if (t
.cs() == "kern") {
1599 Token
const & t
= getToken();
1603 while (num_tokens
--)
1608 if (isValidLength(to_utf8(s
)))
1612 cell
->push_back(MathAtom(new InsetMathKern
));
1614 cell
->push_back(MathAtom(new InsetMathKern(s
)));
1617 else if (t
.cs() == "label") {
1618 // FIXME: This is swallowed in inline formulas
1619 docstring label
= parse_verbatim_item();
1622 if (grid
.asHullInset()) {
1623 grid
.asHullInset()->label(cellrow
, label
);
1625 cell
->push_back(createInsetMath(t
.cs(), buf
));
1626 cell
->push_back(MathAtom(new InsetMathBrace(ar
)));
1630 else if (t
.cs() == "choose" || t
.cs() == "over"
1631 || t
.cs() == "atop" || t
.cs() == "brace"
1632 || t
.cs() == "brack") {
1633 MathAtom at
= createInsetMath(t
.cs(), buf
);
1634 at
.nucleus()->cell(0) = *cell
;
1636 parse(at
.nucleus()->cell(1), flags
, mode
);
1637 cell
->push_back(at
);
1641 else if (t
.cs() == "color") {
1642 docstring
const color
= parse_verbatim_item();
1643 cell
->push_back(MathAtom(new InsetMathColor(buf
, true, color
)));
1644 parse(cell
->back().nucleus()->cell(0), flags
, mode
);
1648 else if (t
.cs() == "textcolor") {
1649 docstring
const color
= parse_verbatim_item();
1650 cell
->push_back(MathAtom(new InsetMathColor(buf
, false, color
)));
1651 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, InsetMath::TEXT_MODE
);
1654 else if (t
.cs() == "normalcolor") {
1655 cell
->push_back(createInsetMath(t
.cs(), buf
));
1656 parse(cell
->back().nucleus()->cell(0), flags
, mode
);
1660 else if (t
.cs() == "substack") {
1661 cell
->push_back(createInsetMath(t
.cs(), buf
));
1662 parse2(cell
->back(), FLAG_ITEM
, mode
, false);
1665 else if (t
.cs() == "xymatrix") {
1666 odocstringstream os
;
1667 while (good() && nextToken().cat() != catBegin
)
1668 os
<< getToken().asInput();
1669 cell
->push_back(createInsetMath(t
.cs() + os
.str(), buf
));
1670 parse2(cell
->back(), FLAG_ITEM
, mode
, false);
1673 else if (t
.cs() == "framebox" || t
.cs() == "makebox") {
1674 cell
->push_back(createInsetMath(t
.cs(), buf
));
1675 parse(cell
->back().nucleus()->cell(0), FLAG_OPTION
, InsetMath::TEXT_MODE
);
1676 parse(cell
->back().nucleus()->cell(1), FLAG_OPTION
, InsetMath::TEXT_MODE
);
1677 parse(cell
->back().nucleus()->cell(2), FLAG_ITEM
, InsetMath::TEXT_MODE
);
1680 else if (t
.cs() == "tag") {
1681 if (nextToken().character() == '*') {
1683 cell
->push_back(createInsetMath(t
.cs() + '*', buf
));
1685 cell
->push_back(createInsetMath(t
.cs(), buf
));
1686 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, InsetMath::TEXT_MODE
);
1689 else if (t
.cs() == "hspace" && nextToken().character() != '*') {
1690 docstring
const name
= t
.cs();
1691 docstring
const arg
= parse_verbatim_item();
1693 if (isValidLength(to_utf8(arg
), &length
))
1694 cell
->push_back(MathAtom(new InsetMathSpace(length
)));
1696 // Since the Length class cannot use length variables
1697 // we must not create an InsetMathSpace.
1698 cell
->push_back(MathAtom(new MathMacro(buf
, name
)));
1700 mathed_parse_cell(ar
, '{' + arg
+ '}', mode_
);
1706 else if (t
.cs() == "infer") {
1708 parse(ar
, FLAG_OPTION
, mode
);
1709 cell
->push_back(createInsetMath(t
.cs(), buf
));
1710 parse2(cell
->back(), FLAG_ITEM
, mode
, false);
1714 else if (1 && t
.cs() == "ar") {
1715 auto_ptr
<InsetMathXYArrow
> p(new InsetMathXYArrow
);
1716 // try to read target
1717 parse(p
->cell(0), FLAG_OTPTION
, mode
);
1718 // try to read label
1719 if (nextToken().cat() == catSuper
|| nextToken().cat() == catSub
) {
1720 p
->up_
= nextToken().cat() == catSuper
;
1722 parse(p
->cell(1), FLAG_ITEM
, mode
);
1723 //lyxerr << "read label: " << p->cell(1) << endl;
1726 cell
->push_back(MathAtom(p
.release()));
1727 //lyxerr << "read cell: " << cell << endl;
1731 else if (t
.cs() == "lyxmathsym") {
1733 if (getToken().cat() != catBegin
) {
1734 error("'{' expected in \\" + t
.cs());
1739 CatCode cat
= nextToken().cat();
1740 while (good() && (count
|| cat
!= catEnd
)) {
1741 if (cat
== catBegin
)
1743 else if (cat
== catEnd
)
1745 cmd
+= getToken().asInput();
1746 cat
= nextToken().cat();
1748 if (getToken().cat() != catEnd
) {
1749 error("'}' expected in \\" + t
.cs());
1754 cmd
= Encodings::fromLaTeXCommand(cmd
, rem
);
1755 for (size_t i
= 0; i
< cmd
.size(); ++i
)
1756 cell
->push_back(MathAtom(new InsetMathChar(cmd
[i
])));
1758 char_type c
= rem
[0];
1759 cell
->push_back(MathAtom(new InsetMathChar(c
)));
1760 cmd
= rem
.substr(1);
1764 } while (cmd
.size());
1767 else if (t
.cs().size()) {
1768 bool const is_user_macro
=
1769 buf
&& (mode_
& Parse::TRACKMACRO
1770 ? buf
->usermacros
.count(t
.cs()) != 0
1771 : buf
->getMacro(t
.cs(), false) != 0);
1772 latexkeys
const * l
= in_word_set(t
.cs());
1773 if (l
&& !is_user_macro
) {
1774 if (l
->inset
== "big") {
1776 docstring
const delim
= getToken().asInput();
1777 if (InsetMathBig::isBigInsetDelim(delim
))
1778 cell
->push_back(MathAtom(
1779 new InsetMathBig(t
.cs(), delim
)));
1781 cell
->push_back(createInsetMath(t
.cs(), buf
));
1786 else if (l
->inset
== "font") {
1787 cell
->push_back(createInsetMath(t
.cs(), buf
));
1788 parse(cell
->back().nucleus()->cell(0),
1789 FLAG_ITEM
, asMode(mode
, l
->extra
));
1792 else if (l
->inset
== "oldfont") {
1793 cell
->push_back(createInsetMath(t
.cs(), buf
));
1794 parse(cell
->back().nucleus()->cell(0),
1795 flags
| FLAG_ALIGN
, asMode(mode
, l
->extra
));
1796 if (prevToken().cat() != catAlign
&&
1797 prevToken().cs() != "\\")
1802 else if (l
->inset
== "style") {
1803 cell
->push_back(createInsetMath(t
.cs(), buf
));
1804 parse(cell
->back().nucleus()->cell(0),
1805 flags
| FLAG_ALIGN
, mode
);
1806 if (prevToken().cat() != catAlign
&&
1807 prevToken().cs() != "\\")
1813 MathAtom at
= createInsetMath(t
.cs(), buf
);
1814 for (InsetMath::idx_type i
= 0; i
< at
->nargs(); ++i
)
1815 parse(at
.nucleus()->cell(i
),
1816 FLAG_ITEM
, asMode(mode
, l
->extra
));
1817 cell
->push_back(at
);
1822 bool is_unicode_symbol
= false;
1823 if (mode
== InsetMath::TEXT_MODE
&& !is_user_macro
) {
1825 docstring cmd
= prevToken().asInput();
1826 CatCode cat
= nextToken().cat();
1827 if (cat
== catBegin
) {
1829 while (good() && (count
|| cat
!= catEnd
)) {
1830 cat
= nextToken().cat();
1831 cmd
+= getToken().asInput();
1833 if (cat
== catBegin
)
1835 else if (cat
== catEnd
)
1841 Encodings::fromLaTeXCommand(cmd
, is_combining
);
1843 if (cat
== catLetter
)
1845 cmd
+= getToken().asInput();
1847 if (cat
== catLetter
)
1849 c
= Encodings::fromLaTeXCommand(cmd
, is_combining
);
1852 is_unicode_symbol
= true;
1853 cell
->push_back(MathAtom(new InsetMathChar(c
)));
1855 while (num_tokens
--)
1859 if (!is_unicode_symbol
) {
1860 MathAtom at
= is_user_macro
?
1861 MathAtom(new MathMacro(buf
, t
.cs()))
1862 : createInsetMath(t
.cs(), buf
);
1863 InsetMath::mode_type m
= mode
;
1864 //if (m == InsetMath::UNDECIDED_MODE)
1865 //lyxerr << "default creation: m1: " << m << endl;
1866 if (at
->currentMode() != InsetMath::UNDECIDED_MODE
)
1867 m
= at
->currentMode();
1868 //lyxerr << "default creation: m2: " << m << endl;
1869 InsetMath::idx_type start
= 0;
1870 // this fails on \bigg[...\bigg]
1872 //parse(opt, FLAG_OPTION, InsetMath::VERBATIM_MODE);
1875 // at.nucleus()->cell(0) = opt;
1877 for (InsetMath::idx_type i
= start
; i
< at
->nargs(); ++i
) {
1878 parse(at
.nucleus()->cell(i
), FLAG_ITEM
, m
);
1881 cell
->push_back(at
);
1887 if (flags
& FLAG_LEAVE
) {
1888 flags
&= ~FLAG_LEAVE
;
1897 } // anonymous namespace
1900 bool mathed_parse_cell(MathData
& ar
, docstring
const & str
, Parse::flags f
)
1902 return Parser(str
, f
, ar
.buffer()).parse(ar
, 0, f
& Parse::TEXTMODE
?
1903 InsetMath::TEXT_MODE
: InsetMath::MATH_MODE
);
1907 bool mathed_parse_cell(MathData
& ar
, istream
& is
, Parse::flags f
)
1909 return Parser(is
, f
, ar
.buffer()).parse(ar
, 0, f
& Parse::TEXTMODE
?
1910 InsetMath::TEXT_MODE
: InsetMath::MATH_MODE
);
1914 bool mathed_parse_normal(Buffer
* buf
, MathAtom
& t
, docstring
const & str
,
1917 return Parser(str
, f
, buf
).parse(t
);
1921 bool mathed_parse_normal(Buffer
* buf
, MathAtom
& t
, Lexer
& lex
,
1924 return Parser(lex
, f
, buf
).parse(t
);
1928 bool mathed_parse_normal(InsetMathGrid
& grid
, docstring
const & str
,
1931 return Parser(str
, f
, &grid
.buffer()).parse1(grid
, 0, f
& Parse::TEXTMODE
?
1932 InsetMath::TEXT_MODE
: InsetMath::MATH_MODE
, false);
1938 fill(theCatcode
, theCatcode
+ 128, catOther
);
1939 fill(theCatcode
+ 'a', theCatcode
+ 'z' + 1, catLetter
);
1940 fill(theCatcode
+ 'A', theCatcode
+ 'Z' + 1, catLetter
);
1942 theCatcode
[int('\\')] = catEscape
;
1943 theCatcode
[int('{')] = catBegin
;
1944 theCatcode
[int('}')] = catEnd
;
1945 theCatcode
[int('$')] = catMath
;
1946 theCatcode
[int('&')] = catAlign
;
1947 theCatcode
[int('\n')] = catNewline
;
1948 theCatcode
[int('#')] = catParameter
;
1949 theCatcode
[int('^')] = catSuper
;
1950 theCatcode
[int('_')] = catSub
;
1951 theCatcode
[int(0x7f)] = catIgnore
;
1952 theCatcode
[int(' ')] = catSpace
;
1953 theCatcode
[int('\t')] = catSpace
;
1954 theCatcode
[int('\r')] = catNewline
;
1955 theCatcode
[int('~')] = catActive
;
1956 theCatcode
[int('%')] = catComment
;