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(), strlen(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 {}");
450 MathedInsetTypes fractype = LM_OT_FRAC;
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);
678 fractype = LM_OT_STACKREL;
682 MathFracInset fc(fractype);
684 mathed_parse(num, par, FLAG_BRACE|FLAG_BRACE_LAST);
686 mathed_parse(den, par, FLAG_BRACE|FLAG_BRACE_LAST);
687 fc.SetData(num, den);
688 data.insertInset(fc.Clone(), LM_TC_ACTIVE_INSET);
698 MathRootInset rt(size);
701 mathed_parse(ar1, par, FLAG_BRACK_END);
702 rt.setArgumentIdx(0);
703 rt.setData(ar1); // I belive that line is not needed (Lgb)
706 mathed_parse(ar2, par, FLAG_BRACE|FLAG_BRACE_LAST);
708 rt.setArgumentIdx(1);
709 rt.setData(ar2); // I belive that this line is not needed (Lgb)
711 data.insertInset(rt.Clone(), LM_TC_ACTIVE_INSET);
714 MathSqrtInset rt(size);
716 mathed_parse(ar, par, FLAG_BRACE|FLAG_BRACE_LAST);
717 rt.setData(ar); // I belive that this line is not needed (Lgb)
718 data.insertInset(rt.Clone(), LM_TC_ACTIVE_INSET);
726 if (lfd == LM_TK_SYM || lfd == LM_TK_STR || lfd == LM_TK_BOP|| lfd == LM_TK_SPECIAL)
727 lfd = (lfd == LM_TK_SYM) ? yylval.l->id: yylval.i;
728 // lyxerr << "L[" << lfd << " " << lfd << "]";
730 mathed_parse(ar, par, FLAG_RIGHT);
732 // lyxerr << "R[" << rgd << "]";
733 if (rgd == LM_TK_SYM || rgd == LM_TK_STR || rgd == LM_TK_BOP || rgd == LM_TK_SPECIAL)
734 rgd = (rgd == LM_TK_SYM) ? yylval.l->id: yylval.i;
735 MathDelimInset * dl = new MathDelimInset(lfd, rgd);
737 data.insertInset(dl, LM_TC_ACTIVE_INSET);
738 // lyxerr << "RL[" << lfd << " " << rgd << "]";
743 if (flags & FLAG_RIGHT) {
747 mathPrintError("Unmatched right delimiter");
752 varcode = static_cast<MathedTextCodes>(yylval.l->id);
753 yy_mtextmode = bool(varcode == LM_TC_TEXTRM);
754 flags |= (FLAG_BRACE|FLAG_BRACE_FONT);
759 MathDecorationInset * sq = new MathDecorationInset(yylval.l->id,
762 mathed_parse(ar, par, FLAG_BRACE|FLAG_BRACE_LAST);
764 data.insertInset(sq, LM_TC_ACTIVE_INSET);
769 setAccent(yylval.l->id);
773 //lyxerr << "prepare line " << par->getRowSt().size()
774 // << " not numbered\n";
775 last_numbered = false;
781 data.insert(t, LM_TC_CONST);
783 MathedInset * bg = new MathFuncInset(yylval.l->name);
784 data.insertInset(bg, LM_TC_INSET);
789 data.insertInset(new MathFuncInset(yylval.l->name, LM_OT_FUNCLIM),
795 // save this value, yylval.s might get overwritten soon
796 const string name = yylval.s;
797 //lyxerr << "LM_TK_UNDEF: str = " << name << endl;
798 if (MathMacroTable::hasTemplate(name)) {
799 MathMacro * m = MathMacroTable::cloneTemplate(name);
800 //lyxerr << "Macro: " << m->GetData() << endl;
801 for (int i = 0; i < m->nargs(); ++i) {
803 mathed_parse(ar, par, FLAG_BRACE|FLAG_BRACE_LAST);
806 do_insert(data, m, m->getTCode());
808 MathedInset * q = new MathFuncInset(name, LM_OT_UNDEF);
809 do_insert(data, q, LM_TC_INSET);
815 if (mathed_env != yylval.i && yylval.i != LM_OT_MATRIX)
816 mathPrintError("Unmatched environment");
817 // debug info [made that conditional -JMarc]
818 if (lyxerr.debugging(Debug::MATHED))
819 lyxerr << "[" << yylval.i << "]" << endl;
822 //if (mt) { // && (flags & FLAG_END)) {
823 // par.setData(array);
832 if (yylval.i == LM_OT_MATRIX) {
833 //lyxerr << "###### Reading LM_OT_MATRIX \n";
836 ar[0] = ar2[0] = '\0';
837 char rg = LexGetArg(0);
839 strcpy(ar2, yytext.data());
842 strcpy(ar, yytext.data());
843 int const nc = parse_align(ar, ar2);
845 MathParInset * mm = new MathMatrixInset(nc, 0);
846 mm->SetAlign(ar2[0], ar);
848 mathed_parse(dat, mm, FLAG_END);
849 data.insertInset(mm, LM_TC_ACTIVE_INSET);
852 } else if (is_eqn_type(yylval.i)) {
853 //lyxerr << "###### Reading is_eqn_type \n";
855 mathPrintError("Misplaced environment");
859 mathed_env = static_cast<MathedInsetTypes>(yylval.i);
860 if (mathed_env != LM_OT_MIN) {
861 //lyxerr << "###### Reading mathed_env != LM_OT_MIN \n";
862 size = LM_ST_DISPLAY;
863 if (is_multiline(mathed_env)) {
864 //lyxerr << "###### Reading is_multiline(mathed_env) \n";
866 if (is_multicolumn(mathed_env)) {
867 //lyxerr << "###### Reading is_multicolumn(mathed_env) \n";
868 if (mathed_env != LM_OT_ALIGNAT &&
869 mathed_env != LM_OT_ALIGNATN &&
871 //lyxerr << "###### Reading is !align\n";
875 lyxerr << "Math parse error: unexpected '"
879 cols = strToInt(string(yytext.data()));
884 //mt = create_multiline(mathed_env, cols);
885 //if (mtx) *mtx = mt;
887 //MathMatrixInset mat = create_multiline(mathed_env, cols);
888 //data.insertInset(mat.Clone(), LM_TC_ACTIVE_INSET);
890 par = new MathMatrixInset(create_multiline(mathed_env, cols));
894 par->SetType(mathed_env);
897 lyxerr[Debug::MATHED] << "MATH BEGIN[" << mathed_env << "]" << endl;
899 MathMacro * m = MathMacroTable::cloneTemplate(yytext.data());
900 data.insertInset(m, m->getTCode());
902 mathed_parse(dat, par, FLAG_END);
908 MathMacro * m = MathMacroTable::cloneTemplate(yylval.l->name);
909 do_insert(data, m, m->getTCode());
915 char const rg = LexGetArg('\0', true);
917 mathPrintError("Expected '{'");
919 lyxerr << "[" << yytext.data() << "]" << endl;
923 last_label = yytext.data();
924 //lyxerr << "prepare line " << par->getRowSt().size()
925 // << " label: " << last_label << endl;
930 mathPrintError("Unrecognized token");
932 lyxerr << "[" << t << " " << yytext.data() << "]" << endl;
938 lyxerr << " Math Panic, expect problems!" << endl;
939 // Search for the end command.
942 } while (t != LM_TK_END && t);
946 if ((flags & FLAG_BRACE_OPT)/* && t!= '^' && t!= '_'*/) {
947 flags &= ~FLAG_BRACE_OPT;
955 if (last_numbered == false) {
956 //lyxerr << "last line " << par->getRowSt().size() << " not numbered\n";
957 if (par->getRowSt().size() == 0)
958 par->getRowSt().push_back();
959 par->getRowSt().back().setNumbered(false);
961 if (last_label.size()) {
962 //lyxerr << "last line " << par->getRowSt().size() << " labeled: "
963 // << last_label << endl;
964 if (par->getRowSt().size() == 0)
965 par->getRowSt().push_back();
966 par->getRowSt().back().setLabel(last_label);
971 void mathed_parser_file(istream & is, int lineno)
978 int mathed_parser_lineno()