X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fmathed%2FMathParser.cpp;h=9b43da8cdda5b7d7a40a2b415f0cea3fe1ae9c7b;hb=7b7322b831f1bd916293b0e20c827db429ed3773;hp=8ad3173d224206c95064847bf0ffb8d1606d3a96;hpb=976307231db4c3bfcd3736b88be99054812bdc8e;p=lyx.git diff --git a/src/mathed/MathParser.cpp b/src/mathed/MathParser.cpp index 8ad3173d22..9b43da8cdd 100644 --- a/src/mathed/MathParser.cpp +++ b/src/mathed/MathParser.cpp @@ -42,6 +42,7 @@ following hack as starting point to write some macros: #include "InsetMathArray.h" #include "InsetMathBig.h" #include "InsetMathBrace.h" +#include "InsetMathCancelto.h" #include "InsetMathChar.h" #include "InsetMathColor.h" #include "InsetMathComment.h" @@ -50,19 +51,22 @@ following hack as starting point to write some macros: #include "InsetMathEnv.h" #include "InsetMathFrac.h" #include "InsetMathKern.h" -#include "MathMacro.h" +#include "InsetMathMacro.h" #include "InsetMathPar.h" #include "InsetMathRef.h" #include "InsetMathRoot.h" #include "InsetMathScript.h" +#include "InsetMathSideset.h" #include "InsetMathSpace.h" #include "InsetMathSplit.h" #include "InsetMathSqrt.h" +#include "InsetMathStackrel.h" #include "InsetMathString.h" #include "InsetMathTabular.h" -#include "MathMacroTemplate.h" +#include "InsetMathMacroTemplate.h" +#include "MathExtern.h" #include "MathFactory.h" -#include "MathMacroArgument.h" +#include "InsetMathMacroArgument.h" #include "MathSupport.h" #include "Buffer.h" @@ -70,8 +74,8 @@ following hack as starting point to write some macros: #include "Encoding.h" #include "Lexer.h" -#include "support/debug.h" #include "support/convert.h" +#include "support/debug.h" #include "support/docstream.h" #include @@ -153,11 +157,11 @@ docstring escapeSpecialChars(docstring const & str, bool textmode) /*! * Add the row \p cellrow to \p grid. - * \returns wether the row could be added. Adding a row can fail for + * \returns whether the row could be added. Adding a row can fail for * environments like "equation" that have a fixed number of rows. */ -bool addRow(InsetMathGrid & grid, InsetMathGrid::row_type & cellrow, - docstring const & vskip, bool allow_newpage_ = true) +bool addRow(InsetMathGrid & grid, row_type & cellrow, + docstring const & vskip, bool allow_newpage = true) { ++cellrow; if (cellrow == grid.nrows()) { @@ -174,24 +178,24 @@ bool addRow(InsetMathGrid & grid, InsetMathGrid::row_type & cellrow, lyxerr << "ignoring extra row"; if (!vskip.empty()) lyxerr << " with extra space " << to_utf8(vskip); - if (!allow_newpage_) + if (!allow_newpage) lyxerr << " with no page break allowed"; lyxerr << '.' << endl; return false; } } grid.vcrskip(Length(to_utf8(vskip)), cellrow - 1); - grid.rowinfo(cellrow - 1).allow_newpage_ = allow_newpage_; + grid.rowinfo(cellrow - 1).allow_newpage = allow_newpage; return true; } /*! * Add the column \p cellcol to \p grid. - * \returns wether the column could be added. Adding a column can fail for + * \returns whether the column could be added. Adding a column can fail for * environments like "eqnarray" that have a fixed number of columns. */ -bool addCol(InsetMathGrid & grid, InsetMathGrid::col_type & cellcol) +bool addCol(InsetMathGrid & grid, col_type & cellcol) { ++cellcol; if (cellcol == grid.ncols()) { @@ -225,14 +229,18 @@ bool addCol(InsetMathGrid & grid, InsetMathGrid::col_type & cellcol) * \endverbatim * will result in a grid with 3 rows (+ the dummy row that is always present), * because the last '\\' opens a new row. + * Do never delete a row that contains a multicolumn, even if all cells empty, + * since the multicolumn information would get lost otherwise. * Note that this is only needed for inner-hull grid types, such as array * or aligned, but not for outer-hull grid types, such as eqnarray or align. */ void delEmptyLastRow(InsetMathGrid & grid) { - InsetMathGrid::row_type const row = grid.nrows() - 1; - for (InsetMathGrid::col_type col = 0; col < grid.ncols(); ++col) { - if (!grid.cell(grid.index(row, col)).empty()) + row_type const row = grid.nrows() - 1; + for (col_type col = 0; col < grid.ncols(); ++col) { + idx_type const idx = grid.index(row, col); + if (!grid.cell(idx).empty() || + grid.cellinfo(idx).multi != InsetMathGrid::CELL_NORMAL) return; } // Copy the row information of the empty row (which would contain the @@ -252,7 +260,7 @@ bool innerHull(docstring const & name) return name == "array" || name == "cases" || name == "aligned" || name == "alignedat" || name == "gathered" || name == "split" || name == "subarray" || name == "tabular" || name == "matrix" - || name.substr(1) == "matrix"; + || name == "smallmatrix" || name.substr(1) == "matrix"; } @@ -333,9 +341,9 @@ public: /// char_type character() const { return char_; } /// - docstring asString() const { return cs_.size() ? cs_ : docstring(1, char_); } + docstring asString() const { return !cs_.empty() ? cs_ : docstring(1, char_); } /// - docstring asInput() const { return cs_.size() ? '\\' + cs_ : docstring(1, char_); } + docstring asInput() const { return !cs_.empty() ? '\\' + cs_ : docstring(1, char_); } private: /// @@ -349,9 +357,9 @@ private: ostream & operator<<(ostream & os, Token const & t) { - if (t.cs().size()) { + if (!t.cs().empty()) { docstring const & cs = t.cs(); - // FIXME: For some strange reason, the stream operator instanciate + // FIXME: For some strange reason, the stream operator instantiate // a new Token before outputting the contents of t.cs(). // Because of this the line // os << '\\' << cs; @@ -362,9 +370,9 @@ ostream & operator<<(ostream & os, Token const & t) os << '\\' << to_utf8(cs); } else if (t.cat() == catLetter) - os << t.character(); + os << static_cast(t.character()); else - os << '[' << t.character() << ',' << t.cat() << ']'; + os << '[' << static_cast(t.character()) << ',' << t.cat() << ']'; return os; } @@ -392,11 +400,15 @@ public: bool parse1(InsetMathGrid & grid, unsigned flags, mode_type mode, bool numbered); /// - MathData parse(unsigned flags, mode_type mode); - /// int lineno() const { return lineno_; } /// void putback(); + /// store current position + void pushPosition(); + /// restore previous position + void popPosition(); + /// forget last saved position + void dropPosition(); private: /// @@ -420,8 +432,6 @@ private: /// void push_back(Token const & t); /// - void pop_back(); - /// Token const & prevToken() const; /// Token const & nextToken() const; @@ -444,6 +454,8 @@ private: vector tokens_; /// unsigned pos_; + /// + std::vector positions_; /// Stack of active environments vector environments_; /// @@ -484,12 +496,6 @@ void Parser::push_back(Token const & t) } -void Parser::pop_back() -{ - tokens_.pop_back(); -} - - Token const & Parser::prevToken() const { static const Token dummy; @@ -525,6 +531,25 @@ void Parser::putback() } +void Parser::pushPosition() +{ + positions_.push_back(pos_); +} + + +void Parser::popPosition() +{ + pos_ = positions_.back(); + positions_.pop_back(); +} + + +void Parser::dropPosition() +{ + positions_.pop_back(); +} + + bool Parser::good() const { return pos_ < tokens_.size(); @@ -535,7 +560,6 @@ char_type Parser::getChar() { if (!good()) { error("The input stream is not well..."); - putback(); return 0; } return tokens_[pos_++].character(); @@ -544,9 +568,12 @@ char_type Parser::getChar() docstring Parser::getArg(char_type left, char_type right) { + docstring result; skipSpaces(); - docstring result; + if (!good()) + return result; + char_type c = getChar(); if (c != left) @@ -594,7 +621,7 @@ void Parser::tokenize(istream & is) void Parser::tokenize(docstring const & buffer) { - idocstringstream is(mode_ & Parse::VERBATIM + idocstringstream is((mode_ & Parse::VERBATIM) ? escapeSpecialChars(buffer, mode_ & Parse::TEXTMODE) : buffer, ios::in | ios::binary); @@ -716,7 +743,8 @@ docstring Parser::parse_verbatim_option() skipSpaces(); docstring res; if (nextToken().character() == '[') { - Token t = getToken(); + // eat [ + getToken(); for (Token t = getToken(); t.character() != ']' && good(); t = getToken()) { if (t.cat() == catBegin) { putback(); @@ -734,7 +762,8 @@ docstring Parser::parse_verbatim_item() skipSpaces(); docstring res; if (nextToken().cat() == catBegin) { - Token t = getToken(); + // eat catBegin + getToken(); for (Token t = getToken(); t.cat() != catEnd && good(); t = getToken()) { if (t.cat() == catBegin) { putback(); @@ -748,14 +777,6 @@ docstring Parser::parse_verbatim_item() } -MathData Parser::parse(unsigned flags, mode_type mode) -{ - MathData ar(buffer_); - parse(ar, flags, mode); - return ar; -} - - bool Parser::parse(MathData & array, unsigned flags, mode_type mode) { InsetMathGrid grid(buffer_, 1, 1); @@ -775,9 +796,8 @@ void Parser::parse2(MathAtom & at, const unsigned flags, const mode_type mode, bool Parser::parse1(InsetMathGrid & grid, unsigned flags, const mode_type mode, const bool numbered) { - int limits = 0; - InsetMathGrid::row_type cellrow = 0; - InsetMathGrid::col_type cellcol = 0; + row_type cellrow = 0; + col_type cellcol = 0; MathData * cell = &grid.cell(grid.index(cellrow, cellcol)); Buffer * buf = buffer_; @@ -849,9 +869,16 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, Token const & n = getToken(); if (n.cat() == catMath) { // TeX's $$...$$ syntax for displayed math - cell->push_back(MathAtom(new InsetMathHull(buf, hullEquation))); - parse2(cell->back(), FLAG_SIMPLE, InsetMath::MATH_MODE, false); - getToken(); // skip the second '$' token + if (mode == InsetMath::UNDECIDED_MODE) { + cell->push_back(MathAtom(new InsetMathHull(buf, hullEquation))); + parse2(cell->back(), FLAG_SIMPLE, InsetMath::MATH_MODE, false); + getToken(); // skip the second '$' token + } else { + // This is not an outer hull and display math is + // not allowed inside text mode environments. + error("bad math environment $$"); + break; + } } else { // simple $...$ stuff putback(); @@ -872,8 +899,19 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, } else { - error("something strange in the parser"); - break; + Token const & n = getToken(); + if (n.cat() == catMath) { + error("something strange in the parser"); + break; + } else { + // This is inline math ($...$), but the parser thinks we are + // already in math mode and latex would issue an error, unless we + // are inside a text mode user macro. We have no way to tell, so + // let's play safe by using \ensuremath, as it will work in any case. + putback(); + cell->push_back(MathAtom(new InsetMathEnsureMath(buf))); + parse(cell->back().nucleus()->cell(0), FLAG_SIMPLE, InsetMath::MATH_MODE); + } } } @@ -891,12 +929,17 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, } else if (t.cat() == catParameter) { - Token const & n = getToken(); - cell->push_back(MathAtom(new MathMacroArgument(n.character()-'0'))); + Token const & n = nextToken(); + char_type c = n.character(); + if (c && '0' < c && c <= '9') { + cell->push_back(MathAtom(new InsetMathMacroArgument(c - '0'))); + getToken(); + } else + cell->push_back(MathAtom(new InsetMathHash())); } else if (t.cat() == catActive) - cell->push_back(MathAtom(new InsetMathChar(t.character()))); + cell->push_back(MathAtom(new InsetMathSpace(string(1, t.character()), ""))); else if (t.cat() == catBegin) { MathData ar; @@ -930,7 +973,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, bool up = (t.cat() == catSuper); // we need no new script inset if the last thing was a scriptinset, // which has that script already not the same script already - if (!cell->size()) + if (cell->empty()) cell->push_back(MathAtom(new InsetMathScript(buf, up))); else if (cell->back()->asScriptInset() && !cell->back()->asScriptInset()->has(up)) @@ -961,10 +1004,6 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, p->nuc().erase(0); parse(p->cell(p->idxOfScript(up)), FLAG_ITEM, mode); - if (limits) { - p->limits(limits); - limits = 0; - } } else if (t.character() == ']' && (flags & FLAG_BRACK_LAST)) { @@ -974,7 +1013,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, else if (t.cat() == catOther) { char_type c = t.character(); - if (isAsciiOrMathAlpha(c) + if (!Encodings::isUnicodeTextOnly(c) || mode_ & Parse::VERBATIM || !(mode_ & Parse::USETEXT) || mode == InsetMath::TEXT_MODE) { @@ -983,7 +1022,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, MathAtom at = createInsetMath("text", buf); at.nucleus()->cell(0).push_back(MathAtom(new InsetMathChar(c))); while (nextToken().cat() == catOther - && !isAsciiOrMathAlpha(nextToken().character())) { + && Encodings::isUnicodeTextOnly(nextToken().character())) { c = getToken().character(); at.nucleus()->cell(0).push_back(MathAtom(new InsetMathChar(c))); } @@ -994,10 +1033,10 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, else if (t.cat() == catComment) { docstring s; while (good()) { - Token const & t = getToken(); - if (t.cat() == catNewline) + Token const & tt = getToken(); + if (tt.cat() == catNewline) break; - s += t.asString(); + s += tt.asInput(); } cell->push_back(MathAtom(new InsetMathComment(buf, s))); skipSpaces(); @@ -1008,7 +1047,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, // else if (t.cs() == "lyxlock") { - if (cell->size()) + if (!cell->empty()) cell->back().nucleus()->lock(true); } @@ -1016,10 +1055,10 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, t.cs() == "def") { if (t.cs() == "global") getToken(); - + // get name docstring name = getToken().cs(); - + // read parameters int nargs = 0; docstring pars; @@ -1028,25 +1067,25 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, ++nargs; } nargs /= 2; - + // read definition MathData def; parse(def, FLAG_ITEM, InsetMath::UNDECIDED_MODE); - + // is a version for display attached? skipSpaces(); MathData display; if (nextToken().cat() == catBegin) parse(display, FLAG_ITEM, InsetMath::MATH_MODE); - - cell->push_back(MathAtom(new MathMacroTemplate(buf, + + cell->push_back(MathAtom(new InsetMathMacroTemplate(buf, name, nargs, 0, MacroTypeDef, vector(), def, display))); if (buf && (mode_ & Parse::TRACKMACRO)) buf->usermacros.insert(name); } - + else if (t.cs() == "newcommand" || t.cs() == "renewcommand" || t.cs() == "newlyxcommand") { @@ -1060,13 +1099,13 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, error("'}' in \\newcommand expected"); return success_; } - + // get arity docstring const arg = getArg('[', ']'); int nargs = 0; if (!arg.empty()) nargs = convert(arg); - + // optional argument given? skipSpaces(); int optionals = 0; @@ -1077,24 +1116,24 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, parse(optionalValues[optionals], FLAG_BRACK_LAST, mode); ++optionals; } - + MathData def; parse(def, FLAG_ITEM, InsetMath::UNDECIDED_MODE); - + // is a version for display attached? skipSpaces(); MathData display; if (nextToken().cat() == catBegin) parse(display, FLAG_ITEM, InsetMath::MATH_MODE); - - cell->push_back(MathAtom(new MathMacroTemplate(buf, + + cell->push_back(MathAtom(new InsetMathMacroTemplate(buf, name, nargs, optionals, MacroTypeNewcommand, optionalValues, def, display))); if (buf && (mode_ & Parse::TRACKMACRO)) buf->usermacros.insert(name); } - + else if (t.cs() == "newcommandx" || t.cs() == "renewcommandx") { // \newcommandx{\foo}[2][usedefault, addprefix=\global,1=default]{#1,#2} @@ -1109,7 +1148,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, } } else name = getToken().cs(); - + // get arity docstring const arg = getArg('[', ']'); if (arg.empty()) { @@ -1117,14 +1156,14 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, return success_; } int nargs = convert(arg); - + // get options int optionals = 0; vector optionalValues; if (nextToken().character() == '[') { // skip '[' getToken(); - + // handle 'opt=value' options, separated by ','. skipSpaces(); while (nextToken().character() != ']' && good()) { @@ -1137,14 +1176,14 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, "for given optional parameter."); return success_; } - + // skip '=' if (getToken().character() != '=') { error("'=' and optional parameter value " "expected for \\newcommandx"); return success_; } - + // get value int optNum = max(size_t(n), optionalValues.size()); optionalValues.resize(optNum); @@ -1159,12 +1198,12 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, } else if (nextToken().cat() == catLetter) { // we in fact ignore every non-optional // parameter - + // get option name docstring opt; while (nextToken().cat() == catLetter) opt += getChar(); - + // value? skipSpaces(); MathData value; @@ -1172,14 +1211,14 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, getToken(); while (nextToken().character() != ']' && nextToken().character() != ',') - parse(value, FLAG_ITEM, + parse(value, FLAG_ITEM, InsetMath::UNDECIDED_MODE); } } else { error("option for \\newcommandx expected"); return success_; } - + // skip komma skipSpaces(); if (nextToken().character() == ',') { @@ -1191,7 +1230,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, return success_; } } - + // skip ']' if (!good()) return success_; @@ -1208,7 +1247,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, if (nextToken().cat() == catBegin) parse(display, FLAG_ITEM, InsetMath::MATH_MODE); - cell->push_back(MathAtom(new MathMacroTemplate(buf, + cell->push_back(MathAtom(new InsetMathMacroTemplate(buf, name, nargs, optionals, MacroTypeNewcommandx, optionalValues, def, display))); @@ -1217,13 +1256,19 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, } else if (t.cs() == "(") { - cell->push_back(MathAtom(new InsetMathEnsureMath(buf))); - parse(cell->back().nucleus()->cell(0), FLAG_SIMPLE2, InsetMath::MATH_MODE); + if (mode == InsetMath::UNDECIDED_MODE) { + cell->push_back(MathAtom(new InsetMathHull(buf, hullSimple))); + parse2(cell->back(), FLAG_SIMPLE2, InsetMath::MATH_MODE, false); + } else { + // Don't create nested math hulls (bug #5392) + cell->push_back(MathAtom(new InsetMathEnsureMath(buf))); + parse(cell->back().nucleus()->cell(0), FLAG_SIMPLE2, InsetMath::MATH_MODE); + } } else if (t.cs() == "[") { if (mode != InsetMath::UNDECIDED_MODE) { - error("bad math environment"); + error("bad math environment ["); break; } cell->push_back(MathAtom(new InsetMathHull(buf, hullEquation))); @@ -1278,14 +1323,36 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, else if (t.cs() == "\\") { if (flags & FLAG_ALIGN) return success_; - bool added = false; + bool starred = false; + docstring arg; if (nextToken().asInput() == "*") { getToken(); - added = addRow(grid, cellrow, docstring(), false); - } else if (good()) - added = addRow(grid, cellrow, getArg('[', ']')); - else + starred = true; + } else if (nextToken().asInput() == "[") + arg = getArg('[', ']'); + else if (!good()) error("missing token after \\\\"); + // skip "{}" added in front of "[" (the + // counterpart is in InsetMathGrid::eolString()) + // skip spaces because formula could come from tex2lyx + bool skipBraces = false; + pushPosition(); + if (nextToken().cat() == catBegin) { + getToken(); + if (nextToken().cat() == catEnd) { + getToken(); + pushPosition(); + skipSpaces(); + if (nextToken().asInput() == "[") + skipBraces = true; + popPosition(); + } + } + if (skipBraces) + dropPosition(); + else + popPosition(); + bool const added = addRow(grid, cellrow, arg, !starred); if (added) { cellcol = 0; if (grid.asHullInset()) @@ -1296,81 +1363,89 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, } } -#if 0 - else if (t.cs() == "multicolumn") { - // extract column count and insert dummy cells + else if (t.cs() == "multicolumn" && grid.handlesMulticolumn()) { + // if the columns are specified numerically, + // extract column count and insert dummy cells, + // otherwise parse it as an user macro MathData count; parse(count, FLAG_ITEM, mode); - int cols = 1; - if (!extractNumber(count, cols)) { - success_ = false; - lyxerr << " can't extract number of cells from " << count << endl; - } - // resize the table if necessary - for (int i = 0; i < cols; ++i) { - if (addCol(grid, cellcol)) { - cell = &grid.cell(grid.index( - cellrow, cellcol)); - // mark this as dummy - grid.cellinfo(grid.index( - cellrow, cellcol)).dummy_ = true; + int cols = 0; + // limit arbitrarily to 100 columns + if (extractNumber(count, cols) && cols > 0 && cols < 100) { + // resize the table if necessary + size_t first = grid.index(cellrow, cellcol); + for (int i = 1; i < cols; ++i) { + if (addCol(grid, cellcol)) { + size_t const idx = grid.index(cellrow, cellcol); + grid.cellinfo(idx).multi = + InsetMathGrid::CELL_PART_OF_MULTICOLUMN; + } } - } - // the last cell is the real thing, not a dummy - grid.cellinfo(grid.index(cellrow, cellcol)).dummy_ = false; - // read special alignment - MathData align; - parse(align, FLAG_ITEM, mode); - //grid.cellinfo(grid.index(cellrow, cellcol)).align_ = extractString(align); + // the first cell is the real thing, not a dummy + cell = &grid.cell(first); + grid.cellinfo(first).multi = + InsetMathGrid::CELL_BEGIN_OF_MULTICOLUMN; + + // read special alignment + MathData align; + parse(align, FLAG_ITEM, mode); + grid.cellinfo(first).align = asString(align); - // parse the remaining contents into the "real" cell - parse(*cell, FLAG_ITEM, mode); + // parse the remaining contents into the "real" cell + parse(*cell, FLAG_ITEM, mode); + } else { + MathAtom at = MathAtom(new InsetMathMacro(buf, t.cs())); + cell->push_back(at); + cell->push_back(MathAtom(new InsetMathBrace(count))); + } } -#endif else if (t.cs() == "limits" || t.cs() == "nolimits") { - CatCode cat = nextToken().cat(); - if (cat == catSuper || cat == catSub) - limits = t.cs() == "limits" ? 1 : -1; + if (!cell->empty()) + cell->back()->limits(t.cs() == "limits" ? LIMITS : NO_LIMITS); else { MathAtom at = createInsetMath(t.cs(), buf); cell->push_back(at); } } - else if (t.cs() == "nonumber") { - if (grid.asHullInset()) - grid.asHullInset()->numbered(cellrow, false); - } + // \notag is the same as \nonumber if amsmath is used + else if ((t.cs() == "nonumber" || t.cs() == "notag") && + grid.asHullInset()) + grid.asHullInset()->numbered(cellrow, false); - else if (t.cs() == "number") { - if (grid.asHullInset()) - grid.asHullInset()->numbered(cellrow, true); - } + else if (t.cs() == "number" && grid.asHullInset()) + grid.asHullInset()->numbered(cellrow, true); else if (t.cs() == "hline") { - grid.rowinfo(cellrow).lines_ ++; + grid.rowinfo(cellrow).lines++; } else if (t.cs() == "sqrt") { MathData ar; parse(ar, FLAG_OPTION, mode); - if (ar.size()) { + if (!ar.empty()) { cell->push_back(MathAtom(new InsetMathRoot(buf))); - cell->back().nucleus()->cell(0) = ar; - parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode); - } else { + cell->back().nucleus()->cell(1) = ar; + } else cell->push_back(MathAtom(new InsetMathSqrt(buf))); + parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode); + } + + else if (t.cs() == "cancelto") { + MathData ar; + parse(ar, FLAG_ITEM, mode); + cell->push_back(MathAtom(new InsetMathCancelto(buf))); + cell->back().nucleus()->cell(1) = ar; parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode); - } } else if (t.cs() == "unit") { // Allowed formats \unit[val]{unit} MathData ar; parse(ar, FLAG_OPTION, mode); - if (ar.size()) { + if (!ar.empty()) { cell->push_back(MathAtom(new InsetMathFrac(buf, InsetMathFrac::UNIT))); cell->back().nucleus()->cell(0) = ar; parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode); @@ -1384,7 +1459,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, // Here allowed formats are \unitfrac[val]{num}{denom} MathData ar; parse(ar, FLAG_OPTION, mode); - if (ar.size()) { + if (!ar.empty()) { cell->push_back(MathAtom(new InsetMathFrac(buf, InsetMathFrac::UNITFRAC, 3))); cell->back().nucleus()->cell(2) = ar; } else { @@ -1397,7 +1472,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, else if (t.cs() == "cfrac") { // allowed formats are \cfrac[pos]{num}{denom} docstring const arg = getArg('[', ']'); - //lyxerr << "got so far: '" << arg << "'" << endl; + //lyxerr << "got so far: '" << arg << "'" << endl; if (arg == "l") cell->push_back(MathAtom(new InsetMathFrac(buf, InsetMathFrac::CFRACLEFT))); else if (arg == "r") @@ -1412,14 +1487,67 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode); } + else if (t.cs() == "sideset") { + // Here allowed formats are \sideset{_{bl}^{tl}}{_{br}^{tr}}{operator} + MathData ar[2]; + InsetMathScript * script[2] = {0, 0}; + for (int i = 0; i < 2; ++i) { + parse(ar[i], FLAG_ITEM, mode); + if (ar[i].size() == 1) + script[i] = ar[i][0].nucleus()->asScriptInset(); + } + bool const hasscript[2] = {script[0] != nullptr, script[1] != nullptr}; + cell->push_back(MathAtom(new InsetMathSideset(buf, hasscript[0], hasscript[1]))); + if (hasscript[0]) { + if (script[0]->hasDown()) + cell->back().nucleus()->cell(1) = script[0]->down(); + if (script[0]->hasUp()) + cell->back().nucleus()->cell(2) = script[0]->up(); + } else + cell->back().nucleus()->cell(1) = ar[0]; + if (hasscript[1]) { + if (script[1]->hasDown()) + cell->back().nucleus()->cell(2 + hasscript[0]) = script[1]->down(); + if (script[1]->hasUp()) + cell->back().nucleus()->cell(3 + hasscript[0]) = script[1]->up(); + } else + cell->back().nucleus()->cell(2 + hasscript[0]) = ar[1]; + parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode); + } + + else if (t.cs() == "stackrel") { + // Here allowed formats are \stackrel[subscript]{superscript}{operator} + MathData ar; + parse(ar, FLAG_OPTION, mode); + cell->push_back(MathAtom(new InsetMathStackrel(buf, !ar.empty()))); + if (!ar.empty()) + cell->back().nucleus()->cell(2) = ar; + parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode); + parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode); + } + else if (t.cs() == "xrightarrow" || t.cs() == "xleftarrow") { cell->push_back(createInsetMath(t.cs(), buf)); parse(cell->back().nucleus()->cell(1), FLAG_OPTION, mode); parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode); } + else if (t.cs() == "xhookrightarrow" || t.cs() == "xhookleftarrow" || + t.cs() == "xRightarrow" || t.cs() == "xLeftarrow" || + t.cs() == "xleftrightarrow" || t.cs() == "xLeftrightarrow" || + t.cs() == "xrightharpoondown" || t.cs() == "xrightharpoonup" || + t.cs() == "xleftharpoondown" || t.cs() == "xleftharpoonup" || + t.cs() == "xleftrightharpoons" || t.cs() == "xrightleftharpoons" || + t.cs() == "xmapsto") { + cell->push_back(createInsetMath(t.cs(), buf)); + parse(cell->back().nucleus()->cell(1), FLAG_OPTION, mode); + parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode); + } + else if (t.cs() == "ref" || t.cs() == "eqref" || t.cs() == "prettyref" - || t.cs() == "pageref" || t.cs() == "vpageref" || t.cs() == "vref") { + || t.cs() == "nameref" || t.cs() == "pageref" + || t.cs() == "vpageref" || t.cs() == "vref" + || t.cs() == "formatted" || t.cs() == "labelonly") { cell->push_back(MathAtom(new InsetMathRef(buf, t.cs()))); docstring const opt = parse_verbatim_option(); docstring const ref = parse_verbatim_item(); @@ -1458,6 +1586,13 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, else if (t.cs() == "begin") { docstring const name = getArg('{', '}'); + + if (name.empty()) { + success_ = false; + error("found invalid environment"); + return success_; + } + environments_.push_back(name); if (name == "array" || name == "subarray") { @@ -1465,7 +1600,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, docstring const halign = parse_verbatim_item(); cell->push_back(MathAtom(new InsetMathArray(buf, name, InsetMathGrid::guessColumns(halign), 1, (char)valign[0], halign))); - parse2(cell->back(), FLAG_END, mode, false); + parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, false); } else if (name == "tabular") { @@ -1490,14 +1625,20 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, } else if (name == "math") { - cell->push_back(MathAtom(new InsetMathEnsureMath(buf))); - parse(cell->back().nucleus()->cell(0), FLAG_END, InsetMath::MATH_MODE); + if (mode == InsetMath::UNDECIDED_MODE) { + cell->push_back(MathAtom(new InsetMathHull(buf, hullSimple))); + parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, false); + } else { + // Don't create nested math hulls (bug #5392) + cell->push_back(MathAtom(new InsetMathEnsureMath(buf))); + parse(cell->back().nucleus()->cell(0), FLAG_END, InsetMath::MATH_MODE); + } } else if (name == "equation" || name == "equation*" || name == "displaymath") { if (mode != InsetMath::UNDECIDED_MODE) { - error("bad math environment"); + error("bad math environment " + name); break; } cell->push_back(MathAtom(new InsetMathHull(buf, hullEquation))); @@ -1506,7 +1647,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, else if (name == "eqnarray" || name == "eqnarray*") { if (mode != InsetMath::UNDECIDED_MODE) { - error("bad math environment"); + error("bad math environment " + name); break; } cell->push_back(MathAtom(new InsetMathHull(buf, hullEqnArray))); @@ -1514,17 +1655,19 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, } else if (name == "align" || name == "align*") { - if (mode != InsetMath::UNDECIDED_MODE) { - error("bad math environment"); - break; + if (mode == InsetMath::UNDECIDED_MODE) { + cell->push_back(MathAtom(new InsetMathHull(buf, hullAlign))); + parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name)); + } else { + cell->push_back(MathAtom(new InsetMathSplit(buf, name, + 'c', !stared(name)))); + parse2(cell->back(), FLAG_END, mode, !stared(name)); } - cell->push_back(MathAtom(new InsetMathHull(buf, hullAlign))); - parse2(cell->back(), FLAG_END, InsetMath::MATH_MODE, !stared(name)); } else if (name == "flalign" || name == "flalign*") { if (mode != InsetMath::UNDECIDED_MODE) { - error("bad math environment"); + error("bad math environment " + name); break; } cell->push_back(MathAtom(new InsetMathHull(buf, hullFlAlign))); @@ -1533,7 +1676,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, else if (name == "alignat" || name == "alignat*") { if (mode != InsetMath::UNDECIDED_MODE) { - error("bad math environment"); + error("bad math environment " + name); break; } // ignore this for a while @@ -1544,7 +1687,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, else if (name == "xalignat" || name == "xalignat*") { if (mode != InsetMath::UNDECIDED_MODE) { - error("bad math environment"); + error("bad math environment " + name); break; } // ignore this for a while @@ -1555,7 +1698,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, else if (name == "xxalignat") { if (mode != InsetMath::UNDECIDED_MODE) { - error("bad math environment"); + error("bad math environment " + name); break; } // ignore this for a while @@ -1566,7 +1709,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, else if (name == "multline" || name == "multline*") { if (mode != InsetMath::UNDECIDED_MODE) { - error("bad math environment"); + error("bad math environment " + name); break; } cell->push_back(MathAtom(new InsetMathHull(buf, hullMultline))); @@ -1575,7 +1718,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, else if (name == "gather" || name == "gather*") { if (mode != InsetMath::UNDECIDED_MODE) { - error("bad math environment"); + error("bad math environment " + name); break; } cell->push_back(MathAtom(new InsetMathHull(buf, hullGather))); @@ -1598,7 +1741,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, lyxerr << "found math environment `" << to_utf8(name) << "' in symbols file with unsupported inset `" - << to_utf8(l->inset) + << l->inset << "'." << endl; } // create generic environment inset @@ -1609,7 +1752,8 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, else { success_ = false; - if (!(mode_ & Parse::QUIET)) { + if (!(mode_ & Parse::QUIET) && + !(mode_ & Parse::TRACKMACRO)) { dump(); lyxerr << "found unknown math environment '" << to_utf8(name) << "'" << endl; @@ -1620,12 +1764,12 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, } } - else if (t.cs() == "kern") { + else if (t.cs() == "kern" || t.cs() == "mkern") { // FIXME: A hack... docstring s; int num_tokens = 0; while (true) { - Token const & t = getToken(); + Token const & tt = getToken(); ++num_tokens; if (!good()) { s.clear(); @@ -1633,12 +1777,12 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, putback(); break; } - s += t.character(); + s += tt.character(); if (isValidLength(to_utf8(s))) break; } if (s.empty()) - cell->push_back(MathAtom(new InsetMathKern)); + cell->push_back(MathAtom(new InsetMathMacro(buf, t.cs()))); else cell->push_back(MathAtom(new InsetMathKern(s))); } @@ -1709,6 +1853,14 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, delEmptyLastRow(subgrid); } + else if (t.cs() == "Diagram") { + odocstringstream os; + while (good() && nextToken().cat() != catBegin) + os << getToken().asInput(); + cell->push_back(createInsetMath(t.cs() + os.str(), buf)); + parse2(cell->back(), FLAG_ITEM, mode, false); + } + else if (t.cs() == "framebox" || t.cs() == "makebox") { cell->push_back(createInsetMath(t.cs(), buf)); parse(cell->back().nucleus()->cell(0), FLAG_OPTION, InsetMath::TEXT_MODE); @@ -1725,22 +1877,54 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, parse(cell->back().nucleus()->cell(0), FLAG_ITEM, InsetMath::TEXT_MODE); } - else if (t.cs() == "hspace" && nextToken().character() != '*') { - docstring const name = t.cs(); + else if (t.cs() == "hspace") { + bool const prot = nextToken().character() == '*'; + if (prot) + getToken(); + docstring const & name = t.cs(); docstring const arg = parse_verbatim_item(); Length length; - if (isValidLength(to_utf8(arg), &length)) - cell->push_back(MathAtom(new InsetMathSpace(length))); + if (prot && arg == "\\fill") + cell->push_back(MathAtom(new InsetMathSpace("hspace*{\\fill}", ""))); + else if (isValidLength(to_utf8(arg), &length)) + cell->push_back(MathAtom(new InsetMathSpace(length, prot))); else { // Since the Length class cannot use length variables // we must not create an InsetMathSpace. - cell->push_back(MathAtom(new MathMacro(buf, name))); + cell->push_back(MathAtom(new InsetMathMacro(buf, name))); MathData ar; mathed_parse_cell(ar, '{' + arg + '}', mode_); cell->append(ar); } } + else if (t.cs() == "smash") { + skipSpaces(); + if (nextToken().asInput() == "[") { + // Since the phantom inset cannot handle optional arguments + // other than b and t, we must not create an InsetMathPhantom + // if opt is different from b and t (bug 8967). + docstring const opt = parse_verbatim_option(); + if (opt == "t" || opt == "b") { + cell->push_back(createInsetMath(t.cs() + opt, buf)); + parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode); + } else { + docstring const arg = parse_verbatim_item(); + cell->push_back(MathAtom(new InsetMathMacro(buf, t.cs()))); + MathData ar; + mathed_parse_cell(ar, '[' + opt + ']', mode_); + cell->append(ar); + ar = MathData(); + mathed_parse_cell(ar, '{' + arg + '}', mode_); + cell->append(ar); + } + } + else { + cell->push_back(createInsetMath(t.cs(), buf)); + parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode); + } + } + #if 0 else if (t.cs() == "infer") { MathData ar; @@ -1748,23 +1932,6 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, cell->push_back(createInsetMath(t.cs(), buf)); parse2(cell->back(), FLAG_ITEM, mode, false); } - - // Disabled - else if (1 && t.cs() == "ar") { - auto_ptr p(new InsetMathXYArrow); - // try to read target - parse(p->cell(0), FLAG_OTPTION, mode); - // try to read label - if (nextToken().cat() == catSuper || nextToken().cat() == catSub) { - p->up_ = nextToken().cat() == catSuper; - getToken(); - parse(p->cell(1), FLAG_ITEM, mode); - //lyxerr << "read label: " << p->cell(1) << endl; - } - - cell->push_back(MathAtom(p.release())); - //lyxerr << "read cell: " << cell << endl; - } #endif else if (t.cs() == "lyxmathsym") { @@ -1788,29 +1955,35 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, error("'}' expected in \\" + t.cs()); return success_; } + bool termination; docstring rem; do { - cmd = Encodings::fromLaTeXCommand(cmd, rem); - for (size_t i = 0; i < cmd.size(); ++i) - cell->push_back(MathAtom(new InsetMathChar(cmd[i]))); - if (rem.size()) { + cmd = Encodings::fromLaTeXCommand(cmd, + Encodings::MATH_CMD | Encodings::TEXT_CMD, + termination, rem); + for (char_type c : cmd) + cell->push_back(MathAtom(new InsetMathChar(c))); + if (!rem.empty()) { char_type c = rem[0]; cell->push_back(MathAtom(new InsetMathChar(c))); cmd = rem.substr(1); rem.clear(); } else cmd.clear(); - } while (cmd.size()); + } while (!cmd.empty()); } - else if (t.cs().size()) { + else if (!t.cs().empty()) { bool const no_mhchem = - (t.cs() == "ce" || t.cs() == "cf") && buf - && buf->params().use_mhchem == BufferParams::package_off; + (t.cs() == "ce" || t.cs() == "cf") + && buf && buf->params().use_package("mhchem") == + BufferParams::package_off; + bool const is_user_macro = no_mhchem || - (buf && (mode_ & Parse::TRACKMACRO - ? buf->usermacros.count(t.cs()) != 0 - : buf->getMacro(t.cs(), false) != 0)); + (buf && ((mode_ & Parse::TRACKMACRO) + ? buf->usermacros.count(t.cs()) != 0 + : buf->getMacro(t.cs(), false) != 0)); + latexkeys const * l = in_word_set(t.cs()); if (l && !is_user_macro) { if (l->inset == "big") { @@ -1821,7 +1994,10 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, new InsetMathBig(t.cs(), delim))); else { cell->push_back(createInsetMath(t.cs(), buf)); - putback(); + // For some reason delim.empty() + // is always false here + if (delim.at(0)) + putback(); } } @@ -1831,7 +2007,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, FLAG_ITEM, asMode(mode, l->extra)); } - else if (l->inset == "oldfont") { + else if (l->inset == "oldfont" || l->inset == "textsize") { cell->push_back(createInsetMath(t.cs(), buf)); parse(cell->back().nucleus()->cell(0), flags | FLAG_ALIGN, asMode(mode, l->extra)); @@ -1851,9 +2027,15 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, putback(); } + else if (l->inset == "underset" || l->inset == "overset") { + cell->push_back(createInsetMath(t.cs(), buf)); + parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode); + parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode); + } + else { MathAtom at = createInsetMath(t.cs(), buf); - for (InsetMath::idx_type i = 0; i < at->nargs(); ++i) + for (idx_type i = 0; i < at->nargs(); ++i) parse(at.nucleus()->cell(i), FLAG_ITEM, asMode(mode, l->extra)); cell->push_back(at); @@ -1879,8 +2061,10 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, } } bool is_combining; - char_type c = - Encodings::fromLaTeXCommand(cmd, is_combining); + bool termination; + char_type c = Encodings::fromLaTeXCommand(cmd, + Encodings::MATH_CMD | Encodings::TEXT_CMD, + is_combining, termination); if (is_combining) { if (cat == catLetter) cmd += '{'; @@ -1888,9 +2072,24 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, ++num_tokens; if (cat == catLetter) cmd += '}'; - c = Encodings::fromLaTeXCommand(cmd, is_combining); + c = Encodings::fromLaTeXCommand(cmd, + Encodings::MATH_CMD | Encodings::TEXT_CMD, + is_combining, termination); } - if (c) { + if (c && buf && buf->params().encoding().encodable(c)) { + if (termination) { + if (nextToken().cat() == catBegin) { + getToken(); + if (nextToken().cat() == catEnd) { + getToken(); + } else + putback(); + } else { + while (nextToken().cat() == catSpace) { + getToken(); + } + } + } is_unicode_symbol = true; cell->push_back(MathAtom(new InsetMathChar(c))); } else { @@ -1900,7 +2099,7 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, } if (!is_unicode_symbol) { MathAtom at = is_user_macro ? - MathAtom(new MathMacro(buf, t.cs())) + MathAtom(new InsetMathMacro(buf, t.cs())) : createInsetMath(t.cs(), buf); InsetMath::mode_type m = mode; //if (m == InsetMath::UNDECIDED_MODE) @@ -1908,17 +2107,18 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, if (at->currentMode() != InsetMath::UNDECIDED_MODE) m = at->currentMode(); //lyxerr << "default creation: m2: " << m << endl; - InsetMath::idx_type start = 0; + idx_type start = 0; // this fails on \bigg[...\bigg] //MathData opt; //parse(opt, FLAG_OPTION, InsetMath::VERBATIM_MODE); - //if (opt.size()) { + //if (!opt.empty()) { // start = 1; // at.nucleus()->cell(0) = opt; //} - for (InsetMath::idx_type i = start; i < at->nargs(); ++i) { + for (idx_type i = start; i < at->nargs(); ++i) { parse(at.nucleus()->cell(i), FLAG_ITEM, m); - skipSpaces(); + if (mode == InsetMath::MATH_MODE) + skipSpaces(); } cell->push_back(at); } @@ -1927,7 +2127,6 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, if (flags & FLAG_LEAVE) { - flags &= ~FLAG_LEAVE; break; } } @@ -1939,16 +2138,27 @@ bool Parser::parse1(InsetMathGrid & grid, unsigned flags, } // anonymous namespace +// FIXME This will likely need some work. +char const * latexkeys::MathMLtype() const +{ + // Consider as identifier (mml:mi): ordinary and alphabetical characters. + if (extra == "mathord" || extra == "mathalpha") + return "mi"; + // Consider as operators (mml:mo): everything else. + return "mo"; +} + + bool mathed_parse_cell(MathData & ar, docstring const & str, Parse::flags f) { - return Parser(str, f, ar.buffer()).parse(ar, 0, f & Parse::TEXTMODE ? + return Parser(str, f, ar.buffer()).parse(ar, 0, (f & Parse::TEXTMODE) ? InsetMath::TEXT_MODE : InsetMath::MATH_MODE); } bool mathed_parse_cell(MathData & ar, istream & is, Parse::flags f) { - return Parser(is, f, ar.buffer()).parse(ar, 0, f & Parse::TEXTMODE ? + return Parser(is, f, ar.buffer()).parse(ar, 0, (f & Parse::TEXTMODE) ? InsetMath::TEXT_MODE : InsetMath::MATH_MODE); } @@ -1970,7 +2180,7 @@ bool mathed_parse_normal(Buffer * buf, MathAtom & t, Lexer & lex, bool mathed_parse_normal(InsetMathGrid & grid, docstring const & str, Parse::flags f) { - return Parser(str, f, &grid.buffer()).parse1(grid, 0, f & Parse::TEXTMODE ? + return Parser(str, f, &grid.buffer()).parse1(grid, 0, (f & Parse::TEXTMODE) ? InsetMath::TEXT_MODE : InsetMath::MATH_MODE, false); }