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 /* read the next word */
116 static void tok_preview(char *s
)
120 if (c
> 0 && def_chopped(c
)) {
125 while (c
> 0 && !def_chopped(c
) && (!tok_line
|| (!src_top() || c
!= eqn_end
))) {
133 /* push back the given word */
134 static void tok_unpreview(char *s
)
138 src_back((unsigned char) s
[--n
]);
141 /* read a keyword; return zero on success */
142 static int tok_keyword(void)
146 for (i
= 0; i
< LEN(kwds
); i
++)
147 if (!strcmp(kwds
[i
], tok
))
153 /* read the next argument of a macro call; return zero if read a ',' */
154 static int tok_readarg(struct sbuf
*sbuf
)
157 int pdepth
= 0; /* number of nested parenthesis */
158 int quotes
= 0; /* inside double quotes */
159 while (c
> 0 && (pdepth
|| quotes
|| (c
!= ',' && c
!= ')'))) {
161 if (!quotes
&& c
== ')')
163 if (!quotes
&& c
== '(')
168 sbuf_add(sbuf
, c
= src_next());
169 if (c
== '*' || c
== 'n')
170 sbuf_add(sbuf
, c
= src_next());
172 sbuf_add(sbuf
, c
= src_next());
173 sbuf_add(sbuf
, c
= src_next());
174 } else if (c
== '[') {
175 while (c
> 0 && c
!= ']')
176 sbuf_add(sbuf
, c
= src_next());
181 return c
== ',' ? 0 : 1;
184 /* expand a macro; return zero on success */
185 static int tok_expand(void)
187 char *args
[10] = {NULL
};
188 struct sbuf sbufs
[10];
191 if (src_macro(tok
)) {
194 if (c
== '(') { /* macro arguments follow */
197 sbuf_init(&sbufs
[n
]);
198 if (tok_readarg(&sbufs
[n
++]))
202 for (i
= 0; i
< n
; i
++)
203 args
[i
] = sbuf_buf(&sbufs
[i
]);
204 src_expand(tok
, args
);
205 for (i
= 0; i
< n
; i
++)
206 sbuf_done(&sbufs
[i
]);
213 /* read until .EQ or eqn_beg */
220 while ((c
= src_next()) > 0) {
223 printf(".%s %s \"%s\n",
224 tok_part
? "as" : "ds", EQNS
, sbuf_buf(&ln
));
232 if (c
== '\n' && !tok_part
) {
233 printf("%s", sbuf_buf(&ln
));
234 tok_lf(sbuf_buf(&ln
));
235 if (tok_eq(sbuf_buf(&ln
)) && !tok_en()) {
241 if (c
== '\n' && tok_part
) {
242 printf(".lf %d\n", src_lineget());
243 printf("\\*%s%s", escarg(EQNS
), sbuf_buf(&ln
));
253 /* collect the output of this eqn block */
254 void tok_eqnout(char *s
)
257 printf(".ds %s \"%s%s%s\n", EQNS
, ESAVE
, s
, ELOAD
);
258 printf(".lf %d\n", src_lineget() - 1);
259 printf("\\&\\*%s\n", escarg(EQNS
));
261 printf(".as %s \"%s%s%s\n", EQNS
, ESAVE
, s
, ELOAD
);
265 /* return the length of a utf-8 character based on its first byte */
266 static int utf8len(int c
)
268 if (c
> 0 && c
<= 0x7f)
283 /* return the type of a token */
284 static int char_type(char *s
)
286 int c
= (unsigned char) s
[0];
292 if ((t
= def_type(s
)) >= 0)
294 if (c
== '~' || c
== '^')
296 if (ispunct(c
) && (c
!= '\\' || !s
[1]))
301 /* read the next token */
302 static int tok_read(void)
305 char *e
= tok
+ sizeof(tok
) - 2;
312 tok_prevsep
= tok_cursep
;
313 tok_cursep
= def_chopped(c
);
316 if (c
== ' ' || c
== '\n') {
317 while (c
> 0 && (c
== ' ' || c
== '\n'))
322 tok_curtype
= T_SPACE
;
334 if (c2
>= '1' && c2
<= '9' && !src_arg(c2
- '0')) {
341 if (!tok_keyword()) {
342 tok_curtype
= T_KEYWORD
;
352 if (strchr(T_SOFTSEP
, c
)) {
360 } else if (c
== '[') {
361 while (c
&& c
!= ']') {
368 } else if (c
== '"') {
370 while (c
> 0 && c
!= '"') {
384 /* two-character operators */
386 switch (T_BIN(c
, c2
)) {
387 case T_BIN('<', '='):
388 case T_BIN('>', '='):
389 case T_BIN('=', '='):
390 case T_BIN('!', '='):
391 case T_BIN('>', '>'):
392 case T_BIN('<', '<'):
393 case T_BIN(':', '='):
394 case T_BIN('-', '>'):
395 case T_BIN('<', '-'):
396 case T_BIN('-', '+'):
404 tok_curtype
= char_type(tok
);
409 while (--i
> 0 && s
< e
)
412 tok_curtype
= char_type(tok
);
419 return tok
[0] ? tok
: NULL
;
422 /* current token type */
425 return tok
[0] ? tok_curtype
: 0;
428 /* return nonzero if current token is a chops the equation */
429 int tok_chops(int soft
)
431 if (!tok_get() || tok_curtype
== T_KEYWORD
)
434 return strchr(T_SOFTSEP
, (unsigned char) tok_get()[0]) != NULL
;
435 return def_chopped((unsigned char) tok_get()[0]);
438 /* read the next token, return the previous */
441 strcpy(tok_prev
, tok
);
443 return tok_prev
[0] ? tok_prev
: NULL
;
446 /* like tok_pop() but ignore T_SPACE tokens; if sep, read until chopped */
447 char *tok_poptext(int sep
)
449 while (tok_type() == T_SPACE
)
453 strcat(tok_prev
, tok
);
455 } while (tok
[0] && !tok_chops(!sep
));
456 return tok_prev
[0] ? tok_prev
: NULL
;
460 static void tok_blanks(void)
462 while (tok_type() == T_SPACE
)
466 /* if the next token is s, return zero and skip it */
470 if (tok_get() && !s
[1] && strchr("{}~^\t", s
[0]) && !strcmp(s
, tok_get())) {
474 if (tok_type() != T_KEYWORD
|| !tok_get() || strcmp(s
, tok_get()))
480 /* read delim command */
485 if (!strcmp("off", delim
)) {
494 /* read macro definition */
495 static void tok_macrodef(struct sbuf
*def
)
500 while (c
> 0 && isspace(c
))
504 while (c
> 0 && c
!= delim
) {
510 /* read the next macro command */
518 src_define(name
, sbuf_buf(&def
));
522 /* return 1 if inside inline equations */