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"
68 #include "BufferParams.h"
72 #include "support/debug.h"
73 #include "support/convert.h"
74 #include "support/docstream.h"
86 InsetMath::mode_type
asMode(InsetMath::mode_type oldmode
, docstring
const & str
)
88 //lyxerr << "handling mode: '" << str << "'" << endl;
89 if (str
== "mathmode")
90 return InsetMath::MATH_MODE
;
91 if (str
== "textmode" || str
== "forcetext")
92 return InsetMath::TEXT_MODE
;
97 bool stared(docstring
const & s
)
99 size_t const n
= s
.size();
100 return n
&& s
[n
- 1] == '*';
104 docstring
const repl(docstring
const & oldstr
, char_type
const c
,
105 docstring
const & macro
, bool textmode
= false)
111 for (i
= 0, j
= 0; i
< oldstr
.size(); ++i
) {
112 if (c
== oldstr
[i
]) {
113 newstr
.append(oldstr
, j
, i
- j
);
114 newstr
.append(macro
);
116 if (macro
.size() > 2 && j
< oldstr
.size())
117 newstr
+= (textmode
&& oldstr
[j
] == ' ' ? '\\' : ' ');
125 newstr
.append(oldstr
, j
, i
- j
);
130 docstring
escapeSpecialChars(docstring
const & str
, bool textmode
)
132 docstring
const backslash
= textmode
? from_ascii("\\textbackslash")
133 : from_ascii("\\backslash");
134 docstring
const caret
= textmode
? from_ascii("\\textasciicircum")
135 : from_ascii("\\mathcircumflex");
136 docstring
const tilde
= textmode
? from_ascii("\\textasciitilde")
137 : from_ascii("\\sim");
139 return repl(repl(repl(repl(repl(repl(repl(repl(repl(repl(str
,
140 '\\', backslash
, textmode
),
141 '^', caret
, textmode
),
142 '~', tilde
, textmode
),
143 '_', from_ascii("\\_")),
144 '$', from_ascii("\\$")),
145 '#', from_ascii("\\#")),
146 '&', from_ascii("\\&")),
147 '%', from_ascii("\\%")),
148 '{', from_ascii("\\{")),
149 '}', from_ascii("\\}"));
154 * Add the row \p cellrow to \p grid.
155 * \returns wether the row could be added. Adding a row can fail for
156 * environments like "equation" that have a fixed number of rows.
158 bool addRow(InsetMathGrid
& grid
, InsetMathGrid::row_type
& cellrow
,
159 docstring
const & vskip
, bool allow_newpage_
= true)
162 if (cellrow
== grid
.nrows()) {
163 //lyxerr << "adding row " << cellrow << endl;
164 grid
.addRow(cellrow
- 1);
165 if (cellrow
== grid
.nrows()) {
166 // We can't add a row to this grid, so let's
167 // append the content of this cell to the previous
169 // This does not happen in well formed .lyx files,
170 // but LyX versions 1.3.x and older could create
171 // such files and tex2lyx can still do that.
173 lyxerr
<< "ignoring extra row";
175 lyxerr
<< " with extra space " << to_utf8(vskip
);
177 lyxerr
<< " with no page break allowed";
178 lyxerr
<< '.' << endl
;
182 grid
.vcrskip(Length(to_utf8(vskip
)), cellrow
- 1);
183 grid
.rowinfo(cellrow
- 1).allow_newpage_
= allow_newpage_
;
189 * Add the column \p cellcol to \p grid.
190 * \returns wether the column could be added. Adding a column can fail for
191 * environments like "eqnarray" that have a fixed number of columns.
193 bool addCol(InsetMathGrid
& grid
, InsetMathGrid::col_type
& cellcol
)
196 if (cellcol
== grid
.ncols()) {
197 //lyxerr << "adding column " << cellcol << endl;
198 grid
.addCol(cellcol
);
199 if (cellcol
== grid
.ncols()) {
200 // We can't add a column to this grid, so let's
201 // append the content of this cell to the previous
203 // This does not happen in well formed .lyx files,
204 // but LyX versions 1.3.x and older could create
205 // such files and tex2lyx can still do that.
207 lyxerr
<< "ignoring extra column." << endl
;
216 * Check wether the last row is empty and remove it if yes.
217 * Otherwise the following code
225 * will result in a grid with 3 rows (+ the dummy row that is always present),
226 * because the last '\\' opens a new row.
228 void delEmptyLastRow(InsetMathGrid
& grid
)
230 InsetMathGrid::row_type
const row
= grid
.nrows() - 1;
231 for (InsetMathGrid::col_type col
= 0; col
< grid
.ncols(); ++col
) {
232 if (!grid
.cell(grid
.index(row
, col
)).empty())
235 // Copy the row information of the empty row (which would contain the
236 // last hline in the example above) to the dummy row and delete the
238 grid
.rowinfo(row
+ 1) = grid
.rowinfo(row
);
243 // These are TeX's catcodes
245 catEscape
, // 0 backslash
255 catSpace
, // 10 space
256 catLetter
, // 11 a-zA-Z
257 catOther
, // 12 none of the above
260 catInvalid
// 15 <delete>
263 CatCode theCatcode
[128];
266 inline CatCode
catcode(char_type c
)
268 /* The only characters that are not catOther lie in the pure ASCII
269 * range. Therefore theCatcode has only 128 entries.
270 * TeX itself deals with 8bit characters, so if needed this table
271 * could be enlarged to 256 entries.
272 * Any larger value does not make sense, since the fact that we use
273 * unicode internally does not change Knuth's TeX engine.
274 * Apart from that a table for the full 21bit UCS4 range would waste
275 * too much memory. */
279 return theCatcode
[c
];
284 FLAG_ALIGN
= 1 << 0, // next & or \\ ends the parsing process
285 FLAG_BRACE_LAST
= 1 << 1, // next closing brace ends the parsing
286 FLAG_RIGHT
= 1 << 2, // next \\right ends the parsing process
287 FLAG_END
= 1 << 3, // next \\end ends the parsing process
288 FLAG_BRACK_LAST
= 1 << 4, // next closing bracket ends the parsing
289 FLAG_TEXTMODE
= 1 << 5, // we are in a box
290 FLAG_ITEM
= 1 << 6, // read a (possibly braced) token
291 FLAG_LEAVE
= 1 << 7, // leave the loop at the end
292 FLAG_SIMPLE
= 1 << 8, // next $ leaves the loop
293 FLAG_EQUATION
= 1 << 9, // next \] leaves the loop
294 FLAG_SIMPLE2
= 1 << 10, // next \) leaves the loop
295 FLAG_OPTION
= 1 << 11, // read [...] style option
296 FLAG_BRACED
= 1 << 12 // read {...} style argument
301 // Helper class for parsing
307 Token() : cs_(), char_(0), cat_(catIgnore
) {}
309 Token(char_type c
, CatCode cat
) : cs_(), char_(c
), cat_(cat
) {}
311 explicit Token(docstring
const & cs
) : cs_(cs
), char_(0), cat_(catIgnore
) {}
314 docstring
const & cs() const { return cs_
; }
316 CatCode
cat() const { return cat_
; }
318 char_type
character() const { return char_
; }
320 docstring
asString() const { return cs_
.size() ? cs_
: docstring(1, char_
); }
322 docstring
asInput() const { return cs_
.size() ? '\\' + cs_
: docstring(1, char_
); }
334 ostream
& operator<<(ostream
& os
, Token
const & t
)
337 docstring
const & cs
= t
.cs();
338 // FIXME: For some strange reason, the stream operator instanciate
339 // a new Token before outputting the contents of t.cs().
340 // Because of this the line
342 // below becomes recursive.
343 // In order to avoid that we return early:
346 os
<< '\\' << to_utf8(cs
);
348 else if (t
.cat() == catLetter
)
351 os
<< '[' << t
.character() << ',' << t
.cat() << ']';
359 typedef InsetMath::mode_type mode_type
;
361 typedef Parse::flags parse_mode
;
364 Parser(Lexer
& lex
, parse_mode mode
, Buffer
* buf
);
365 /// Only use this for reading from .lyx file format, for the reason
366 /// see Parser::tokenize(istream &).
367 Parser(istream
& is
, parse_mode mode
, Buffer
* buf
);
369 Parser(docstring
const & str
, parse_mode mode
, Buffer
* buf
);
372 bool parse(MathAtom
& at
);
374 bool parse(MathData
& array
, unsigned flags
, mode_type mode
);
376 bool parse1(InsetMathGrid
& grid
, unsigned flags
, mode_type mode
,
379 MathData
parse(unsigned flags
, mode_type mode
);
381 int lineno() const { return lineno_
; }
387 void parse2(MathAtom
& at
, unsigned flags
, mode_type mode
, bool numbered
);
388 /// get arg delimited by 'left' and 'right'
389 docstring
getArg(char_type left
, char_type right
);
393 void error(string
const & msg
);
394 void error(docstring
const & msg
) { error(to_utf8(msg
)); }
395 /// dump contents to screen
397 /// Only use this for reading from .lyx file format (see
398 /// implementation for reason)
399 void tokenize(istream
& is
);
401 void tokenize(docstring
const & s
);
403 void skipSpaceTokens(idocstream
& is
, char_type c
);
405 void push_back(Token
const & t
);
409 Token
const & prevToken() const;
411 Token
const & nextToken() const;
413 Token
const & getToken();
414 /// skips spaces if any
417 void lex(docstring
const & s
);
421 docstring
parse_verbatim_item();
423 docstring
parse_verbatim_option();
428 vector
<Token
> tokens_
;
431 /// Stack of active environments
432 vector
<docstring
> environments_
;
442 Parser::Parser(Lexer
& lexer
, parse_mode mode
, Buffer
* buf
)
443 : lineno_(lexer
.lineNumber()), pos_(0), mode_(mode
), success_(true),
446 tokenize(lexer
.getStream());
451 Parser::Parser(istream
& is
, parse_mode mode
, Buffer
* buf
)
452 : lineno_(0), pos_(0), mode_(mode
), success_(true), buffer_(buf
)
458 Parser::Parser(docstring
const & str
, parse_mode mode
, Buffer
* buf
)
459 : lineno_(0), pos_(0), mode_(mode
), success_(true), buffer_(buf
)
465 void Parser::push_back(Token
const & t
)
467 tokens_
.push_back(t
);
471 void Parser::pop_back()
477 Token
const & Parser::prevToken() const
479 static const Token dummy
;
480 return pos_
> 0 ? tokens_
[pos_
- 1] : dummy
;
484 Token
const & Parser::nextToken() const
486 static const Token dummy
;
487 return good() ? tokens_
[pos_
] : dummy
;
491 Token
const & Parser::getToken()
493 static const Token dummy
;
494 //lyxerr << "looking at token " << tokens_[pos_] << " pos: " << pos_ << endl;
495 return good() ? tokens_
[pos_
++] : dummy
;
499 void Parser::skipSpaces()
501 while (nextToken().cat() == catSpace
|| nextToken().cat() == catNewline
)
506 void Parser::putback()
512 bool Parser::good() const
514 return pos_
< tokens_
.size();
518 char_type
Parser::getChar()
521 error("The input stream is not well...");
525 return tokens_
[pos_
++].character();
529 docstring
Parser::getArg(char_type left
, char_type right
)
534 char_type c
= getChar();
539 while ((c
= getChar()) != right
&& good())
546 void Parser::skipSpaceTokens(idocstream
& is
, char_type c
)
548 // skip trailing spaces
549 while (catcode(c
) == catSpace
|| catcode(c
) == catNewline
)
552 //lyxerr << "putting back: " << c << endl;
557 void Parser::tokenize(istream
& is
)
559 // eat everything up to the next \end_inset or end of stream
560 // and store it in s for further tokenization
565 if (s
.size() >= 10 && s
.substr(s
.size() - 10) == "\\end_inset") {
566 s
= s
.substr(0, s
.size() - 10);
570 // Remove the space after \end_inset
571 if (is
.get(c
) && c
!= ' ')
575 tokenize(from_utf8(s
));
579 void Parser::tokenize(docstring
const & buffer
)
581 idocstringstream
is(mode_
& Parse::VERBATIM
582 ? escapeSpecialChars(buffer
, mode_
& Parse::TEXTMODE
)
583 : buffer
, ios::in
| ios::binary
);
587 //lyxerr << "reading c: " << c << endl;
589 switch (catcode(c
)) {
593 if (catcode(c
) == catNewline
)
594 ; //push_back(Token("par"));
596 push_back(Token('\n', catNewline
));
604 while (is.get(c) && catcode(c) != catNewline)
614 error("unexpected end of input");
617 if (catcode(c
) == catLetter
) {
619 while (is
.get(c
) && catcode(c
) == catLetter
)
621 skipSpaceTokens(is
, c
);
630 push_back(Token(c
, catcode(c
)));
632 skipSpaceTokens(is
, c
);
637 if (!(mode_
& Parse::QUIET
))
638 lyxerr
<< "ignoring a char: " << int(c
) << endl
;
643 push_back(Token(c
, catcode(c
)));
653 void Parser::dump() const
655 lyxerr
<< "\nTokens: ";
656 for (unsigned i
= 0; i
< tokens_
.size(); ++i
) {
659 lyxerr
<< tokens_
[i
];
661 lyxerr
<< " pos: " << pos_
<< endl
;
665 void Parser::error(string
const & msg
)
668 if (!(mode_
& Parse::QUIET
)) {
669 lyxerr
<< "Line ~" << lineno_
<< ": Math parse error: "
676 bool Parser::parse(MathAtom
& at
)
679 MathData
ar(buffer_
);
680 parse(ar
, false, InsetMath::UNDECIDED_MODE
);
681 if (ar
.size() != 1 || ar
.front()->getType() == hullNone
) {
682 if (!(mode_
& Parse::QUIET
))
683 lyxerr
<< "unusual contents found: " << ar
<< endl
;
684 at
= MathAtom(new InsetMathPar(buffer_
, ar
));
685 //if (at->nargs() > 0)
686 // at.nucleus()->cell(0) = ar;
688 // lyxerr << "unusual contents found: " << ar << endl;
696 docstring
Parser::parse_verbatim_option()
700 if (nextToken().character() == '[') {
701 Token t
= getToken();
702 for (Token t
= getToken(); t
.character() != ']' && good(); t
= getToken()) {
703 if (t
.cat() == catBegin
) {
705 res
+= '{' + parse_verbatim_item() + '}';
714 docstring
Parser::parse_verbatim_item()
718 if (nextToken().cat() == catBegin
) {
719 Token t
= getToken();
720 for (Token t
= getToken(); t
.cat() != catEnd
&& good(); t
= getToken()) {
721 if (t
.cat() == catBegin
) {
723 res
+= '{' + parse_verbatim_item() + '}';
733 MathData
Parser::parse(unsigned flags
, mode_type mode
)
735 MathData
ar(buffer_
);
736 parse(ar
, flags
, mode
);
741 bool Parser::parse(MathData
& array
, unsigned flags
, mode_type mode
)
743 InsetMathGrid
grid(buffer_
, 1, 1);
744 parse1(grid
, flags
, mode
, false);
745 array
= grid
.cell(0);
750 void Parser::parse2(MathAtom
& at
, const unsigned flags
, const mode_type mode
,
753 parse1(*(at
.nucleus()->asGridInset()), flags
, mode
, numbered
);
757 bool Parser::parse1(InsetMathGrid
& grid
, unsigned flags
,
758 const mode_type mode
, const bool numbered
)
761 InsetMathGrid::row_type cellrow
= 0;
762 InsetMathGrid::col_type cellcol
= 0;
763 MathData
* cell
= &grid
.cell(grid
.index(cellrow
, cellcol
));
764 Buffer
* buf
= buffer_
;
766 if (grid
.asHullInset())
767 grid
.asHullInset()->numbered(cellrow
, numbered
);
770 //lyxerr << " flags: " << flags << endl;
771 //lyxerr << " mode: " << mode << endl;
772 //lyxerr << "grid: " << grid << endl;
775 Token
const & t
= getToken();
778 lyxerr
<< "t: " << t
<< " flags: " << flags
<< endl
;
779 lyxerr
<< "mode: " << mode
<< endl
;
784 if (flags
& FLAG_ITEM
) {
786 if (t
.cat() == catBegin
) {
787 // skip the brace and collect everything to the next matching
789 parse1(grid
, FLAG_BRACE_LAST
, mode
, numbered
);
793 // handle only this single token, leave the loop if done
798 if (flags
& FLAG_BRACED
) {
799 if (t
.cat() == catSpace
)
802 if (t
.cat() != catBegin
) {
803 error("opening brace expected");
807 // skip the brace and collect everything to the next matching
809 flags
= FLAG_BRACE_LAST
;
813 if (flags
& FLAG_OPTION
) {
814 if (t
.cat() == catOther
&& t
.character() == '[') {
816 parse(ar
, FLAG_BRACK_LAST
, mode
);
819 // no option found, put back token and we are done
828 if (t
.cat() == catMath
) {
829 if (mode
!= InsetMath::MATH_MODE
) {
830 // we are inside some text mode thingy, so opening new math is allowed
831 Token
const & n
= getToken();
832 if (n
.cat() == catMath
) {
833 // TeX's $$...$$ syntax for displayed math
834 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullEquation
)));
835 parse2(cell
->back(), FLAG_SIMPLE
, InsetMath::MATH_MODE
, false);
836 getToken(); // skip the second '$' token
838 // simple $...$ stuff
840 if (mode
== InsetMath::UNDECIDED_MODE
) {
841 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullSimple
)));
842 parse2(cell
->back(), FLAG_SIMPLE
, InsetMath::MATH_MODE
, false);
844 // Don't create nested math hulls (bug #5392)
845 cell
->push_back(MathAtom(new InsetMathEnsureMath(buf
)));
846 parse(cell
->back().nucleus()->cell(0), FLAG_SIMPLE
, InsetMath::MATH_MODE
);
851 else if (flags
& FLAG_SIMPLE
) {
852 // this is the end of the formula
857 error("something strange in the parser");
862 else if (t
.cat() == catLetter
)
863 cell
->push_back(MathAtom(new InsetMathChar(t
.character())));
865 else if (t
.cat() == catSpace
&& mode
!= InsetMath::MATH_MODE
) {
866 if (cell
->empty() || cell
->back()->getChar() != ' ')
867 cell
->push_back(MathAtom(new InsetMathChar(t
.character())));
870 else if (t
.cat() == catNewline
&& mode
!= InsetMath::MATH_MODE
) {
871 if (cell
->empty() || cell
->back()->getChar() != ' ')
872 cell
->push_back(MathAtom(new InsetMathChar(' ')));
875 else if (t
.cat() == catParameter
) {
876 Token
const & n
= getToken();
877 cell
->push_back(MathAtom(new MathMacroArgument(n
.character()-'0')));
880 else if (t
.cat() == catActive
)
881 cell
->push_back(MathAtom(new InsetMathChar(t
.character())));
883 else if (t
.cat() == catBegin
) {
885 parse(ar
, FLAG_BRACE_LAST
, mode
);
886 // do not create a BraceInset if they were written by LyX
887 // this helps to keep the annoyance of "a choose b" to a minimum
888 if (ar
.size() == 1 && ar
[0]->extraBraces())
891 cell
->push_back(MathAtom(new InsetMathBrace(ar
)));
894 else if (t
.cat() == catEnd
) {
895 if (flags
& FLAG_BRACE_LAST
)
897 error("found '}' unexpectedly");
898 //LASSERT(false, /**/);
899 //add(cell, '}', LM_TC_TEX);
902 else if (t
.cat() == catAlign
) {
903 //lyxerr << " column now " << (cellcol + 1)
904 // << " max: " << grid.ncols() << endl;
905 if (flags
& FLAG_ALIGN
)
907 if (addCol(grid
, cellcol
))
908 cell
= &grid
.cell(grid
.index(cellrow
, cellcol
));
911 else if (t
.cat() == catSuper
|| t
.cat() == catSub
) {
912 bool up
= (t
.cat() == catSuper
);
913 // we need no new script inset if the last thing was a scriptinset,
914 // which has that script already not the same script already
916 cell
->push_back(MathAtom(new InsetMathScript(buf
, up
)));
917 else if (cell
->back()->asScriptInset() &&
918 !cell
->back()->asScriptInset()->has(up
))
919 cell
->back().nucleus()->asScriptInset()->ensure(up
);
920 else if (cell
->back()->asScriptInset())
921 cell
->push_back(MathAtom(new InsetMathScript(buf
, up
)));
923 cell
->back() = MathAtom(new InsetMathScript(buf
, cell
->back(), up
));
924 InsetMathScript
* p
= cell
->back().nucleus()->asScriptInset();
925 // special handling of {}-bases
926 // Here we could remove the brace inset for things
927 // like {a'}^2 and add the braces back in
928 // InsetMathScript::write().
929 // We do not do it, since it is not possible to detect
930 // reliably whether the braces are needed because the
931 // nucleus contains more than one symbol, or whether
932 // they are needed for unknown commands like \xx{a}_0
933 // or \yy{a}{b}_0. This was done in revision 14819
934 // in an unreliable way. See this thread
935 // http://www.mail-archive.com/lyx-devel%40lists.lyx.org/msg104917.html
937 parse(p
->cell(p
->idxOfScript(up
)), FLAG_ITEM
, mode
);
944 else if (t
.character() == ']' && (flags
& FLAG_BRACK_LAST
)) {
945 //lyxerr << "finished reading option" << endl;
949 else if (t
.cat() == catOther
) {
950 char_type c
= t
.character();
951 if (isAsciiOrMathAlpha(c
)
952 || mode_
& Parse::VERBATIM
953 || !(mode_
& Parse::USETEXT
)
954 || mode
== InsetMath::TEXT_MODE
) {
955 cell
->push_back(MathAtom(new InsetMathChar(c
)));
957 MathAtom at
= createInsetMath("text", buf
);
958 at
.nucleus()->cell(0).push_back(MathAtom(new InsetMathChar(c
)));
959 while (nextToken().cat() == catOther
960 && !isAsciiOrMathAlpha(nextToken().character())) {
961 c
= getToken().character();
962 at
.nucleus()->cell(0).push_back(MathAtom(new InsetMathChar(c
)));
968 else if (t
.cat() == catComment
) {
971 Token
const & t
= getToken();
972 if (t
.cat() == catNewline
)
976 cell
->push_back(MathAtom(new InsetMathComment(buf
, s
)));
984 else if (t
.cs() == "lyxlock") {
986 cell
->back().nucleus()->lock(true);
989 else if ((t
.cs() == "global" && nextToken().cs() == "def") ||
991 if (t
.cs() == "global")
995 docstring name
= getToken().cs();
1000 while (good() && nextToken().cat() != catBegin
) {
1001 pars
+= getToken().cs();
1008 parse(def
, FLAG_ITEM
, InsetMath::UNDECIDED_MODE
);
1010 // is a version for display attached?
1013 if (nextToken().cat() == catBegin
)
1014 parse(display
, FLAG_ITEM
, InsetMath::MATH_MODE
);
1016 cell
->push_back(MathAtom(new MathMacroTemplate(buf
,
1017 name
, nargs
, 0, MacroTypeDef
,
1018 vector
<MathData
>(), def
, display
)));
1020 if (buf
&& (mode_
& Parse::TRACKMACRO
))
1021 buf
->usermacros
.insert(name
);
1024 else if (t
.cs() == "newcommand" ||
1025 t
.cs() == "renewcommand" ||
1026 t
.cs() == "newlyxcommand") {
1028 if (getToken().cat() != catBegin
) {
1029 error("'{' in \\newcommand expected (1) ");
1032 docstring name
= getToken().cs();
1033 if (getToken().cat() != catEnd
) {
1034 error("'}' in \\newcommand expected");
1039 docstring
const arg
= getArg('[', ']');
1042 nargs
= convert
<int>(arg
);
1044 // optional argument given?
1047 vector
<MathData
> optionalValues
;
1048 while (nextToken().character() == '[') {
1050 optionalValues
.push_back(MathData());
1051 parse(optionalValues
[optionals
], FLAG_BRACK_LAST
, mode
);
1056 parse(def
, FLAG_ITEM
, InsetMath::UNDECIDED_MODE
);
1058 // is a version for display attached?
1061 if (nextToken().cat() == catBegin
)
1062 parse(display
, FLAG_ITEM
, InsetMath::MATH_MODE
);
1064 cell
->push_back(MathAtom(new MathMacroTemplate(buf
,
1065 name
, nargs
, optionals
, MacroTypeNewcommand
,
1066 optionalValues
, def
, display
)));
1068 if (buf
&& (mode_
& Parse::TRACKMACRO
))
1069 buf
->usermacros
.insert(name
);
1072 else if (t
.cs() == "newcommandx" ||
1073 t
.cs() == "renewcommandx") {
1074 // \newcommandx{\foo}[2][usedefault, addprefix=\global,1=default]{#1,#2}
1077 if (nextToken().cat() == catBegin
) {
1079 name
= getToken().cs();
1080 if (getToken().cat() != catEnd
) {
1081 error("'}' in \\newcommandx expected");
1085 name
= getToken().cs();
1088 docstring
const arg
= getArg('[', ']');
1090 error("[num] in \\newcommandx expected");
1093 int nargs
= convert
<int>(arg
);
1097 vector
<MathData
> optionalValues
;
1098 if (nextToken().character() == '[') {
1102 // handle 'opt=value' options, separated by ','.
1104 while (nextToken().character() != ']' && good()) {
1105 if (nextToken().character() >= '1'
1106 && nextToken().character() <= '9') {
1107 // optional value -> get parameter number
1108 int n
= getChar() - '0';
1110 error("Arity of \\newcommandx too low "
1111 "for given optional parameter.");
1116 if (getToken().character() != '=') {
1117 error("'=' and optional parameter value "
1118 "expected for \\newcommandx");
1123 int optNum
= max(size_t(n
), optionalValues
.size());
1124 optionalValues
.resize(optNum
);
1125 optionalValues
[n
- 1].clear();
1126 while (nextToken().character() != ']'
1127 && nextToken().character() != ',') {
1129 parse(data
, FLAG_ITEM
, InsetMath::UNDECIDED_MODE
);
1130 optionalValues
[n
- 1].append(data
);
1132 optionals
= max(n
, optionals
);
1133 } else if (nextToken().cat() == catLetter
) {
1134 // we in fact ignore every non-optional
1139 while (nextToken().cat() == catLetter
)
1145 if (nextToken().character() == '=') {
1147 while (nextToken().character() != ']'
1148 && nextToken().character() != ',')
1149 parse(value
, FLAG_ITEM
,
1150 InsetMath::UNDECIDED_MODE
);
1153 error("option for \\newcommandx expected");
1159 if (nextToken().character() == ',') {
1162 } else if (nextToken().character() != ']') {
1163 error("Expecting ',' or ']' in options "
1164 "of \\newcommandx");
1177 parse(def
, FLAG_ITEM
, InsetMath::UNDECIDED_MODE
);
1179 // is a version for display attached?
1182 if (nextToken().cat() == catBegin
)
1183 parse(display
, FLAG_ITEM
, InsetMath::MATH_MODE
);
1185 cell
->push_back(MathAtom(new MathMacroTemplate(buf
,
1186 name
, nargs
, optionals
, MacroTypeNewcommandx
,
1187 optionalValues
, def
, display
)));
1189 if (buf
&& (mode_
& Parse::TRACKMACRO
))
1190 buf
->usermacros
.insert(name
);
1193 else if (t
.cs() == "(") {
1194 if (mode
== InsetMath::MATH_MODE
) {
1195 error("bad math environment");
1198 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullSimple
)));
1199 parse2(cell
->back(), FLAG_SIMPLE2
, InsetMath::MATH_MODE
, false);
1202 else if (t
.cs() == "[") {
1203 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1204 error("bad math environment");
1207 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullEquation
)));
1208 parse2(cell
->back(), FLAG_EQUATION
, InsetMath::MATH_MODE
, false);
1211 else if (t
.cs() == "protect")
1212 // ignore \\protect, will hopefully be re-added during output
1215 else if (t
.cs() == "end") {
1216 if (flags
& FLAG_END
) {
1217 // eat environment name
1218 docstring
const name
= getArg('{', '}');
1219 if (environments_
.empty())
1220 error("'found \\end{" + name
+
1221 "}' without matching '\\begin{" +
1223 else if (name
!= environments_
.back())
1224 error("'\\end{" + name
+
1225 "}' does not match '\\begin{" +
1226 environments_
.back() + "}'");
1228 environments_
.pop_back();
1229 // Delete empty last row in matrix
1231 // If you abuse InsetMathGrid for
1232 // non-matrix like structures you
1233 // probably need to refine this test.
1234 // Right now we only have to test for
1235 // single line hull insets.
1236 if (grid
.nrows() > 1)
1237 delEmptyLastRow(grid
);
1241 error("found 'end' unexpectedly");
1244 else if (t
.cs() == ")") {
1245 if (flags
& FLAG_SIMPLE2
)
1247 error("found '\\)' unexpectedly");
1250 else if (t
.cs() == "]") {
1251 if (flags
& FLAG_EQUATION
)
1253 error("found '\\]' unexpectedly");
1256 else if (t
.cs() == "\\") {
1257 if (flags
& FLAG_ALIGN
)
1260 if (nextToken().asInput() == "*") {
1262 added
= addRow(grid
, cellrow
, docstring(), false);
1264 added
= addRow(grid
, cellrow
, getArg('[', ']'));
1266 error("missing token after \\\\");
1269 if (grid
.asHullInset())
1270 grid
.asHullInset()->numbered(
1272 cell
= &grid
.cell(grid
.index(cellrow
,
1278 else if (t
.cs() == "multicolumn") {
1279 // extract column count and insert dummy cells
1281 parse(count
, FLAG_ITEM
, mode
);
1283 if (!extractNumber(count
, cols
)) {
1285 lyxerr
<< " can't extract number of cells from " << count
<< endl
;
1287 // resize the table if necessary
1288 for (int i
= 0; i
< cols
; ++i
) {
1289 if (addCol(grid
, cellcol
)) {
1290 cell
= &grid
.cell(grid
.index(
1292 // mark this as dummy
1293 grid
.cellinfo(grid
.index(
1294 cellrow
, cellcol
)).dummy_
= true;
1297 // the last cell is the real thing, not a dummy
1298 grid
.cellinfo(grid
.index(cellrow
, cellcol
)).dummy_
= false;
1300 // read special alignment
1302 parse(align
, FLAG_ITEM
, mode
);
1303 //grid.cellinfo(grid.index(cellrow, cellcol)).align_ = extractString(align);
1305 // parse the remaining contents into the "real" cell
1306 parse(*cell
, FLAG_ITEM
, mode
);
1310 else if (t
.cs() == "limits")
1313 else if (t
.cs() == "nolimits")
1316 else if (t
.cs() == "nonumber") {
1317 if (grid
.asHullInset())
1318 grid
.asHullInset()->numbered(cellrow
, false);
1321 else if (t
.cs() == "number") {
1322 if (grid
.asHullInset())
1323 grid
.asHullInset()->numbered(cellrow
, true);
1326 else if (t
.cs() == "hline") {
1327 grid
.rowinfo(cellrow
).lines_
++;
1330 else if (t
.cs() == "sqrt") {
1332 parse(ar
, FLAG_OPTION
, mode
);
1334 cell
->push_back(MathAtom(new InsetMathRoot(buf
)));
1335 cell
->back().nucleus()->cell(0) = ar
;
1336 parse(cell
->back().nucleus()->cell(1), FLAG_ITEM
, mode
);
1338 cell
->push_back(MathAtom(new InsetMathSqrt(buf
)));
1339 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, mode
);
1343 else if (t
.cs() == "unit") {
1344 // Allowed formats \unit[val]{unit}
1346 parse(ar
, FLAG_OPTION
, mode
);
1348 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::UNIT
)));
1349 cell
->back().nucleus()->cell(0) = ar
;
1350 parse(cell
->back().nucleus()->cell(1), FLAG_ITEM
, mode
);
1352 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::UNIT
, 1)));
1353 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, mode
);
1357 else if (t
.cs() == "unitfrac") {
1358 // Here allowed formats are \unitfrac[val]{num}{denom}
1360 parse(ar
, FLAG_OPTION
, mode
);
1362 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::UNITFRAC
, 3)));
1363 cell
->back().nucleus()->cell(2) = ar
;
1365 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::UNITFRAC
)));
1367 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, mode
);
1368 parse(cell
->back().nucleus()->cell(1), FLAG_ITEM
, mode
);
1371 else if (t
.cs() == "cfrac") {
1372 // allowed formats are \cfrac[pos]{num}{denom}
1373 docstring
const arg
= getArg('[', ']');
1374 //lyxerr << "got so far: '" << arg << "'" << endl;
1376 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::CFRACLEFT
)));
1377 else if (arg
== "r")
1378 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::CFRACRIGHT
)));
1379 else if (arg
.empty() || arg
== "c")
1380 cell
->push_back(MathAtom(new InsetMathFrac(buf
, InsetMathFrac::CFRAC
)));
1382 error("found invalid optional argument");
1385 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, mode
);
1386 parse(cell
->back().nucleus()->cell(1), FLAG_ITEM
, mode
);
1389 else if (t
.cs() == "xrightarrow" || t
.cs() == "xleftarrow") {
1390 cell
->push_back(createInsetMath(t
.cs(), buf
));
1391 parse(cell
->back().nucleus()->cell(1), FLAG_OPTION
, mode
);
1392 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, mode
);
1395 else if (t
.cs() == "ref" || t
.cs() == "eqref" || t
.cs() == "prettyref"
1396 || t
.cs() == "pageref" || t
.cs() == "vpageref" || t
.cs() == "vref") {
1397 cell
->push_back(MathAtom(new InsetMathRef(buf
, t
.cs())));
1398 parse(cell
->back().nucleus()->cell(1), FLAG_OPTION
, mode
);
1399 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, mode
);
1402 else if (t
.cs() == "left") {
1404 Token
const & tl
= getToken();
1405 // \| and \Vert are equivalent, and InsetMathDelim
1407 // FIXME: fix this in InsetMathDelim itself!
1408 docstring
const l
= tl
.cs() == "|" ? from_ascii("Vert") : tl
.asString();
1410 parse(ar
, FLAG_RIGHT
, mode
);
1414 Token
const & tr
= getToken();
1415 docstring
const r
= tr
.cs() == "|" ? from_ascii("Vert") : tr
.asString();
1416 cell
->push_back(MathAtom(new InsetMathDelim(buf
, l
, r
, ar
)));
1419 else if (t
.cs() == "right") {
1420 if (flags
& FLAG_RIGHT
)
1422 //lyxerr << "got so far: '" << cell << "'" << endl;
1423 error("Unmatched right delimiter");
1427 else if (t
.cs() == "begin") {
1428 docstring
const name
= getArg('{', '}');
1429 environments_
.push_back(name
);
1431 if (name
== "array" || name
== "subarray") {
1432 docstring
const valign
= parse_verbatim_option() + 'c';
1433 docstring
const halign
= parse_verbatim_item();
1434 cell
->push_back(MathAtom(new InsetMathArray(buf
, name
,
1435 InsetMathGrid::guessColumns(halign
), 1, (char)valign
[0], halign
)));
1436 parse2(cell
->back(), FLAG_END
, mode
, false);
1439 else if (name
== "tabular") {
1440 docstring
const valign
= parse_verbatim_option() + 'c';
1441 docstring
const halign
= parse_verbatim_item();
1442 cell
->push_back(MathAtom(new InsetMathTabular(buf
, name
,
1443 InsetMathGrid::guessColumns(halign
), 1, (char)valign
[0], halign
)));
1444 parse2(cell
->back(), FLAG_END
, InsetMath::TEXT_MODE
, false);
1447 else if (name
== "split" || name
== "cases") {
1448 cell
->push_back(createInsetMath(name
, buf
));
1449 parse2(cell
->back(), FLAG_END
, mode
, false);
1452 else if (name
== "alignedat") {
1453 docstring
const valign
= parse_verbatim_option() + 'c';
1454 // ignore this for a while
1456 cell
->push_back(MathAtom(new InsetMathSplit(buf
, name
, (char)valign
[0])));
1457 parse2(cell
->back(), FLAG_END
, mode
, false);
1460 else if (name
== "math") {
1461 if (mode
== InsetMath::MATH_MODE
) {
1462 error("bad math environment");
1465 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullSimple
)));
1466 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, true);
1469 else if (name
== "equation" || name
== "equation*"
1470 || name
== "displaymath") {
1471 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1472 error("bad math environment");
1475 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullEquation
)));
1476 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, (name
== "equation"));
1479 else if (name
== "eqnarray" || name
== "eqnarray*") {
1480 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1481 error("bad math environment");
1484 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullEqnArray
)));
1485 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1488 else if (name
== "align" || name
== "align*") {
1489 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1490 error("bad math environment");
1493 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullAlign
)));
1494 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1497 else if (name
== "flalign" || name
== "flalign*") {
1498 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1499 error("bad math environment");
1502 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullFlAlign
)));
1503 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1506 else if (name
== "alignat" || name
== "alignat*") {
1507 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1508 error("bad math environment");
1511 // ignore this for a while
1513 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullAlignAt
)));
1514 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1517 else if (name
== "xalignat" || name
== "xalignat*") {
1518 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1519 error("bad math environment");
1522 // ignore this for a while
1524 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullXAlignAt
)));
1525 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1528 else if (name
== "xxalignat") {
1529 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1530 error("bad math environment");
1533 // ignore this for a while
1535 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullXXAlignAt
)));
1536 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1539 else if (name
== "multline" || name
== "multline*") {
1540 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1541 error("bad math environment");
1544 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullMultline
)));
1545 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1548 else if (name
== "gather" || name
== "gather*") {
1549 if (mode
!= InsetMath::UNDECIDED_MODE
) {
1550 error("bad math environment");
1553 cell
->push_back(MathAtom(new InsetMathHull(buf
, hullGather
)));
1554 parse2(cell
->back(), FLAG_END
, InsetMath::MATH_MODE
, !stared(name
));
1557 else if (latexkeys
const * l
= in_word_set(name
)) {
1558 if (l
->inset
== "matrix") {
1559 cell
->push_back(createInsetMath(name
, buf
));
1560 parse2(cell
->back(), FLAG_END
, mode
, false);
1561 } else if (l
->inset
== "split") {
1562 docstring
const valign
= parse_verbatim_option() + 'c';
1563 cell
->push_back(MathAtom(
1564 new InsetMathSplit(buf
, name
, (char)valign
[0])));
1565 parse2(cell
->back(), FLAG_END
, mode
, false);
1568 if (!(mode_
& Parse::QUIET
)) {
1570 lyxerr
<< "found math environment `"
1572 << "' in symbols file with unsupported inset `"
1573 << to_utf8(l
->inset
)
1576 // create generic environment inset
1577 cell
->push_back(MathAtom(new InsetMathEnv(buf
, name
)));
1578 parse(cell
->back().nucleus()->cell(0), FLAG_END
, mode
);
1584 if (!(mode_
& Parse::QUIET
)) {
1586 lyxerr
<< "found unknown math environment '"
1587 << to_utf8(name
) << "'" << endl
;
1589 // create generic environment inset
1590 cell
->push_back(MathAtom(new InsetMathEnv(buf
, name
)));
1591 parse(cell
->back().nucleus()->cell(0), FLAG_END
, mode
);
1595 else if (t
.cs() == "kern") {
1600 Token
const & t
= getToken();
1604 while (num_tokens
--)
1609 if (isValidLength(to_utf8(s
)))
1613 cell
->push_back(MathAtom(new InsetMathKern
));
1615 cell
->push_back(MathAtom(new InsetMathKern(s
)));
1618 else if (t
.cs() == "label") {
1619 // FIXME: This is swallowed in inline formulas
1620 docstring label
= parse_verbatim_item();
1623 if (grid
.asHullInset()) {
1624 grid
.asHullInset()->label(cellrow
, label
);
1626 cell
->push_back(createInsetMath(t
.cs(), buf
));
1627 cell
->push_back(MathAtom(new InsetMathBrace(ar
)));
1631 else if (t
.cs() == "choose" || t
.cs() == "over"
1632 || t
.cs() == "atop" || t
.cs() == "brace"
1633 || t
.cs() == "brack") {
1634 MathAtom at
= createInsetMath(t
.cs(), buf
);
1635 at
.nucleus()->cell(0) = *cell
;
1637 parse(at
.nucleus()->cell(1), flags
, mode
);
1638 cell
->push_back(at
);
1642 else if (t
.cs() == "color") {
1643 docstring
const color
= parse_verbatim_item();
1644 cell
->push_back(MathAtom(new InsetMathColor(buf
, true, color
)));
1645 parse(cell
->back().nucleus()->cell(0), flags
, mode
);
1649 else if (t
.cs() == "textcolor") {
1650 docstring
const color
= parse_verbatim_item();
1651 cell
->push_back(MathAtom(new InsetMathColor(buf
, false, color
)));
1652 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, InsetMath::TEXT_MODE
);
1655 else if (t
.cs() == "normalcolor") {
1656 cell
->push_back(createInsetMath(t
.cs(), buf
));
1657 parse(cell
->back().nucleus()->cell(0), flags
, mode
);
1661 else if (t
.cs() == "substack") {
1662 cell
->push_back(createInsetMath(t
.cs(), buf
));
1663 parse2(cell
->back(), FLAG_ITEM
, mode
, false);
1666 else if (t
.cs() == "xymatrix") {
1667 odocstringstream os
;
1668 while (good() && nextToken().cat() != catBegin
)
1669 os
<< getToken().asInput();
1670 cell
->push_back(createInsetMath(t
.cs() + os
.str(), buf
));
1671 parse2(cell
->back(), FLAG_ITEM
, mode
, false);
1674 else if (t
.cs() == "framebox" || t
.cs() == "makebox") {
1675 cell
->push_back(createInsetMath(t
.cs(), buf
));
1676 parse(cell
->back().nucleus()->cell(0), FLAG_OPTION
, InsetMath::TEXT_MODE
);
1677 parse(cell
->back().nucleus()->cell(1), FLAG_OPTION
, InsetMath::TEXT_MODE
);
1678 parse(cell
->back().nucleus()->cell(2), FLAG_ITEM
, InsetMath::TEXT_MODE
);
1681 else if (t
.cs() == "tag") {
1682 if (nextToken().character() == '*') {
1684 cell
->push_back(createInsetMath(t
.cs() + '*', buf
));
1686 cell
->push_back(createInsetMath(t
.cs(), buf
));
1687 parse(cell
->back().nucleus()->cell(0), FLAG_ITEM
, InsetMath::TEXT_MODE
);
1690 else if (t
.cs() == "hspace" && nextToken().character() != '*') {
1691 docstring
const name
= t
.cs();
1692 docstring
const arg
= parse_verbatim_item();
1694 if (isValidLength(to_utf8(arg
), &length
))
1695 cell
->push_back(MathAtom(new InsetMathSpace(length
)));
1697 // Since the Length class cannot use length variables
1698 // we must not create an InsetMathSpace.
1699 cell
->push_back(MathAtom(new MathMacro(buf
, name
)));
1701 mathed_parse_cell(ar
, '{' + arg
+ '}', mode_
);
1707 else if (t
.cs() == "infer") {
1709 parse(ar
, FLAG_OPTION
, mode
);
1710 cell
->push_back(createInsetMath(t
.cs(), buf
));
1711 parse2(cell
->back(), FLAG_ITEM
, mode
, false);
1715 else if (1 && t
.cs() == "ar") {
1716 auto_ptr
<InsetMathXYArrow
> p(new InsetMathXYArrow
);
1717 // try to read target
1718 parse(p
->cell(0), FLAG_OTPTION
, mode
);
1719 // try to read label
1720 if (nextToken().cat() == catSuper
|| nextToken().cat() == catSub
) {
1721 p
->up_
= nextToken().cat() == catSuper
;
1723 parse(p
->cell(1), FLAG_ITEM
, mode
);
1724 //lyxerr << "read label: " << p->cell(1) << endl;
1727 cell
->push_back(MathAtom(p
.release()));
1728 //lyxerr << "read cell: " << cell << endl;
1732 else if (t
.cs() == "lyxmathsym") {
1734 if (getToken().cat() != catBegin
) {
1735 error("'{' expected in \\" + t
.cs());
1740 CatCode cat
= nextToken().cat();
1741 while (good() && (count
|| cat
!= catEnd
)) {
1742 if (cat
== catBegin
)
1744 else if (cat
== catEnd
)
1746 cmd
+= getToken().asInput();
1747 cat
= nextToken().cat();
1749 if (getToken().cat() != catEnd
) {
1750 error("'}' expected in \\" + t
.cs());
1755 cmd
= Encodings::fromLaTeXCommand(cmd
, rem
);
1756 for (size_t i
= 0; i
< cmd
.size(); ++i
)
1757 cell
->push_back(MathAtom(new InsetMathChar(cmd
[i
])));
1759 char_type c
= rem
[0];
1760 cell
->push_back(MathAtom(new InsetMathChar(c
)));
1761 cmd
= rem
.substr(1);
1765 } while (cmd
.size());
1768 else if (t
.cs().size()) {
1769 bool const no_mhchem
=
1770 (t
.cs() == "ce" || t
.cs() == "cf") && buf
1771 && buf
->params().use_mhchem
== BufferParams::package_off
;
1772 bool const is_user_macro
= no_mhchem
||
1773 (buf
&& (mode_
& Parse::TRACKMACRO
1774 ? buf
->usermacros
.count(t
.cs()) != 0
1775 : buf
->getMacro(t
.cs(), false) != 0));
1776 latexkeys
const * l
= in_word_set(t
.cs());
1777 if (l
&& !is_user_macro
) {
1778 if (l
->inset
== "big") {
1780 docstring
const delim
= getToken().asInput();
1781 if (InsetMathBig::isBigInsetDelim(delim
))
1782 cell
->push_back(MathAtom(
1783 new InsetMathBig(t
.cs(), delim
)));
1785 cell
->push_back(createInsetMath(t
.cs(), buf
));
1790 else if (l
->inset
== "font") {
1791 cell
->push_back(createInsetMath(t
.cs(), buf
));
1792 parse(cell
->back().nucleus()->cell(0),
1793 FLAG_ITEM
, asMode(mode
, l
->extra
));
1796 else if (l
->inset
== "oldfont") {
1797 cell
->push_back(createInsetMath(t
.cs(), buf
));
1798 parse(cell
->back().nucleus()->cell(0),
1799 flags
| FLAG_ALIGN
, asMode(mode
, l
->extra
));
1800 if (prevToken().cat() != catAlign
&&
1801 prevToken().cs() != "\\")
1806 else if (l
->inset
== "style") {
1807 cell
->push_back(createInsetMath(t
.cs(), buf
));
1808 parse(cell
->back().nucleus()->cell(0),
1809 flags
| FLAG_ALIGN
, mode
);
1810 if (prevToken().cat() != catAlign
&&
1811 prevToken().cs() != "\\")
1817 MathAtom at
= createInsetMath(t
.cs(), buf
);
1818 for (InsetMath::idx_type i
= 0; i
< at
->nargs(); ++i
)
1819 parse(at
.nucleus()->cell(i
),
1820 FLAG_ITEM
, asMode(mode
, l
->extra
));
1821 cell
->push_back(at
);
1826 bool is_unicode_symbol
= false;
1827 if (mode
== InsetMath::TEXT_MODE
&& !is_user_macro
) {
1829 docstring cmd
= prevToken().asInput();
1830 CatCode cat
= nextToken().cat();
1831 if (cat
== catBegin
) {
1833 while (good() && (count
|| cat
!= catEnd
)) {
1834 cat
= nextToken().cat();
1835 cmd
+= getToken().asInput();
1837 if (cat
== catBegin
)
1839 else if (cat
== catEnd
)
1845 Encodings::fromLaTeXCommand(cmd
, is_combining
);
1847 if (cat
== catLetter
)
1849 cmd
+= getToken().asInput();
1851 if (cat
== catLetter
)
1853 c
= Encodings::fromLaTeXCommand(cmd
, is_combining
);
1856 is_unicode_symbol
= true;
1857 cell
->push_back(MathAtom(new InsetMathChar(c
)));
1859 while (num_tokens
--)
1863 if (!is_unicode_symbol
) {
1864 MathAtom at
= is_user_macro
?
1865 MathAtom(new MathMacro(buf
, t
.cs()))
1866 : createInsetMath(t
.cs(), buf
);
1867 InsetMath::mode_type m
= mode
;
1868 //if (m == InsetMath::UNDECIDED_MODE)
1869 //lyxerr << "default creation: m1: " << m << endl;
1870 if (at
->currentMode() != InsetMath::UNDECIDED_MODE
)
1871 m
= at
->currentMode();
1872 //lyxerr << "default creation: m2: " << m << endl;
1873 InsetMath::idx_type start
= 0;
1874 // this fails on \bigg[...\bigg]
1876 //parse(opt, FLAG_OPTION, InsetMath::VERBATIM_MODE);
1879 // at.nucleus()->cell(0) = opt;
1881 for (InsetMath::idx_type i
= start
; i
< at
->nargs(); ++i
) {
1882 parse(at
.nucleus()->cell(i
), FLAG_ITEM
, m
);
1885 cell
->push_back(at
);
1891 if (flags
& FLAG_LEAVE
) {
1892 flags
&= ~FLAG_LEAVE
;
1901 } // anonymous namespace
1904 bool mathed_parse_cell(MathData
& ar
, docstring
const & str
, Parse::flags f
)
1906 return Parser(str
, f
, ar
.buffer()).parse(ar
, 0, f
& Parse::TEXTMODE
?
1907 InsetMath::TEXT_MODE
: InsetMath::MATH_MODE
);
1911 bool mathed_parse_cell(MathData
& ar
, istream
& is
, Parse::flags f
)
1913 return Parser(is
, f
, ar
.buffer()).parse(ar
, 0, f
& Parse::TEXTMODE
?
1914 InsetMath::TEXT_MODE
: InsetMath::MATH_MODE
);
1918 bool mathed_parse_normal(Buffer
* buf
, MathAtom
& t
, docstring
const & str
,
1921 return Parser(str
, f
, buf
).parse(t
);
1925 bool mathed_parse_normal(Buffer
* buf
, MathAtom
& t
, Lexer
& lex
,
1928 return Parser(lex
, f
, buf
).parse(t
);
1932 bool mathed_parse_normal(InsetMathGrid
& grid
, docstring
const & str
,
1935 return Parser(str
, f
, &grid
.buffer()).parse1(grid
, 0, f
& Parse::TEXTMODE
?
1936 InsetMath::TEXT_MODE
: InsetMath::MATH_MODE
, false);
1942 fill(theCatcode
, theCatcode
+ 128, catOther
);
1943 fill(theCatcode
+ 'a', theCatcode
+ 'z' + 1, catLetter
);
1944 fill(theCatcode
+ 'A', theCatcode
+ 'Z' + 1, catLetter
);
1946 theCatcode
[int('\\')] = catEscape
;
1947 theCatcode
[int('{')] = catBegin
;
1948 theCatcode
[int('}')] = catEnd
;
1949 theCatcode
[int('$')] = catMath
;
1950 theCatcode
[int('&')] = catAlign
;
1951 theCatcode
[int('\n')] = catNewline
;
1952 theCatcode
[int('#')] = catParameter
;
1953 theCatcode
[int('^')] = catSuper
;
1954 theCatcode
[int('_')] = catSub
;
1955 theCatcode
[int(0x7f)] = catIgnore
;
1956 theCatcode
[int(' ')] = catSpace
;
1957 theCatcode
[int('\t')] = catSpace
;
1958 theCatcode
[int('\r')] = catNewline
;
1959 theCatcode
[int('~')] = catActive
;
1960 theCatcode
[int('%')] = catComment
;