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_TOP 0x010000 /* top-level boxes */
17 #define EQN_SUB 0x020000 /* this is a subscript */
18 #define EQN_FROM 0x040000 /* this is a from block */
20 static char gfont
[FNLEN
] = "2";
21 static char grfont
[FNLEN
] = "1";
22 static char gbfont
[FNLEN
] = "3";
23 static char gsize
[FNLEN
] = "\\n[" EQNSZ
"]";
24 static char eqn_lineup
[128]; /* the lineup horizontal request */
25 static int eqn_lineupreg
; /* the number register holding lineup width */
26 static int eqn_mk
; /* the value of MK */
28 static struct box
*eqn_box(int flg
, struct box
*pre
, int sz0
, char *fn0
);
30 /* read equations until delim is read */
31 static int eqn_boxuntil(struct box
*box
, int sz0
, char *fn0
, char *delim
)
33 struct box
*sub
= NULL
;
34 while (tok_get() && tok_jmp(delim
)) {
35 if (!strcmp("}", tok_get()))
37 sub
= eqn_box(box
->style
, sub
? box
: NULL
, sz0
, fn0
);
45 static void sizesub(int dst
, int src
, int style
, int src_style
)
47 if (TS_SZ(style
) > TS_SZ(src_style
)) {
48 printf(".nr %s %s*7/10\n", nregname(dst
), nreg(src
));
49 printf(".if %s<%d .nr %s %d\n",
50 nreg(dst
), e_minimumsize
,
51 nregname(dst
), e_minimumsize
);
53 printf(".nr %s %s\n", nregname(dst
), nreg(src
));
57 static char *tok_quotes(char *s
)
59 if (s
&& s
[0] == '"') {
60 s
[strlen(s
) - 1] = '\0';
66 static char *tok_improve(char *s
)
68 if (s
&& s
[0] == '-' && s
[1] == '\0')
70 if (s
&& s
[0] == '+' && s
[1] == '\0')
72 if (s
&& s
[0] == '\'' && s
[1] == '\0')
77 static void eqn_bracketsizes(void)
80 char bufs
[NSIZES
][BRLEN
];
81 char *sizes
[NSIZES
] = {NULL
};
83 snprintf(sign
, sizeof(sign
), "%s", tok_quotes(tok_poptext(1)));
84 n
= atoi(tok_poptext(1));
85 for (i
= 0; i
< n
; i
++) {
86 char *size
= tok_quotes(tok_poptext(1));
88 snprintf(bufs
[i
], sizeof(bufs
[i
]), "%s", size
);
92 def_sizesput(sign
, sizes
);
95 static void eqn_bracketpieces(void)
97 char sign
[BRLEN
], top
[BRLEN
], mid
[BRLEN
], bot
[BRLEN
], cen
[BRLEN
];
98 snprintf(sign
, sizeof(sign
), "%s", tok_quotes(tok_poptext(1)));
99 snprintf(top
, sizeof(top
), "%s", tok_quotes(tok_poptext(1)));
100 snprintf(mid
, sizeof(mid
), "%s", tok_quotes(tok_poptext(1)));
101 snprintf(bot
, sizeof(bot
), "%s", tok_quotes(tok_poptext(1)));
102 snprintf(cen
, sizeof(cen
), "%s", tok_quotes(tok_poptext(1)));
103 def_piecesput(sign
, top
, mid
, bot
, cen
);
106 static int typenum(char *s
)
108 if (!strcmp("ord", s
) || !strcmp("ordinary", s
))
110 if (!strcmp("op", s
) || !strcmp("operator", s
))
112 if (!strcmp("bin", s
) || !strcmp("binary", s
))
114 if (!strcmp("rel", s
) || !strcmp("relation", s
))
116 if (!strcmp("open", s
) || !strcmp("opening", s
))
118 if (!strcmp("close", s
) || !strcmp("closing", s
))
120 if (!strcmp("punct", s
) || !strcmp("punctuation", s
))
122 if (!strcmp("inner", s
) || !strcmp("inner", s
))
127 static void eqn_chartype(void)
129 char gl
[GNLEN
], type
[NMLEN
];
130 snprintf(type
, sizeof(type
), "%s", tok_quotes(tok_poptext(1)));
131 snprintf(gl
, sizeof(gl
), "%s", tok_quotes(tok_poptext(1)));
132 if (typenum(type
) >= 0)
133 def_typeput(gl
, typenum(type
));
136 static int eqn_commands(void)
140 if (!tok_jmp("delim")) {
144 if (!tok_jmp("define")) {
148 if (!tok_jmp("gfont")) {
149 strcpy(gfont
, tok_quotes(tok_poptext(1)));
152 if (!tok_jmp("grfont")) {
153 strcpy(grfont
, tok_quotes(tok_poptext(1)));
156 if (!tok_jmp("gbfont")) {
157 strcpy(gbfont
, tok_quotes(tok_poptext(1)));
160 if (!tok_jmp("gsize")) {
161 sz
= tok_quotes(tok_poptext(1));
162 if (sz
[0] == '-' || sz
[0] == '+')
163 sprintf(gsize
, "\\n%s%s", escarg(EQNSZ
), sz
);
168 if (!tok_jmp("set")) {
169 strcpy(var
, tok_poptext(1));
170 def_set(var
, atoi(tok_poptext(1)));
173 if (!tok_jmp("bracketsizes")) {
177 if (!tok_jmp("bracketpieces")) {
181 if (!tok_jmp("chartype")) {
188 static int eqn_gaps(struct box
*box
, int szreg
)
191 box_puttext(box
, T_GAP
, "\\h'%du*%sp/100u'",
196 box_puttext(box
, T_GAP
, "\\h'%du*%sp/100u'",
200 if (!tok_jmp("\t")) {
201 box_puttext(box
, T_GAP
, "\t");
207 static char *tok_font(int tok
, char *fn
)
211 if (tok
== T_LETTER
|| tok
== T_STRING
)
216 static void tok_expect(char *s
)
219 fprintf(stderr
, "neateqn: expected %s bot got %s\n",
225 static void eqn_pile(struct box
*box
, int sz0
, char *fn0
, int adj
)
227 struct box
*pile
[NPILES
] = {NULL
};
232 rowspace
= atoi(tok_poptext(1));
236 pile
[n
++] = box_alloc(sz0
, 0, box
->style
);
237 } while (!eqn_boxuntil(pile
[n
- 1], sz0
, fn0
, "above"));
239 box_pile(box
, pile
, adj
, rowspace
);
240 for (i
= 0; i
< n
; i
++)
244 static void eqn_matrix(struct box
*box
, int sz0
, char *fn0
)
246 struct box
*cols
[NPILES
][NPILES
] = {{NULL
}};
254 colspace
= atoi(tok_poptext(1));
258 if (!tok_jmp("col") || !tok_jmp("ccol"))
260 else if (!tok_jmp("lcol"))
262 else if (!tok_jmp("rcol"))
268 i
= atoi(tok_poptext(1));
274 cols
[ncols
][nrows
++] = box_alloc(sz0
, 0, box
->style
);
275 } while (!eqn_boxuntil(cols
[ncols
][nrows
- 1],
281 box_matrix(box
, ncols
, cols
, adj
, colspace
, rowspace
);
282 for (i
= 0; i
< ncols
; i
++)
283 for (j
= 0; j
< NPILES
; j
++)
285 box_free(cols
[i
][j
]);
288 static int italic(char *fn
)
290 return (!strcmp("I", fn
) || !strcmp("2", fn
) ||
291 gfont
== fn
|| !strcmp(gfont
, fn
)) ? T_ITALIC
: 0;
294 /* read a box without fractions */
295 static struct box
*eqn_left(int flg
, struct box
*pre
, int sz0
, char *fn0
)
297 struct box
*box
= NULL
;
298 struct box
*sub_sub
= NULL
, *sub_sup
= NULL
;
299 struct box
*sub_from
= NULL
, *sub_to
= NULL
;
300 struct box
*sqrt
, *inner
;
301 char left
[NMLEN
] = "", right
[NMLEN
] = "";
304 int subsz
= nregmk();
306 int style
= EQN_TSMASK
& flg
;
309 while (!eqn_commands())
311 box
= box_alloc(sz
, pre
? pre
->tcur
: 0, style
);
312 while (!eqn_gaps(box
, sz
))
315 if (!tok_jmp("fat")) {
316 } else if (!tok_jmp("roman")) {
318 } else if (!tok_jmp("italic")) {
320 } else if (!tok_jmp("bold")) {
322 } else if (!tok_jmp("font")) {
323 strcpy(fn
, tok_poptext(1));
324 } else if (!tok_jmp("size")) {
325 sz
= box_size(box
, tok_poptext(1));
326 } else if (!tok_jmp("fwd")) {
327 dx
+= atoi(tok_poptext(1));
328 } else if (!tok_jmp("back")) {
329 dx
-= atoi(tok_poptext(1));
330 } else if (!tok_jmp("down")) {
331 dy
+= atoi(tok_poptext(1));
332 } else if (!tok_jmp("up")) {
333 dy
-= atoi(tok_poptext(1));
338 if (!tok_jmp("sqrt")) {
339 sqrt
= eqn_left(TS_MK0(style
), NULL
, sz
, fn
);
340 printf(".ft %s\n", grfont
);
343 } else if (!tok_jmp("pile") || !tok_jmp("cpile")) {
344 eqn_pile(box
, sz
, fn
, 'c');
345 } else if (!tok_jmp("lpile")) {
346 eqn_pile(box
, sz
, fn
, 'l');
347 } else if (!tok_jmp("rpile")) {
348 eqn_pile(box
, sz
, fn
, 'r');
349 } else if (!tok_jmp("matrix")) {
350 eqn_matrix(box
, sz
, fn
);
351 } else if (!tok_jmp("vcenter")) {
352 inner
= eqn_left(flg
, pre
, sz
, fn
);
353 box_vcenter(box
, inner
);
355 } else if (!tok_jmp("{")) {
356 eqn_boxuntil(box
, sz
, fn
, "}");
357 } else if (!tok_jmp("left")) {
358 inner
= box_alloc(sz
, 0, style
);
359 snprintf(left
, sizeof(left
), "%s", tok_quotes(tok_poptext(0)));
360 eqn_boxuntil(inner
, sz
, fn
, "right");
361 snprintf(right
, sizeof(right
), "%s", tok_quotes(tok_poptext(0)));
362 printf(".ft %s\n", grfont
);
363 box_wrap(box
, inner
, left
[0] ? left
: NULL
,
364 right
[0] ? right
: NULL
);
366 } else if (tok_get() && tok_type() != T_KEYWORD
) {
368 box_move(box
, dy
, dx
);
369 box_putf(box
, "\\s%s", escarg(nreg(sz
)));
371 char *cfn
= tok_font(tok_type(), fn
);
372 box_puttext(box
, tok_type() | italic(cfn
), "\\f%s%s",
373 escarg(cfn
), tok_improve(tok_get()));
375 } while (!tok_sep(0));
377 box_move(box
, -dy
, -dx
);
380 if (!tok_jmp("dyad")) {
381 printf(".ft %s\n", grfont
);
382 box_accent(box
, "\\(ab");
383 } else if (!tok_jmp("bar")) {
384 printf(".ft %s\n", grfont
);
386 } else if (!tok_jmp("under")) {
387 printf(".ft %s\n", grfont
);
389 } else if (!tok_jmp("vec")) {
390 printf(".ft %s\n", grfont
);
391 box_accent(box
, "\\s[\\n(.s/2]\\(->\\s0");
392 } else if (!tok_jmp("tilde")) {
393 printf(".ft %s\n", grfont
);
394 box_accent(box
, "\\s[\\n(.s*3/4]\\(ap\\s0");
395 } else if (!tok_jmp("hat")) {
396 printf(".ft %s\n", grfont
);
397 box_accent(box
, "ˆ");
398 } else if (!tok_jmp("dot")) {
399 printf(".ft %s\n", grfont
);
400 box_accent(box
, ".");
401 } else if (!tok_jmp("dotdot")) {
402 printf(".ft %s\n", grfont
);
403 box_accent(box
, "..");
408 if (!tok_jmp("sub")) {
409 sizesub(subsz
, sz0
, ts_sup(style
), style
);
410 sub_sub
= eqn_left(ts_sup(style
) | EQN_SUB
, NULL
, subsz
, fn0
);
412 if ((sub_sub
|| !(flg
& EQN_SUB
)) && !tok_jmp("sup")) {
413 sizesub(subsz
, sz0
, ts_sub(style
), style
);
414 sub_sup
= eqn_left(ts_sub(style
), NULL
, subsz
, fn0
);
416 if (sub_sub
|| sub_sup
)
417 box_sub(box
, sub_sub
, sub_sup
);
418 if (!tok_jmp("from")) {
419 sizesub(subsz
, sz0
, ts_sub(style
), style
);
420 sub_from
= eqn_left(ts_sub(style
) | EQN_FROM
, NULL
, subsz
, fn0
);
422 if ((sub_from
|| !(flg
& EQN_FROM
)) && !tok_jmp("to")) {
423 sizesub(subsz
, sz0
, ts_sup(style
), style
);
424 sub_to
= eqn_left(ts_sup(style
), NULL
, subsz
, fn0
);
426 if (sub_from
|| sub_to
) {
427 inner
= box_alloc(sz0
, 0, style
);
428 box_from(inner
, box
, sub_from
, sub_to
);
445 static struct box
*eqn_box(int flg
, struct box
*pre
, int sz0
, char *fn0
)
448 struct box
*sub_num
= NULL
, *sub_den
= NULL
;
449 int style
= flg
& EQN_TSMASK
;
450 box
= eqn_left(flg
, pre
, sz0
, fn0
);
451 while (!tok_jmp("over")) {
453 sub_den
= eqn_left(TS_MK0(style
), NULL
, sz0
, fn0
);
454 box
= box_alloc(sz0
, pre
? pre
->tcur
: 0, style
);
455 printf(".ft %s\n", grfont
);
456 box_over(box
, sub_num
, sub_den
);
463 static struct box
*eqn_read(int style
)
465 struct box
*box
, *sub
;
466 int szreg
= nregmk();
467 printf(".nr %s %s\n", nregname(szreg
), gsize
);
468 box
= box_alloc(szreg
, 0, style
);
470 if (!tok_jmp("mark")) {
471 eqn_mk
= !eqn_mk
? 1 : eqn_mk
;
472 box_markpos(box
, EQNMK
);
475 if (!tok_jmp("lineup")) {
477 box_markpos(box
, nregname(eqn_lineupreg
));
478 sprintf(eqn_lineup
, "\\h'\\n%su-%su'",
479 escarg(EQNMK
), nreg(eqn_lineupreg
));
482 sub
= eqn_box(style
| EQN_TOP
, box
, szreg
, NULL
);
496 for (i
= 0; def_macros
[i
][0]; i
++)
497 in_define(def_macros
[i
][0], def_macros
[i
][1]);
502 printf(".nr %s \\n(.s\n", EQNSZ
);
503 printf(".nr %s \\n(.f\n", EQNFN
);
504 eqn_lineupreg
= nregmk();
505 box
= eqn_read(tok_inline() ? TS_T
: TS_D
);
506 printf(".nr MK %d\n", eqn_mk
);
507 if (!box_empty(box
)) {
508 sprintf(eqnblk
, "%s%s", eqn_lineup
, box_toreg(box
));
510 printf(".ps \\n%s\n", escarg(EQNSZ
));
511 printf(".ft \\n%s\n", escarg(EQNFN
));
513 printf(".lf %d\n", in_lineget());
514 eqn_lineup
[0] = '\0';
515 nregrm(eqn_lineupreg
);