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: (c) 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 "math_parser.h"
26 #include "math_parser.h"
27 #include "math_iter.h"
28 #include "math_inset.h"
29 #include "math_macro.h"
30 #include "math_root.h"
34 FLAG_BRACE = 1, // A { needed
35 FLAG_BRACE_ARG = 2, // Next { is argument
36 FLAG_BRACE_OPT = 4, // Optional {
37 FLAG_BRACE_LAST = 8, // Last } ends the parsing process
38 FLAG_BRACK_ARG = 16, // Optional [
39 FLAG_RIGHT = 32, // Next right ends the parsing process
40 FLAG_END = 64, // Next end ends the parsing process
41 FLAG_BRACE_FONT = 128, // Next } closes a font
42 FLAG_BRACK_END = 256 // Next ] ends the parsing process
48 static short mathed_env = LM_EN_INTEXT;
50 char *mathed_label = NULL;
52 char const *latex_mathenv[] = {
62 char const *latex_mathspace[] = {
63 "!", ",", ":", ";", "quad", "qquad"
66 char const *latex_special_chars = "#$%&_{}";
68 // These are lexical codes, not semantic
74 LexBOP, // Binary operators or relations
86 static lexcode_enum lexcode[256];
87 static char yytext[256];
90 static bool yy_mtextmode=false;
93 char *strnew(char const* s)
95 char *s1 = new char[strlen(s)+1]; // this leaks when not delete[]'ed
101 static void mathPrintError(char const *msg)
103 fprintf(stderr, "Line ~%d: Math parse error: %s\n", yylineno, msg);
107 static void LexInitCodes()
111 for (i=0; i<=255; i++) {
112 if (isalpha(i)) lexcode[i] = LexAlpha;
113 else if (isdigit(i)) lexcode[i] = LexDigit;
114 else if (isspace(i)) lexcode[i] = LexSpace;
115 else lexcode[i] = LexNone;
118 lexcode['\t'] = lexcode['\f'] = lexcode[' '] = LexSpace;
119 lexcode['\n'] = LexNewLine;
120 lexcode['%'] = LexComment;
121 lexcode['#'] = LexArgument;
122 lexcode['+'] = lexcode['-'] = lexcode['*'] = lexcode['/'] =
123 lexcode['<'] = lexcode['>'] = lexcode['='] = LexBOP;
125 lexcode['!'] = lexcode[','] = lexcode[':'] = lexcode[';'] = LexMathSpace;
126 lexcode['('] = lexcode[')'] = lexcode['|'] = lexcode['.'] = lexcode['?'] = LexOther;
127 lexcode['\'']= LexAlpha;
129 lexcode['['] = lexcode[']'] = lexcode['^'] = lexcode['_'] =
130 lexcode['&'] = LexSelf;
132 lexcode['\\'] = LexESC;
133 lexcode['{'] = LexOpen;
134 lexcode['}'] = LexClose;
137 static char LexGetArg(char lf, bool accept_spaces=false)
139 char c, rg, *p = &yytext[0];
142 while (!feof(yyin)) {
145 if (!lf) lf = c; else
147 fprintf(stderr, "Math parse error: unexpected '%c'\n", c);
151 rg = (lf=='{') ? '}': ((lf=='[') ? ']': ((lf=='(') ? ')': 0));
153 fprintf(stderr, "Math parse error: unknown bracket '%c'\n", lf);
160 if ((c>' ' || (c==' ' && accept_spaces)) && bcnt>0) *(p++) = c;
161 } while (bcnt>0 && !feof(yyin));
166 static int yylex(void)
168 static int init_done = 0;
171 if (!init_done) LexInitCodes();
173 while (!feof(yyin)) {
176 if (yy_mtextmode && c==' ') {
181 if (lexcode[c]==LexNewLine) {
186 if (lexcode[c]==LexComment)
187 do c = getc(yyin); while (c!='\n' % !feof(yyin)); // eat comments
189 if (lexcode[c]==LexDigit || lexcode[c]==LexOther || lexcode[c]==LexMathSpace)
190 { yylval.i= c; return LM_TK_STR; }
191 if (lexcode[c]==LexAlpha) { yylval.i=c; return LM_TK_ALPHA; }
192 if (lexcode[c]==LexBOP) { yylval.i=c; return LM_TK_BOP; }
193 if (lexcode[c]==LexSelf) { return c; }
194 if (lexcode[c]==LexArgument) {
197 return LM_TK_ARGUMENT;
199 if (lexcode[c]==LexOpen) { return LM_TK_OPEN; }
200 if (lexcode[c]==LexClose) { return LM_TK_CLOSE; }
202 if (lexcode[c]==LexESC) {
204 if (c=='\\') { return LM_TK_NEWLINE; }
205 if (c=='(') { yylval.i = LM_EN_INTEXT; return LM_TK_BEGIN; }
206 if (c==')') { yylval.i = LM_EN_INTEXT; return LM_TK_END; }
207 if (c=='[') { yylval.i = LM_EN_DISPLAY; return LM_TK_BEGIN; }
208 if (c==']') { yylval.i = LM_EN_DISPLAY; return LM_TK_END; }
209 if (strchr(latex_special_chars, c)) {
211 return LM_TK_SPECIAL;
213 if (lexcode[c]==LexMathSpace) {
215 for (i=0; i<4 && c!=latex_mathspace[i][0]; i++);
216 yylval.i = (i<4) ? i: 0;
219 if (lexcode[c]==LexAlpha || lexcode[c]==LexDigit) {
220 char* p = &yytext[0];
221 while (lexcode[c]==LexAlpha || lexcode[c]==LexDigit) {
227 if (!feof(yyin)) ungetc(c, yyin);
228 latexkeys *l = in_word_set (yytext, strlen(yytext));
230 if (l->token==LM_TK_BEGIN || l->token==LM_TK_END) {
233 // for (i=0; i<5 && strncmp(yytext, latex_mathenv[i],
234 // strlen(latex_mathenv[i])); i++);
236 for (i=0; i<6 && strcmp(yytext, latex_mathenv[i]); i++);
239 if (l->token==LM_TK_SPACE)
254 int parse_align(char *hor, char *)
259 for (c=hor; c && *c>' '; c++) nc++;
263 // Accent hacks only for 0.12. Stolen from Cursor.
267 void setAccent(int ac)
269 if (ac > 0 && accent < 8) {
270 nestaccent[accent++] = ac;
272 accent = 0; // consumed!
276 MathedInset *doAccent(byte c, MathedTextCodes t)
280 for (int i=accent-1; i>=0; i--) {
282 ac = new MathAccentInset(c, t, nestaccent[i]);
284 ac = new MathAccentInset(ac, nestaccent[i]);
286 accent = 0; // consumed!
292 MathedInset *doAccent(MathedInset *p)
296 for (int i=accent-1; i>=0; i--) {
298 ac = new MathAccentInset(p, nestaccent[i]);
300 ac = new MathAccentInset(ac, nestaccent[i]);
302 accent = 0; // consumed!
310 LyxArrayBase *mathed_parse(unsigned flags, LyxArrayBase *array, MathParInset **mtx)
312 int t = yylex(), tprev = 0;
314 static int plevel = -1;
315 static int size = LM_ST_TEXT;
316 MathedTextCodes varcode = LM_TC_VAR;
317 MathedInset* binset = 0;
318 static MathMacroTemplate *macro=0;
323 MathParInset *mt = (mtx) ? *mtx: 0;//(MathParInset*)NULL;
324 MathedRowSt *crow = (mt) ? mt->getRowSt(): 0;
327 if (!array) array = new LyxArrayBase;
328 MathedIter data(array);
330 if ((flags & FLAG_BRACE) && t != LM_TK_OPEN) {
331 if ((flags & FLAG_BRACK_ARG) && t=='[') {
334 mathPrintError("Expected {. Maybe you forgot to enclose an argument in {}");
339 MathedInsetTypes fractype = LM_OT_FRAC;
344 data.Insert(doAccent(yylval.i, varcode));
346 data.Insert (yylval.i, varcode); //LM_TC_VAR);
352 data.Insert(macro->getMacroPar(yylval.i-1), LM_TC_INSET);
356 case LM_TK_NEWCOMMAND:
361 // This name lives until quitting, for that reason
362 // I didn't care on deleting explicitly. Later I will.
363 char const *name = strnew(&yytext[1]);
364 // ugly trick to be removed soon (lyx3)
371 macro = new MathMacroTemplate(name, na);
372 flags = FLAG_BRACE|FLAG_BRACE_LAST;
374 macro->SetData(array);
379 data.Insert (yylval.i, LM_TC_SPECIAL);
385 data.Insert(doAccent(yylval.i, LM_TC_CONST));
387 data.Insert (yylval.i, LM_TC_CONST);
393 if (accent && tprev==LM_TK_ACCENT) {
394 acc_braces[acc_brace++] = brace;
397 if (flags & FLAG_BRACE_OPT) {
398 flags &= ~FLAG_BRACE_OPT;
402 if (flags & FLAG_BRACE)
403 flags &= ~FLAG_BRACE;
405 data.Insert ('{', LM_TC_TEX);
413 mathPrintError("Unmatching braces");
417 if (acc_brace && brace==acc_braces[acc_brace-1]-1) {
421 if (flags & FLAG_BRACE_FONT) {
423 yy_mtextmode = false;
424 flags &= ~FLAG_BRACE_FONT;
427 if (brace == 0 && (flags & FLAG_BRACE_LAST)) {
431 data.Insert ('}', LM_TC_TEX);
438 if (flags & FLAG_BRACK_ARG) {
439 flags &= ~FLAG_BRACK_ARG;
440 char rg=LexGetArg('[');
442 mathPrintError("Expected ']'");
446 // if (arg) strcpy(arg, yytext);
453 if (flags & FLAG_BRACK_END) {
463 MathParInset *p = new MathParInset(size, "", LM_OT_SCRIPT);
464 LyxArrayBase * ar = mathed_parse(FLAG_BRACE_OPT|FLAG_BRACE_LAST, NULL);
466 // fprintf(stderr, "UP[%d]", p->GetStyle());
467 data.Insert (p, LM_TC_UP);
472 MathParInset *p = new MathParInset(size, "", LM_OT_SCRIPT);
473 LyxArrayBase * ar = mathed_parse(FLAG_BRACE_OPT|FLAG_BRACE_LAST, NULL);
475 data.Insert (p, LM_TC_DOWN);
482 binset->SetLimits((bool)(yylval.l->id));
490 if ((flags & FLAG_END) && mt && data.getCol()<mt->GetColumns()-1) {
491 data.setNumCols(mt->GetColumns());
492 data.Insert('T', LM_TC_TAB);
494 mathPrintError("Unexpected tab");
495 // debug info. [made that conditional -JMarc]
496 if (lyxerr.debugging(Error::MATHED))
497 fprintf(stderr, "%d %d\n", data.getCol(), mt->GetColumns());
502 if (mt && (flags & FLAG_END)) {
503 if (mt->Permit(LMPF_ALLOW_CR)) {
505 crow->setNext(new MathedRowSt(mt->GetColumns()+1)); // this leaks
506 crow = crow->getNext();
508 data.Insert('K', LM_TC_CR);
510 mathPrintError("Unexpected newline");
516 binset = new MathBigopInset(yylval.l->name,yylval.l->id);
522 if (yylval.l->id < 256) {
523 MathedTextCodes tc = MathIsBOPS(yylval.l->id) ? LM_TC_BOPS: LM_TC_SYMB;
525 data.Insert(doAccent(yylval.l->id, tc));
527 data.Insert (yylval.l->id, tc);
529 MathFuncInset *bg = new MathFuncInset(yylval.l->name);
531 data.Insert(doAccent(bg));
533 data.Insert(bg, true);
540 data.Insert(doAccent(yylval.i, LM_TC_BOP));
542 data.Insert (yylval.i, LM_TC_BOP);
548 mt->UserSetSize(yylval.l->id);
555 MathSpaceInset *sp = new MathSpaceInset(yylval.i);
562 MathDotsInset *p = new MathDotsInset(yylval.l->name, yylval.l->id);
567 fractype = LM_OT_STACKREL;
570 MathFracInset *fc = new MathFracInset(fractype);
571 LyxArrayBase* num = mathed_parse(FLAG_BRACE|FLAG_BRACE_LAST);
572 LyxArrayBase* den = mathed_parse(FLAG_BRACE|FLAG_BRACE_LAST);
573 fc->SetData(num, den);
574 data.Insert(fc, LM_TC_ACTIVE_INSET);
584 rt = new MathRootInset(size);
585 rt->setArgumentIdx(0);
586 rt->SetData(mathed_parse(FLAG_BRACK_END, 0, &rt));
587 rt->setArgumentIdx(1);
590 rt = new MathSqrtInset(size);
592 rt->SetData(mathed_parse(FLAG_BRACE|FLAG_BRACE_LAST, 0, &rt));
593 data.Insert(rt, LM_TC_ACTIVE_INSET);
601 if (lfd==LM_TK_SYM || lfd==LM_TK_STR || lfd==LM_TK_BOP|| lfd==LM_TK_SPECIAL)
602 lfd = (lfd==LM_TK_SYM) ? yylval.l->id: yylval.i;
603 // fprintf(stderr, "L[%d %c]", lfd, lfd);
604 LyxArrayBase* a = mathed_parse(FLAG_RIGHT);
606 // fprintf(stderr, "R[%d]", rgd);
607 if (rgd==LM_TK_SYM || rgd==LM_TK_STR || rgd==LM_TK_BOP || rgd==LM_TK_SPECIAL)
608 rgd = (rgd==LM_TK_SYM) ? yylval.l->id: yylval.i;
609 MathDelimInset *dl = new MathDelimInset(lfd, rgd);
611 data.Insert(dl, LM_TC_ACTIVE_INSET);
612 // fprintf(stderr, "RL[%d %d]", lfd, rgd);
617 if (flags & FLAG_RIGHT) {
621 mathPrintError("Unmatched right delimiter");
629 varcode = (MathedTextCodes)yylval.l->id;
630 yy_mtextmode = (bool)(varcode==LM_TC_TEXTRM);
631 flags |= (FLAG_BRACE|FLAG_BRACE_FONT);
636 MathDecorationInset *sq = new MathDecorationInset(yylval.l->id, size);
637 sq->SetData(mathed_parse(FLAG_BRACE|FLAG_BRACE_LAST));
638 data.Insert(sq, LM_TC_ACTIVE_INSET);
642 case LM_TK_ACCENT: setAccent(yylval.l->id); break;
647 crow->setNumbered(false);
654 MathedInset *bg = new MathFuncInset(yylval.l->name);
663 data.Insert(new MathFuncInset(yylval.l->name, LM_OT_FUNCLIM));
670 MathMacroTable::mathMTable.getMacro(yylval.s);
673 data.Insert(doAccent(p), p->getTCode());
675 data.Insert(p, p->getTCode());
676 for (int i=0; p->setArgumentIdx(i); i++)
677 p->SetData(mathed_parse(FLAG_BRACE|FLAG_BRACE_LAST));
680 MathedInset *q = new MathFuncInset(yylval.s, LM_OT_UNDEF);
682 data.Insert(doAccent(q));
691 if (mathed_env != yylval.i && yylval.i!=LM_EN_ARRAY)
692 mathPrintError("Unmatched environment");
693 // debug info [made that conditional -JMarc]
694 if (lyxerr.debugging(Error::MATHED))
695 fprintf(stderr, "[%d]\n", yylval.i);
697 if (mt) { // && (flags & FLAG_END)) {
705 if (yylval.i==LM_EN_ARRAY) {
706 char ar[120], ar2[8];
707 ar[0] = ar2[0] = '\0';
708 char rg=LexGetArg(0);
714 int nc = parse_align(ar, ar2);
715 MathParInset* mm = new MathMatrixInset(nc, 0);
716 mm->SetAlign(ar2[0], ar);
717 data.Insert(mm, LM_TC_ACTIVE_INSET);
718 mathed_parse(FLAG_END, mm->GetData(), &mm);
720 if (yylval.i>=LM_EN_INTEXT && yylval.i<=LM_EN_EQNARRAY) {
722 mathPrintError("Misplaced environment");
726 mathPrintError("NULL paragraph.");
730 mathed_env = yylval.i;
731 if (mathed_env>=LM_EN_DISPLAY) {
732 size = LM_ST_DISPLAY;
733 if (mathed_env>LM_EN_EQUATION) {
734 mt = new MathMatrixInset(3, -1);
735 mt->SetAlign(' ', "rcl");
738 // data.Insert(' ', LM_TC_TAB);
739 // data.Insert(' ', LM_TC_TAB);
743 mt->SetType(mathed_env);
744 crow = mt->getRowSt();
748 fprintf(stderr, "MATH BEGIN[%d]\n", mathed_env);
751 // fprintf(stderr, "MATHCRO[%s]",yytext);
753 MathMacroTable::mathMTable.getMacro(yytext);
755 data.Insert(p, p->getTCode());
756 p->setArgumentIdx(0);
757 mathed_parse(FLAG_END, p->GetData(), (MathParInset**)&p);
758 // for (int i=0; p->setArgumentIdx(i); i++)
759 // p->SetData(mathed_parse(FLAG_BRACE|FLAG_BRACE_LAST));
761 mathPrintError("Unrecognized environment");
769 MathMacroTable::mathMTable.getMacro(yylval.l->name);
773 data.Insert(doAccent(p));
775 data.Insert(p, ((MathMacro*)p)->getTCode());
782 char rg = LexGetArg('\0', true);
784 mathPrintError("Expected '{'");
786 fprintf(stderr, "[%s]\n", yytext); fflush(stderr);
791 // This is removed by crow's destructor. Bad design? yes, this
792 // will be changed after 0.12
793 crow->setLabel(strnew(yytext));
796 // where is this math_label free'ed?
797 // Supposedly in ~formula, another bad hack,
798 // give me some time please.
799 mathed_label = strnew(yytext);
802 fprintf(stderr, "Label[%d]\n", mathed_label);
807 mathPrintError("Unrecognized token");
809 fprintf(stderr, "[%d %s]\n", t, yytext);
814 fprintf(stderr, " Math Panic, expect problems!\n");
815 // Search for the end command.
816 do t = yylex (); while (t != LM_TK_END && t);
820 if ((flags & FLAG_BRACE_OPT)/* && t!='^' && t!='_'*/) {
821 flags &= ~FLAG_BRACE_OPT;
822 //data.Insert (LM_TC_CLOSE);
831 void mathed_parser_file(FILE* file, int lineno)
835 if (!MathMacroTable::built)
836 MathMacroTable::mathMTable.builtinMacros();
840 int mathed_parser_lineno()