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
))
136 /* read chartype command arguments and perform it */
137 static void eqn_chartype(void)
139 char gl
[GNLEN
], type
[NMLEN
];
140 snprintf(type
, sizeof(type
), "%s", tok_quotes(tok_poptext(1)));
141 snprintf(gl
, sizeof(gl
), "%s", tok_quotes(tok_poptext(1)));
142 if (typenum(type
) >= 0)
143 def_typeput(gl
, typenum(type
));
146 /* read breakcost command arguments and perform it */
147 static void eqn_breakcost(void)
151 snprintf(tok
, sizeof(tok
), "%s", tok_quotes(tok_poptext(1)));
152 cost
= atoi(tok_poptext(1));
153 type
= !strcmp("any", tok
) ? 0 : typenum(tok
);
155 def_brcostput(type
, cost
);
158 /* read general eqn commands */
159 static int eqn_commands(void)
163 if (!tok_jmp("delim")) {
167 if (!tok_jmp("define")) {
171 if (!tok_jmp("gfont")) {
172 strcpy(gfont
, tok_quotes(tok_poptext(1)));
175 if (!tok_jmp("grfont")) {
176 strcpy(grfont
, tok_quotes(tok_poptext(1)));
179 if (!tok_jmp("gbfont")) {
180 strcpy(gbfont
, tok_quotes(tok_poptext(1)));
183 if (!tok_jmp("gsize")) {
184 sz
= tok_quotes(tok_poptext(1));
185 if (sz
[0] == '-' || sz
[0] == '+')
186 sprintf(gsize
, "\\n%s%s", escarg(EQNSZ
), sz
);
191 if (!tok_jmp("set")) {
192 strcpy(var
, tok_poptext(1));
193 def_set(var
, atoi(tok_poptext(1)));
196 if (!tok_jmp("bracketsizes")) {
200 if (!tok_jmp("bracketpieces")) {
204 if (!tok_jmp("chartype")) {
208 if (!tok_jmp("breakcost")) {
215 /* read user-specified spaces */
216 static int eqn_gaps(struct box
*box
, int szreg
)
219 box_puttext(box
, T_GAP
, "\\h'%du*%sp/100u'",
224 box_puttext(box
, T_GAP
, "\\h'%du*%sp/100u'",
228 if (!tok_jmp("\t")) {
229 box_puttext(box
, T_GAP
, "\t");
235 /* return the font of the given token type */
236 static char *tok_font(int tok
, char *fn
)
240 if (tok
== T_LETTER
|| tok
== T_STRING
)
245 /* check the next token */
246 static void tok_expect(char *s
)
249 fprintf(stderr
, "neateqn: expected %s bot got %s\n",
255 /* read pile command */
256 static void eqn_pile(struct box
*box
, int sz0
, char *fn0
, int adj
)
258 struct box
*pile
[NPILES
] = {NULL
};
263 rowspace
= atoi(tok_poptext(1));
268 errdie("neateqn: unterminated pile\n");
269 pile
[n
++] = box_alloc(sz0
, 0, box
->style
);
270 } while (!eqn_boxuntil(pile
[n
- 1], sz0
, fn0
, "above"));
272 box_pile(box
, pile
, adj
, rowspace
);
273 for (i
= 0; i
< n
; i
++)
277 /* read matrix command */
278 static void eqn_matrix(struct box
*box
, int sz0
, char *fn0
)
280 struct box
*cols
[NPILES
][NPILES
] = {{NULL
}};
288 colspace
= atoi(tok_poptext(1));
292 if (!tok_jmp("col") || !tok_jmp("ccol"))
294 else if (!tok_jmp("lcol"))
296 else if (!tok_jmp("rcol"))
302 i
= atoi(tok_poptext(1));
309 errdie("neateqn: unterminated matrix col\n");
310 cols
[ncols
][nrows
++] = box_alloc(sz0
, 0, box
->style
);
311 } while (!eqn_boxuntil(cols
[ncols
][nrows
- 1],
317 box_matrix(box
, ncols
, cols
, adj
, colspace
, rowspace
);
318 for (i
= 0; i
< ncols
; i
++)
319 for (j
= 0; j
< NPILES
; j
++)
321 box_free(cols
[i
][j
]);
324 /* return nonzero if fn is italic */
325 static int italic(char *fn
)
327 return (!strcmp("I", fn
) || !strcmp("2", fn
) ||
328 gfont
== fn
|| !strcmp(gfont
, fn
)) ? T_ITALIC
: 0;
331 /* read a box without fractions */
332 static struct box
*eqn_left(int flg
, struct box
*pre
, int sz0
, char *fn0
)
334 struct box
*box
= NULL
;
335 struct box
*sub_sub
= NULL
, *sub_sup
= NULL
;
336 struct box
*sub_from
= NULL
, *sub_to
= NULL
;
337 struct box
*sqrt
, *inner
;
338 char left
[NMLEN
] = "", right
[NMLEN
] = "";
343 int style
= EQN_TSMASK
& flg
;
346 while (!eqn_commands())
348 box
= box_alloc(sz
, pre
? pre
->tcur
: 0, style
);
349 if (!eqn_gaps(box
, sz
)) {
350 while (!eqn_gaps(box
, sz
))
355 if (!tok_jmp("fat")) {
356 } else if (!tok_jmp("roman")) {
358 } else if (!tok_jmp("italic")) {
360 } else if (!tok_jmp("bold")) {
362 } else if (!tok_jmp("font")) {
363 strcpy(fn
, tok_poptext(1));
364 } else if (!tok_jmp("size")) {
365 sz
= box_size(box
, tok_poptext(1));
366 } else if (!tok_jmp("fwd")) {
367 dx
+= atoi(tok_poptext(1));
368 } else if (!tok_jmp("back")) {
369 dx
-= atoi(tok_poptext(1));
370 } else if (!tok_jmp("down")) {
371 dy
+= atoi(tok_poptext(1));
372 } else if (!tok_jmp("up")) {
373 dy
-= atoi(tok_poptext(1));
378 if (!tok_jmp("sqrt")) {
379 sqrt
= eqn_left(TS_MK0(style
), NULL
, sz
, fn
);
380 printf(".ft %s\n", grfont
);
383 } else if (!tok_jmp("pile") || !tok_jmp("cpile")) {
384 eqn_pile(box
, sz
, fn
, 'c');
385 } else if (!tok_jmp("lpile")) {
386 eqn_pile(box
, sz
, fn
, 'l');
387 } else if (!tok_jmp("rpile")) {
388 eqn_pile(box
, sz
, fn
, 'r');
389 } else if (!tok_jmp("matrix")) {
390 eqn_matrix(box
, sz
, fn
);
391 } else if (!tok_jmp("vcenter")) {
392 inner
= eqn_left(flg
, pre
, sz
, fn
);
393 box_vcenter(box
, inner
);
395 } else if (!tok_jmp("{")) {
396 eqn_boxuntil(box
, sz
, fn
, "}");
397 } else if (!tok_jmp("left")) {
398 inner
= box_alloc(sz
, 0, style
);
399 snprintf(left
, sizeof(left
), "%s", tok_quotes(tok_poptext(0)));
400 eqn_boxuntil(inner
, sz
, fn
, "right");
401 snprintf(right
, sizeof(right
), "%s", tok_quotes(tok_poptext(0)));
402 printf(".ft %s\n", grfont
);
403 box_wrap(box
, inner
, left
[0] ? left
: NULL
,
404 right
[0] ? right
: NULL
);
406 } else if (tok_get() && tok_type() != T_KEYWORD
) {
408 box_move(box
, dy
, dx
);
409 box_putf(box
, "\\s%s", escarg(nreg(sz
)));
411 char *cfn
= tok_font(tok_type(), fn
);
413 box_puttext(box
, tok_type() | italic(cfn
), "\\f%s%s",
414 escarg(cfn
), tok_improve(tok_get()));
415 chops
= tok_chops(0);
417 if (chops
) /* what we read was a splitting */
419 } while (!tok_chops(0)); /* the next token is splitting */
421 box_move(box
, -dy
, -dx
);
424 if (!tok_jmp("dyad")) {
425 printf(".ft %s\n", grfont
);
426 box_accent(box
, "\\(ab");
427 } else if (!tok_jmp("bar")) {
428 printf(".ft %s\n", grfont
);
430 } else if (!tok_jmp("under")) {
431 printf(".ft %s\n", grfont
);
433 } else if (!tok_jmp("vec")) {
434 printf(".ft %s\n", grfont
);
435 box_accent(box
, "\\s[\\n(.s/2u]\\(->\\s0");
436 } else if (!tok_jmp("tilde")) {
437 printf(".ft %s\n", grfont
);
438 box_accent(box
, "\\s[\\n(.s*3u/4u]\\(ap\\s0");
439 } else if (!tok_jmp("hat")) {
440 printf(".ft %s\n", grfont
);
441 box_accent(box
, "ˆ");
442 } else if (!tok_jmp("dot")) {
443 printf(".ft %s\n", grfont
);
444 box_accent(box
, ".");
445 } else if (!tok_jmp("dotdot")) {
446 printf(".ft %s\n", grfont
);
447 box_accent(box
, "..");
453 if (!tok_jmp("sub")) {
454 sizesub(subsz
, sz0
, ts_sup(style
), style
);
455 sub_sub
= eqn_left(ts_sup(style
) | EQN_SUB
, NULL
, subsz
, fn0
);
457 if ((sub_sub
|| !(flg
& EQN_SUB
)) && !tok_jmp("sup")) {
458 sizesub(subsz
, sz0
, ts_sub(style
), style
);
459 sub_sup
= eqn_left(ts_sub(style
), NULL
, subsz
, fn0
);
461 if (sub_sub
|| sub_sup
)
462 box_sub(box
, sub_sub
, sub_sup
);
463 if (!tok_jmp("from")) {
464 sizesub(subsz
, sz0
, ts_sub(style
), style
);
465 sub_from
= eqn_left(ts_sub(style
) | EQN_FROM
, NULL
, subsz
, fn0
);
467 if ((sub_from
|| !(flg
& EQN_FROM
)) && !tok_jmp("to")) {
468 sizesub(subsz
, sz0
, ts_sup(style
), style
);
469 sub_to
= eqn_left(ts_sup(style
), NULL
, subsz
, fn0
);
471 if (sub_from
|| sub_to
) {
472 inner
= box_alloc(sz0
, 0, style
);
473 box_from(inner
, box
, sub_from
, sub_to
);
490 static struct box
*eqn_box(int flg
, struct box
*pre
, int sz0
, char *fn0
)
493 struct box
*sub_num
= NULL
, *sub_den
= NULL
;
494 int style
= flg
& EQN_TSMASK
;
495 box
= eqn_left(flg
, pre
, sz0
, fn0
);
496 while (!tok_jmp("over")) {
498 sub_den
= eqn_left(TS_MK0(style
), NULL
, sz0
, fn0
);
499 box
= box_alloc(sz0
, pre
? pre
->tcur
: 0, style
);
500 printf(".ft %s\n", grfont
);
501 box_over(box
, sub_num
, sub_den
);
508 /* read an equation, either inline or block */
509 static struct box
*eqn_read(int style
)
511 struct box
*box
, *sub
;
512 int szreg
= nregmk();
513 printf(".nr %s %s\n", nregname(szreg
), gsize
);
514 box
= box_alloc(szreg
, 0, style
);
516 if (!tok_jmp("mark")) {
517 eqn_mk
= !eqn_mk
? 1 : eqn_mk
;
518 box_markpos(box
, EQNMK
);
521 if (!tok_jmp("lineup")) {
523 box_markpos(box
, nregname(eqn_lineupreg
));
524 sprintf(eqn_lineup
, "\\h'\\n%su-%su'",
525 escarg(EQNMK
), nreg(eqn_lineupreg
));
528 sub
= eqn_box(style
, box
, szreg
, NULL
);
529 box_merge(box
, sub
, 1);
537 void errdie(char *msg
)
539 fprintf(stderr
, "%s", msg
);
543 int main(int argc
, char **argv
)
548 for (i
= 1; i
< argc
; i
++) {
549 if (argv
[i
][0] != '-' || !argv
[i
][1])
551 if (argv
[i
][1] == 'c') {
552 def_choppedset(argv
[i
][2] ? argv
[i
] + 2 : argv
[++i
]);
554 fprintf(stderr
, "Usage: neateqn [options] <input >output\n\n"
556 " -c chars \tcharacters that chop equations\n");
560 for (i
= 0; def_macros
[i
][0]; i
++)
561 src_define(def_macros
[i
][0], def_macros
[i
][1]);
566 printf(".nr %s \\n(.s\n", EQNSZ
);
567 printf(".nr %s \\n(.f\n", EQNFN
);
568 eqn_lineupreg
= nregmk();
569 box
= eqn_read(tok_inline() ? TS_T
: TS_D
);
570 printf(".nr MK %d\n", eqn_mk
);
571 if (!box_empty(box
)) {
572 sprintf(eqnblk
, "%s%s", eqn_lineup
, box_toreg(box
));
574 printf(".ps \\n%s\n", escarg(EQNSZ
));
575 printf(".ft \\n%s\n", escarg(EQNFN
));
577 printf(".lf %d\n", src_lineget());
578 eqn_lineup
[0] = '\0';
579 nregrm(eqn_lineupreg
);