2 * neateqn preprocessor of neatroff
4 * Copyright (C) 2014-2015 Ali Gholami Rudi <ali at rudi dot ir>
6 * This program is released under the Modified BSD license.
14 /* flags passed to eqn_box() */
15 #define EQN_TSMASK 0x00ffff /* style mask */
16 #define EQN_SUB 0x020000 /* this is a subscript */
17 #define EQN_FROM 0x040000 /* this is a from block */
19 static char gfont
[FNLEN
] = "2";
20 static char grfont
[FNLEN
] = "1";
21 static char gbfont
[FNLEN
] = "3";
22 static char gsize
[FNLEN
] = "\\n[" EQNSZ
"]";
23 static char eqn_lineup
[128]; /* the lineup horizontal request */
24 static int eqn_lineupreg
; /* the number register holding lineup width */
25 static int eqn_mk
; /* the value of MK */
27 static struct box
*eqn_box(int flg
, struct box
*pre
, int sz0
, char *fn0
);
29 /* read equations until delim is read */
30 static int eqn_boxuntil(struct box
*box
, int sz0
, char *fn0
, char *delim
)
32 struct box
*sub
= NULL
;
33 while (tok_get() && tok_jmp(delim
)) {
34 if (!strcmp("}", tok_get()))
36 sub
= eqn_box(box
->style
, sub
? box
: NULL
, sz0
, fn0
);
37 box_merge(box
, sub
, 0);
44 static void sizesub(int dst
, int src
, int style
, int src_style
)
46 if (TS_SZ(style
) > TS_SZ(src_style
)) {
47 printf(".nr %s %s*7/10\n", nregname(dst
), nreg(src
));
48 printf(".if %s<%d .nr %s %d\n",
49 nreg(dst
), e_minimumsize
,
50 nregname(dst
), e_minimumsize
);
52 printf(".nr %s %s\n", nregname(dst
), nreg(src
));
56 static char *tok_quotes(char *s
)
58 if (s
&& s
[0] == '"') {
59 s
[strlen(s
) - 1] = '\0';
65 static char *tok_improve(char *s
)
67 if (s
&& s
[0] == '-' && s
[1] == '\0')
69 if (s
&& s
[0] == '+' && s
[1] == '\0')
71 if (s
&& s
[0] == '\'' && s
[1] == '\0')
76 static void eqn_bracketsizes(void)
79 char bufs
[NSIZES
][BRLEN
];
80 char *sizes
[NSIZES
] = {NULL
};
82 snprintf(sign
, sizeof(sign
), "%s", tok_quotes(tok_poptext(1)));
83 n
= atoi(tok_poptext(1));
84 for (i
= 0; i
< n
; i
++) {
85 char *size
= tok_quotes(tok_poptext(1));
87 snprintf(bufs
[i
], sizeof(bufs
[i
]), "%s", size
);
91 def_sizesput(sign
, sizes
);
94 static void eqn_bracketpieces(void)
96 char sign
[BRLEN
], top
[BRLEN
], mid
[BRLEN
], bot
[BRLEN
], cen
[BRLEN
];
97 snprintf(sign
, sizeof(sign
), "%s", tok_quotes(tok_poptext(1)));
98 snprintf(top
, sizeof(top
), "%s", tok_quotes(tok_poptext(1)));
99 snprintf(mid
, sizeof(mid
), "%s", tok_quotes(tok_poptext(1)));
100 snprintf(bot
, sizeof(bot
), "%s", tok_quotes(tok_poptext(1)));
101 snprintf(cen
, sizeof(cen
), "%s", tok_quotes(tok_poptext(1)));
102 def_piecesput(sign
, top
, mid
, bot
, cen
);
105 static int typenum(char *s
)
107 if (!strcmp("ord", s
) || !strcmp("ordinary", s
))
109 if (!strcmp("op", s
) || !strcmp("operator", s
))
111 if (!strcmp("bin", s
) || !strcmp("binary", s
))
113 if (!strcmp("rel", s
) || !strcmp("relation", s
))
115 if (!strcmp("open", s
) || !strcmp("opening", s
))
117 if (!strcmp("close", s
) || !strcmp("closing", s
))
119 if (!strcmp("punct", s
) || !strcmp("punctuation", s
))
121 if (!strcmp("inner", s
) || !strcmp("inner", s
))
126 static void eqn_chartype(void)
128 char gl
[GNLEN
], type
[NMLEN
];
129 snprintf(type
, sizeof(type
), "%s", tok_quotes(tok_poptext(1)));
130 snprintf(gl
, sizeof(gl
), "%s", tok_quotes(tok_poptext(1)));
131 if (typenum(type
) >= 0)
132 def_typeput(gl
, typenum(type
));
135 static void eqn_breakcost(void)
139 snprintf(tok
, sizeof(tok
), "%s", tok_quotes(tok_poptext(1)));
140 cost
= atoi(tok_poptext(1));
141 type
= !strcmp("any", tok
) ? 0 : typenum(tok
);
143 def_brcostput(type
, cost
);
146 static int eqn_commands(void)
150 if (!tok_jmp("delim")) {
154 if (!tok_jmp("define")) {
158 if (!tok_jmp("gfont")) {
159 strcpy(gfont
, tok_quotes(tok_poptext(1)));
162 if (!tok_jmp("grfont")) {
163 strcpy(grfont
, tok_quotes(tok_poptext(1)));
166 if (!tok_jmp("gbfont")) {
167 strcpy(gbfont
, tok_quotes(tok_poptext(1)));
170 if (!tok_jmp("gsize")) {
171 sz
= tok_quotes(tok_poptext(1));
172 if (sz
[0] == '-' || sz
[0] == '+')
173 sprintf(gsize
, "\\n%s%s", escarg(EQNSZ
), sz
);
178 if (!tok_jmp("set")) {
179 strcpy(var
, tok_poptext(1));
180 def_set(var
, atoi(tok_poptext(1)));
183 if (!tok_jmp("bracketsizes")) {
187 if (!tok_jmp("bracketpieces")) {
191 if (!tok_jmp("chartype")) {
195 if (!tok_jmp("breakcost")) {
202 static int eqn_gaps(struct box
*box
, int szreg
)
205 box_puttext(box
, T_GAP
, "\\h'%du*%sp/100u'",
210 box_puttext(box
, T_GAP
, "\\h'%du*%sp/100u'",
214 if (!tok_jmp("\t")) {
215 box_puttext(box
, T_GAP
, "\t");
221 static char *tok_font(int tok
, char *fn
)
225 if (tok
== T_LETTER
|| tok
== T_STRING
)
230 static void tok_expect(char *s
)
233 fprintf(stderr
, "neateqn: expected %s bot got %s\n",
239 static void eqn_pile(struct box
*box
, int sz0
, char *fn0
, int adj
)
241 struct box
*pile
[NPILES
] = {NULL
};
246 rowspace
= atoi(tok_poptext(1));
250 pile
[n
++] = box_alloc(sz0
, 0, box
->style
);
251 } while (!eqn_boxuntil(pile
[n
- 1], sz0
, fn0
, "above"));
253 box_pile(box
, pile
, adj
, rowspace
);
254 for (i
= 0; i
< n
; i
++)
258 static void eqn_matrix(struct box
*box
, int sz0
, char *fn0
)
260 struct box
*cols
[NPILES
][NPILES
] = {{NULL
}};
268 colspace
= atoi(tok_poptext(1));
272 if (!tok_jmp("col") || !tok_jmp("ccol"))
274 else if (!tok_jmp("lcol"))
276 else if (!tok_jmp("rcol"))
282 i
= atoi(tok_poptext(1));
288 cols
[ncols
][nrows
++] = box_alloc(sz0
, 0, box
->style
);
289 } while (!eqn_boxuntil(cols
[ncols
][nrows
- 1],
295 box_matrix(box
, ncols
, cols
, adj
, colspace
, rowspace
);
296 for (i
= 0; i
< ncols
; i
++)
297 for (j
= 0; j
< NPILES
; j
++)
299 box_free(cols
[i
][j
]);
302 static int italic(char *fn
)
304 return (!strcmp("I", fn
) || !strcmp("2", fn
) ||
305 gfont
== fn
|| !strcmp(gfont
, fn
)) ? T_ITALIC
: 0;
308 /* read a box without fractions */
309 static struct box
*eqn_left(int flg
, struct box
*pre
, int sz0
, char *fn0
)
311 struct box
*box
= NULL
;
312 struct box
*sub_sub
= NULL
, *sub_sup
= NULL
;
313 struct box
*sub_from
= NULL
, *sub_to
= NULL
;
314 struct box
*sqrt
, *inner
;
315 char left
[NMLEN
] = "", right
[NMLEN
] = "";
318 int subsz
= nregmk();
320 int style
= EQN_TSMASK
& flg
;
323 while (!eqn_commands())
325 box
= box_alloc(sz
, pre
? pre
->tcur
: 0, style
);
326 while (!eqn_gaps(box
, sz
))
329 if (!tok_jmp("fat")) {
330 } else if (!tok_jmp("roman")) {
332 } else if (!tok_jmp("italic")) {
334 } else if (!tok_jmp("bold")) {
336 } else if (!tok_jmp("font")) {
337 strcpy(fn
, tok_poptext(1));
338 } else if (!tok_jmp("size")) {
339 sz
= box_size(box
, tok_poptext(1));
340 } else if (!tok_jmp("fwd")) {
341 dx
+= atoi(tok_poptext(1));
342 } else if (!tok_jmp("back")) {
343 dx
-= atoi(tok_poptext(1));
344 } else if (!tok_jmp("down")) {
345 dy
+= atoi(tok_poptext(1));
346 } else if (!tok_jmp("up")) {
347 dy
-= atoi(tok_poptext(1));
352 if (!tok_jmp("sqrt")) {
353 sqrt
= eqn_left(TS_MK0(style
), NULL
, sz
, fn
);
354 printf(".ft %s\n", grfont
);
357 } else if (!tok_jmp("pile") || !tok_jmp("cpile")) {
358 eqn_pile(box
, sz
, fn
, 'c');
359 } else if (!tok_jmp("lpile")) {
360 eqn_pile(box
, sz
, fn
, 'l');
361 } else if (!tok_jmp("rpile")) {
362 eqn_pile(box
, sz
, fn
, 'r');
363 } else if (!tok_jmp("matrix")) {
364 eqn_matrix(box
, sz
, fn
);
365 } else if (!tok_jmp("vcenter")) {
366 inner
= eqn_left(flg
, pre
, sz
, fn
);
367 box_vcenter(box
, inner
);
369 } else if (!tok_jmp("{")) {
370 eqn_boxuntil(box
, sz
, fn
, "}");
371 } else if (!tok_jmp("left")) {
372 inner
= box_alloc(sz
, 0, style
);
373 snprintf(left
, sizeof(left
), "%s", tok_quotes(tok_poptext(0)));
374 eqn_boxuntil(inner
, sz
, fn
, "right");
375 snprintf(right
, sizeof(right
), "%s", tok_quotes(tok_poptext(0)));
376 printf(".ft %s\n", grfont
);
377 box_wrap(box
, inner
, left
[0] ? left
: NULL
,
378 right
[0] ? right
: NULL
);
380 } else if (tok_get() && tok_type() != T_KEYWORD
) {
382 box_move(box
, dy
, dx
);
383 box_putf(box
, "\\s%s", escarg(nreg(sz
)));
385 char *cfn
= tok_font(tok_type(), fn
);
386 box_puttext(box
, tok_type() | italic(cfn
), "\\f%s%s",
387 escarg(cfn
), tok_improve(tok_get()));
389 } while (!tok_sep(0));
391 box_move(box
, -dy
, -dx
);
394 if (!tok_jmp("dyad")) {
395 printf(".ft %s\n", grfont
);
396 box_accent(box
, "\\(ab");
397 } else if (!tok_jmp("bar")) {
398 printf(".ft %s\n", grfont
);
400 } else if (!tok_jmp("under")) {
401 printf(".ft %s\n", grfont
);
403 } else if (!tok_jmp("vec")) {
404 printf(".ft %s\n", grfont
);
405 box_accent(box
, "\\s[\\n(.s/2u]\\(->\\s0");
406 } else if (!tok_jmp("tilde")) {
407 printf(".ft %s\n", grfont
);
408 box_accent(box
, "\\s[\\n(.s*3u/4u]\\(ap\\s0");
409 } else if (!tok_jmp("hat")) {
410 printf(".ft %s\n", grfont
);
411 box_accent(box
, "ˆ");
412 } else if (!tok_jmp("dot")) {
413 printf(".ft %s\n", grfont
);
414 box_accent(box
, ".");
415 } else if (!tok_jmp("dotdot")) {
416 printf(".ft %s\n", grfont
);
417 box_accent(box
, "..");
422 if (!tok_jmp("sub")) {
423 sizesub(subsz
, sz0
, ts_sup(style
), style
);
424 sub_sub
= eqn_left(ts_sup(style
) | EQN_SUB
, NULL
, subsz
, fn0
);
426 if ((sub_sub
|| !(flg
& EQN_SUB
)) && !tok_jmp("sup")) {
427 sizesub(subsz
, sz0
, ts_sub(style
), style
);
428 sub_sup
= eqn_left(ts_sub(style
), NULL
, subsz
, fn0
);
430 if (sub_sub
|| sub_sup
)
431 box_sub(box
, sub_sub
, sub_sup
);
432 if (!tok_jmp("from")) {
433 sizesub(subsz
, sz0
, ts_sub(style
), style
);
434 sub_from
= eqn_left(ts_sub(style
) | EQN_FROM
, NULL
, subsz
, fn0
);
436 if ((sub_from
|| !(flg
& EQN_FROM
)) && !tok_jmp("to")) {
437 sizesub(subsz
, sz0
, ts_sup(style
), style
);
438 sub_to
= eqn_left(ts_sup(style
), NULL
, subsz
, fn0
);
440 if (sub_from
|| sub_to
) {
441 inner
= box_alloc(sz0
, 0, style
);
442 box_from(inner
, box
, sub_from
, sub_to
);
459 static struct box
*eqn_box(int flg
, struct box
*pre
, int sz0
, char *fn0
)
462 struct box
*sub_num
= NULL
, *sub_den
= NULL
;
463 int style
= flg
& EQN_TSMASK
;
464 box
= eqn_left(flg
, pre
, sz0
, fn0
);
465 while (!tok_jmp("over")) {
467 sub_den
= eqn_left(TS_MK0(style
), NULL
, sz0
, fn0
);
468 box
= box_alloc(sz0
, pre
? pre
->tcur
: 0, style
);
469 printf(".ft %s\n", grfont
);
470 box_over(box
, sub_num
, sub_den
);
477 static struct box
*eqn_read(int style
)
479 struct box
*box
, *sub
;
480 int szreg
= nregmk();
481 printf(".nr %s %s\n", nregname(szreg
), gsize
);
482 box
= box_alloc(szreg
, 0, style
);
484 if (!tok_jmp("mark")) {
485 eqn_mk
= !eqn_mk
? 1 : eqn_mk
;
486 box_markpos(box
, EQNMK
);
489 if (!tok_jmp("lineup")) {
491 box_markpos(box
, nregname(eqn_lineupreg
));
492 sprintf(eqn_lineup
, "\\h'\\n%su-%su'",
493 escarg(EQNMK
), nreg(eqn_lineupreg
));
496 sub
= eqn_box(style
, box
, szreg
, NULL
);
497 box_merge(box
, sub
, 1);
510 for (i
= 0; def_macros
[i
][0]; i
++)
511 in_define(def_macros
[i
][0], def_macros
[i
][1]);
516 printf(".nr %s \\n(.s\n", EQNSZ
);
517 printf(".nr %s \\n(.f\n", EQNFN
);
518 eqn_lineupreg
= nregmk();
519 box
= eqn_read(tok_inline() ? TS_T
: TS_D
);
520 printf(".nr MK %d\n", eqn_mk
);
521 if (!box_empty(box
)) {
522 sprintf(eqnblk
, "%s%s", eqn_lineup
, box_toreg(box
));
524 printf(".ps \\n%s\n", escarg(EQNSZ
));
525 printf(".ft \\n%s\n", escarg(EQNFN
));
527 printf(".lf %d\n", in_lineget());
528 eqn_lineup
[0] = '\0';
529 nregrm(eqn_lineupreg
);