X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fmathed%2Fmath_parser.C;h=cee5e1b6fb84c06e2bcf951d9d630ef85d865fbe;hb=b54bf51387237b8fd8e9c6064143cff4239c2eec;hp=55bda8fe2b274166d5cb63cf772ccf3b1cab9a7d;hpb=29663cf58fff194d2b230dff353158c2461b7a43;p=lyx.git diff --git a/src/mathed/math_parser.C b/src/mathed/math_parser.C index 55bda8fe2b..cee5e1b6fb 100644 --- a/src/mathed/math_parser.C +++ b/src/mathed/math_parser.C @@ -1,25 +1,12 @@ -/* - * File: math_parser.C - * Purpose: Parser for mathed - * Author: Alejandro Aguilar Sierra - * Created: January 1996 - * Description: Parse LaTeX2e math mode code. - * - * Dependencies: Xlib, XForms - * - * Copyright: 1996, Alejandro Aguilar Sierra - * - * Version: 0.8beta. - * - * You are free to use and modify this code under the terms of - * the GNU General Public Licence version 2 or later. +/** The math parser + \author André Pönitz (2001) */ -/* +/* -If someone desperately needs partial "structures" (such as a few cells of -an array inset or similar) (s)he could uses the following hack as starting -point to write some macros: +If someone desperately needs partial "structures" (such as a few +cells of an array inset or similar) (s)he could uses the +following hack as starting point to write some macros: \newif\ifcomment \commentfalse @@ -45,9 +32,6 @@ point to write some macros: #include -#include -#include - #ifdef __GNUG__ #pragma implementation #endif @@ -56,52 +40,73 @@ point to write some macros: #include "math_inset.h" #include "math_arrayinset.h" #include "math_braceinset.h" -#include "math_casesinset.h" +#include "math_boxinset.h" #include "math_charinset.h" #include "math_deliminset.h" +#include "math_envinset.h" +#include "math_extern.h" #include "math_factory.h" -#include "math_funcinset.h" #include "math_kerninset.h" #include "math_macro.h" -#include "math_macrotable.h" #include "math_macrotemplate.h" #include "math_hullinset.h" +#include "math_parboxinset.h" +#include "math_parinset.h" #include "math_rootinset.h" -#include "math_sqrtinset.h" #include "math_scriptinset.h" -#include "math_specialcharinset.h" -#include "math_splitinset.h" +#include "math_sizeinset.h" #include "math_sqrtinset.h" +#include "math_stringinset.h" #include "math_support.h" +#include "math_xyarrowinset.h" + +//#include "insets/insetref.h" +#include "ref_inset.h" + #include "lyxlex.h" #include "debug.h" +#include "support/LAssert.h" #include "support/lstrings.h" +#include +#include + using std::istream; using std::ostream; using std::ios; using std::endl; -using std::stack; +using std::fill; +using std::vector; +using std::atoi; + + +//#define FILEDEBUG namespace { -bool stared(string const & s) +MathInset::mode_type asMode(string const & str) { - unsigned n = s.size(); - return n && s[n - 1] == '*'; + if (str == "mathmode") + return MathInset::MATH_MODE; + if (str == "textmode" || str == "forcetext") + return MathInset::TEXT_MODE; + if (str == "verbatimmode") + return MathInset::VERBATIM_MODE; + return MathInset::UNDECIDED_MODE; } -void add(MathArray & ar, char c, MathTextCodes code) +bool stared(string const & s) { - ar.push_back(MathAtom(new MathCharInset(c, code))); + string::size_type const n = s.size(); + return n && s[n - 1] == '*'; } // These are TeX's catcodes enum CatCode { - catEscape, // 0 backslash + catEscape, // 0 backslash catBegin, // 1 { catEnd, // 2 } catMath, // 3 $ @@ -110,7 +115,7 @@ enum CatCode { catParameter, // 6 # catSuper, // 7 ^ catSub, // 8 _ - catIgnore, // 9 + catIgnore, // 9 catSpace, // 10 space catLetter, // 11 a-zA-Z catOther, // 12 none of the above @@ -119,7 +124,7 @@ enum CatCode { catInvalid // 15 }; -CatCode theCatcode[256]; +CatCode theCatcode[256]; inline CatCode catcode(unsigned char c) @@ -129,40 +134,41 @@ inline CatCode catcode(unsigned char c) enum { - FLAG_BRACE_LAST = 1 << 1, // last closing brace ends the parsing process + FLAG_BRACE_LAST = 1 << 1, // last closing brace ends the parsing FLAG_RIGHT = 1 << 2, // next \\right ends the parsing process FLAG_END = 1 << 3, // next \\end ends the parsing process - FLAG_BRACK_END = 1 << 4, // next closing bracket ends the parsing process - FLAG_ITEM = 1 << 7, // read a (possibly braced token) - FLAG_BLOCK = 1 << 8, // next block ends the parsing process - FLAG_LEAVE = 1 << 9 // leave the loop at the end + FLAG_BRACK_LAST = 1 << 4, // next closing bracket ends the parsing + FLAG_TEXTMODE = 1 << 5, // we are in a box + FLAG_ITEM = 1 << 6, // read a (possibly braced token) + FLAG_LEAVE = 1 << 7, // leave the loop at the end + FLAG_SIMPLE = 1 << 8, // next $ leaves the loop + FLAG_EQUATION = 1 << 9, // next \] leaves the loop + FLAG_SIMPLE2 = 1 << 10, // next \) leaves the loop + FLAG_OPTION = 1 << 11 // read [...] style option }; void catInit() { - for (int i = 0; i <= 255; ++i) - theCatcode[i] = catOther; - for (int i = 'a'; i <= 'z'; ++i) - theCatcode[i] = catLetter; - for (int i = 'A'; i <= 'Z'; ++i) - theCatcode[i] = catLetter; - - theCatcode['\\'] = catEscape; - theCatcode['{'] = catBegin; - theCatcode['}'] = catEnd; - theCatcode['$'] = catMath; - theCatcode['&'] = catAlign; - theCatcode['\n'] = catNewline; - theCatcode['#'] = catParameter; - theCatcode['^'] = catSuper; - theCatcode['_'] = catSub; - theCatcode[''] = catIgnore; - theCatcode[' '] = catSpace; - theCatcode['\t'] = catSpace; - theCatcode['\r'] = catSpace; - theCatcode['~'] = catActive; - theCatcode['%'] = catComment; + fill(theCatcode, theCatcode + 256, catOther); + fill(theCatcode + 'a', theCatcode + 'z' + 1, catLetter); + fill(theCatcode + 'A', theCatcode + 'Z' + 1, catLetter); + + theCatcode['\\'] = catEscape; + theCatcode['{'] = catBegin; + theCatcode['}'] = catEnd; + theCatcode['$'] = catMath; + theCatcode['&'] = catAlign; + theCatcode['\n'] = catNewline; + theCatcode['#'] = catParameter; + theCatcode['^'] = catSuper; + theCatcode['_'] = catSub; + theCatcode[''] = catIgnore; + theCatcode[' '] = catSpace; + theCatcode['\t'] = catSpace; + theCatcode['\r'] = catNewline; + theCatcode['~'] = catActive; + theCatcode['%'] = catComment; } @@ -178,7 +184,7 @@ public: /// Token(char c, CatCode cat) : cs_(), char_(c), cat_(cat) {} /// - Token(const string & cs) : cs_(cs), char_(0), cat_(catIgnore) {} + Token(string const & cs) : cs_(cs), char_(0), cat_(catIgnore) {} /// string const & cs() const { return cs_; } @@ -187,11 +193,9 @@ public: /// char character() const { return char_; } /// - string asString() const; - /// - bool isCR() const; + string asString() const { return cs_.size() ? cs_ : string(1, char_); } -private: +private: /// string cs_; /// @@ -200,27 +204,6 @@ private: CatCode cat_; }; -bool Token::isCR() const -{ - return cs_ == "\\" || cs_ == "cr" || cs_ == "crcr"; -} - -string Token::asString() const -{ - return cs_.size() ? cs_ : string(1, char_); -} - -bool operator==(Token const & s, Token const & t) -{ - return s.character() == t.character() - && s.cat() == t.cat() && s.cs() == t.cs(); -} - -bool operator!=(Token const & s, Token const & t) -{ - return !(s == t); -} - ostream & operator<<(ostream & os, Token const & t) { if (t.cs().size()) @@ -234,17 +217,18 @@ ostream & operator<<(ostream & os, Token const & t) class Parser { public: + /// + typedef MathInset::mode_type mode_type; + /// Parser(LyXLex & lex); /// Parser(istream & is); /// - string parse_macro(); - /// - bool parse_normal(MathAtom &); + bool parse(MathAtom & at); /// - void parse_into(MathArray & array, unsigned flags, MathTextCodes = LM_TC_MIN); + void parse(MathArray & array, unsigned flags, mode_type mode); /// int lineno() const { return lineno_; } /// @@ -252,20 +236,25 @@ public: private: /// - string getArg(char lf, char rf); + void parse1(MathGridInset & grid, unsigned flags, mode_type mode, + bool numbered); + /// + void parse2(MathAtom & at, unsigned flags, mode_type mode, bool numbered); + /// get arg delimited by 'left' and 'right' + string getArg(char left, char right); /// char getChar(); /// void error(string const & msg); - /// - bool parse_lines(MathAtom & t, bool numbered, bool outmost); - -private: + /// dump contents to screen + void dump() const; /// void tokenize(istream & is); /// void tokenize(string const & s); /// + void skipSpaceTokens(istream & is, char c); + /// void push_back(Token const & t); /// void pop_back(); @@ -275,6 +264,8 @@ private: Token const & nextToken() const; /// Token const & getToken(); + /// skips spaces if any + void skipSpaces(); /// void lex(string const & s); /// @@ -283,20 +274,14 @@ private: /// int lineno_; /// - std::vector tokens_; + vector tokens_; /// unsigned pos_; - /// - bool curr_num_; - /// - string curr_label_; - /// - string curr_skip_; }; Parser::Parser(LyXLex & lexer) - : lineno_(lexer.getLineNo()), pos_(0), curr_num_(false) + : lineno_(lexer.getLineNo()), pos_(0) { tokenize(lexer.getStream()); lexer.eatLine(); @@ -304,7 +289,7 @@ Parser::Parser(LyXLex & lexer) Parser::Parser(istream & is) - : lineno_(0), pos_(0), curr_num_(false) + : lineno_(0), pos_(0) { tokenize(is); } @@ -339,10 +324,18 @@ Token const & Parser::nextToken() const Token const & Parser::getToken() { static const Token dummy; + //lyxerr << "looking at token " << tokens_[pos_] << " pos: " << pos_ << '\n'; return good() ? tokens_[pos_++] : dummy; } +void Parser::skipSpaces() +{ + while (nextToken().cat() == catSpace || nextToken().cat() == catNewline) + getToken(); +} + + void Parser::putback() { --pos_; @@ -358,26 +351,39 @@ bool Parser::good() const char Parser::getChar() { if (!good()) - lyxerr << "The input stream is not well..." << endl; + error("The input stream is not well..."); return tokens_[pos_++].character(); } -string Parser::getArg(char lf, char rg) +string Parser::getArg(char left, char right) { + skipSpaces(); + string result; char c = getChar(); - if (c != lf) + if (c != left) putback(); - else - while ((c = getChar()) != rg && good()) + else + while ((c = getChar()) != right && good()) result += c; return result; } +void Parser::skipSpaceTokens(istream & is, char c) +{ + // skip trailing spaces + while (catcode(c) == catSpace || catcode(c) == catNewline) + if (!is.get(c)) + break; + //lyxerr << "putting back: " << c << "\n"; + is.putback(c); +} + + void Parser::tokenize(istream & is) { // eat everything up to the next \end_inset or end of stream @@ -400,7 +406,7 @@ void Parser::tokenize(istream & is) void Parser::tokenize(string const & buffer) { static bool init_done = false; - + if (!init_done) { catInit(); init_done = true; @@ -410,16 +416,17 @@ void Parser::tokenize(string const & buffer) char c; while (is.get(c)) { + //lyxerr << "reading c: " << c << "\n"; switch (catcode(c)) { case catNewline: { - ++lineno_; + ++lineno_; is.get(c); if (catcode(c) == catNewline) ; //push_back(Token("par")); else { - push_back(Token(' ', catSpace)); - is.putback(c); + push_back(Token('\n', catNewline)); + is.putback(c); } break; } @@ -427,457 +434,555 @@ void Parser::tokenize(string const & buffer) case catComment: { while (is.get(c) && catcode(c) != catNewline) ; - ++lineno_; + ++lineno_; break; } case catEscape: { is.get(c); - string s(1, c); - if (catcode(c) == catLetter) { - while (is.get(c) && catcode(c) == catLetter) - s += c; - if (catcode(c) == catSpace) - while (is.get(c) && catcode(c) == catSpace) - ; - is.putback(c); - } - push_back(Token(s)); + if (!is) { + error("unexpected end of input"); + } else { + string s(1, c); + if (catcode(c) == catLetter) { + // collect letters + while (is.get(c) && catcode(c) == catLetter) + s += c; + skipSpaceTokens(is, c); + } + push_back(Token(s)); + } break; } - default: + case catSuper: + case catSub: { push_back(Token(c, catcode(c))); - } - } - -#if 0 - lyxerr << "\nTokens: "; - for (unsigned i = 0; i < tokens_.size(); ++i) - lyxerr << tokens_[i]; - lyxerr << "\n"; -#endif -} - - -void Parser::error(string const & msg) -{ - lyxerr << "Line ~" << lineno_ << ": Math parse error: " << msg << endl; - //exit(1); -} - - -bool Parser::parse_lines(MathAtom & t, bool numbered, bool outmost) -{ - MathGridInset * p = t->asGridInset(); - if (!p) { - lyxerr << "error in Parser::parse_lines() 1\n"; - return false; - } - - const int cols = p->ncols(); - - // save global variables - bool const saved_num = curr_num_; - string const saved_label = curr_label_; - - for (int row = 0; true; ++row) { - // reset global variables - curr_num_ = numbered; - curr_label_.erase(); - - // reading a row - for (int col = 0; col < cols; ++col) { - //lyxerr << "reading cell " << row << " " << col << "\n"; - parse_into(p->cell(col + row * cols), FLAG_BLOCK); - - // no ampersand - if (prevToken().cat() != catAlign) { - //lyxerr << "less cells read than normal in row/col: " - // << row << " " << col << "\n"; + is.get(c); + skipSpaceTokens(is, c); break; } - } - if (outmost) { - MathHullInset * m = t->asHullInset(); - if (!m) { - lyxerr << "error in Parser::parse_lines() 2\n"; - return false; - } - m->numbered(row, curr_num_); - m->label(row, curr_label_); - if (curr_skip_.size()) { - m->vskip(LyXLength(curr_skip_), row); - curr_skip_.erase(); + case catIgnore: { + lyxerr << "ignoring a char: " << int(c) << "\n"; + break; } - } - // no newline? - if (!prevToken().isCR()) { - //lyxerr << "no newline here\n"; - break; + default: + push_back(Token(c, catcode(c))); } - - p->appendRow(); } - // restore "global" variables - curr_num_ = saved_num; - curr_label_ = saved_label; - - return true; +#ifdef FILEDEBUG + dump(); +#endif } -string Parser::parse_macro() +void Parser::dump() const { - string name = "{error}"; - - while (nextToken().cat() == catSpace) - getToken(); - - if (getToken().cs() != "newcommand") { - lyxerr << "\\newcommand expected\n"; - return name; - } - - if (getToken().cat() != catBegin) { - lyxerr << "'{' in \\newcommand expected (1)\n"; - return name; - } - - name = getToken().cs(); - - if (getToken().cat() != catEnd) { - lyxerr << "'}' expected\n"; - return name; - } - - string arg = getArg('[', ']'); - int narg = arg.empty() ? 0 : atoi(arg.c_str()); - - if (getToken().cat() != catBegin) { - lyxerr << "'{' in \\newcommand expected (2)\n"; - return name; + lyxerr << "\nTokens: "; + for (unsigned i = 0; i < tokens_.size(); ++i) { + if (i == pos_) + lyxerr << " <#> "; + lyxerr << tokens_[i]; } - - MathArray ar; - parse_into(ar, FLAG_BRACE_LAST); - MathMacroTable::create(name, narg, ar); - return name; + lyxerr << " pos: " << pos_ << "\n"; } -bool Parser::parse_normal(MathAtom & matrix) +void Parser::error(string const & msg) { - while (nextToken().cat() == catSpace) - getToken(); - - Token const & t = getToken(); - - if (t.cs() == "(") { - matrix = MathAtom(new MathHullInset(LM_OT_SIMPLE)); - parse_into(matrix->cell(0), 0); - return true; - } - - if (t.cat() == catMath) { - Token const & n = getToken(); - if (n.cat() == catMath) { - // TeX's $$...$$ syntax for displayed math - matrix = MathAtom(new MathHullInset(LM_OT_EQUATION)); - MathHullInset * p = matrix->asHullInset(); - parse_into(p->cell(0), 0); - p->numbered(0, curr_num_); - p->label(0, curr_label_); - } else { - // simple $...$ stuff - putback(); - matrix = MathAtom(new MathHullInset(LM_OT_SIMPLE)); - parse_into(matrix->cell(0), 0); - } - return true; - } - - if (!t.cs().size()) { - lyxerr << "start of math expected, got '" << t << "'\n"; - return false; - } - - string const & cs = t.cs(); - - if (cs == "[") { - curr_num_ = 0; - curr_label_.erase(); - matrix = MathAtom(new MathHullInset(LM_OT_EQUATION)); - MathHullInset * p = matrix->asHullInset(); - parse_into(p->cell(0), 0); - p->numbered(0, curr_num_); - p->label(0, curr_label_); - return true; - } - - if (cs != "begin") { - lyxerr << "'begin' of un-simple math expected, got '" << cs << "'\n"; - return false; - } + lyxerr << "Line ~" << lineno_ << ": Math parse error: " << msg << endl; + dump(); + //exit(1); +} - string const name = getArg('{', '}'); - if (name == "equation" || name == "equation*" || name == "displaymath") { - curr_num_ = (name == "equation"); - curr_label_.erase(); - matrix = MathAtom(new MathHullInset(LM_OT_EQUATION)); - MathHullInset * p = matrix->asHullInset(); - parse_into(p->cell(0), FLAG_END); - p->numbered(0, curr_num_); - p->label(0, curr_label_); +bool Parser::parse(MathAtom & at) +{ + skipSpaces(); + MathArray ar; + parse(ar, false, MathInset::UNDECIDED_MODE); + if (ar.size() != 1 || ar.front()->getType() == "none") { + lyxerr << "unusual contents found: " << ar << endl; + at = MathAtom(new MathParInset); + if (at->nargs() > 0) + at.nucleus()->cell(0) = ar; + else + lyxerr << "unusual contents found: " << ar << endl; return true; } + at = ar[0]; + return true; +} - if (name == "eqnarray" || name == "eqnarray*") { - matrix = MathAtom(new MathHullInset(LM_OT_EQNARRAY)); - return parse_lines(matrix, !stared(name), true); - } - - if (name == "align" || name == "align*") { - matrix = MathAtom(new MathHullInset(LM_OT_ALIGN)); - return parse_lines(matrix, !stared(name), true); - } - - if (name == "alignat" || name == "alignat*") { - int nc = 2 * atoi(getArg('{', '}').c_str()); - matrix = MathAtom(new MathHullInset(LM_OT_ALIGNAT, nc)); - return parse_lines(matrix, !stared(name), true); - } - - if (name == "xalignat" || name == "xalignat*") { - int nc = 2 * atoi(getArg('{', '}').c_str()); - matrix = MathAtom(new MathHullInset(LM_OT_XALIGNAT, nc)); - return parse_lines(matrix, !stared(name), true); - } - - if (name == "xxalignat") { - int nc = 2 * atoi(getArg('{', '}').c_str()); - matrix = MathAtom(new MathHullInset(LM_OT_XXALIGNAT, nc)); - return parse_lines(matrix, !stared(name), true); - } - if (name == "multline" || name == "multline*") { - matrix = MathAtom(new MathHullInset(LM_OT_MULTLINE)); - return parse_lines(matrix, !stared(name), true); - } +void Parser::parse(MathArray & array, unsigned flags, mode_type mode) +{ + MathGridInset grid(1, 1); + parse1(grid, flags, mode, false); + array = grid.cell(0); +} - if (name == "gather" || name == "gather*") { - matrix = MathAtom(new MathHullInset(LM_OT_GATHER)); - return parse_lines(matrix, !stared(name), true); - } - lyxerr[Debug::MATHED] << "1: unknown math environment: " << name << "\n"; - lyxerr << "1: unknown math environment: " << name << "\n"; - return false; +void Parser::parse2(MathAtom & at, unsigned flags, mode_type mode, + bool numbered) +{ + parse1(*(at.nucleus()->asGridInset()), flags, mode, numbered); } -void Parser::parse_into(MathArray & array, unsigned flags, MathTextCodes code) +void Parser::parse1(MathGridInset & grid, unsigned flags, + mode_type mode, bool numbered) { - bool panic = false; int limits = 0; + MathGridInset::row_type cellrow = 0; + MathGridInset::col_type cellcol = 0; + MathArray * cell = &grid.cell(grid.index(cellrow, cellcol)); + + if (grid.asHullInset()) + grid.asHullInset()->numbered(cellrow, numbered); + + //dump(); + //lyxerr << "grid: " << grid << endl; while (good()) { Token const & t = getToken(); - - //lyxerr << "t: " << t << " flags: " << flags << "'\n"; - //array.dump(lyxerr); - //lyxerr << "\n"; + +#ifdef FILEDEBUG + lyxerr << "t: " << t << " flags: " << flags << "\n"; + //cell->dump(); + lyxerr << "\n"; +#endif if (flags & FLAG_ITEM) { + if (t.cat() == catSpace) + continue; + flags &= ~FLAG_ITEM; - if (t.cat() == catBegin) { + if (t.cat() == catBegin) { // skip the brace and collect everything to the next matching // closing brace flags |= FLAG_BRACE_LAST; continue; - } else { - // handle only this single token, leave the loop if done - flags |= FLAG_LEAVE; } + + // handle only this single token, leave the loop if done + flags |= FLAG_LEAVE; } - if (flags & FLAG_BLOCK) { - if (t.cat() == catAlign || t.isCR()) - return; - if (t.cs() == "end") { - getArg('{', '}'); - return; + + if (flags & FLAG_OPTION) { + if (t.cat() == catOther && t.character() == '[') { + MathArray ar; + parse(ar, FLAG_BRACK_LAST, mode); + cell->append(ar); + } else { + // no option found, put back token and we are done + putback(); } + return; } // // cat codes // - if (t.cat() == catMath) - break; + if (t.cat() == catMath) { + if (mode != MathInset::MATH_MODE) { + // we are inside some text mode thingy, so opening new math is allowed + Token const & n = getToken(); + if (n.cat() == catMath) { + // TeX's $$...$$ syntax for displayed math + cell->push_back(MathAtom(new MathHullInset("equation"))); + parse2(cell->back(), FLAG_SIMPLE, MathInset::MATH_MODE, false); + getToken(); // skip the second '$' token + } else { + // simple $...$ stuff + putback(); + cell->push_back(MathAtom(new MathHullInset("simple"))); + parse2(cell->back(), FLAG_SIMPLE, MathInset::MATH_MODE, false); + } + } + + else if (flags & FLAG_SIMPLE) { + // this is the end of the formula + return; + } + + else { + error("something strange in the parser\n"); + break; + } + } else if (t.cat() == catLetter) - add(array, t.character(), code); + cell->push_back(MathAtom(new MathCharInset(t.character()))); + + else if (t.cat() == catSpace && mode != MathInset::MATH_MODE) + cell->push_back(MathAtom(new MathCharInset(t.character()))); - else if (t.cat() == catSpace && code == LM_TC_TEXTRM) - add(array, t.character(), code); + else if (t.cat() == catNewline && mode != MathInset::MATH_MODE) + cell->push_back(MathAtom(new MathCharInset(t.character()))); else if (t.cat() == catParameter) { Token const & n = getToken(); - array.push_back(MathAtom(new MathMacroArgument(n.character() - '0'))); + cell->push_back(MathAtom(new MathMacroArgument(n.character()-'0'))); } + else if (t.cat() == catActive) + cell->push_back(MathAtom(new MathCharInset(t.character()))); + else if (t.cat() == catBegin) { MathArray ar; - parse_into(ar, FLAG_BRACE_LAST); -#ifndef WITH_WARNINGS -#warning this might be wrong in general! -#endif - // ignore braces around simple items - if (ar.size() == 1 || (ar.size() == 2 && ar.back()->asScriptInset())) { - array.push_back(ar); - } else { - array.push_back(MathAtom(new MathBraceInset)); - array.back()->cell(0).swap(ar); - } + parse(ar, FLAG_BRACE_LAST, mode); + // do not create a BraceInset if they were written by LyX + // this helps to keep the annoyance of "a choose b" to a minimum + if (ar.size() == 1 && ar[0]->extraBraces()) + cell->append(ar); + else + cell->push_back(MathAtom(new MathBraceInset(ar))); } else if (t.cat() == catEnd) { if (flags & FLAG_BRACE_LAST) return; - //lyxerr << "found '}' unexpectedly, array: '" << array << "'\n"; - lyxerr << "found '}' unexpectedly\n"; - add(array, '}', LM_TC_TEX); + error("found '}' unexpectedly"); + //lyx::Assert(0); + //add(cell, '}', LM_TC_TEX); } - + else if (t.cat() == catAlign) { - //lyxerr << "found tab unexpectedly, array: '" << array << "'\n"; - lyxerr << "found tab unexpectedly\n"; - add(array, '&', LM_TC_TEX); + ++cellcol; + //lyxerr << " column now " << cellcol << " max: " << grid.ncols() << "\n"; + if (cellcol == grid.ncols()) { + lyxerr << "adding column " << cellcol << "\n"; + grid.addCol(cellcol - 1); + } + cell = &grid.cell(grid.index(cellrow, cellcol)); } - + else if (t.cat() == catSuper || t.cat() == catSub) { - bool up = (t.cat() == catSuper); - MathScriptInset * p = 0; - if (array.size()) - p = array.back()->asScriptInset(); - if (!p || p->has(up)) { - array.push_back(MathAtom(new MathScriptInset(up))); - p = array.back()->asScriptInset(); + if (mode == MathInset::VERBATIM_MODE) + cell->push_back(MathAtom(new MathStringInset(t.asString()))); + else { + 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()) + cell->push_back(MathAtom(new MathScriptInset(up))); + else if (cell->back()->asScriptInset() && + !cell->back()->asScriptInset()->has(up)) + cell->back().nucleus()->asScriptInset()->ensure(up); + else if (cell->back()->asScriptInset()) + cell->push_back(MathAtom(new MathScriptInset(up))); + else + cell->back() = MathAtom(new MathScriptInset(cell->back(), up)); + MathScriptInset * p = cell->back().nucleus()->asScriptInset(); + parse(p->cell(up), FLAG_ITEM, mode); + p->limits(limits); + limits = 0; + // special handling of {}-bases + // is this always correct? + if (p->nuc().size() == 1 && p->nuc().back()->asNestInset() && + p->nuc().back()->extraBraces()) + p->nuc() = p->nuc().back()->asNestInset()->cell(0); } - p->ensure(up); - parse_into(p->cell(up), FLAG_ITEM); - p->limits(limits); - limits = 0; } - else if (t.character() == ']' && (flags & FLAG_BRACK_END)) + else if (t.character() == ']' && (flags & FLAG_BRACK_LAST)) { + lyxerr << "finished reading option\n"; return; + } else if (t.cat() == catOther) - add(array, t.character(), code); - + cell->push_back(MathAtom(new MathCharInset(t.character()))); + // // control sequences - // + // + + else if (t.cs() == "lyxlock") { + if (cell->size()) + cell->back().nucleus()->lock(true); + } + + else if (t.cs() == "def" || t.cs() == "newcommand") { + string name; + int nargs = 0; + if (t.cs() == "def") { + // get name + name = getToken().cs(); + + // read parameter + string pars; + while (good() && nextToken().cat() != catBegin) { + pars += getToken().cs(); + ++nargs; + } + nargs /= 2; + //lyxerr << "read \\def parameter list '" << pars << "'\n"; + + } else { // t.cs() == "newcommand" + + if (getToken().cat() != catBegin) { + error("'{' in \\newcommand expected (1) \n"); + return; + } + + name = getToken().cs(); + + if (getToken().cat() != catEnd) { + error("'}' in \\newcommand expected\n"); + return; + } + + string arg = getArg('[', ']'); + if (!arg.empty()) + nargs = atoi(arg.c_str()); + + } + + MathArray ar1; + parse(ar1, FLAG_ITEM, MathInset::UNDECIDED_MODE); + + // we cannot handle recursive stuff at all + //MathArray test; + //test.push_back(createMathInset(name)); + //if (ar1.contains(test)) { + // error("we cannot handle recursive macros at all.\n"); + // return; + //} + + // is a version for display attached? + skipSpaces(); + MathArray ar2; + if (nextToken().cat() == catBegin) { + parse(ar2, FLAG_ITEM, MathInset::MATH_MODE); + } + + cell->push_back(MathAtom(new MathMacroTemplate(name, nargs, ar1, ar2))); + } + + else if (t.cs() == "(") { + cell->push_back(MathAtom(new MathHullInset("simple"))); + parse2(cell->back(), FLAG_SIMPLE2, MathInset::MATH_MODE, false); + } + + else if (t.cs() == "[") { + cell->push_back(MathAtom(new MathHullInset("equation"))); + parse2(cell->back(), FLAG_EQUATION, MathInset::MATH_MODE, false); + } + else if (t.cs() == "protect") - // ignore \\protect, will be re-added during output + // ignore \\protect, will hopefully be re-added during output ; - else if (t.cs() == "end") - break; + else if (t.cs() == "end") { + if (flags & FLAG_END) { + // eat environment name + //string const name = + getArg('{', '}'); + // FIXME: check that we ended the correct environment + return; + } + error("found 'end' unexpectedly"); + } - else if (t.cs() == ")") - break; + else if (t.cs() == ")") { + if (flags & FLAG_SIMPLE2) + return; + error("found '\\)' unexpectedly"); + } - else if (t.cs() == "]") - break; + else if (t.cs() == "]") { + if (flags & FLAG_EQUATION) + return; + error("found '\\]' unexpectedly"); + } else if (t.cs() == "\\") { - curr_skip_ = getArg('[', ']'); - //lyxerr << "found newline unexpectedly, array: '" << array << "'\n"; - lyxerr << "found newline unexpectedly\n"; - array.push_back(createMathInset("\\")); + grid.vcrskip(LyXLength(getArg('[', ']')), cellrow); + ++cellrow; + cellcol = 0; + if (cellrow == grid.nrows()) + grid.addRow(cellrow - 1); + if (grid.asHullInset()) + grid.asHullInset()->numbered(cellrow, numbered); + cell = &grid.cell(grid.index(cellrow, cellcol)); + } + +#if 1 + else if (t.cs() == "multicolumn") { + // extract column count and insert dummy cells + MathArray count; + parse(count, FLAG_ITEM, mode); + int cols = 1; + if (!extractNumber(count, cols)) { + lyxerr << " can't extract number of cells from " << count << "\n"; + } + // resize the table if necessary + for (int i = 0; i < cols; ++i) { + ++cellcol; + if (cellcol == grid.ncols()) { + lyxerr << "adding column " << cellcol << "\n"; + grid.addCol(cellcol - 1); + } + cell = &grid.cell(grid.index(cellrow, cellcol)); + // mark this as dummy + grid.cellinfo(grid.index(cellrow, cellcol)).dummy_ = true; + } + // the last cell is the real thng, not a dummy + grid.cellinfo(grid.index(cellrow, cellcol)).dummy_ = false; + + // read special alignment + MathArray align; + parse(align, FLAG_ITEM, mode); + //grid.cellinfo(grid.index(cellrow, cellcol)).align_ = extractString(align); + + // parse the remaining contents into the "real" cell + parse(*cell, FLAG_ITEM, mode); } - +#endif + else if (t.cs() == "limits") limits = 1; - + else if (t.cs() == "nolimits") limits = -1; - - else if (t.cs() == "nonumber") - curr_num_ = false; - else if (t.cs() == "number") - curr_num_ = true; + else if (t.cs() == "nonumber") { + if (grid.asHullInset()) + grid.asHullInset()->numbered(cellrow, false); + } + + else if (t.cs() == "number") { + if (grid.asHullInset()) + grid.asHullInset()->numbered(cellrow, true); + } + + else if (t.cs() == "hline") { + if (grid.asHullInset()) + grid.asHullInset()->rowinfo(cellrow + 1); + } else if (t.cs() == "sqrt") { - char c = getChar(); - if (c == '[') { - array.push_back(MathAtom(new MathRootInset)); - parse_into(array.back()->cell(0), FLAG_BRACK_END); - parse_into(array.back()->cell(1), FLAG_ITEM); + MathArray ar; + parse(ar, FLAG_OPTION, mode); + if (ar.size()) { + cell->push_back(MathAtom(new MathRootInset)); + cell->back().nucleus()->cell(0) = ar; + parse(cell->back().nucleus()->cell(1), FLAG_ITEM, mode); } else { - putback(); - array.push_back(MathAtom(new MathSqrtInset)); - parse_into(array.back()->cell(0), FLAG_ITEM); + cell->push_back(MathAtom(new MathSqrtInset)); + parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode); } } - + + else if (t.cs() == "ref") { + cell->push_back(MathAtom(new RefInset)); + parse(cell->back().nucleus()->cell(1), FLAG_OPTION, mode); + parse(cell->back().nucleus()->cell(0), FLAG_ITEM, mode); + } + else if (t.cs() == "left") { string l = getToken().asString(); MathArray ar; - parse_into(ar, FLAG_RIGHT); + parse(ar, FLAG_RIGHT, mode); string r = getToken().asString(); - MathAtom dl(new MathDelimInset(l, r)); - dl->cell(0) = ar; - array.push_back(dl); + cell->push_back(MathAtom(new MathDelimInset(l, r, ar))); } - + else if (t.cs() == "right") { - if (!(flags & FLAG_RIGHT)) { - //lyxerr << "got so far: '" << array << "'\n"; - error("Unmatched right delimiter"); - } + if (flags & FLAG_RIGHT) + return; + //lyxerr << "got so far: '" << cell << "'\n"; + error("Unmatched right delimiter"); return; } -/* - case LM_TK_STY: - { - lyxerr[Debug::MATHED] << "LM_TK_STY not implemented\n"; - //MathArray tmp = array; - //MathSizeInset * p = new MathSizeInset(MathStyles(lval_->id)); - //array.push_back(p); - //parse_into(p->cell(0), FLAG_BRACE_FONT); - break; - } - -*/ - else if (t.cs() == "begin") { - string const name = getArg('{', '}'); - if (name == "array") { + string const name = getArg('{', '}'); + if (name == "array" || name == "subarray") { string const valign = getArg('[', ']') + 'c'; string const halign = getArg('{', '}'); - array.push_back( - MathAtom(new MathArrayInset(halign.size(), 1, valign[0], halign))); - parse_lines(array.back(), false, false); - } else if (name == "split") { - array.push_back(MathAtom(new MathSplitInset(1))); - parse_lines(array.back(), false, false); - } else if (name == "cases") { - array.push_back(MathAtom(new MathCasesInset)); - parse_lines(array.back(), false, false); - } else - lyxerr << "unknow math inset begin '" << name << "'\n"; + cell->push_back(MathAtom(new MathArrayInset(name, valign[0], halign))); + parse2(cell->back(), FLAG_END, mode, false); + } + + else if (name == "split" || name == "cases" || + name == "gathered" || name == "aligned") { + cell->push_back(createMathInset(name)); + parse2(cell->back(), FLAG_END, mode, false); + } + + else if (name == "math") { + cell->push_back(MathAtom(new MathHullInset("simple"))); + parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, true); + } + + else if (name == "equation" || name == "equation*" + || name == "displaymath") { + cell->push_back(MathAtom(new MathHullInset("equation"))); + parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, (name == "equation")); + } + + else if (name == "eqnarray" || name == "eqnarray*") { + cell->push_back(MathAtom(new MathHullInset("eqnarray"))); + parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name)); + } + + else if (name == "align" || name == "align*") { + cell->push_back(MathAtom(new MathHullInset("align"))); + parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name)); + } + + else if (name == "alignat" || name == "alignat*") { + // ignore this for a while + getArg('{', '}'); + cell->push_back(MathAtom(new MathHullInset("alignat"))); + parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name)); + } + + else if (name == "xalignat" || name == "xalignat*") { + // ignore this for a while + getArg('{', '}'); + cell->push_back(MathAtom(new MathHullInset("xalignat"))); + parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name)); + } + + else if (name == "xxalignat") { + // ignore this for a while + getArg('{', '}'); + cell->push_back(MathAtom(new MathHullInset("xxalignat"))); + parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name)); + } + + else if (name == "multline" || name == "multline*") { + cell->push_back(MathAtom(new MathHullInset("multline"))); + parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name)); + } + + else if (name == "gather" || name == "gather*") { + cell->push_back(MathAtom(new MathHullInset("gather"))); + parse2(cell->back(), FLAG_END, MathInset::MATH_MODE, !stared(name)); + } + + else if (latexkeys const * l = in_word_set(name)) { + if (l->inset == "matrix") { + cell->push_back(createMathInset(name)); + parse2(cell->back(), FLAG_END, mode, false); + } + } + + else { + // lyxerr << "unknow math inset begin '" << name << "'\n"; + // create generic environment inset + cell->push_back(MathAtom(new MathEnvInset(name))); + parse(cell->back().nucleus()->cell(0), FLAG_END, mode); + } } - + else if (t.cs() == "kern") { #ifdef WITH_WARNINGS #warning A hack... @@ -886,76 +991,112 @@ void Parser::parse_into(MathArray & array, unsigned flags, MathTextCodes code) while (1) { Token const & t = getToken(); if (!good()) { - putback(); + putback(); break; } s += t.character(); if (isValidLength(s)) break; } - array.push_back(MathAtom(new MathKernInset(s))); + cell->push_back(MathAtom(new MathKernInset(s))); } else if (t.cs() == "label") { - curr_label_ = getArg('{', '}'); + MathArray ar; + parse(ar, FLAG_ITEM, MathInset::VERBATIM_MODE); + if (grid.asHullInset()) { + grid.asHullInset()->label(cellrow, asString(ar)); + } else { + cell->push_back(createMathInset(t.cs())); + cell->push_back(MathAtom(new MathBraceInset(ar))); + } } else if (t.cs() == "choose" || t.cs() == "over" || t.cs() == "atop") { - MathAtom p = createMathInset(t.cs()); - array.swap(p->cell(0)); - parse_into(p->cell(1), flags, code); - array.push_back(p); + MathAtom at = createMathInset(t.cs()); + at.nucleus()->cell(0) = *cell; + cell->clear(); + parse(at.nucleus()->cell(1), flags, mode); + cell->push_back(at); return; } -/* + else if (t.cs() == "substack") { + cell->push_back(createMathInset(t.cs())); + parse2(cell->back(), FLAG_ITEM, mode, false); + } + + else if (t.cs() == "xymatrix") { + cell->push_back(createMathInset(t.cs())); + parse2(cell->back(), FLAG_ITEM, mode, false); + } + +#if 0 // Disabled - else if (t.cs() == "mbox") { - array.push_back(createMathInset(t.cs())); - // slurp in the argument of mbox - - MathBoxInset * p = array.back()->asBoxInset(); - //lyx::assert(p); + else if (1 && t.cs() == "ar") { + MathXYArrowInset * p = new MathXYArrowInset; + // 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) << "\n"; + } + + cell->push_back(MathAtom(p)); + //lyxerr << "read cell: " << cell << "\n"; } -*/ - +#endif + else if (t.cs().size()) { latexkeys const * l = in_word_set(t.cs()); if (l) { - if (l->token == LM_TK_FONT) { - //lyxerr << "starting font\n"; - //CatCode catSpaceSave = theCatcode[' ']; - //if (l->id == LM_TC_TEXTRM) { - // // temporarily change catcode - // theCatcode[' '] = catLetter; - //} - - MathArray ar; - parse_into(ar, FLAG_ITEM, static_cast(l->id)); - array.push_back(ar); - - // undo catcode changes - ////theCatcode[' '] = catSpaceSave; - //lyxerr << "ending font\n"; + if (l->inset == "font") { + cell->push_back(createMathInset(t.cs())); + parse(cell->back().nucleus()->cell(0), FLAG_ITEM, asMode(l->extra)); + } + + else if (l->inset == "oldfont") { + cell->push_back(createMathInset(t.cs())); + parse(cell->back().nucleus()->cell(0), flags, asMode(l->extra)); + return; } - else if (l->token == LM_TK_OLDFONT) { - code = static_cast(l->id); + else if (l->inset == "style") { + cell->push_back(createMathInset(t.cs())); + parse(cell->back().nucleus()->cell(0), flags, mode); + return; + } + + else if (l->inset == "parbox") { + // read optional positioning and width + MathArray pos, width; + parse(pos, FLAG_OPTION, MathInset::VERBATIM_MODE); + parse(width, FLAG_ITEM, MathInset::VERBATIM_MODE); + cell->push_back(createMathInset(t.cs())); + parse(cell->back().nucleus()->cell(0), FLAG_ITEM, MathInset::TEXT_MODE); + cell->back().nucleus()->asParboxInset()->setPosition(asString(pos)); + cell->back().nucleus()->asParboxInset()->setWidth(asString(width)); } else { - MathAtom p = createMathInset(t.cs()); - for (MathInset::idx_type i = 0; i < p->nargs(); ++i) - parse_into(p->cell(i), FLAG_ITEM); - array.push_back(p); + MathAtom at = createMathInset(t.cs()); + for (MathInset::idx_type i = 0; i < at->nargs(); ++i) + parse(at.nucleus()->cell(i), FLAG_ITEM, asMode(l->extra)); + cell->push_back(at); } } else { - MathAtom p = createMathInset(t.cs()); - for (MathInset::idx_type i = 0; i < p->nargs(); ++i) - parse_into(p->cell(i), FLAG_ITEM); - array.push_back(p); + MathAtom at = createMathInset(t.cs()); + MathInset::mode_type m = mode; + if (m == MathInset::UNDECIDED_MODE) + m = at->currentMode(); + for (MathInset::idx_type i = 0; i < at->nargs(); ++i) + parse(at.nucleus()->cell(i), FLAG_ITEM, m); + cell->push_back(at); } } @@ -965,15 +1106,6 @@ void Parser::parse_into(MathArray & array, unsigned flags, MathTextCodes code) break; } } - - if (panic) { - lyxerr << " Math Panic, expect problems!\n"; - // Search for the end command. - Token t; - do { - t = getToken(); - } while (good() && t.cs() != "end"); - } } @@ -990,47 +1122,24 @@ void mathed_parse_cell(MathArray & ar, string const & str) void mathed_parse_cell(MathArray & ar, istream & is) { - Parser(is).parse_into(ar, 0); + Parser(is).parse(ar, 0, MathInset::MATH_MODE); } - -string mathed_parse_macro(string const & str) -{ - istringstream is(str.c_str()); - Parser parser(is); - return parser.parse_macro(); -} - -string mathed_parse_macro(istream & is) -{ - Parser parser(is); - return parser.parse_macro(); -} - -string mathed_parse_macro(LyXLex & lex) -{ - Parser parser(lex); - return parser.parse_macro(); -} - - - bool mathed_parse_normal(MathAtom & t, string const & str) { istringstream is(str.c_str()); - Parser parser(is); - return parser.parse_normal(t); + return Parser(is).parse(t); } + bool mathed_parse_normal(MathAtom & t, istream & is) { - Parser parser(is); - return parser.parse_normal(t); + return Parser(is).parse(t); } + bool mathed_parse_normal(MathAtom & t, LyXLex & lex) { - Parser parser(lex); - return parser.parse_normal(t); + return Parser(lex).parse(t); }