2 * NEATEQN NEATROFF PREPROCESSOR
4 * Copyright (C) 2014-2017 Ali Gholami Rudi <ali at rudi dot ir>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 /* flags passed to eqn_box() */
25 #define EQN_TSMASK 0x00ffff /* style mask */
26 #define EQN_SUB 0x020000 /* this is a subscript */
27 #define EQN_FROM 0x040000 /* this is a from block */
29 static char gfont
[FNLEN
] = "2";
30 static char grfont
[FNLEN
] = "1";
31 static char gbfont
[FNLEN
] = "3";
32 static char gsize
[FNLEN
] = "\\n[" EQNSZ
"]";
33 static char eqn_lineup
[128]; /* the lineup horizontal request */
34 static int eqn_lineupreg
; /* the number register holding lineup width */
35 static int eqn_mk
; /* the value of MK */
37 static struct box
*eqn_box(int flg
, struct box
*pre
, int sz0
, char *fn0
);
39 /* read equations until delim is read */
40 static int eqn_boxuntil(struct box
*box
, int sz0
, char *fn0
, char *delim
)
42 struct box
*sub
= NULL
;
43 while (tok_get() && tok_jmp(delim
)) {
44 if (!strcmp("}", tok_get()))
46 sub
= eqn_box(box
->style
, sub
? box
: NULL
, sz0
, fn0
);
47 box_merge(box
, sub
, 0);
54 static void sizesub(int dst
, int src
, int style
, int src_style
)
56 if (TS_SZ(style
) > TS_SZ(src_style
)) {
57 printf(".nr %s %s*7/10\n", nregname(dst
), nreg(src
));
58 printf(".if %s<%d .nr %s %d\n",
59 nreg(dst
), e_minimumsize
,
60 nregname(dst
), e_minimumsize
);
62 printf(".nr %s %s\n", nregname(dst
), nreg(src
));
66 static char *tok_quotes(char *s
)
68 if (s
&& s
[0] == '"') {
69 s
[strlen(s
) - 1] = '\0';
75 static char *tok_improve(char *s
)
77 if (s
&& s
[0] == '-' && s
[1] == '\0')
79 if (s
&& s
[0] == '+' && s
[1] == '\0')
81 if (s
&& s
[0] == '\'' && s
[1] == '\0')
86 static void eqn_bracketsizes(void)
89 char bufs
[NSIZES
][BRLEN
];
90 char *sizes
[NSIZES
] = {NULL
};
92 snprintf(sign
, sizeof(sign
), "%s", tok_quotes(tok_poptext(1)));
93 n
= atoi(tok_poptext(1));
94 for (i
= 0; i
< n
; i
++) {
95 char *size
= tok_quotes(tok_poptext(1));
97 snprintf(bufs
[i
], sizeof(bufs
[i
]), "%s", size
);
101 def_sizesput(sign
, sizes
);
104 static void eqn_bracketpieces(void)
106 char sign
[BRLEN
], top
[BRLEN
], mid
[BRLEN
], bot
[BRLEN
], cen
[BRLEN
];
107 snprintf(sign
, sizeof(sign
), "%s", tok_quotes(tok_poptext(1)));
108 snprintf(top
, sizeof(top
), "%s", tok_quotes(tok_poptext(1)));
109 snprintf(mid
, sizeof(mid
), "%s", tok_quotes(tok_poptext(1)));
110 snprintf(bot
, sizeof(bot
), "%s", tok_quotes(tok_poptext(1)));
111 snprintf(cen
, sizeof(cen
), "%s", tok_quotes(tok_poptext(1)));
112 def_piecesput(sign
, top
, mid
, bot
, cen
);
115 static int typenum(char *s
)
117 if (!strcmp("ord", s
) || !strcmp("ordinary", s
))
119 if (!strcmp("op", s
) || !strcmp("operator", s
))
121 if (!strcmp("bin", s
) || !strcmp("binary", s
))
123 if (!strcmp("rel", s
) || !strcmp("relation", s
))
125 if (!strcmp("open", s
) || !strcmp("opening", s
))
127 if (!strcmp("close", s
) || !strcmp("closing", s
))
129 if (!strcmp("punct", s
) || !strcmp("punctuation", s
))
131 if (!strcmp("inner", s
) || !strcmp("inner", s
))
136 static void eqn_chartype(void)
138 char gl
[GNLEN
], type
[NMLEN
];
139 snprintf(type
, sizeof(type
), "%s", tok_quotes(tok_poptext(1)));
140 snprintf(gl
, sizeof(gl
), "%s", tok_quotes(tok_poptext(1)));
141 if (typenum(type
) >= 0)
142 def_typeput(gl
, typenum(type
));
145 static void eqn_breakcost(void)
149 snprintf(tok
, sizeof(tok
), "%s", tok_quotes(tok_poptext(1)));
150 cost
= atoi(tok_poptext(1));
151 type
= !strcmp("any", tok
) ? 0 : typenum(tok
);
153 def_brcostput(type
, cost
);
156 static int eqn_commands(void)
160 if (!tok_jmp("delim")) {
164 if (!tok_jmp("define")) {
168 if (!tok_jmp("gfont")) {
169 strcpy(gfont
, tok_quotes(tok_poptext(1)));
172 if (!tok_jmp("grfont")) {
173 strcpy(grfont
, tok_quotes(tok_poptext(1)));
176 if (!tok_jmp("gbfont")) {
177 strcpy(gbfont
, tok_quotes(tok_poptext(1)));
180 if (!tok_jmp("gsize")) {
181 sz
= tok_quotes(tok_poptext(1));
182 if (sz
[0] == '-' || sz
[0] == '+')
183 sprintf(gsize
, "\\n%s%s", escarg(EQNSZ
), sz
);
188 if (!tok_jmp("set")) {
189 strcpy(var
, tok_poptext(1));
190 def_set(var
, atoi(tok_poptext(1)));
193 if (!tok_jmp("bracketsizes")) {
197 if (!tok_jmp("bracketpieces")) {
201 if (!tok_jmp("chartype")) {
205 if (!tok_jmp("breakcost")) {
212 static int eqn_gaps(struct box
*box
, int szreg
)
215 box_puttext(box
, T_GAP
, "\\h'%du*%sp/100u'",
220 box_puttext(box
, T_GAP
, "\\h'%du*%sp/100u'",
224 if (!tok_jmp("\t")) {
225 box_puttext(box
, T_GAP
, "\t");
231 static char *tok_font(int tok
, char *fn
)
235 if (tok
== T_LETTER
|| tok
== T_STRING
)
240 static void tok_expect(char *s
)
243 fprintf(stderr
, "neateqn: expected %s bot got %s\n",
249 static void eqn_pile(struct box
*box
, int sz0
, char *fn0
, int adj
)
251 struct box
*pile
[NPILES
] = {NULL
};
256 rowspace
= atoi(tok_poptext(1));
260 pile
[n
++] = box_alloc(sz0
, 0, box
->style
);
261 } while (!eqn_boxuntil(pile
[n
- 1], sz0
, fn0
, "above"));
263 box_pile(box
, pile
, adj
, rowspace
);
264 for (i
= 0; i
< n
; i
++)
268 static void eqn_matrix(struct box
*box
, int sz0
, char *fn0
)
270 struct box
*cols
[NPILES
][NPILES
] = {{NULL
}};
278 colspace
= atoi(tok_poptext(1));
282 if (!tok_jmp("col") || !tok_jmp("ccol"))
284 else if (!tok_jmp("lcol"))
286 else if (!tok_jmp("rcol"))
292 i
= atoi(tok_poptext(1));
298 cols
[ncols
][nrows
++] = box_alloc(sz0
, 0, box
->style
);
299 } while (!eqn_boxuntil(cols
[ncols
][nrows
- 1],
305 box_matrix(box
, ncols
, cols
, adj
, colspace
, rowspace
);
306 for (i
= 0; i
< ncols
; i
++)
307 for (j
= 0; j
< NPILES
; j
++)
309 box_free(cols
[i
][j
]);
312 static int italic(char *fn
)
314 return (!strcmp("I", fn
) || !strcmp("2", fn
) ||
315 gfont
== fn
|| !strcmp(gfont
, fn
)) ? T_ITALIC
: 0;
318 /* read a box without fractions */
319 static struct box
*eqn_left(int flg
, struct box
*pre
, int sz0
, char *fn0
)
321 struct box
*box
= NULL
;
322 struct box
*sub_sub
= NULL
, *sub_sup
= NULL
;
323 struct box
*sub_from
= NULL
, *sub_to
= NULL
;
324 struct box
*sqrt
, *inner
;
325 char left
[NMLEN
] = "", right
[NMLEN
] = "";
330 int style
= EQN_TSMASK
& flg
;
333 while (!eqn_commands())
335 box
= box_alloc(sz
, pre
? pre
->tcur
: 0, style
);
336 if (!eqn_gaps(box
, sz
)) {
337 while (!eqn_gaps(box
, sz
))
342 if (!tok_jmp("fat")) {
343 } else if (!tok_jmp("roman")) {
345 } else if (!tok_jmp("italic")) {
347 } else if (!tok_jmp("bold")) {
349 } else if (!tok_jmp("font")) {
350 strcpy(fn
, tok_poptext(1));
351 } else if (!tok_jmp("size")) {
352 sz
= box_size(box
, tok_poptext(1));
353 } else if (!tok_jmp("fwd")) {
354 dx
+= atoi(tok_poptext(1));
355 } else if (!tok_jmp("back")) {
356 dx
-= atoi(tok_poptext(1));
357 } else if (!tok_jmp("down")) {
358 dy
+= atoi(tok_poptext(1));
359 } else if (!tok_jmp("up")) {
360 dy
-= atoi(tok_poptext(1));
365 if (!tok_jmp("sqrt")) {
366 sqrt
= eqn_left(TS_MK0(style
), NULL
, sz
, fn
);
367 printf(".ft %s\n", grfont
);
370 } else if (!tok_jmp("pile") || !tok_jmp("cpile")) {
371 eqn_pile(box
, sz
, fn
, 'c');
372 } else if (!tok_jmp("lpile")) {
373 eqn_pile(box
, sz
, fn
, 'l');
374 } else if (!tok_jmp("rpile")) {
375 eqn_pile(box
, sz
, fn
, 'r');
376 } else if (!tok_jmp("matrix")) {
377 eqn_matrix(box
, sz
, fn
);
378 } else if (!tok_jmp("vcenter")) {
379 inner
= eqn_left(flg
, pre
, sz
, fn
);
380 box_vcenter(box
, inner
);
382 } else if (!tok_jmp("{")) {
383 eqn_boxuntil(box
, sz
, fn
, "}");
384 } else if (!tok_jmp("left")) {
385 inner
= box_alloc(sz
, 0, style
);
386 snprintf(left
, sizeof(left
), "%s", tok_quotes(tok_poptext(0)));
387 eqn_boxuntil(inner
, sz
, fn
, "right");
388 snprintf(right
, sizeof(right
), "%s", tok_quotes(tok_poptext(0)));
389 printf(".ft %s\n", grfont
);
390 box_wrap(box
, inner
, left
[0] ? left
: NULL
,
391 right
[0] ? right
: NULL
);
393 } else if (tok_get() && tok_type() != T_KEYWORD
) {
395 box_move(box
, dy
, dx
);
396 box_putf(box
, "\\s%s", escarg(nreg(sz
)));
398 char *cfn
= tok_font(tok_type(), fn
);
399 box_puttext(box
, tok_type() | italic(cfn
), "\\f%s%s",
400 escarg(cfn
), tok_improve(tok_get()));
402 } while (!tok_sep(0));
404 box_move(box
, -dy
, -dx
);
407 if (!tok_jmp("dyad")) {
408 printf(".ft %s\n", grfont
);
409 box_accent(box
, "\\(ab");
410 } else if (!tok_jmp("bar")) {
411 printf(".ft %s\n", grfont
);
413 } else if (!tok_jmp("under")) {
414 printf(".ft %s\n", grfont
);
416 } else if (!tok_jmp("vec")) {
417 printf(".ft %s\n", grfont
);
418 box_accent(box
, "\\s[\\n(.s/2u]\\(->\\s0");
419 } else if (!tok_jmp("tilde")) {
420 printf(".ft %s\n", grfont
);
421 box_accent(box
, "\\s[\\n(.s*3u/4u]\\(ap\\s0");
422 } else if (!tok_jmp("hat")) {
423 printf(".ft %s\n", grfont
);
424 box_accent(box
, "ˆ");
425 } else if (!tok_jmp("dot")) {
426 printf(".ft %s\n", grfont
);
427 box_accent(box
, ".");
428 } else if (!tok_jmp("dotdot")) {
429 printf(".ft %s\n", grfont
);
430 box_accent(box
, "..");
436 if (!tok_jmp("sub")) {
437 sizesub(subsz
, sz0
, ts_sup(style
), style
);
438 sub_sub
= eqn_left(ts_sup(style
) | EQN_SUB
, NULL
, subsz
, fn0
);
440 if ((sub_sub
|| !(flg
& EQN_SUB
)) && !tok_jmp("sup")) {
441 sizesub(subsz
, sz0
, ts_sub(style
), style
);
442 sub_sup
= eqn_left(ts_sub(style
), NULL
, subsz
, fn0
);
444 if (sub_sub
|| sub_sup
)
445 box_sub(box
, sub_sub
, sub_sup
);
446 if (!tok_jmp("from")) {
447 sizesub(subsz
, sz0
, ts_sub(style
), style
);
448 sub_from
= eqn_left(ts_sub(style
) | EQN_FROM
, NULL
, subsz
, fn0
);
450 if ((sub_from
|| !(flg
& EQN_FROM
)) && !tok_jmp("to")) {
451 sizesub(subsz
, sz0
, ts_sup(style
), style
);
452 sub_to
= eqn_left(ts_sup(style
), NULL
, subsz
, fn0
);
454 if (sub_from
|| sub_to
) {
455 inner
= box_alloc(sz0
, 0, style
);
456 box_from(inner
, box
, sub_from
, sub_to
);
473 static struct box
*eqn_box(int flg
, struct box
*pre
, int sz0
, char *fn0
)
476 struct box
*sub_num
= NULL
, *sub_den
= NULL
;
477 int style
= flg
& EQN_TSMASK
;
478 box
= eqn_left(flg
, pre
, sz0
, fn0
);
479 while (!tok_jmp("over")) {
481 sub_den
= eqn_left(TS_MK0(style
), NULL
, sz0
, fn0
);
482 box
= box_alloc(sz0
, pre
? pre
->tcur
: 0, style
);
483 printf(".ft %s\n", grfont
);
484 box_over(box
, sub_num
, sub_den
);
491 static struct box
*eqn_read(int style
)
493 struct box
*box
, *sub
;
494 int szreg
= nregmk();
495 printf(".nr %s %s\n", nregname(szreg
), gsize
);
496 box
= box_alloc(szreg
, 0, style
);
498 if (!tok_jmp("mark")) {
499 eqn_mk
= !eqn_mk
? 1 : eqn_mk
;
500 box_markpos(box
, EQNMK
);
503 if (!tok_jmp("lineup")) {
505 box_markpos(box
, nregname(eqn_lineupreg
));
506 sprintf(eqn_lineup
, "\\h'\\n%su-%su'",
507 escarg(EQNMK
), nreg(eqn_lineupreg
));
510 sub
= eqn_box(style
, box
, szreg
, NULL
);
511 box_merge(box
, sub
, 1);
524 for (i
= 0; def_macros
[i
][0]; i
++)
525 in_define(def_macros
[i
][0], def_macros
[i
][1]);
530 printf(".nr %s \\n(.s\n", EQNSZ
);
531 printf(".nr %s \\n(.f\n", EQNFN
);
532 eqn_lineupreg
= nregmk();
533 box
= eqn_read(tok_inline() ? TS_T
: TS_D
);
534 printf(".nr MK %d\n", eqn_mk
);
535 if (!box_empty(box
)) {
536 sprintf(eqnblk
, "%s%s", eqn_lineup
, box_toreg(box
));
538 printf(".ps \\n%s\n", escarg(EQNSZ
));
539 printf(".ft \\n%s\n", escarg(EQNFN
));
541 printf(".lf %d\n", in_lineget());
542 eqn_lineup
[0] = '\0';
543 nregrm(eqn_lineupreg
);