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;
144 void mathPrintError(string const & msg)
146 lyxerr << "Line ~" << yylineno << ": Math parse error: " << msg << endl;
152 for (int i = 0; i <= 255; ++i) {
154 lexcode[i] = LexAlpha;
156 lexcode[i] = LexDigit;
158 lexcode[i] = LexSpace;
160 lexcode[i] = LexNone;
163 lexcode['\t'] = lexcode['\f'] = lexcode[' '] = LexSpace;
164 lexcode['\n'] = LexNewLine;
165 lexcode['%'] = LexComment;
166 lexcode['#'] = LexArgument;
167 lexcode['+'] = lexcode['-'] = lexcode['*'] = lexcode['/']
168 = lexcode['<'] = lexcode['>'] = lexcode['='] = LexBOP;
170 lexcode['!'] = lexcode[','] = lexcode[':']
171 = lexcode[';'] = LexMathSpace;
173 lexcode['('] = lexcode[')'] = lexcode['|'] = lexcode['.'] =
174 lexcode['?'] = LexOther;
176 lexcode['\''] = lexcode['@'] = LexAlpha;
178 lexcode['['] = lexcode[']'] = lexcode['^'] = lexcode['_'] =
179 lexcode['&'] = LexSelf;
181 lexcode['\\'] = LexESC;
182 lexcode['{'] = LexOpen;
183 lexcode['}'] = LexClose;
187 char LexGetArg(char lf, bool accept_spaces = false)
191 while (yyis->good()) {
194 unsigned char c = cc;
199 lyxerr << "Math parse error: unexpected '" << c << "'" << endl;
208 : ((lf == '(') ? ')' : 0));
210 lyxerr << "Math parse error: unknown bracket '" << lf << "'" << endl;
213 char * p = &yytext[0];
218 unsigned char c = cc;
221 if ((c > ' ' || (c == ' ' && accept_spaces)) && bcnt > 0)
223 } while (bcnt > 0 && yyis->good() && p - yytext.data() < 255);
232 static int init_done = 0;
234 if (!init_done) LexInitCodes();
238 while (yyis->good()) {
242 if (yy_mtextmode && c == ' ') {
245 } else if (lexcode[c] == LexNewLine) {
248 } else if (lexcode[c] == LexComment) {
252 } while (c != '\n' % yyis->good()); // eat comments
253 } else if (lexcode[c] == LexDigit
254 || lexcode[c] == LexOther
255 || lexcode[c] == LexMathSpace) {
258 } else if (lexcode[c] == LexAlpha) {
261 } else if (lexcode[c] == LexBOP) {
264 } else if (lexcode[c] == LexSelf) {
266 } else if (lexcode[c] == LexArgument) {
270 return LM_TK_ARGUMENT;
271 } else if (lexcode[c] == LexOpen) {
273 } else if (lexcode[c] == LexClose) {
275 } else if (lexcode[c] == LexESC) {
279 return LM_TK_NEWLINE;
282 yylval.i = LM_OT_MIN;
286 yylval.i = LM_OT_MIN;
290 yylval.i = LM_OT_PAR;
294 yylval.i = LM_OT_PAR;
297 if (strchr(latex_special_chars, c)) {
299 return LM_TK_SPECIAL;
301 if (lexcode[c] == LexMathSpace) {
303 for (i = 0; i < 4 && static_cast<int>(c) != latex_mathspace[i][0]; ++i);
304 yylval.i = (i < 4) ? i : 0;
307 if (lexcode[c] == LexAlpha || lexcode[c] == LexDigit) {
308 char * p = &yytext[0];
309 while ((lexcode[c] == LexAlpha || lexcode[c] == LexDigit)
310 && p - yytext.data() < 255) {
319 //lyxerr << "reading: '" << yytext.data() << "'\n";
320 latexkeys const * l = in_word_set(yytext.data());
322 if (l->token == LM_TK_BEGIN || l->token == LM_TK_END) {
325 // for (i = 0; i < 5 && compare(yytext, latex_mathenv[i],
326 // strlen(latex_mathenv[i])); ++i);
329 i < latex_mathenv_num
330 && compare(yytext.data(), latex_mathenv[i]); ++i);
332 } else if (l->token == LM_TK_SPACE)
338 yylval.s = yytext.data();
348 int parse_align(char * hor, char *)
351 for (char * c = hor; c && *c > ' '; ++c) ++nc;
356 // Accent hacks only for 0.12. Stolen from Cursor.
360 void setAccent(int ac)
362 if (ac > 0 && accent < 8)
363 nestaccent[accent++] = ac;
365 accent = 0; // consumed!
369 MathedInset * doAccent(byte c, MathedTextCodes t)
371 MathedInset * ac = 0;
373 for (int i = accent - 1; i >= 0; --i) {
375 ac = new MathAccentInset(c, t, nestaccent[i]);
377 ac = new MathAccentInset(ac, nestaccent[i]);
379 accent = 0; // consumed!
385 MathedInset * doAccent(MathedInset * p)
387 MathedInset * ac = 0;
389 for (int i = accent - 1; i >= 0; --i) {
391 ac = new MathAccentInset(p, nestaccent[i]);
393 ac = new MathAccentInset(ac, nestaccent[i]);
395 accent = 0; // consumed!
401 void do_insert(MathedIter & it, MathedInset * m, MathedTextCodes t)
404 it.insertInset(doAccent(m), t);
406 it.insertInset(m, t);
410 void handle_frac(MathedIter & it, MathParInset * & par, MathedInsetTypes t)
414 mathed_parse(num, par, FLAG_BRACE|FLAG_BRACE_LAST);
416 mathed_parse(den, par, FLAG_BRACE|FLAG_BRACE_LAST);
417 fc.SetData(num, den);
418 it.insertInset(fc.Clone(), LM_TC_ACTIVE_INSET);
426 void mathed_parse(MathedArray & array, MathParInset * & par, unsigned flags)
431 static int plevel = -1;
432 static int size = LM_ST_TEXT;
433 MathedTextCodes varcode = LM_TC_VAR;
434 MathedInset * binset = 0;
436 string last_label; // last label seen
437 bool last_numbered = true; // have we seen '\nonumber' lately?
444 MathedIter data(&array);
446 //lyxerr << "t: " << t << " par: " << par << " flags: " << flags;
447 //lyxerr << "label: '" << last_label << "' ";
448 //array.dump(lyxerr);
451 if ((flags & FLAG_BRACE) && t != LM_TK_OPEN) {
452 if ((flags & FLAG_BRACK_ARG) && t == '[') {
454 mathPrintError("Expected {. Maybe you forgot to enclose an argument in {}");
464 data.insertInset(doAccent(yylval.i, varcode), LM_TC_INSET);
466 data.insert(yylval.i, varcode); //LM_TC_VAR);
471 data.insertInset(new MathMacroArgument(yylval.i), LM_TC_INSET);
475 case LM_TK_NEWCOMMAND:
480 string name = &yytext[1];
482 char const c = yyis->peek();
485 na = lyx::atoi(yytext.data());
487 //lyxerr << "LM_TK_NEWCOMMAND: name: " << name << " " << na << endl;
492 par->xo(na); // abuse xo
493 flags = FLAG_BRACE|FLAG_BRACE_LAST;
499 data.insert(yylval.i, LM_TC_SPECIAL);
504 data.insertInset(doAccent(yylval.i, LM_TC_CONST), LM_TC_INSET);
506 data.insert(yylval.i, LM_TC_CONST);
511 if (accent && tprev == LM_TK_ACCENT) {
512 acc_braces[acc_brace++] = brace;
515 if (flags & FLAG_BRACE_OPT) {
516 flags &= ~FLAG_BRACE_OPT;
520 if (flags & FLAG_BRACE)
521 flags &= ~FLAG_BRACE;
523 data.insert('{', LM_TC_TEX);
530 mathPrintError("Unmatching braces");
534 if (acc_brace && brace == acc_braces[acc_brace - 1] - 1) {
538 if (flags & FLAG_BRACE_FONT) {
540 yy_mtextmode = false;
541 flags &= ~FLAG_BRACE_FONT;
544 if (brace == 0 && (flags & FLAG_BRACE_LAST)) {
548 data.insert('}', LM_TC_TEX);
552 if (flags & FLAG_BRACK_ARG) {
553 flags &= ~FLAG_BRACK_ARG;
554 char const rg = LexGetArg('[');
556 mathPrintError("Expected ']'");
560 // if (arg) strcpy(arg, yytext);
562 data.insert('[', LM_TC_CONST);
566 if (flags & FLAG_BRACK_END) {
570 data.insert(']', LM_TC_CONST);
575 MathParInset * p = new MathParInset(size, "", LM_OT_SCRIPT);
577 mathed_parse(ar, par, FLAG_BRACE_OPT|FLAG_BRACE_LAST);
579 // lyxerr << "UP[" << p->GetStyle() << "]" << endl;
580 data.insertInset(p, LM_TC_UP);
586 MathParInset * p = new MathParInset(size, "",
589 mathed_parse(ar, par, FLAG_BRACE_OPT|FLAG_BRACE_LAST);
591 data.insertInset(p, LM_TC_DOWN);
597 binset->SetLimits(bool(yylval.l->id));
603 data.insert('T', LM_TC_TAB);
607 data.setNumCols(par->GetColumns());
611 //lyxerr << "reading line " << par->getRowSt().size() << "\n";
612 if (flags & FLAG_END) {
613 if (par->Permit(LMPF_ALLOW_CR)) {
614 par->getRowSt().push_back();
616 //lyxerr << "line " << par->getRowSt().size() << " not numbered\n";
617 par->getRowSt().back().setNumbered(false);
618 last_numbered = true;
620 if (last_label.size()) {
621 //lyxerr << "line " << par->getRowSt().size() << " labeled: "
622 // << last_label << endl;
623 par->getRowSt().back().setLabel(last_label);
626 data.insert('K', LM_TC_CR);
628 mathPrintError("Unexpected newline");
634 binset = new MathBigopInset(yylval.l->name, yylval.l->id);
635 data.insertInset(binset, LM_TC_INSET);
640 if (yylval.l->id < 256) {
641 MathedTextCodes tc = MathIsBOPS(yylval.l->id) ? LM_TC_BOPS: LM_TC_SYMB;
643 data.insertInset(doAccent(yylval.l->id, tc), LM_TC_INSET);
645 data.insert(yylval.l->id, tc);
647 MathFuncInset * bg = new MathFuncInset(yylval.l->name);
649 data.insertInset(doAccent(bg), LM_TC_INSET);
652 #warning This is suspisious! (Lgb)
654 // it should not take a bool as second arg (Lgb)
655 data.insertInset(bg, true);
663 data.insertInset(doAccent(yylval.i, LM_TC_BOP), LM_TC_INSET);
665 data.insert(yylval.i, LM_TC_BOP);
669 par->UserSetSize(yylval.l->id);
674 MathSpaceInset * sp = new MathSpaceInset(yylval.i);
675 data.insertInset(sp, LM_TC_INSET);
681 MathDotsInset * p = new MathDotsInset(yylval.l->name, yylval.l->id);
682 data.insertInset(p, LM_TC_INSET);
687 handle_frac(data, par, LM_OT_ATOP);
691 handle_frac(data, par, LM_OT_STACKREL);
695 handle_frac(data, par, LM_OT_FRAC);
704 MathRootInset rt(size);
707 mathed_parse(ar1, par, FLAG_BRACK_END);
708 rt.setArgumentIdx(0);
709 rt.setData(ar1); // I belive that line is not needed (Lgb)
712 mathed_parse(ar2, par, FLAG_BRACE|FLAG_BRACE_LAST);
714 rt.setArgumentIdx(1);
715 rt.setData(ar2); // I belive that this line is not needed (Lgb)
717 data.insertInset(rt.Clone(), LM_TC_ACTIVE_INSET);
720 MathSqrtInset rt(size);
722 mathed_parse(ar, par, FLAG_BRACE|FLAG_BRACE_LAST);
723 rt.setData(ar); // I belive that this line is not needed (Lgb)
724 data.insertInset(rt.Clone(), LM_TC_ACTIVE_INSET);
732 if (lfd == LM_TK_SYM || lfd == LM_TK_STR || lfd == LM_TK_BOP|| lfd == LM_TK_SPECIAL)
733 lfd = (lfd == LM_TK_SYM) ? yylval.l->id: yylval.i;
734 // lyxerr << "L[" << lfd << " " << lfd << "]";
736 mathed_parse(ar, par, FLAG_RIGHT);
738 // lyxerr << "R[" << rgd << "]";
739 if (rgd == LM_TK_SYM || rgd == LM_TK_STR || rgd == LM_TK_BOP || rgd == LM_TK_SPECIAL)
740 rgd = (rgd == LM_TK_SYM) ? yylval.l->id: yylval.i;
741 MathDelimInset * dl = new MathDelimInset(lfd, rgd);
743 data.insertInset(dl, LM_TC_ACTIVE_INSET);
744 // lyxerr << "RL[" << lfd << " " << rgd << "]";
749 if (flags & FLAG_RIGHT) {
753 mathPrintError("Unmatched right delimiter");
758 varcode = static_cast<MathedTextCodes>(yylval.l->id);
759 yy_mtextmode = bool(varcode == LM_TC_TEXTRM);
760 flags |= (FLAG_BRACE|FLAG_BRACE_FONT);
765 MathDecorationInset * sq = new MathDecorationInset(yylval.l->id,
768 mathed_parse(ar, par, FLAG_BRACE|FLAG_BRACE_LAST);
770 data.insertInset(sq, LM_TC_ACTIVE_INSET);
775 setAccent(yylval.l->id);
779 //lyxerr << "prepare line " << par->getRowSt().size()
780 // << " not numbered\n";
781 last_numbered = false;
787 data.insert(t, LM_TC_CONST);
789 MathedInset * bg = new MathFuncInset(yylval.l->name);
790 data.insertInset(bg, LM_TC_INSET);
795 data.insertInset(new MathFuncInset(yylval.l->name, LM_OT_FUNCLIM),
801 // save this value, yylval.s might get overwritten soon
802 const string name = yylval.s;
803 //lyxerr << "LM_TK_UNDEF: str = " << name << endl;
804 if (MathMacroTable::hasTemplate(name)) {
805 MathMacro * m = MathMacroTable::cloneTemplate(name);
806 //lyxerr << "Macro: " << m->GetData() << endl;
807 for (int i = 0; i < m->nargs(); ++i) {
809 mathed_parse(ar, par, FLAG_BRACE|FLAG_BRACE_LAST);
812 do_insert(data, m, m->getTCode());
814 MathedInset * q = new MathFuncInset(name, LM_OT_UNDEF);
815 do_insert(data, q, LM_TC_INSET);
821 if (mathed_env != yylval.i && yylval.i != LM_OT_MATRIX)
822 mathPrintError("Unmatched environment");
823 // debug info [made that conditional -JMarc]
824 if (lyxerr.debugging(Debug::MATHED))
825 lyxerr << "[" << yylval.i << "]" << endl;
828 //if (mt) { // && (flags & FLAG_END)) {
829 // par.setData(array);
838 if (yylval.i == LM_OT_MATRIX) {
839 //lyxerr << "###### Reading LM_OT_MATRIX \n";
842 ar[0] = ar2[0] = '\0';
843 char rg = LexGetArg(0);
845 strcpy(ar2, yytext.data());
848 strcpy(ar, yytext.data());
849 int const nc = parse_align(ar, ar2);
851 MathParInset * mm = new MathMatrixInset(nc, 0);
852 mm->SetAlign(ar2[0], ar);
854 mathed_parse(dat, mm, FLAG_END);
855 data.insertInset(mm, LM_TC_ACTIVE_INSET);
858 } else if (is_eqn_type(yylval.i)) {
859 //lyxerr << "###### Reading is_eqn_type \n";
861 mathPrintError("Misplaced environment");
865 mathed_env = static_cast<MathedInsetTypes>(yylval.i);
866 if (mathed_env != LM_OT_MIN) {
867 //lyxerr << "###### Reading mathed_env != LM_OT_MIN \n";
868 size = LM_ST_DISPLAY;
869 if (is_multiline(mathed_env)) {
870 //lyxerr << "###### Reading is_multiline(mathed_env) \n";
872 if (is_multicolumn(mathed_env)) {
873 //lyxerr << "###### Reading is_multicolumn(mathed_env) \n";
874 if (mathed_env != LM_OT_ALIGNAT &&
875 mathed_env != LM_OT_ALIGNATN &&
877 //lyxerr << "###### Reading is !align\n";
881 lyxerr << "Math parse error: unexpected '"
885 cols = strToInt(string(yytext.data()));
890 //mt = create_multiline(mathed_env, cols);
891 //if (mtx) *mtx = mt;
893 //MathMatrixInset mat = create_multiline(mathed_env, cols);
894 //data.insertInset(mat.Clone(), LM_TC_ACTIVE_INSET);
896 par = new MathMatrixInset(create_multiline(mathed_env, cols));
900 par->SetType(mathed_env);
903 lyxerr[Debug::MATHED] << "MATH BEGIN[" << mathed_env << "]" << endl;
905 MathMacro * m = MathMacroTable::cloneTemplate(yytext.data());
906 data.insertInset(m, m->getTCode());
908 mathed_parse(dat, par, FLAG_END);
914 MathMacro * m = MathMacroTable::cloneTemplate(yylval.l->name);
915 do_insert(data, m, m->getTCode());
921 char const rg = LexGetArg('\0', true);
923 mathPrintError("Expected '{'");
925 lyxerr << "[" << yytext.data() << "]" << endl;
929 last_label = yytext.data();
930 //lyxerr << "prepare line " << par->getRowSt().size()
931 // << " label: " << last_label << endl;
936 mathPrintError("Unrecognized token");
938 lyxerr << "[" << t << " " << yytext.data() << "]" << endl;
944 lyxerr << " Math Panic, expect problems!" << endl;
945 // Search for the end command.
948 } while (t != LM_TK_END && t);
952 if ((flags & FLAG_BRACE_OPT)/* && t!= '^' && t!= '_'*/) {
953 flags &= ~FLAG_BRACE_OPT;
961 if (last_numbered == false) {
962 //lyxerr << "last line " << par->getRowSt().size() << " not numbered\n";
963 if (par->getRowSt().size() == 0)
964 par->getRowSt().push_back();
965 par->getRowSt().back().setNumbered(false);
967 if (last_label.size()) {
968 //lyxerr << "last line " << par->getRowSt().size() << " labeled: "
969 // << last_label << endl;
970 if (par->getRowSt().size() == 0)
971 par->getRowSt().push_back();
972 par->getRowSt().back().setLabel(last_label);
977 void mathed_parser_file(istream & is, int lineno)
984 int mathed_parser_lineno()