3 * Purpose: Parser for mathed
4 * Author: Alejandro Aguilar Sierra <asierra@servidor.unam.mx>
5 * Created: January 1996
6 * Description: Parse LaTeX2e math mode code.
8 * Dependencies: Xlib, XForms
10 * Copyright: 1996, Alejandro Aguilar Sierra
14 * You are free to use and modify this code under the terms of
15 * the GNU General Public Licence version 2 or later.
23 #pragma implementation
26 #include "math_parser.h"
28 #include "math_rowst.h"
29 #include "math_iter.h"
30 #include "math_inset.h"
31 #include "math_macro.h"
32 #include "math_macrotable.h"
33 #include "math_macrotemplate.h"
34 #include "math_root.h"
35 #include "math_matrixinset.h"
36 #include "math_accentinset.h"
37 #include "math_bigopinset.h"
38 #include "math_funcinset.h"
39 #include "math_spaceinset.h"
40 #include "math_dotsinset.h"
41 #include "math_fracinset.h"
42 #include "math_deliminset.h"
43 #include "math_decorationinset.h"
45 #include "support/lyxlib.h"
46 #include "mathed/support.h"
47 #include "boost/array.hpp"
53 extern MathMatrixInset create_multiline(short int type, int cols);
58 FLAG_BRACE = 1, // A { needed
59 FLAG_BRACE_ARG = 2, // Next { is argument
60 FLAG_BRACE_OPT = 4, // Optional {
61 FLAG_BRACE_LAST = 8, // Last } ends the parsing process
62 FLAG_BRACK_ARG = 16, // Optional [
63 FLAG_RIGHT = 32, // Next right ends the parsing process
64 FLAG_END = 64, // Next end ends the parsing process
65 FLAG_BRACE_FONT = 128, // Next } closes a font
66 FLAG_BRACK_END = 256 // Next ] ends the parsing process
86 MathedInsetTypes mathed_env = LM_OT_MIN;
92 int const latex_mathenv_num = 12;
93 char const * latex_mathenv[latex_mathenv_num] = {
109 char const * latex_special_chars = "#$%&_{}";
114 // These are lexical codes, not semantic
120 LexBOP, // Binary operators or relations
133 lexcode_enum lexcode[256];
135 #warning Replace with string
138 boost::array<char, 256> yytext;
141 bool yy_mtextmode= false;
145 void mathPrintError(string const & msg)
147 lyxerr << "Line ~" << yylineno << ": Math parse error: " << msg << endl;
153 for (int i = 0; i <= 255; ++i) {
155 lexcode[i] = LexAlpha;
157 lexcode[i] = LexDigit;
159 lexcode[i] = LexSpace;
161 lexcode[i] = LexNone;
164 lexcode['\t'] = lexcode['\f'] = lexcode[' '] = LexSpace;
165 lexcode['\n'] = LexNewLine;
166 lexcode['%'] = LexComment;
167 lexcode['#'] = LexArgument;
168 lexcode['+'] = lexcode['-'] = lexcode['*'] = lexcode['/']
169 = lexcode['<'] = lexcode['>'] = lexcode['='] = LexBOP;
171 lexcode['!'] = lexcode[','] = lexcode[':']
172 = lexcode[';'] = LexMathSpace;
174 lexcode['('] = lexcode[')'] = lexcode['|'] = lexcode['.'] =
175 lexcode['?'] = LexOther;
177 lexcode['\''] = lexcode['@'] = LexAlpha;
179 lexcode['['] = lexcode[']'] = lexcode['^'] = lexcode['_'] =
180 lexcode['&'] = LexSelf;
182 lexcode['\\'] = LexESC;
183 lexcode['{'] = LexOpen;
184 lexcode['}'] = LexClose;
188 char LexGetArg(char lf, bool accept_spaces = false)
192 while (yyis->good()) {
195 unsigned char c = cc;
200 lyxerr << "Math parse error: unexpected '" << c << "'" << endl;
209 : ((lf == '(') ? ')' : 0));
211 lyxerr << "Math parse error: unknown bracket '" << lf << "'" << endl;
214 char * p = &yytext[0];
219 unsigned char c = cc;
222 if ((c > ' ' || (c == ' ' && accept_spaces)) && bcnt > 0)
224 } while (bcnt > 0 && yyis->good() && p - yytext.data() < 255);
233 static int init_done = 0;
235 if (!init_done) LexInitCodes();
239 while (yyis->good()) {
243 if (yy_mtextmode && c == ' ') {
246 } else if (lexcode[c] == LexNewLine) {
249 } else if (lexcode[c] == LexComment) {
253 } while (c != '\n' % yyis->good()); // eat comments
254 } else if (lexcode[c] == LexDigit
255 || lexcode[c] == LexOther
256 || lexcode[c] == LexMathSpace) {
259 } else if (lexcode[c] == LexAlpha) {
262 } else if (lexcode[c] == LexBOP) {
265 } else if (lexcode[c] == LexSelf) {
267 } else if (lexcode[c] == LexArgument) {
271 return LM_TK_ARGUMENT;
272 } else if (lexcode[c] == LexOpen) {
274 } else if (lexcode[c] == LexClose) {
276 } else if (lexcode[c] == LexESC) {
280 return LM_TK_NEWLINE;
283 yylval.i = LM_OT_MIN;
287 yylval.i = LM_OT_MIN;
291 yylval.i = LM_OT_PAR;
295 yylval.i = LM_OT_PAR;
298 if (strchr(latex_special_chars, c)) {
300 return LM_TK_SPECIAL;
302 if (lexcode[c] == LexMathSpace) {
304 for (i = 0; i < 4 && static_cast<int>(c) != latex_mathspace[i][0]; ++i);
305 yylval.i = (i < 4) ? i : 0;
308 if (lexcode[c] == LexAlpha || lexcode[c] == LexDigit) {
309 char * p = &yytext[0];
310 while ((lexcode[c] == LexAlpha || lexcode[c] == LexDigit)
311 && p - yytext.data() < 255) {
320 //lyxerr << "reading: '" << yytext.data() << "'\n";
321 latexkeys const * l = in_word_set(yytext.data());
323 if (l->token == LM_TK_BEGIN || l->token == LM_TK_END) {
326 // for (i = 0; i < 5 && compare(yytext, latex_mathenv[i],
327 // strlen(latex_mathenv[i])); ++i);
330 i < latex_mathenv_num
331 && compare(yytext.data(), latex_mathenv[i]); ++i);
333 } else if (l->token == LM_TK_SPACE)
339 yylval.s = yytext.data();
350 int parse_align(char * hor, char *)
353 for (char * c = hor; c && *c > ' '; ++c) ++nc;
358 // Accent hacks only for 0.12. Stolen from Cursor.
363 void setAccent(int ac)
365 if (ac > 0 && accent < 8)
366 nestaccent[accent++] = ac;
368 accent = 0; // consumed!
372 MathedInset * doAccent(byte c, MathedTextCodes t)
374 MathedInset * ac = 0;
376 for (int i = accent - 1; i >= 0; --i) {
378 ac = new MathAccentInset(c, t, nestaccent[i]);
380 ac = new MathAccentInset(ac, nestaccent[i]);
382 accent = 0; // consumed!
388 MathedInset * doAccent(MathedInset * p)
390 MathedInset * ac = 0;
392 for (int i = accent - 1; i >= 0; --i) {
394 ac = new MathAccentInset(p, nestaccent[i]);
396 ac = new MathAccentInset(ac, nestaccent[i]);
398 accent = 0; // consumed!
404 void do_insert(MathedIter & it, MathedInset * m, MathedTextCodes t)
407 it.insertInset(doAccent(m), t);
409 it.insertInset(m, t);
417 void mathed_parse(MathedArray & array, MathParInset * & par, unsigned flags)
422 static int plevel = -1;
423 static int size = LM_ST_TEXT;
424 MathedTextCodes varcode = LM_TC_VAR;
425 MathedInset * binset = 0;
427 string last_label; // last label seen
428 bool last_numbered = true; // have we seen '\nonumber' lately?
435 MathedIter data(&array);
437 //lyxerr << "t: " << t << " par: " << par << " flags: " << flags;
438 //lyxerr << "label: '" << last_label << "' ";
439 //array.dump(lyxerr);
442 if ((flags & FLAG_BRACE) && t != LM_TK_OPEN) {
443 if ((flags & FLAG_BRACK_ARG) && t == '[') {
445 mathPrintError("Expected {. Maybe you forgot to enclose an argument in {}");
455 data.insertInset(doAccent(yylval.i, varcode), LM_TC_INSET);
457 data.insert(yylval.i, varcode); //LM_TC_VAR);
462 data.insertInset(new MathMacroArgument(yylval.i), LM_TC_INSET);
466 case LM_TK_NEWCOMMAND:
471 string name = &yytext[1];
473 char const c = yyis->peek();
476 na = lyx::atoi(yytext.data());
478 //lyxerr << "LM_TK_NEWCOMMAND: name: " << name << " " << na << endl;
483 par->xo(na); // abuse xo
484 flags = FLAG_BRACE|FLAG_BRACE_LAST;
490 data.insert(yylval.i, LM_TC_SPECIAL);
495 data.insertInset(doAccent(yylval.i, LM_TC_CONST), LM_TC_INSET);
497 data.insert(yylval.i, LM_TC_CONST);
502 if (accent && tprev == LM_TK_ACCENT) {
503 acc_braces[acc_brace++] = brace;
506 if (flags & FLAG_BRACE_OPT) {
507 flags &= ~FLAG_BRACE_OPT;
511 if (flags & FLAG_BRACE)
512 flags &= ~FLAG_BRACE;
514 data.insert('{', LM_TC_TEX);
521 mathPrintError("Unmatching braces");
525 if (acc_brace && brace == acc_braces[acc_brace - 1] - 1) {
529 if (flags & FLAG_BRACE_FONT) {
531 yy_mtextmode = false;
532 flags &= ~FLAG_BRACE_FONT;
535 if (brace == 0 && (flags & FLAG_BRACE_LAST)) {
539 data.insert('}', LM_TC_TEX);
543 if (flags & FLAG_BRACK_ARG) {
544 flags &= ~FLAG_BRACK_ARG;
545 char const rg = LexGetArg('[');
547 mathPrintError("Expected ']'");
551 // if (arg) strcpy(arg, yytext);
553 data.insert('[', LM_TC_CONST);
557 if (flags & FLAG_BRACK_END) {
561 data.insert(']', LM_TC_CONST);
566 MathParInset * p = new MathParInset(size, "", LM_OT_SCRIPT);
568 mathed_parse(ar, par, FLAG_BRACE_OPT|FLAG_BRACE_LAST);
570 // lyxerr << "UP[" << p->GetStyle() << "]" << endl;
571 data.insertInset(p, LM_TC_UP);
577 MathParInset * p = new MathParInset(size, "",
580 mathed_parse(ar, par, FLAG_BRACE_OPT|FLAG_BRACE_LAST);
582 data.insertInset(p, LM_TC_DOWN);
588 binset->SetLimits(bool(yylval.l->id));
594 data.insert('T', LM_TC_TAB);
598 data.setNumCols(par->GetColumns());
602 //lyxerr << "reading line " << par->getRowSt().size() << "\n";
603 if (flags & FLAG_END) {
604 if (par->Permit(LMPF_ALLOW_CR)) {
605 par->getRowSt().push_back();
607 //lyxerr << "line " << par->getRowSt().size() << " not numbered\n";
608 par->getRowSt().back().setNumbered(false);
609 last_numbered = true;
611 if (last_label.size()) {
612 //lyxerr << "line " << par->getRowSt().size() << " labeled: "
613 // << last_label << endl;
614 par->getRowSt().back().setLabel(last_label);
617 data.insert('K', LM_TC_CR);
619 mathPrintError("Unexpected newline");
625 binset = new MathBigopInset(yylval.l->name, yylval.l->id);
626 data.insertInset(binset, LM_TC_INSET);
631 if (yylval.l->id < 256) {
632 MathedTextCodes tc = MathIsBOPS(yylval.l->id) ? LM_TC_BOPS: LM_TC_SYMB;
634 data.insertInset(doAccent(yylval.l->id, tc), LM_TC_INSET);
636 data.insert(yylval.l->id, tc);
638 MathFuncInset * bg = new MathFuncInset(yylval.l->name);
640 data.insertInset(doAccent(bg), LM_TC_INSET);
643 #warning This is suspisious! (Lgb)
645 // it should not take a bool as second arg (Lgb)
646 data.insertInset(bg, true);
654 data.insertInset(doAccent(yylval.i, LM_TC_BOP), LM_TC_INSET);
656 data.insert(yylval.i, LM_TC_BOP);
660 par->UserSetSize(yylval.l->id);
665 MathSpaceInset * sp = new MathSpaceInset(yylval.i);
666 data.insertInset(sp, LM_TC_INSET);
672 MathDotsInset * p = new MathDotsInset(yylval.l->name, yylval.l->id);
673 data.insertInset(p, LM_TC_INSET);
683 mathed_parse(num, par, FLAG_BRACE|FLAG_BRACE_LAST);
685 mathed_parse(den, par, FLAG_BRACE|FLAG_BRACE_LAST);
686 fc.SetData(num, den);
687 data.insertInset(fc.Clone(), LM_TC_ACTIVE_INSET);
697 MathRootInset rt(size);
700 mathed_parse(ar1, par, FLAG_BRACK_END);
701 rt.setArgumentIdx(0);
702 rt.setData(ar1); // I belive that line is not needed (Lgb)
705 mathed_parse(ar2, par, FLAG_BRACE|FLAG_BRACE_LAST);
707 rt.setArgumentIdx(1);
708 rt.setData(ar2); // I belive that this line is not needed (Lgb)
710 data.insertInset(rt.Clone(), LM_TC_ACTIVE_INSET);
713 MathSqrtInset rt(size);
715 mathed_parse(ar, par, FLAG_BRACE|FLAG_BRACE_LAST);
716 rt.setData(ar); // I belive that this line is not needed (Lgb)
717 data.insertInset(rt.Clone(), LM_TC_ACTIVE_INSET);
725 if (lfd == LM_TK_SYM || lfd == LM_TK_STR || lfd == LM_TK_BOP|| lfd == LM_TK_SPECIAL)
726 lfd = (lfd == LM_TK_SYM) ? yylval.l->id: yylval.i;
727 // lyxerr << "L[" << lfd << " " << lfd << "]";
729 mathed_parse(ar, par, FLAG_RIGHT);
731 // lyxerr << "R[" << rgd << "]";
732 if (rgd == LM_TK_SYM || rgd == LM_TK_STR || rgd == LM_TK_BOP || rgd == LM_TK_SPECIAL)
733 rgd = (rgd == LM_TK_SYM) ? yylval.l->id: yylval.i;
734 MathDelimInset * dl = new MathDelimInset(lfd, rgd);
736 data.insertInset(dl, LM_TC_ACTIVE_INSET);
737 // lyxerr << "RL[" << lfd << " " << rgd << "]";
742 if (flags & FLAG_RIGHT) {
746 mathPrintError("Unmatched right delimiter");
751 varcode = static_cast<MathedTextCodes>(yylval.l->id);
752 yy_mtextmode = bool(varcode == LM_TC_TEXTRM);
753 flags |= (FLAG_BRACE|FLAG_BRACE_FONT);
758 MathDecorationInset * sq = new MathDecorationInset(yylval.l->id,
761 mathed_parse(ar, par, FLAG_BRACE|FLAG_BRACE_LAST);
763 data.insertInset(sq, LM_TC_ACTIVE_INSET);
768 setAccent(yylval.l->id);
772 //lyxerr << "prepare line " << par->getRowSt().size()
773 // << " not numbered\n";
774 last_numbered = false;
780 data.insert(t, LM_TC_CONST);
782 MathedInset * bg = new MathFuncInset(yylval.l->name);
783 data.insertInset(bg, LM_TC_INSET);
788 data.insertInset(new MathFuncInset(yylval.l->name, LM_OT_FUNCLIM),
794 // save this value, yylval.s might get overwritten soon
795 const string name = yylval.s;
796 //lyxerr << "LM_TK_UNDEF: str = " << name << endl;
797 if (MathMacroTable::hasTemplate(name)) {
798 MathMacro * m = MathMacroTable::cloneTemplate(name);
799 //lyxerr << "Macro: " << m->GetData() << endl;
800 for (int i = 0; i < m->nargs(); ++i) {
802 mathed_parse(ar, par, FLAG_BRACE|FLAG_BRACE_LAST);
805 do_insert(data, m, m->getTCode());
807 MathedInset * q = new MathFuncInset(name, LM_OT_UNDEF);
808 do_insert(data, q, LM_TC_INSET);
814 if (mathed_env != yylval.i && yylval.i != LM_OT_MATRIX)
815 mathPrintError("Unmatched environment");
816 // debug info [made that conditional -JMarc]
817 if (lyxerr.debugging(Debug::MATHED))
818 lyxerr << "[" << yylval.i << "]" << endl;
821 //if (mt) { // && (flags & FLAG_END)) {
822 // par.setData(array);
831 if (yylval.i == LM_OT_MATRIX) {
832 //lyxerr << "###### Reading LM_OT_MATRIX \n";
835 ar[0] = ar2[0] = '\0';
836 char rg = LexGetArg(0);
838 strcpy(ar2, yytext.data());
841 strcpy(ar, yytext.data());
842 int const nc = parse_align(ar, ar2);
844 MathParInset * mm = new MathMatrixInset(nc, 0);
845 mm->SetAlign(ar2[0], ar);
847 mathed_parse(dat, mm, FLAG_END);
848 data.insertInset(mm, LM_TC_ACTIVE_INSET);
851 } else if (is_eqn_type(yylval.i)) {
852 //lyxerr << "###### Reading is_eqn_type \n";
854 mathPrintError("Misplaced environment");
858 mathed_env = static_cast<MathedInsetTypes>(yylval.i);
859 if (mathed_env != LM_OT_MIN) {
860 //lyxerr << "###### Reading mathed_env != LM_OT_MIN \n";
861 size = LM_ST_DISPLAY;
862 if (is_multiline(mathed_env)) {
863 //lyxerr << "###### Reading is_multiline(mathed_env) \n";
865 if (is_multicolumn(mathed_env)) {
866 //lyxerr << "###### Reading is_multicolumn(mathed_env) \n";
867 if (mathed_env != LM_OT_ALIGNAT &&
868 mathed_env != LM_OT_ALIGNATN &&
870 //lyxerr << "###### Reading is !align\n";
874 lyxerr << "Math parse error: unexpected '"
878 cols = strToInt(string(yytext.data()));
883 //mt = create_multiline(mathed_env, cols);
884 //if (mtx) *mtx = mt;
886 //MathMatrixInset mat = create_multiline(mathed_env, cols);
887 //data.insertInset(mat.Clone(), LM_TC_ACTIVE_INSET);
889 par = new MathMatrixInset(create_multiline(mathed_env, cols));
893 par->SetType(mathed_env);
896 lyxerr[Debug::MATHED] << "MATH BEGIN[" << mathed_env << "]" << endl;
898 MathMacro * m = MathMacroTable::cloneTemplate(yytext.data());
899 data.insertInset(m, m->getTCode());
901 mathed_parse(dat, par, FLAG_END);
907 MathMacro * m = MathMacroTable::cloneTemplate(yylval.l->name);
908 do_insert(data, m, m->getTCode());
914 char const rg = LexGetArg('\0', true);
916 mathPrintError("Expected '{'");
918 lyxerr << "[" << yytext.data() << "]" << endl;
922 last_label = yytext.data();
923 //lyxerr << "prepare line " << par->getRowSt().size()
924 // << " label: " << last_label << endl;
929 mathPrintError("Unrecognized token");
931 lyxerr << "[" << t << " " << yytext.data() << "]" << endl;
937 lyxerr << " Math Panic, expect problems!" << endl;
938 // Search for the end command.
941 } while (t != LM_TK_END && t);
945 if ((flags & FLAG_BRACE_OPT)/* && t!= '^' && t!= '_'*/) {
946 flags &= ~FLAG_BRACE_OPT;
954 if (last_numbered == false) {
955 //lyxerr << "last line " << par->getRowSt().size() << " not numbered\n";
956 if (par->getRowSt().size() == 0)
957 par->getRowSt().push_back();
958 par->getRowSt().back().setNumbered(false);
960 if (last_label.size()) {
961 //lyxerr << "last line " << par->getRowSt().size() << " labeled: "
962 // << last_label << endl;
963 if (par->getRowSt().size() == 0)
964 par->getRowSt().push_back();
965 par->getRowSt().back().setLabel(last_label);
970 void mathed_parser_file(istream & is, int lineno)
977 int mathed_parser_lineno()