README: make the instructions for CM fonts more concise
[neateqn.git] / eqn.c
blobe8aff880ba5fb2e2a7cdec9ae38fb611a524cd6a
1 /*
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.
18 #include <ctype.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include "eqn.h"
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()))
45 return 1;
46 sub = eqn_box(box->style, sub ? box : NULL, sz0, fn0);
47 box_merge(box, sub, 0);
48 box_free(sub);
50 return 0;
53 /* subscript size */
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);
61 } else {
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';
70 return s + 1;
72 return s ? s : "";
75 static char *tok_improve(char *s)
77 if (s && s[0] == '-' && s[1] == '\0')
78 return "\\(mi";
79 if (s && s[0] == '+' && s[1] == '\0')
80 return "\\(pl";
81 if (s && s[0] == '\'' && s[1] == '\0')
82 return "\\(fm";
83 return tok_quotes(s);
86 static void eqn_bracketsizes(void)
88 char sign[BRLEN];
89 char bufs[NSIZES][BRLEN];
90 char *sizes[NSIZES] = {NULL};
91 int n, i;
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));
96 if (i < NSIZES) {
97 snprintf(bufs[i], sizeof(bufs[i]), "%s", size);
98 sizes[i] = bufs[i];
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))
118 return T_ORD;
119 if (!strcmp("op", s) || !strcmp("operator", s))
120 return T_BIGOP;
121 if (!strcmp("bin", s) || !strcmp("binary", s))
122 return T_BINOP;
123 if (!strcmp("rel", s) || !strcmp("relation", s))
124 return T_RELOP;
125 if (!strcmp("open", s) || !strcmp("opening", s))
126 return T_LEFT;
127 if (!strcmp("close", s) || !strcmp("closing", s))
128 return T_RIGHT;
129 if (!strcmp("punct", s) || !strcmp("punctuation", s))
130 return T_PUNC;
131 if (!strcmp("inner", s) || !strcmp("inner", s))
132 return T_INNER;
133 return T_ORD;
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)
147 char tok[NMLEN];
148 int cost, type;
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);
152 if (type >= 0)
153 def_brcostput(type, cost);
156 static int eqn_commands(void)
158 char var[LNLEN];
159 char *sz;
160 if (!tok_jmp("delim")) {
161 tok_delim();
162 return 0;
164 if (!tok_jmp("define")) {
165 tok_macro();
166 return 0;
168 if (!tok_jmp("gfont")) {
169 strcpy(gfont, tok_quotes(tok_poptext(1)));
170 return 0;
172 if (!tok_jmp("grfont")) {
173 strcpy(grfont, tok_quotes(tok_poptext(1)));
174 return 0;
176 if (!tok_jmp("gbfont")) {
177 strcpy(gbfont, tok_quotes(tok_poptext(1)));
178 return 0;
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);
184 else
185 strcpy(gsize, sz);
186 return 0;
188 if (!tok_jmp("set")) {
189 strcpy(var, tok_poptext(1));
190 def_set(var, atoi(tok_poptext(1)));
191 return 0;
193 if (!tok_jmp("bracketsizes")) {
194 eqn_bracketsizes();
195 return 0;
197 if (!tok_jmp("bracketpieces")) {
198 eqn_bracketpieces();
199 return 0;
201 if (!tok_jmp("chartype")) {
202 eqn_chartype();
203 return 0;
205 if (!tok_jmp("breakcost")) {
206 eqn_breakcost();
207 return 0;
209 return 1;
212 static int eqn_gaps(struct box *box, int szreg)
214 if (!tok_jmp("~")) {
215 box_puttext(box, T_GAP, "\\h'%du*%sp/100u'",
216 S_S3, nreg(szreg));
217 return 0;
219 if (!tok_jmp("^")) {
220 box_puttext(box, T_GAP, "\\h'%du*%sp/100u'",
221 S_S1, nreg(szreg));
222 return 0;
224 if (!tok_jmp("\t")) {
225 box_puttext(box, T_GAP, "\t");
226 return 0;
228 return 1;
231 static char *tok_font(int tok, char *fn)
233 if (fn && fn[0])
234 return fn;
235 if (tok == T_LETTER || tok == T_STRING)
236 return gfont;
237 return grfont;
240 static void tok_expect(char *s)
242 if (tok_jmp(s)) {
243 fprintf(stderr, "neateqn: expected %s bot got %s\n",
244 s, tok_get());
245 exit(1);
249 static void eqn_pile(struct box *box, int sz0, char *fn0, int adj)
251 struct box *pile[NPILES] = {NULL};
252 int i;
253 int n = 0;
254 int rowspace = 0;
255 if (tok_jmp("{")) {
256 rowspace = atoi(tok_poptext(1));
257 tok_expect("{");
259 do {
260 pile[n++] = box_alloc(sz0, 0, box->style);
261 } while (!eqn_boxuntil(pile[n - 1], sz0, fn0, "above"));
262 tok_expect("}");
263 box_pile(box, pile, adj, rowspace);
264 for (i = 0; i < n; i++)
265 box_free(pile[i]);
268 static void eqn_matrix(struct box *box, int sz0, char *fn0)
270 struct box *cols[NPILES][NPILES] = {{NULL}};
271 int adj[NPILES];
272 int nrows;
273 int ncols = 0;
274 int colspace = 0;
275 int rowspace = 0;
276 int i, j;
277 if (tok_jmp("{")) {
278 colspace = atoi(tok_poptext(1));
279 tok_expect("{");
281 while (1) {
282 if (!tok_jmp("col") || !tok_jmp("ccol"))
283 adj[ncols] = 'c';
284 else if (!tok_jmp("lcol"))
285 adj[ncols] = 'l';
286 else if (!tok_jmp("rcol"))
287 adj[ncols] = 'r';
288 else
289 break;
290 nrows = 0;
291 if (tok_jmp("{")) {
292 i = atoi(tok_poptext(1));
293 if (i > rowspace)
294 rowspace = i;
295 tok_expect("{");
297 do {
298 cols[ncols][nrows++] = box_alloc(sz0, 0, box->style);
299 } while (!eqn_boxuntil(cols[ncols][nrows - 1],
300 sz0, fn0, "above"));
301 tok_expect("}");
302 ncols++;
304 tok_expect("}");
305 box_matrix(box, ncols, cols, adj, colspace, rowspace);
306 for (i = 0; i < ncols; i++)
307 for (j = 0; j < NPILES; j++)
308 if (cols[i][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] = "";
326 char fn[FNLEN] = "";
327 int sz = sz0;
328 int subsz;
329 int dx = 0, dy = 0;
330 int style = EQN_TSMASK & flg;
331 if (fn0)
332 strcpy(fn, fn0);
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))
339 return box;
341 while (1) {
342 if (!tok_jmp("fat")) {
343 } else if (!tok_jmp("roman")) {
344 strcpy(fn, grfont);
345 } else if (!tok_jmp("italic")) {
346 strcpy(fn, gfont);
347 } else if (!tok_jmp("bold")) {
348 strcpy(fn, gbfont);
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));
361 } else {
362 break;
365 if (!tok_jmp("sqrt")) {
366 sqrt = eqn_left(TS_MK0(style), NULL, sz, fn);
367 printf(".ft %s\n", grfont);
368 box_sqrt(box, sqrt);
369 box_free(sqrt);
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);
381 box_free(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);
392 box_free(inner);
393 } else if (tok_get() && tok_type() != T_KEYWORD) {
394 if (dx || dy)
395 box_move(box, dy, dx);
396 box_putf(box, "\\s%s", escarg(nreg(sz)));
397 do {
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()));
401 tok_pop();
402 } while (!tok_sep(0));
403 if (dx || dy)
404 box_move(box, -dy, -dx);
406 while (tok_get()) {
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);
412 box_bar(box);
413 } else if (!tok_jmp("under")) {
414 printf(".ft %s\n", grfont);
415 box_under(box);
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, "..");
431 } else {
432 break;
435 subsz = nregmk();
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);
457 box_free(box);
458 box = inner;
460 nregrm(subsz);
461 if (sub_sub)
462 box_free(sub_sub);
463 if (sub_sup)
464 box_free(sub_sup);
465 if (sub_from)
466 box_free(sub_from);
467 if (sub_to)
468 box_free(sub_to);
469 return box;
472 /* read a box */
473 static struct box *eqn_box(int flg, struct box *pre, int sz0, char *fn0)
475 struct box *box;
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")) {
480 sub_num = box;
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);
485 box_free(sub_num);
486 box_free(sub_den);
488 return box;
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);
497 while (tok_get()) {
498 if (!tok_jmp("mark")) {
499 eqn_mk = !eqn_mk ? 1 : eqn_mk;
500 box_markpos(box, EQNMK);
501 continue;
503 if (!tok_jmp("lineup")) {
504 eqn_mk = 2;
505 box_markpos(box, nregname(eqn_lineupreg));
506 sprintf(eqn_lineup, "\\h'\\n%su-%su'",
507 escarg(EQNMK), nreg(eqn_lineupreg));
508 continue;
510 sub = eqn_box(style, box, szreg, NULL);
511 box_merge(box, sub, 1);
512 box_free(sub);
514 box_vertspace(box);
515 nregrm(szreg);
516 return box;
519 int main(void)
521 struct box *box;
522 char eqnblk[128];
523 int i;
524 for (i = 0; def_macros[i][0]; i++)
525 in_define(def_macros[i][0], def_macros[i][1]);
526 while (!tok_eqn()) {
527 reg_reset();
528 eqn_mk = 0;
529 tok_pop();
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));
537 tok_eqnout(eqnblk);
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);
544 box_free(box);
546 in_done();
547 return 0;