def: vertically centre int
[neateqn.git] / eqn.c
blob369ff2250d3e1a89500fdd677c59702c1c2d2fdf
1 /*
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.
7 */
8 #include <ctype.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include "eqn.h"
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()))
36 return 1;
37 sub = eqn_box(box->style, sub ? box : NULL, sz0, fn0);
38 box_merge(box, sub);
39 box_free(sub);
41 return 0;
44 /* subscript size */
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);
52 } else {
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';
61 return s + 1;
63 return s ? s : "";
66 static char *tok_improve(char *s)
68 if (s && s[0] == '-' && s[1] == '\0')
69 return "\\(mi";
70 if (s && s[0] == '+' && s[1] == '\0')
71 return "\\(pl";
72 if (s && s[0] == '\'' && s[1] == '\0')
73 return "\\(fm";
74 return tok_quotes(s);
77 static void eqn_bracketsizes(void)
79 char sign[BRLEN];
80 char bufs[NSIZES][BRLEN];
81 char *sizes[NSIZES] = {NULL};
82 int n, i;
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));
87 if (i < NSIZES) {
88 snprintf(bufs[i], sizeof(bufs[i]), "%s", size);
89 sizes[i] = bufs[i];
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))
109 return T_ORD;
110 if (!strcmp("op", s) || !strcmp("operator", s))
111 return T_BIGOP;
112 if (!strcmp("bin", s) || !strcmp("binary", s))
113 return T_BINOP;
114 if (!strcmp("rel", s) || !strcmp("relation", s))
115 return T_RELOP;
116 if (!strcmp("open", s) || !strcmp("opening", s))
117 return T_LEFT;
118 if (!strcmp("close", s) || !strcmp("closing", s))
119 return T_RIGHT;
120 if (!strcmp("punct", s) || !strcmp("punctuation", s))
121 return T_PUNC;
122 if (!strcmp("inner", s) || !strcmp("inner", s))
123 return T_INNER;
124 return T_ORD;
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)
138 char var[LNLEN];
139 char *sz;
140 if (!tok_jmp("delim")) {
141 tok_delim();
142 return 0;
144 if (!tok_jmp("define")) {
145 tok_macro();
146 return 0;
148 if (!tok_jmp("gfont")) {
149 strcpy(gfont, tok_quotes(tok_poptext(1)));
150 return 0;
152 if (!tok_jmp("grfont")) {
153 strcpy(grfont, tok_quotes(tok_poptext(1)));
154 return 0;
156 if (!tok_jmp("gbfont")) {
157 strcpy(gbfont, tok_quotes(tok_poptext(1)));
158 return 0;
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);
164 else
165 strcpy(gsize, sz);
166 return 0;
168 if (!tok_jmp("set")) {
169 strcpy(var, tok_poptext(1));
170 def_set(var, atoi(tok_poptext(1)));
171 return 0;
173 if (!tok_jmp("bracketsizes")) {
174 eqn_bracketsizes();
175 return 0;
177 if (!tok_jmp("bracketpieces")) {
178 eqn_bracketpieces();
179 return 0;
181 if (!tok_jmp("chartype")) {
182 eqn_chartype();
183 return 0;
185 return 1;
188 static int eqn_gaps(struct box *box, int szreg)
190 if (!tok_jmp("~")) {
191 box_puttext(box, T_GAP, "\\h'%du*%sp/100u'",
192 S_S3, nreg(szreg));
193 return 0;
195 if (!tok_jmp("^")) {
196 box_puttext(box, T_GAP, "\\h'%du*%sp/100u'",
197 S_S1, nreg(szreg));
198 return 0;
200 if (!tok_jmp("\t")) {
201 box_puttext(box, T_GAP, "\t");
202 return 0;
204 return 1;
207 static char *tok_font(int tok, char *fn)
209 if (fn && fn[0])
210 return fn;
211 if (tok == T_LETTER || tok == T_STRING)
212 return gfont;
213 return grfont;
216 static void tok_expect(char *s)
218 if (tok_jmp(s)) {
219 fprintf(stderr, "neateqn: expected %s bot got %s\n",
220 s, tok_get());
221 exit(1);
225 static void eqn_pile(struct box *box, int sz0, char *fn0, int adj)
227 struct box *pile[NPILES] = {NULL};
228 int i;
229 int n = 0;
230 int rowspace = 0;
231 if (tok_jmp("{")) {
232 rowspace = atoi(tok_poptext(1));
233 tok_expect("{");
235 do {
236 pile[n++] = box_alloc(sz0, 0, box->style);
237 } while (!eqn_boxuntil(pile[n - 1], sz0, fn0, "above"));
238 tok_expect("}");
239 box_pile(box, pile, adj, rowspace);
240 for (i = 0; i < n; i++)
241 box_free(pile[i]);
244 static void eqn_matrix(struct box *box, int sz0, char *fn0)
246 struct box *cols[NPILES][NPILES] = {{NULL}};
247 int adj[NPILES];
248 int nrows;
249 int ncols = 0;
250 int colspace = 0;
251 int rowspace = 0;
252 int i, j;
253 if (tok_jmp("{")) {
254 colspace = atoi(tok_poptext(1));
255 tok_expect("{");
257 while (1) {
258 if (!tok_jmp("col") || !tok_jmp("ccol"))
259 adj[ncols] = 'c';
260 else if (!tok_jmp("lcol"))
261 adj[ncols] = 'l';
262 else if (!tok_jmp("rcol"))
263 adj[ncols] = 'r';
264 else
265 break;
266 nrows = 0;
267 if (tok_jmp("{")) {
268 i = atoi(tok_poptext(1));
269 if (i > rowspace)
270 rowspace = i;
271 tok_expect("{");
273 do {
274 cols[ncols][nrows++] = box_alloc(sz0, 0, box->style);
275 } while (!eqn_boxuntil(cols[ncols][nrows - 1],
276 sz0, fn0, "above"));
277 tok_expect("}");
278 ncols++;
280 tok_expect("}");
281 box_matrix(box, ncols, cols, adj, colspace, rowspace);
282 for (i = 0; i < ncols; i++)
283 for (j = 0; j < NPILES; j++)
284 if (cols[i][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] = "";
302 char fn[FNLEN] = "";
303 int sz = sz0;
304 int subsz = nregmk();
305 int dx = 0, dy = 0;
306 int style = EQN_TSMASK & flg;
307 if (fn0)
308 strcpy(fn, fn0);
309 while (!eqn_commands())
311 box = box_alloc(sz, pre ? pre->tcur : 0, style);
312 while (!eqn_gaps(box, sz))
314 while (1) {
315 if (!tok_jmp("fat")) {
316 } else if (!tok_jmp("roman")) {
317 strcpy(fn, grfont);
318 } else if (!tok_jmp("italic")) {
319 strcpy(fn, gfont);
320 } else if (!tok_jmp("bold")) {
321 strcpy(fn, gbfont);
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));
334 } else {
335 break;
338 if (!tok_jmp("sqrt")) {
339 sqrt = eqn_left(TS_MK0(style), NULL, sz, fn);
340 printf(".ft %s\n", grfont);
341 box_sqrt(box, sqrt);
342 box_free(sqrt);
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);
354 box_free(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);
365 box_free(inner);
366 } else if (tok_get() && tok_type() != T_KEYWORD) {
367 if (dx || dy)
368 box_move(box, dy, dx);
369 box_putf(box, "\\s%s", escarg(nreg(sz)));
370 do {
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()));
374 tok_pop();
375 } while (!tok_sep(0));
376 if (dx || dy)
377 box_move(box, -dy, -dx);
379 while (tok_get()) {
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);
385 box_bar(box);
386 } else if (!tok_jmp("under")) {
387 printf(".ft %s\n", grfont);
388 box_under(box);
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, "..");
404 } else {
405 break;
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);
429 box_free(box);
430 box = inner;
432 nregrm(subsz);
433 if (sub_sub)
434 box_free(sub_sub);
435 if (sub_sup)
436 box_free(sub_sup);
437 if (sub_from)
438 box_free(sub_from);
439 if (sub_to)
440 box_free(sub_to);
441 return box;
444 /* read a box */
445 static struct box *eqn_box(int flg, struct box *pre, int sz0, char *fn0)
447 struct box *box;
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")) {
452 sub_num = box;
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);
457 box_free(sub_num);
458 box_free(sub_den);
460 return box;
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);
469 while (tok_get()) {
470 if (!tok_jmp("mark")) {
471 eqn_mk = !eqn_mk ? 1 : eqn_mk;
472 box_markpos(box, EQNMK);
473 continue;
475 if (!tok_jmp("lineup")) {
476 eqn_mk = 2;
477 box_markpos(box, nregname(eqn_lineupreg));
478 sprintf(eqn_lineup, "\\h'\\n%su-%su'",
479 escarg(EQNMK), nreg(eqn_lineupreg));
480 continue;
482 sub = eqn_box(style | EQN_TOP, box, szreg, NULL);
483 box_merge(box, sub);
484 box_free(sub);
486 box_vertspace(box);
487 nregrm(szreg);
488 return box;
491 int main(void)
493 struct box *box;
494 char eqnblk[128];
495 int i;
496 for (i = 0; def_macros[i][0]; i++)
497 in_define(def_macros[i][0], def_macros[i][1]);
498 while (!tok_eqn()) {
499 reg_reset();
500 eqn_mk = 0;
501 tok_pop();
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));
509 tok_eqnout(eqnblk);
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);
516 box_free(box);
518 in_done();
519 return 0;