eqn: fix the size of tilde and vec
[neateqn.git] / eqn.c
blob5ebd0ea02315e2fc5b72e30e5c17d083a7bfe589
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_SUB 0x020000 /* this is a subscript */
17 #define EQN_FROM 0x040000 /* this is a from block */
19 static char gfont[FNLEN] = "2";
20 static char grfont[FNLEN] = "1";
21 static char gbfont[FNLEN] = "3";
22 static char gsize[FNLEN] = "\\n[" EQNSZ "]";
23 static char eqn_lineup[128]; /* the lineup horizontal request */
24 static int eqn_lineupreg; /* the number register holding lineup width */
25 static int eqn_mk; /* the value of MK */
27 static struct box *eqn_box(int flg, struct box *pre, int sz0, char *fn0);
29 /* read equations until delim is read */
30 static int eqn_boxuntil(struct box *box, int sz0, char *fn0, char *delim)
32 struct box *sub = NULL;
33 while (tok_get() && tok_jmp(delim)) {
34 if (!strcmp("}", tok_get()))
35 return 1;
36 sub = eqn_box(box->style, sub ? box : NULL, sz0, fn0);
37 box_merge(box, sub, 0);
38 box_free(sub);
40 return 0;
43 /* subscript size */
44 static void sizesub(int dst, int src, int style, int src_style)
46 if (TS_SZ(style) > TS_SZ(src_style)) {
47 printf(".nr %s %s*7/10\n", nregname(dst), nreg(src));
48 printf(".if %s<%d .nr %s %d\n",
49 nreg(dst), e_minimumsize,
50 nregname(dst), e_minimumsize);
51 } else {
52 printf(".nr %s %s\n", nregname(dst), nreg(src));
56 static char *tok_quotes(char *s)
58 if (s && s[0] == '"') {
59 s[strlen(s) - 1] = '\0';
60 return s + 1;
62 return s ? s : "";
65 static char *tok_improve(char *s)
67 if (s && s[0] == '-' && s[1] == '\0')
68 return "\\(mi";
69 if (s && s[0] == '+' && s[1] == '\0')
70 return "\\(pl";
71 if (s && s[0] == '\'' && s[1] == '\0')
72 return "\\(fm";
73 return tok_quotes(s);
76 static void eqn_bracketsizes(void)
78 char sign[BRLEN];
79 char bufs[NSIZES][BRLEN];
80 char *sizes[NSIZES] = {NULL};
81 int n, i;
82 snprintf(sign, sizeof(sign), "%s", tok_quotes(tok_poptext(1)));
83 n = atoi(tok_poptext(1));
84 for (i = 0; i < n; i++) {
85 char *size = tok_quotes(tok_poptext(1));
86 if (i < NSIZES) {
87 snprintf(bufs[i], sizeof(bufs[i]), "%s", size);
88 sizes[i] = bufs[i];
91 def_sizesput(sign, sizes);
94 static void eqn_bracketpieces(void)
96 char sign[BRLEN], top[BRLEN], mid[BRLEN], bot[BRLEN], cen[BRLEN];
97 snprintf(sign, sizeof(sign), "%s", tok_quotes(tok_poptext(1)));
98 snprintf(top, sizeof(top), "%s", tok_quotes(tok_poptext(1)));
99 snprintf(mid, sizeof(mid), "%s", tok_quotes(tok_poptext(1)));
100 snprintf(bot, sizeof(bot), "%s", tok_quotes(tok_poptext(1)));
101 snprintf(cen, sizeof(cen), "%s", tok_quotes(tok_poptext(1)));
102 def_piecesput(sign, top, mid, bot, cen);
105 static int typenum(char *s)
107 if (!strcmp("ord", s) || !strcmp("ordinary", s))
108 return T_ORD;
109 if (!strcmp("op", s) || !strcmp("operator", s))
110 return T_BIGOP;
111 if (!strcmp("bin", s) || !strcmp("binary", s))
112 return T_BINOP;
113 if (!strcmp("rel", s) || !strcmp("relation", s))
114 return T_RELOP;
115 if (!strcmp("open", s) || !strcmp("opening", s))
116 return T_LEFT;
117 if (!strcmp("close", s) || !strcmp("closing", s))
118 return T_RIGHT;
119 if (!strcmp("punct", s) || !strcmp("punctuation", s))
120 return T_PUNC;
121 if (!strcmp("inner", s) || !strcmp("inner", s))
122 return T_INNER;
123 return T_ORD;
126 static void eqn_chartype(void)
128 char gl[GNLEN], type[NMLEN];
129 snprintf(type, sizeof(type), "%s", tok_quotes(tok_poptext(1)));
130 snprintf(gl, sizeof(gl), "%s", tok_quotes(tok_poptext(1)));
131 if (typenum(type) >= 0)
132 def_typeput(gl, typenum(type));
135 static void eqn_breakcost(void)
137 char tok[NMLEN];
138 int cost, type;
139 snprintf(tok, sizeof(tok), "%s", tok_quotes(tok_poptext(1)));
140 cost = atoi(tok_poptext(1));
141 type = !strcmp("any", tok) ? 0 : typenum(tok);
142 if (type >= 0)
143 def_brcostput(type, cost);
146 static int eqn_commands(void)
148 char var[LNLEN];
149 char *sz;
150 if (!tok_jmp("delim")) {
151 tok_delim();
152 return 0;
154 if (!tok_jmp("define")) {
155 tok_macro();
156 return 0;
158 if (!tok_jmp("gfont")) {
159 strcpy(gfont, tok_quotes(tok_poptext(1)));
160 return 0;
162 if (!tok_jmp("grfont")) {
163 strcpy(grfont, tok_quotes(tok_poptext(1)));
164 return 0;
166 if (!tok_jmp("gbfont")) {
167 strcpy(gbfont, tok_quotes(tok_poptext(1)));
168 return 0;
170 if (!tok_jmp("gsize")) {
171 sz = tok_quotes(tok_poptext(1));
172 if (sz[0] == '-' || sz[0] == '+')
173 sprintf(gsize, "\\n%s%s", escarg(EQNSZ), sz);
174 else
175 strcpy(gsize, sz);
176 return 0;
178 if (!tok_jmp("set")) {
179 strcpy(var, tok_poptext(1));
180 def_set(var, atoi(tok_poptext(1)));
181 return 0;
183 if (!tok_jmp("bracketsizes")) {
184 eqn_bracketsizes();
185 return 0;
187 if (!tok_jmp("bracketpieces")) {
188 eqn_bracketpieces();
189 return 0;
191 if (!tok_jmp("chartype")) {
192 eqn_chartype();
193 return 0;
195 if (!tok_jmp("breakcost")) {
196 eqn_breakcost();
197 return 0;
199 return 1;
202 static int eqn_gaps(struct box *box, int szreg)
204 if (!tok_jmp("~")) {
205 box_puttext(box, T_GAP, "\\h'%du*%sp/100u'",
206 S_S3, nreg(szreg));
207 return 0;
209 if (!tok_jmp("^")) {
210 box_puttext(box, T_GAP, "\\h'%du*%sp/100u'",
211 S_S1, nreg(szreg));
212 return 0;
214 if (!tok_jmp("\t")) {
215 box_puttext(box, T_GAP, "\t");
216 return 0;
218 return 1;
221 static char *tok_font(int tok, char *fn)
223 if (fn && fn[0])
224 return fn;
225 if (tok == T_LETTER || tok == T_STRING)
226 return gfont;
227 return grfont;
230 static void tok_expect(char *s)
232 if (tok_jmp(s)) {
233 fprintf(stderr, "neateqn: expected %s bot got %s\n",
234 s, tok_get());
235 exit(1);
239 static void eqn_pile(struct box *box, int sz0, char *fn0, int adj)
241 struct box *pile[NPILES] = {NULL};
242 int i;
243 int n = 0;
244 int rowspace = 0;
245 if (tok_jmp("{")) {
246 rowspace = atoi(tok_poptext(1));
247 tok_expect("{");
249 do {
250 pile[n++] = box_alloc(sz0, 0, box->style);
251 } while (!eqn_boxuntil(pile[n - 1], sz0, fn0, "above"));
252 tok_expect("}");
253 box_pile(box, pile, adj, rowspace);
254 for (i = 0; i < n; i++)
255 box_free(pile[i]);
258 static void eqn_matrix(struct box *box, int sz0, char *fn0)
260 struct box *cols[NPILES][NPILES] = {{NULL}};
261 int adj[NPILES];
262 int nrows;
263 int ncols = 0;
264 int colspace = 0;
265 int rowspace = 0;
266 int i, j;
267 if (tok_jmp("{")) {
268 colspace = atoi(tok_poptext(1));
269 tok_expect("{");
271 while (1) {
272 if (!tok_jmp("col") || !tok_jmp("ccol"))
273 adj[ncols] = 'c';
274 else if (!tok_jmp("lcol"))
275 adj[ncols] = 'l';
276 else if (!tok_jmp("rcol"))
277 adj[ncols] = 'r';
278 else
279 break;
280 nrows = 0;
281 if (tok_jmp("{")) {
282 i = atoi(tok_poptext(1));
283 if (i > rowspace)
284 rowspace = i;
285 tok_expect("{");
287 do {
288 cols[ncols][nrows++] = box_alloc(sz0, 0, box->style);
289 } while (!eqn_boxuntil(cols[ncols][nrows - 1],
290 sz0, fn0, "above"));
291 tok_expect("}");
292 ncols++;
294 tok_expect("}");
295 box_matrix(box, ncols, cols, adj, colspace, rowspace);
296 for (i = 0; i < ncols; i++)
297 for (j = 0; j < NPILES; j++)
298 if (cols[i][j])
299 box_free(cols[i][j]);
302 static int italic(char *fn)
304 return (!strcmp("I", fn) || !strcmp("2", fn) ||
305 gfont == fn || !strcmp(gfont, fn)) ? T_ITALIC : 0;
308 /* read a box without fractions */
309 static struct box *eqn_left(int flg, struct box *pre, int sz0, char *fn0)
311 struct box *box = NULL;
312 struct box *sub_sub = NULL, *sub_sup = NULL;
313 struct box *sub_from = NULL, *sub_to = NULL;
314 struct box *sqrt, *inner;
315 char left[NMLEN] = "", right[NMLEN] = "";
316 char fn[FNLEN] = "";
317 int sz = sz0;
318 int subsz = nregmk();
319 int dx = 0, dy = 0;
320 int style = EQN_TSMASK & flg;
321 if (fn0)
322 strcpy(fn, fn0);
323 while (!eqn_commands())
325 box = box_alloc(sz, pre ? pre->tcur : 0, style);
326 while (!eqn_gaps(box, sz))
328 while (1) {
329 if (!tok_jmp("fat")) {
330 } else if (!tok_jmp("roman")) {
331 strcpy(fn, grfont);
332 } else if (!tok_jmp("italic")) {
333 strcpy(fn, gfont);
334 } else if (!tok_jmp("bold")) {
335 strcpy(fn, gbfont);
336 } else if (!tok_jmp("font")) {
337 strcpy(fn, tok_poptext(1));
338 } else if (!tok_jmp("size")) {
339 sz = box_size(box, tok_poptext(1));
340 } else if (!tok_jmp("fwd")) {
341 dx += atoi(tok_poptext(1));
342 } else if (!tok_jmp("back")) {
343 dx -= atoi(tok_poptext(1));
344 } else if (!tok_jmp("down")) {
345 dy += atoi(tok_poptext(1));
346 } else if (!tok_jmp("up")) {
347 dy -= atoi(tok_poptext(1));
348 } else {
349 break;
352 if (!tok_jmp("sqrt")) {
353 sqrt = eqn_left(TS_MK0(style), NULL, sz, fn);
354 printf(".ft %s\n", grfont);
355 box_sqrt(box, sqrt);
356 box_free(sqrt);
357 } else if (!tok_jmp("pile") || !tok_jmp("cpile")) {
358 eqn_pile(box, sz, fn, 'c');
359 } else if (!tok_jmp("lpile")) {
360 eqn_pile(box, sz, fn, 'l');
361 } else if (!tok_jmp("rpile")) {
362 eqn_pile(box, sz, fn, 'r');
363 } else if (!tok_jmp("matrix")) {
364 eqn_matrix(box, sz, fn);
365 } else if (!tok_jmp("vcenter")) {
366 inner = eqn_left(flg, pre, sz, fn);
367 box_vcenter(box, inner);
368 box_free(inner);
369 } else if (!tok_jmp("{")) {
370 eqn_boxuntil(box, sz, fn, "}");
371 } else if (!tok_jmp("left")) {
372 inner = box_alloc(sz, 0, style);
373 snprintf(left, sizeof(left), "%s", tok_quotes(tok_poptext(0)));
374 eqn_boxuntil(inner, sz, fn, "right");
375 snprintf(right, sizeof(right), "%s", tok_quotes(tok_poptext(0)));
376 printf(".ft %s\n", grfont);
377 box_wrap(box, inner, left[0] ? left : NULL,
378 right[0] ? right : NULL);
379 box_free(inner);
380 } else if (tok_get() && tok_type() != T_KEYWORD) {
381 if (dx || dy)
382 box_move(box, dy, dx);
383 box_putf(box, "\\s%s", escarg(nreg(sz)));
384 do {
385 char *cfn = tok_font(tok_type(), fn);
386 box_puttext(box, tok_type() | italic(cfn), "\\f%s%s",
387 escarg(cfn), tok_improve(tok_get()));
388 tok_pop();
389 } while (!tok_sep(0));
390 if (dx || dy)
391 box_move(box, -dy, -dx);
393 while (tok_get()) {
394 if (!tok_jmp("dyad")) {
395 printf(".ft %s\n", grfont);
396 box_accent(box, "\\(ab");
397 } else if (!tok_jmp("bar")) {
398 printf(".ft %s\n", grfont);
399 box_bar(box);
400 } else if (!tok_jmp("under")) {
401 printf(".ft %s\n", grfont);
402 box_under(box);
403 } else if (!tok_jmp("vec")) {
404 printf(".ft %s\n", grfont);
405 box_accent(box, "\\s[\\n(.s/2u]\\(->\\s0");
406 } else if (!tok_jmp("tilde")) {
407 printf(".ft %s\n", grfont);
408 box_accent(box, "\\s[\\n(.s*3u/4u]\\(ap\\s0");
409 } else if (!tok_jmp("hat")) {
410 printf(".ft %s\n", grfont);
411 box_accent(box, "ˆ");
412 } else if (!tok_jmp("dot")) {
413 printf(".ft %s\n", grfont);
414 box_accent(box, ".");
415 } else if (!tok_jmp("dotdot")) {
416 printf(".ft %s\n", grfont);
417 box_accent(box, "..");
418 } else {
419 break;
422 if (!tok_jmp("sub")) {
423 sizesub(subsz, sz0, ts_sup(style), style);
424 sub_sub = eqn_left(ts_sup(style) | EQN_SUB, NULL, subsz, fn0);
426 if ((sub_sub || !(flg & EQN_SUB)) && !tok_jmp("sup")) {
427 sizesub(subsz, sz0, ts_sub(style), style);
428 sub_sup = eqn_left(ts_sub(style), NULL, subsz, fn0);
430 if (sub_sub || sub_sup)
431 box_sub(box, sub_sub, sub_sup);
432 if (!tok_jmp("from")) {
433 sizesub(subsz, sz0, ts_sub(style), style);
434 sub_from = eqn_left(ts_sub(style) | EQN_FROM, NULL, subsz, fn0);
436 if ((sub_from || !(flg & EQN_FROM)) && !tok_jmp("to")) {
437 sizesub(subsz, sz0, ts_sup(style), style);
438 sub_to = eqn_left(ts_sup(style), NULL, subsz, fn0);
440 if (sub_from || sub_to) {
441 inner = box_alloc(sz0, 0, style);
442 box_from(inner, box, sub_from, sub_to);
443 box_free(box);
444 box = inner;
446 nregrm(subsz);
447 if (sub_sub)
448 box_free(sub_sub);
449 if (sub_sup)
450 box_free(sub_sup);
451 if (sub_from)
452 box_free(sub_from);
453 if (sub_to)
454 box_free(sub_to);
455 return box;
458 /* read a box */
459 static struct box *eqn_box(int flg, struct box *pre, int sz0, char *fn0)
461 struct box *box;
462 struct box *sub_num = NULL, *sub_den = NULL;
463 int style = flg & EQN_TSMASK;
464 box = eqn_left(flg, pre, sz0, fn0);
465 while (!tok_jmp("over")) {
466 sub_num = box;
467 sub_den = eqn_left(TS_MK0(style), NULL, sz0, fn0);
468 box = box_alloc(sz0, pre ? pre->tcur : 0, style);
469 printf(".ft %s\n", grfont);
470 box_over(box, sub_num, sub_den);
471 box_free(sub_num);
472 box_free(sub_den);
474 return box;
477 static struct box *eqn_read(int style)
479 struct box *box, *sub;
480 int szreg = nregmk();
481 printf(".nr %s %s\n", nregname(szreg), gsize);
482 box = box_alloc(szreg, 0, style);
483 while (tok_get()) {
484 if (!tok_jmp("mark")) {
485 eqn_mk = !eqn_mk ? 1 : eqn_mk;
486 box_markpos(box, EQNMK);
487 continue;
489 if (!tok_jmp("lineup")) {
490 eqn_mk = 2;
491 box_markpos(box, nregname(eqn_lineupreg));
492 sprintf(eqn_lineup, "\\h'\\n%su-%su'",
493 escarg(EQNMK), nreg(eqn_lineupreg));
494 continue;
496 sub = eqn_box(style, box, szreg, NULL);
497 box_merge(box, sub, 1);
498 box_free(sub);
500 box_vertspace(box);
501 nregrm(szreg);
502 return box;
505 int main(void)
507 struct box *box;
508 char eqnblk[128];
509 int i;
510 for (i = 0; def_macros[i][0]; i++)
511 in_define(def_macros[i][0], def_macros[i][1]);
512 while (!tok_eqn()) {
513 reg_reset();
514 eqn_mk = 0;
515 tok_pop();
516 printf(".nr %s \\n(.s\n", EQNSZ);
517 printf(".nr %s \\n(.f\n", EQNFN);
518 eqn_lineupreg = nregmk();
519 box = eqn_read(tok_inline() ? TS_T : TS_D);
520 printf(".nr MK %d\n", eqn_mk);
521 if (!box_empty(box)) {
522 sprintf(eqnblk, "%s%s", eqn_lineup, box_toreg(box));
523 tok_eqnout(eqnblk);
524 printf(".ps \\n%s\n", escarg(EQNSZ));
525 printf(".ft \\n%s\n", escarg(EQNFN));
527 printf(".lf %d\n", in_lineget());
528 eqn_lineup[0] = '\0';
529 nregrm(eqn_lineupreg);
530 box_free(box);
532 in_done();
533 return 0;