box: fix atom types only when merging a single atom
[neateqn.git] / eqn.c
blobc233208bac4b1711c87174f0a7ebe1b2211255ee
1 /*
2 * neateqn preprocessor of neatroff
4 * Copyright (C) 2014 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 #define FN2(fn) ((!(fn)[1] && ((fn)[0] == 'I' || (fn)[0] == '2')) ? T_ITALIC : 0)
16 /* flags passed to eqn_box() */
17 #define EQN_TSMASK 0x00ffff /* style mask */
18 #define EQN_TOP 0x010000 /* top-level boxes */
19 #define EQN_SUB 0x020000 /* this is a subscript */
20 #define EQN_FROM 0x040000 /* this is a from block */
22 static char gfont[FNLEN] = "2";
23 static char grfont[FNLEN] = "1";
24 static char gbfont[FNLEN] = "3";
25 static char gsize[FNLEN] = "\\n[" EQNSZ "]";
26 static char eqn_lineup[128]; /* the lineup horizontal request */
27 static int eqn_lineupreg; /* the number register holding lineup width */
28 static int eqn_mk; /* the value of MK */
30 static struct box *eqn_box(int flg, struct box *pre, int sz0, char *fn0);
32 /* read equations until delim is read */
33 static int eqn_boxuntil(struct box *box, int sz0, char *fn0, char *delim)
35 struct box *sub = NULL;
36 while (tok_get() && tok_jmp(delim)) {
37 if (!strcmp("}", tok_get()))
38 return 1;
39 sub = eqn_box(box->style, sub ? box : NULL, sz0, fn0);
40 box_merge(box, sub);
41 box_free(sub);
43 return 0;
46 /* update sz0 by sz1 and write it into sz */
47 static void sizeupdate(int dst, int src, char *new)
49 if (new[0] == '-' || new[0] == '+')
50 printf(".nr %s %s%s\n", nregname(dst), nreg(src), new);
51 else
52 printf(".nr %s %s\n", nregname(dst), new);
55 /* subscript size */
56 static void sizesub(int dst, int src, int style, int src_style)
58 if (TS_SZ(style) > TS_SZ(src_style)) {
59 printf(".nr %s %s*7/10\n", nregname(dst), nreg(src));
60 printf(".if %s<%d .nr %s %d\n",
61 nreg(dst), e_minimumsize,
62 nregname(dst), e_minimumsize);
63 } else {
64 printf(".nr %s %s\n", nregname(dst), nreg(src));
68 static char *tok_removequotes(char *s)
70 if (s[0] == '"') {
71 s[strlen(s) - 1] = '\0';
72 return s + 1;
74 return s;
77 static char *tok_improve(char *s)
79 if (s[0] == '-' && s[1] == '\0')
80 return "\\(mi";
81 if (s[0] == '+' && s[1] == '\0')
82 return "\\(pl";
83 return tok_removequotes(s);
86 static int eqn_commands(struct box *box, int szreg)
88 char var[LNLEN];
89 char *sz;
90 if (!tok_jmp("delim")) {
91 tok_delim();
92 return 0;
94 if (!tok_jmp("define")) {
95 tok_macro();
96 return 0;
98 if (!tok_jmp("~")) {
99 box_puttext(box, T_GAP, "\\h'%du*%sp/100u'",
100 S_S3, nreg(szreg));
101 return 0;
103 if (!tok_jmp("^")) {
104 box_puttext(box, T_GAP, "\\h'%du*%sp/100u'",
105 S_S1, nreg(szreg));
106 return 0;
108 if (!tok_jmp("\t")) {
109 box_puttext(box, T_GAP, "\t");
110 return 0;
112 if (!tok_jmp("gfont")) {
113 strcpy(gfont, tok_removequotes(tok_poptext()));
114 return 0;
116 if (!tok_jmp("grfont")) {
117 strcpy(grfont, tok_removequotes(tok_poptext()));
118 return 0;
120 if (!tok_jmp("gbfont")) {
121 strcpy(gbfont, tok_removequotes(tok_poptext()));
122 return 0;
124 if (!tok_jmp("gsize")) {
125 sz = tok_removequotes(tok_poptext());
126 if (sz[0] == '-' || sz[0] == '+')
127 sprintf(gsize, "\\n%s%s", escarg(EQNSZ), sz);
128 else
129 strcpy(gsize, sz);
130 return 0;
132 if (!tok_jmp("set")) {
133 strcpy(var, tok_poptext());
134 def_set(var, atoi(tok_poptext()));
135 return 0;
137 return 1;
140 static char *tok_font(int tok, char *fn)
142 if (fn && fn[0])
143 return fn;
144 if (tok == T_LETTER || tok == T_STRING)
145 return gfont;
146 return grfont;
149 static void tok_expect(char *s)
151 if (tok_jmp(s)) {
152 fprintf(stderr, "neateqn: expected %s bot got %s\n",
153 s, tok_get());
154 exit(1);
158 static void eqn_pile(struct box *box, int sz0, char *fn0, int adj)
160 struct box *pile[NPILES] = {NULL};
161 int i;
162 int n = 0;
163 tok_expect("{");
164 do {
165 pile[n++] = box_alloc(sz0, 0, box->style);
166 } while (!eqn_boxuntil(pile[n - 1], sz0, fn0, "above"));
167 tok_expect("}");
168 box_pile(box, pile, adj);
169 for (i = 0; i < n; i++)
170 box_free(pile[i]);
173 static void eqn_matrix(struct box *box, int sz0, char *fn0)
175 struct box *cols[NPILES][NPILES] = {{NULL}};
176 int adj[NPILES];
177 int nrows;
178 int ncols = 0;
179 int i, j;
180 tok_expect("{");
181 while (1) {
182 if (!tok_jmp("col") || !tok_jmp("ccol"))
183 adj[ncols] = 'c';
184 else if (!tok_jmp("lcol"))
185 adj[ncols] = 'l';
186 else if (!tok_jmp("rcol"))
187 adj[ncols] = 'r';
188 else
189 break;
190 nrows = 0;
191 tok_expect("{");
192 do {
193 cols[ncols][nrows++] = box_alloc(sz0, 0, box->style);
194 } while (!eqn_boxuntil(cols[ncols][nrows - 1],
195 sz0, fn0, "above"));
196 tok_expect("}");
197 ncols++;
199 tok_expect("}");
200 box_matrix(box, ncols, cols, adj);
201 for (i = 0; i < ncols; i++)
202 for (j = 0; j < NPILES; j++)
203 if (cols[i][j])
204 box_free(cols[i][j]);
207 /* read a box without fractions */
208 static struct box *eqn_left(int flg, struct box *pre, int sz0, char *fn0)
210 struct box *box, *sqrt, *inner;
211 struct box *sub_sub = NULL, *sub_sup = NULL;
212 struct box *sub_from = NULL, *sub_to = NULL;
213 char left[NMLEN] = "", right[NMLEN] = "";
214 char fn[FNLEN] = "";
215 int sz = sz0;
216 int newsz = nregmk();
217 int subsz = nregmk();
218 int dx = 0, dy = 0;
219 int style = EQN_TSMASK & flg;
220 if (fn0)
221 strcpy(fn, fn0);
222 box = box_alloc(sz0, pre ? pre->tcur : 0, style);
223 while (!eqn_commands(box, sz))
225 while (1) {
226 if (!tok_jmp("fat")) {
227 } else if (!tok_jmp("roman")) {
228 strcpy(fn, grfont);
229 } else if (!tok_jmp("italic")) {
230 strcpy(fn, gfont);
231 } else if (!tok_jmp("bold")) {
232 strcpy(fn, gbfont);
233 } else if (!tok_jmp("font")) {
234 strcpy(fn, tok_poptext());
235 } else if (!tok_jmp("size")) {
236 sizeupdate(newsz, sz, tok_poptext());
237 sz = newsz;
238 } else if (!tok_jmp("fwd")) {
239 dx += atoi(tok_poptext());
240 } else if (!tok_jmp("back")) {
241 dx -= atoi(tok_poptext());
242 } else if (!tok_jmp("down")) {
243 dy += atoi(tok_poptext());
244 } else if (!tok_jmp("up")) {
245 dy -= atoi(tok_poptext());
246 } else {
247 break;
250 if (!tok_get())
251 return box;
252 if (!tok_jmp("sqrt")) {
253 sqrt = eqn_left(style, NULL, sz, fn);
254 box_sqrt(box, sqrt);
255 box_free(sqrt);
256 } else if (!tok_jmp("pile") || !tok_jmp("cpile")) {
257 eqn_pile(box, sz, fn, 'c');
258 } else if (!tok_jmp("lpile")) {
259 eqn_pile(box, sz, fn, 'l');
260 } else if (!tok_jmp("rpile")) {
261 eqn_pile(box, sz, fn, 'r');
262 } else if (!tok_jmp("matrix")) {
263 eqn_matrix(box, sz, fn);
264 } else if (!tok_jmp("vcenter")) {
265 inner = eqn_left(flg, pre, sz, fn);
266 box_vcenter(box, inner);
267 box_free(inner);
268 } else if (!tok_jmp("{")) {
269 eqn_boxuntil(box, sz, fn, "}");
270 } else if (!tok_jmp("left")) {
271 inner = box_alloc(sz, 0, style);
272 snprintf(left, sizeof(left), "%s", tok_improve(tok_poptext()));
273 eqn_boxuntil(inner, sz, fn, "right");
274 snprintf(right, sizeof(right), "%s", tok_improve(tok_poptext()));
275 printf(".ft %s\n", grfont);
276 box_wrap(box, inner, left[0] ? left : NULL,
277 right[0] ? right : NULL);
278 } else {
279 if (dx || dy)
280 box_move(box, dy, dx);
281 box_putf(box, "\\s%s", escarg(nreg(sz)));
282 do {
283 char *cfn = tok_font(tok_type(), fn);
284 box_puttext(box, tok_type() | FN2(cfn), "\\f%s%s",
285 escarg(cfn), tok_improve(tok_get()));
286 tok_pop();
287 } while (!tok_sep());
288 if (dx || dy)
289 box_move(box, -dy, -dx);
291 while (tok_get()) {
292 if (!tok_jmp("dyad")) {
293 printf(".ft %s\n", grfont);
294 box_accent(box, "\\(ab");
295 } else if (!tok_jmp("bar")) {
296 printf(".ft %s\n", grfont);
297 box_bar(box);
298 } else if (!tok_jmp("under")) {
299 printf(".ft %s\n", grfont);
300 box_under(box);
301 } else if (!tok_jmp("vec")) {
302 printf(".ft %s\n", grfont);
303 box_accent(box, "\\s[\\n(.s/2]\\(->\\s0");
304 } else if (!tok_jmp("tilde")) {
305 printf(".ft %s\n", grfont);
306 box_accent(box, "\\s[\\n(.s*3/4]\\(ap\\s0");
307 } else if (!tok_jmp("hat")) {
308 printf(".ft %s\n", grfont);
309 box_accent(box, "ˆ");
310 } else if (!tok_jmp("dot")) {
311 printf(".ft %s\n", grfont);
312 box_accent(box, ".");
313 } else if (!tok_jmp("dotdot")) {
314 printf(".ft %s\n", grfont);
315 box_accent(box, "..");
316 } else {
317 break;
320 if (!tok_jmp("sub")) {
321 sizesub(subsz, sz0, ts_sup(style), style);
322 sub_sub = eqn_left(ts_sup(style) | EQN_SUB, NULL, subsz, fn0);
324 if ((sub_sub || !(flg & EQN_SUB)) && !tok_jmp("sup")) {
325 sizesub(subsz, sz0, ts_sub(style), style);
326 sub_sup = eqn_left(ts_sub(style), NULL, subsz, fn0);
328 if (sub_sub || sub_sup)
329 box_sub(box, sub_sub, sub_sup);
330 if (!tok_jmp("from")) {
331 sizesub(subsz, sz0, ts_sub(style), style);
332 sub_from = eqn_left(ts_sub(style) | EQN_FROM, NULL, subsz, fn0);
334 if ((sub_from || !(flg & EQN_FROM)) && !tok_jmp("to")) {
335 sizesub(subsz, sz0, ts_sup(style), style);
336 sub_to = eqn_left(ts_sup(style), NULL, subsz, fn0);
338 if (sub_from || sub_to) {
339 inner = box_alloc(sz0, 0, style);
340 box_from(inner, box, sub_from, sub_to);
341 box_free(box);
342 box = inner;
344 nregrm(subsz);
345 nregrm(newsz);
346 if (sub_sub)
347 box_free(sub_sub);
348 if (sub_sup)
349 box_free(sub_sup);
350 if (sub_from)
351 box_free(sub_from);
352 if (sub_to)
353 box_free(sub_to);
354 return box;
357 /* read a box */
358 static struct box *eqn_box(int flg, struct box *pre, int sz0, char *fn0)
360 struct box *box;
361 struct box *sub_num = NULL, *sub_den = NULL;
362 int style = flg & EQN_TSMASK;
363 box = eqn_left(flg, pre, sz0, fn0);
364 while (!tok_jmp("over")) {
365 sub_num = box;
366 sub_den = eqn_left(TS_MK(TS_SZ(style), 1), NULL, sz0, fn0);
367 box = box_alloc(sz0, pre ? pre->tcur : 0, style);
368 printf(".ft %s\n", grfont);
369 box_over(box, sub_num, sub_den);
370 box_free(sub_num);
371 box_free(sub_den);
373 return box;
376 static struct box *eqn_read(int style)
378 struct box *box, *sub;
379 int szreg = nregmk();
380 printf(".nr %s %s\n", nregname(szreg), gsize);
381 box = box_alloc(szreg, 0, style);
382 while (tok_get()) {
383 if (!tok_jmp("mark")) {
384 eqn_mk = !eqn_mk ? 1 : eqn_mk;
385 box_markpos(box, EQNMK);
386 continue;
388 if (!tok_jmp("lineup")) {
389 eqn_mk = 2;
390 box_markpos(box, nregname(eqn_lineupreg));
391 sprintf(eqn_lineup, "\\h'\\n%su-%su'",
392 escarg(EQNMK), nreg(eqn_lineupreg));
393 continue;
395 sub = eqn_box(style | EQN_TOP, box, szreg, NULL);
396 box_merge(box, sub);
397 box_free(sub);
399 box_vertspace(box);
400 nregrm(szreg);
401 return box;
404 int main(void)
406 struct box *box;
407 char eqnblk[128];
408 int i;
409 for (i = 0; def_macros[i][0]; i++)
410 in_define(def_macros[i][0], def_macros[i][1]);
411 while (!tok_eqn()) {
412 reg_reset();
413 eqn_mk = 0;
414 tok_pop();
415 printf(".nr %s \\n(.s\n", EQNSZ);
416 printf(".nr %s \\n(.f\n", EQNFN);
417 eqn_lineupreg = nregmk();
418 box = eqn_read(tok_inline() ? TS_T : TS_D);
419 printf(".nr MK %d\n", eqn_mk);
420 if (!box_empty(box)) {
421 sprintf(eqnblk, "%s%s", eqn_lineup, box_toreg(box));
422 tok_eqnout(eqnblk);
424 eqn_lineup[0] = '\0';
425 nregrm(eqn_lineupreg);
426 box_free(box);
428 return 0;