1 /* the preprocessor and tokenizer */
8 #define T_BIN(c1, c2) (((c1) << 8) | (c2))
9 #define T_SOFTSEP ("^~{}(),\"\n\t =:|.+-*/\\,()[]<>!")
10 #define ESAVE "\\E*[.eqnbeg]\\R'" EQNFN "0 \\En(.f'\\R'" EQNSZ "0 \\En(.s'"
11 #define ELOAD "\\f[\\En[" EQNFN "0]]\\s[\\En[" EQNSZ "0]]\\E*[.eqnend]"
13 static char *kwds
[] = {
14 "fwd", "down", "back", "up",
15 "bold", "italic", "roman", "font", "fat", "size",
16 "bar", "dot", "dotdot", "dyad", "hat", "under", "vec", "tilde",
17 "sub", "sup", "from", "to", "vcenter",
18 "left", "right", "over", "sqrt",
19 "pile", "lpile", "cpile", "rpile", "above",
20 "matrix", "col", "ccol", "lcol", "rcol",
22 "gfont", "grfont", "gbfont", "gsize", "set", "chartype",
23 "mark", "lineup", "bracketsizes", "bracketpieces", "breakcost",
26 static int tok_eqen
; /* non-zero if inside .EQ/.EN */
27 static int tok_line
; /* inside inline eqn block */
28 static int tok_part
; /* partial line with inline eqn blocks */
29 static char tok
[LNLEN
]; /* current token */
30 static char tok_prev
[LNLEN
]; /* previous token */
31 static int tok_curtype
; /* type of current token */
32 static int tok_cursep
; /* current character is a separator */
33 static int tok_prevsep
; /* previous character was a separator */
34 static int eqn_beg
, eqn_end
; /* inline eqn delimiters */
36 /* return zero if troff request .ab is read */
37 static int tok_req(int a
, int b
)
42 eqln
[i
++] = src_next();
43 if (eqln
[i
- 1] != '.')
45 eqln
[i
++] = src_next();
46 while (eqln
[i
- 1] == ' ' && i
< sizeof(eqln
) - 4)
47 eqln
[i
++] = src_next();
50 eqln
[i
++] = src_next();
61 static int tok_en(void)
63 return tok_req('E', 'N');
66 /* does the line start with eq */
67 static int tok_eq(char *s
)
71 while (isspace((unsigned char) *s
))
73 return s
[0] == 'E' && s
[1] == 'Q';
76 /* read an lf request */
77 static int tok_lf(char *s
)
81 while (isspace((unsigned char) *s
))
83 if (*s
++ != 'l' || *s
++ != 'f')
85 while (isspace((unsigned char) *s
))
87 if (isdigit((unsigned char) *s
))
92 /* read the next input character */
93 static int tok_next(void)
96 if (!tok_eqen
&& !tok_line
)
99 if (tok_eqen
&& c
== '\n' && tok_en())
101 if (tok_line
&& (src_top() && c
== eqn_end
)) {
108 /* push back the last character read */
109 static void tok_back(int c
)
111 if (tok_eqen
|| tok_line
)
115 static int readchar(char *dst
)
129 while (c
> 0 && c
!= ']') {
140 /* read the next word; if opstop, stop at open parenthesis */
141 static void tok_preview(char *s
, int opstop
)
145 if (c
> 0 && def_chopped(c
)) {
149 while (c
> 0 && (!def_chopped(c
) && (!opstop
|| c
!= '(')) &&
150 (!tok_line
|| (!src_top() || c
!= eqn_end
))) {
152 n
+= readchar(s
+ n
);
160 /* push back the given word */
161 static void tok_unpreview(char *s
)
165 src_back((unsigned char) s
[--n
]);
168 /* read a keyword; return zero on success */
169 static int tok_keyword(void)
173 for (i
= 0; i
< LEN(kwds
); i
++)
174 if (!strcmp(kwds
[i
], tok
))
180 /* read the next argument of a macro call; return zero if read a ',' */
181 static int tok_readarg(struct sbuf
*sbuf
)
184 int pdepth
= 0; /* number of nested parenthesis */
185 int quotes
= 0; /* inside double quotes */
186 while (c
> 0 && (pdepth
|| quotes
|| (c
!= ',' && c
!= ')'))) {
188 if (!quotes
&& c
== ')')
190 if (!quotes
&& c
== '(')
195 sbuf_add(sbuf
, c
= src_next());
196 if (c
== '*' || c
== 'n')
197 sbuf_add(sbuf
, c
= src_next());
199 sbuf_add(sbuf
, c
= src_next());
200 sbuf_add(sbuf
, c
= src_next());
201 } else if (c
== '[') {
202 while (c
> 0 && c
!= ']')
203 sbuf_add(sbuf
, c
= src_next());
208 return c
== ',' ? 0 : 1;
211 /* expand a macro; return zero on success */
212 static int tok_expand(void)
214 char *args
[10] = {NULL
};
215 struct sbuf sbufs
[10];
218 if (src_macro(tok
)) {
221 if (c
== '(') { /* macro arguments follow */
224 sbuf_init(&sbufs
[n
]);
225 if (tok_readarg(&sbufs
[n
++]))
229 for (i
= 0; i
< n
; i
++)
230 args
[i
] = sbuf_buf(&sbufs
[i
]);
231 src_expand(tok
, args
);
232 for (i
= 0; i
< n
; i
++)
233 sbuf_done(&sbufs
[i
]);
240 /* read until .EQ or eqn_beg */
247 while ((c
= src_next()) > 0) {
250 printf(".%s %s \"%s\n",
251 tok_part
? "as" : "ds", EQNS
, sbuf_buf(&ln
));
259 if (c
== '\n' && !tok_part
) {
260 printf("%s", sbuf_buf(&ln
));
261 tok_lf(sbuf_buf(&ln
));
262 if (tok_eq(sbuf_buf(&ln
)) && !tok_en()) {
268 if (c
== '\n' && tok_part
) {
269 printf(".lf %d\n", src_lineget());
270 printf("\\*%s%s", escarg(EQNS
), sbuf_buf(&ln
));
280 /* collect the output of this eqn block */
281 void tok_eqnout(char *s
)
284 printf(".ds %s \"%s%s%s\n", EQNS
, ESAVE
, s
, ELOAD
);
285 printf(".lf %d\n", src_lineget() - 1);
286 printf("\\&\\*%s\n", escarg(EQNS
));
288 printf(".as %s \"%s%s%s\n", EQNS
, ESAVE
, s
, ELOAD
);
292 /* return the length of a utf-8 character based on its first byte */
293 static int utf8len(int c
)
308 /* return the type of a token */
309 static int char_type(char *s
)
311 int c
= (unsigned char) s
[0];
317 if ((t
= def_type(s
)) >= 0)
319 if (c
== '~' || c
== '^')
321 if (ispunct(c
) && (c
!= '\\' || !s
[1]))
326 /* read the next token */
327 static int tok_read(void)
330 char *e
= tok
+ sizeof(tok
) - 2;
337 tok_prevsep
= tok_cursep
;
338 tok_cursep
= def_chopped(c
);
341 if (c
== ' ' || c
== '\n') {
342 while (c
> 0 && (c
== ' ' || c
== '\n'))
347 tok_curtype
= T_SPACE
;
359 if (c2
>= '1' && c2
<= '9' && !src_arg(c2
- '0')) {
366 if (!tok_keyword()) {
367 tok_curtype
= T_KEYWORD
;
377 if (strchr(T_SOFTSEP
, c
)) {
385 } else if (c
== '[') {
386 while (c
&& c
!= ']') {
393 } else if (c
== '"') {
395 while (c
> 0 && c
!= '"') {
409 /* two-character operators */
411 switch (T_BIN(c
, c2
)) {
412 case T_BIN('<', '='):
413 case T_BIN('>', '='):
414 case T_BIN('=', '='):
415 case T_BIN('!', '='):
416 case T_BIN('>', '>'):
417 case T_BIN('<', '<'):
418 case T_BIN(':', '='):
419 case T_BIN('-', '>'):
420 case T_BIN('<', '-'):
421 case T_BIN('-', '+'):
429 tok_curtype
= char_type(tok
);
434 while (--i
> 0 && s
< e
)
437 tok_curtype
= char_type(tok
);
444 return tok
[0] ? tok
: NULL
;
447 /* current token type */
450 return tok
[0] ? tok_curtype
: 0;
453 /* return nonzero if current token chops the equation */
454 int tok_chops(int soft
)
456 if (!tok_get() || tok_curtype
== T_KEYWORD
)
459 return strchr(T_SOFTSEP
, (unsigned char) tok_get()[0]) != NULL
;
460 return def_chopped((unsigned char) tok_get()[0]);
463 /* read the next token, return the previous */
466 strcpy(tok_prev
, tok
);
468 return tok_prev
[0] ? tok_prev
: NULL
;
471 /* like tok_pop() but ignore T_SPACE tokens; if sep, read until chopped */
472 char *tok_poptext(int sep
)
474 while (tok_type() == T_SPACE
)
478 strcat(tok_prev
, tok
);
480 } while (tok
[0] && !tok_chops(!sep
));
481 return tok_prev
[0] ? tok_prev
: NULL
;
485 static void tok_blanks(void)
487 while (tok_type() == T_SPACE
)
491 /* if the next token is s, return zero and skip it */
495 if (tok_get() && !s
[1] && strchr("{}~^\t", s
[0]) && !strcmp(s
, tok_get())) {
499 if (tok_type() != T_KEYWORD
|| !tok_get() || strcmp(s
, tok_get()))
505 /* read delim command */
509 tok_preview(delim
, 0);
510 if (!strcmp("off", delim
)) {
519 /* read macro definition */
520 static void tok_macrodef(struct sbuf
*def
)
525 while (c
> 0 && isspace(c
))
529 while (c
> 0 && c
!= delim
) {
535 /* read the next macro command */
540 tok_preview(name
, 0);
543 src_define(name
, sbuf_buf(&def
));
547 /* return 1 if inside inline equations */