1 /* the preprocessor and tokenizer */
8 #define T_BIN(c1, c2) (((c1) << 8) | (c2))
9 #define T_SEP "^~{}\"\n\t "
10 #define T_SOFTSEP (T_SEP "=:|.+-*/\\,()[]<>!")
11 #define ESAVE "\\E*[.eqnbeg]\\R'" EQNFN "0 \\En(.f'\\R'" EQNSZ "0 \\En(.s'"
12 #define ELOAD "\\f[\\En[" EQNFN "0]]\\s[\\En[" EQNSZ "0]]\\E*[.eqnend]"
14 static char *kwds
[] = {
15 "fwd", "down", "back", "up",
16 "bold", "italic", "roman", "font", "fat", "size",
17 "bar", "dot", "dotdot", "dyad", "hat", "under", "vec", "tilde",
18 "sub", "sup", "from", "to", "vcenter",
19 "left", "right", "over", "sqrt",
20 "pile", "lpile", "cpile", "rpile", "above",
21 "matrix", "col", "ccol", "lcol", "rcol",
23 "gfont", "grfont", "gbfont", "gsize", "set", "chartype",
24 "mark", "lineup", "bracketsizes", "bracketpieces", "breakcost",
27 static int tok_eqen
; /* non-zero if inside .EQ/.EN */
28 static int tok_line
; /* inside inline eqn block */
29 static int tok_part
; /* partial line with inline eqn blocks */
30 static char tok
[LNLEN
]; /* current token */
31 static char tok_prev
[LNLEN
]; /* previous token */
32 static int tok_curtype
; /* type of current token */
33 static int tok_cursep
; /* current character is a separator */
34 static int tok_prevsep
; /* previous character was a separator */
35 static int eqn_beg
, eqn_end
; /* inline eqn delimiters */
37 /* return zero if troff request .ab is read */
38 static int tok_req(int a
, int b
)
43 eqln
[i
++] = in_next();
44 if (eqln
[i
- 1] != '.')
46 eqln
[i
++] = in_next();
47 while (eqln
[i
- 1] == ' ' && i
< sizeof(eqln
) - 4)
48 eqln
[i
++] = in_next();
51 eqln
[i
++] = in_next();
62 static int tok_en(void)
64 return tok_req('E', 'N');
67 /* does the line start with eq */
68 static int tok_eq(char *s
)
72 while (isspace((unsigned char) *s
))
74 return s
[0] == 'E' && s
[1] == 'Q';
77 /* read an lf request */
78 static int tok_lf(char *s
)
82 while (isspace((unsigned char) *s
))
84 if (*s
++ != 'l' || *s
++ != 'f')
86 while (isspace((unsigned char) *s
))
88 if (isdigit((unsigned char) *s
))
93 /* read the next input character */
94 static int tok_next(void)
97 if (!tok_eqen
&& !tok_line
)
100 if (tok_eqen
&& c
== '\n' && tok_en())
102 if (tok_line
&& (in_top() && c
== eqn_end
)) {
109 /* push back the last character read */
110 static void tok_back(int c
)
112 if (tok_eqen
|| tok_line
)
116 /* read the next word */
117 static void tok_preview(char *s
)
121 while (c
> 0 && !strchr(T_SEP
, c
) &&
122 (!tok_line
|| (!in_top() || c
!= eqn_end
))) {
130 /* push back the given word */
131 static void tok_unpreview(char *s
)
135 in_back((unsigned char) s
[--n
]);
138 /* read a keyword; return zero on success */
139 static int tok_keyword(void)
143 for (i
= 0; i
< LEN(kwds
); i
++)
144 if (!strcmp(kwds
[i
], tok
))
150 /* read the next argument of a macro call; return zero if read a ',' */
151 static int tok_readarg(struct sbuf
*sbuf
)
154 int pdepth
= 0; /* number of nested parenthesis */
155 while (c
> 0 && (pdepth
|| (c
!= ',' && c
!= ')'))) {
163 return c
== ',' ? 0 : 1;
166 /* expand a macro; return zero on success */
167 static int tok_expand(void)
169 char *args
[10] = {NULL
};
170 struct sbuf sbufs
[10];
174 if (!in_expand(tok
, NULL
))
176 pbeg
= in_macrocall(tok
);
178 tok_unpreview(tok
+ pbeg
+ 1);
181 sbuf_init(&sbufs
[n
]);
182 if (tok_readarg(&sbufs
[n
++]))
185 for (i
= 0; i
< n
; i
++)
186 args
[i
] = sbuf_buf(&sbufs
[i
]);
187 in_expand(tok
, args
);
188 for (i
= 0; i
< n
; i
++)
189 sbuf_done(&sbufs
[i
]);
196 /* read until .EQ or eqn_beg */
203 while ((c
= in_next()) > 0) {
206 printf(".%s %s \"%s\n",
207 tok_part
? "as" : "ds", EQNS
, sbuf_buf(&ln
));
215 if (c
== '\n' && !tok_part
) {
216 printf("%s", sbuf_buf(&ln
));
217 tok_lf(sbuf_buf(&ln
));
218 if (tok_eq(sbuf_buf(&ln
)) && !tok_en()) {
224 if (c
== '\n' && tok_part
) {
225 printf(".lf %d\n", in_lineget());
226 printf("\\*%s%s", escarg(EQNS
), sbuf_buf(&ln
));
236 /* collect the output of this eqn block */
237 void tok_eqnout(char *s
)
240 printf(".ds %s \"%s%s%s\n", EQNS
, ESAVE
, s
, ELOAD
);
241 printf(".lf %d\n", in_lineget() - 1);
242 printf("\\&\\*%s\n", escarg(EQNS
));
244 printf(".as %s \"%s%s%s\n", EQNS
, ESAVE
, s
, ELOAD
);
248 /* return the length of a utf-8 character based on its first byte */
249 static int utf8len(int c
)
251 if (c
> 0 && c
<= 0x7f)
266 /* return the type of a token */
267 static int char_type(char *s
)
269 int c
= (unsigned char) s
[0];
275 if ((t
= def_type(s
)) >= 0)
277 if (c
== '~' || c
== '^')
279 if (ispunct(c
) && (c
!= '\\' || !s
[1]))
284 /* read the next token */
285 static int tok_read(void)
288 char *e
= tok
+ sizeof(tok
) - 2;
295 tok_prevsep
= tok_cursep
;
296 tok_cursep
= !!strchr(T_SEP
, c
);
297 if (c
== ' ' || c
== '\n') {
298 while (c
> 0 && (c
== ' ' || c
== '\n'))
303 tok_curtype
= T_SPACE
;
315 if (c2
>= '1' && c2
<= '9' && !in_arg(c2
- '0'))
320 if (!tok_keyword()) {
321 tok_curtype
= T_KEYWORD
;
331 if (strchr(T_SOFTSEP
, c
)) {
339 } else if (c
== '[') {
340 while (c
&& c
!= ']') {
347 } else if (c
== '"') {
349 while (c
> 0 && c
!= '"') {
363 /* two-character operators */
365 switch (T_BIN(c
, c2
)) {
366 case T_BIN('<', '='):
367 case T_BIN('>', '='):
368 case T_BIN('=', '='):
369 case T_BIN('!', '='):
370 case T_BIN('>', '>'):
371 case T_BIN('<', '<'):
372 case T_BIN(':', '='):
373 case T_BIN('-', '>'):
374 case T_BIN('<', '-'):
375 case T_BIN('-', '+'):
383 tok_curtype
= char_type(tok
);
388 while (--i
> 0 && s
< e
)
391 tok_curtype
= char_type(tok
);
398 return tok
[0] ? tok
: NULL
;
401 /* current token type */
404 return tok
[0] ? tok_curtype
: 0;
407 /* return nonzero if current token is a separator */
408 int tok_sep(int soft
)
410 return !tok_get() || tok_curtype
== T_KEYWORD
||
411 strchr(soft
? T_SOFTSEP
: T_SEP
, (unsigned char) tok_get()[0]);
414 /* read the next token, return the previous */
417 strcpy(tok_prev
, tok
);
419 return tok_prev
[0] ? tok_prev
: NULL
;
422 /* like tok_pop() but ignore T_SPACE tokens; if sep, read until T_SEP */
423 char *tok_poptext(int sep
)
425 while (tok_type() == T_SPACE
)
429 strcat(tok_prev
, tok
);
431 } while (tok
[0] && !tok_sep(!sep
));
432 return tok_prev
[0] ? tok_prev
: NULL
;
436 static void tok_blanks(void)
438 while (tok_type() == T_SPACE
)
442 /* if the next token is s, return zero and skip it */
446 if (tok_get() && !s
[1] && strchr("{}~^\t", s
[0]) && !strcmp(s
, tok_get())) {
450 if (tok_type() != T_KEYWORD
|| !tok_get() || strcmp(s
, tok_get()))
456 /* read delim command */
461 if (!strcmp("off", delim
)) {
470 /* read macro definition */
471 static void tok_macrodef(struct sbuf
*def
)
476 while (c
> 0 && isspace(c
))
480 while (c
> 0 && c
!= delim
) {
486 /* read the next macro command */
494 in_define(name
, sbuf_buf(&def
));
498 /* return 1 if inside inline equations */