home *** CD-ROM | disk | FTP | other *** search
/ Dream 48 / Amiga_Dream_48.iso / Atari / c / cpp.zoo / src / token.c < prev    next >
C/C++ Source or Header  |  1993-06-03  |  13KB  |  645 lines

  1.  
  2. #include <ctype.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <stddef.h>
  6. #include "global.h"
  7. #include "ztype.h"
  8.  
  9. #define BASE10    1
  10. #define BASE8    2
  11. #define BASE16    3
  12.  
  13. #define GRANULARITY 256
  14.  
  15. extern char *next_c;
  16.  
  17. static int tok_flags = 0;
  18. static TokenP tok_blocks, next_free_tok, pushback_list;
  19.  
  20. /*
  21.    alloc_token() -- return space for a Token, either from the free list or
  22.    from freshly malloc()'ed memory
  23. */
  24. TokenP alloc_token()
  25. {
  26.   register TokenP T;
  27.   register int i;
  28.  
  29.   if (!next_free_tok) {
  30.     /* allocate several Token's at once for efficiency */
  31.     T = (TokenP) mallok(GRANULARITY * sizeof (Token));
  32.  
  33.     /* the first pointer is used to chain blocks of Token's together */
  34.     T->next = tok_blocks;
  35.     tok_blocks = T;
  36.     /* string the rest together to form a free list */
  37.     for (i = 1; i < GRANULARITY; i++) {
  38.       T[i].next = T + i + 1;
  39.       T[i].pre_ws = T[i].txt = NULL;
  40.     }
  41.     T[GRANULARITY - 1].next = NULL;
  42.     next_free_tok = T + 1;
  43.   }
  44.   T = next_free_tok;
  45.   next_free_tok = T->next;
  46.   T->hashval = T->val = T->type = T->subtype = T->flags = 0;
  47.   T->next = NULL;
  48.   return T;
  49. }
  50.  
  51. /* free_token() -- return an allocated Token to the free list */
  52. void free_token(T)
  53.   TokenP T;
  54. {
  55.   T->next = NULL;
  56.   free_tlist(T);
  57. }
  58.  
  59. /* free_tlist() -- return a list of Token's to the free list */
  60. void free_tlist(T)
  61.   register TokenP T;
  62. {
  63.   register TokenP T1;
  64.  
  65.   for (T1 = T; T; T = T1) {
  66.     T1 = T->next;
  67.     free(T->pre_ws);
  68.     T->pre_ws = NULL;
  69.     free(T->txt);
  70.     T->txt = NULL;
  71.     T->next = next_free_tok;
  72.     next_free_tok = T;
  73.   }
  74. }
  75.  
  76. /*
  77.    copy_token() -- return a new Token that is a duplicate of the given token
  78. */
  79. TokenP copy_token(T1)
  80.   TokenP T1;
  81. {
  82.   TokenP T2 = alloc_token();
  83.  
  84.   *T2 = *T1;
  85.   T2->pre_ws = strdup(T1->pre_ws);
  86.   T2->txt = strdup(T1->txt);
  87.   T2->next = NULL;
  88.   return T2;
  89. }
  90.  
  91. /* copy_tlist() -- create a duplicate of a list of Token's */
  92. TokenP copy_tlist(T1)
  93.   TokenP T1;
  94. {
  95.   Token head;
  96.   TokenP T2 = &head;
  97.  
  98.   for (T2->next = NULL; T1; T1 = T1->next, T2 = T2->next)
  99.     T2->next = copy_token(T1);
  100.   return head.next;
  101. }
  102.  
  103. /* tok_shutdown() -- free all space allocated for Token's */
  104. void tok_shutdown()
  105. {
  106.   register TokenP T, T1;
  107.   register int i;
  108.  
  109.   for (T1 = T = tok_blocks; T; T = T1) {
  110.     T1 = T->next;
  111.     for (i = 1; i < GRANULARITY; i++) {
  112.       if (T[i].pre_ws)
  113.     free(T[i].pre_ws);
  114.       if (T[i].txt)
  115.     free(T[i].txt);
  116.     }
  117.     free(T);
  118.   }
  119. }
  120.  
  121. /*
  122.    push_tlist() -- "un-read" the list of Token's |T|; token() will return all
  123.    of these tokens in order before reading another token from the input file
  124. */
  125. void push_tlist(T)
  126.   TokenP T;
  127. {
  128.   register TokenP t;
  129.  
  130.   if (!T)
  131.     return;
  132.   t = T;
  133.   while (t->next)
  134.     t = t->next;
  135.   t->next = pushback_list;
  136.   pushback_list = T;
  137. }
  138.  
  139. /* mk_eof() -- makes and returns an EOF_ token */
  140. static TokenP mk_eof()
  141. {
  142.   TokenP T = alloc_token();
  143.  
  144.   T->type = EOF_;
  145.   T->pre_ws = mallok(1);
  146.   *T->pre_ws = '\0';
  147.   T->txt = mallok(1);
  148.   *T->txt = '\0';
  149.   return T;
  150. }
  151.  
  152. /* mk_eol() -- makes and returns an EOL token */
  153. TokenP mk_eol(s, n)
  154.   char *s;
  155.   int n;
  156. {
  157.   TokenP T = alloc_token();
  158.  
  159.   T->pre_ws = mallok(n + 1);
  160.   strncpy(T->pre_ws, s, n);
  161.   T->pre_ws[n] = '\0';
  162.   T->txt = mallok(2);
  163.   T->txt[0] = '\n';
  164.   T->txt[1] = '\0';
  165.   T->type == EOL;
  166.   T->subtype = '\n';
  167.   return T;
  168. }
  169.  
  170. /*
  171.    mk_stopper() -- makes and returns a STOP token.  See expand_tlist() for
  172.    further information.
  173. */
  174. TokenP mk_stopper()
  175. {
  176.   TokenP T = alloc_token();
  177.  
  178.   T->type = STOP;
  179.   T->pre_ws = mallok(1);
  180.   *T->pre_ws = '\0';
  181.   T->txt = mallok(1);
  182.   *T->txt = '\0';
  183.   return T;
  184. }
  185.  
  186. /*
  187.    mk_unmarker() -- makes and returns a special token that informs the
  188.    tokenizer to unmark the macro text associated with token |T|.  See
  189.    expand() for further information.
  190. */
  191. TokenP mk_unmarker(T)
  192.   TokenP T;
  193. {
  194.   TokenP T1 = copy_token(T);
  195.  
  196.   T1->type = UNMARK;
  197.   return T1;
  198. }
  199.  
  200. /* flush_tokenizer() -- discard all Tokens pushed back by push_tlist() */
  201. void flush_tokenizer()
  202. {
  203.   free_tlist(pushback_list);
  204.   pushback_list = NULL;
  205. }
  206.  
  207. /*
  208.    number() -- copies from |s| into the token |T| a string of characters
  209.    denoting an integer or floating-point constant.  Returns a pointer to the
  210.    first uncopied character.
  211. */
  212. static char *number(s, T)
  213.   register char *s;
  214.   TokenP T;
  215. {
  216.   int numtype = BASE10, fpflag = 0;
  217.   char *t;
  218.  
  219.   T->type = NUMBER;
  220.   if (*s == '0') {
  221.     /* check for octal or hexadecimal constant */
  222.     if ((s[1] == 'x' || s[1] == 'X') && isxdigit(s[2])) {
  223.       numtype = BASE16;
  224.       T->flags |= UNS_VAL;
  225.     } else if (is_octal(s[1])) {
  226.       numtype = BASE8;
  227.       T->flags |= UNS_VAL;
  228.     }
  229.   }
  230.   T->val = strtol(s, &t, 0);
  231.   s = t;
  232.   if (numtype != BASE10 || is_isuff(*s)) {
  233.  
  234.     /*
  235.        if we're not in base 10, or the next characters are integer constant
  236.        suffixes, this can't be a floating-point constant
  237.     */
  238.     while (is_isuff(*s)) {
  239.       if (*s == 'u' || *s == 'U')
  240.     T->flags |= UNS_VAL;
  241.       s++;
  242.     }
  243.     return s;
  244.   }
  245.   /* check to see if the number is actually floating point */
  246.   if (*s == '.') {
  247.     fpflag = 1;
  248.     do
  249.       s++;
  250.     while (isdigit(*s));
  251.   }
  252.   if (*s == 'e' || *s == 'E') {
  253.     register char *t = s;
  254.  
  255.     t++;
  256.     if (*t == '-' || *t == '+')
  257.       t++;
  258.     if (isdigit(*t)) {
  259.       fpflag = 1;
  260.       do
  261.     t++;
  262.       while (isdigit(*t));
  263.       s = t;
  264.     }
  265.   }
  266.   if (fpflag) {
  267.     T->type = FP_NUM;
  268.     if (is_fsuff(*s))
  269.       s++;
  270.   }
  271.   return s;
  272. }
  273.  
  274. /*
  275.    char_constant() -- copy from |s| into the token |T| a string of characters
  276.    denoting a character constant.  We do not translate escape sequences at
  277.    this point, though we might need to
  278. */
  279. static char *char_constant(s, T)
  280.   register char *s;
  281.   TokenP T;
  282. {
  283.   T->type = CHAR_CON;
  284.   for (; *s; s++) {
  285.     if (*s == '\'')
  286.       return s + 1;
  287.     if (*s == '\\')
  288.       s++;
  289.   }
  290.   error("unterminated character constant");
  291.   return s;
  292. }
  293.  
  294. /*
  295.    string_literal() -- copy from |s| into the token |T| a string of
  296.    characters denoting a string literal.  We do not translate escape
  297.    sequences at this point, though we might need to
  298. */
  299. static char *string_literal(s, T)
  300.   register char *s;
  301.   TokenP T;
  302. {
  303.   T->type = STR_CON;
  304.   for (; *s; s++) {
  305.     if (*s == '"')
  306.       return s + 1;
  307.     if (*s == '\\')
  308.       s++;
  309.   }
  310.   error("unterminated string literal");
  311.   return s;
  312. }
  313.  
  314. /*
  315.    include_name() -- copy from |s| into the token |T| a string of characters
  316.    denoting an #include file specifier enclosed in <>. |s| points to the
  317.    character after the '<'.
  318. */
  319. static char *include_name(s, T)
  320.   register char *s;
  321.   TokenP T;
  322. {
  323.   T->type = INC_NAM;
  324.   for (; *s; s++) {
  325.     if (*s == '>')
  326.       return s + 1;
  327.   }
  328.   error("unterminated include file name");
  329. }
  330.  
  331. /* set_mode() -- set the tokenizer flags to |m| */
  332. void set_mode(m)
  333.   int m;
  334. {
  335.   tok_flags = m;
  336. }
  337.  
  338. /*
  339.    change_mode() -- twiddle the tokenizer flags; in particular, set the flags
  340.    specified in |raise| and clear the flags specified in |lower|
  341. */
  342. void change_mode(raise, lower)
  343. {
  344.   tok_flags |= raise;
  345.   tok_flags &= (~lower);
  346. }
  347.  
  348. /* get_mode() -- return the current value of the tokenizer flags */
  349. int get_mode()
  350. {
  351.   return tok_flags;
  352. }
  353.  
  354. /*
  355.    xlate_token() -- determines the type of the next preprocessor token in the
  356.    string pointed to by |s|.  Information about the token found is placed in
  357.    the Token |T|.  Returns a pointer to the first character not in the token
  358.    read.
  359. */
  360. static char *xlate_token(s, T)
  361.   register char *s;
  362.   TokenP T;
  363. {
  364.   if (is_ctoks(*s)) {
  365.     char *t;
  366.  
  367.     T->hashval = hash_id(s, &t);
  368.     s = t;
  369.     T->type = ID;
  370.     return t;
  371.   } else if (isdigit(*s))
  372.     return number(s, T);
  373.   else
  374.     switch (*s++) {
  375.     case '.':
  376.       T->subtype = '.';
  377.       if (*s == '.' && s[1] == '.') {
  378.     s += 2;
  379.     T->type = DONT_CARE;
  380.       } else if (isdigit(*s))
  381.     s = number(s - 1, T);
  382.       else
  383.     T->type = DONT_CARE;
  384.       break;
  385.     case '#':
  386.       if (*s == '#') {
  387.     s++;
  388.     T->type = TOK_CAT;
  389.       } else
  390.     T->type = POUND;
  391.       break;
  392.     case '&':
  393.       T->subtype = '&';
  394.       if (*s == '&') {
  395.     s++;
  396.     T->type = L_AND_OP;
  397.       } else if (*s == '=') {
  398.     s++;
  399.     T->type = DONT_CARE;
  400.       } else
  401.     T->type = B_AND_OP;
  402.       break;
  403.     case '|':
  404.       T->subtype = '|';
  405.       if (*s == '|') {
  406.     s++;
  407.     T->type = L_OR_OP;
  408.       } else if (*s == '=') {
  409.     s++;
  410.     T->type = DONT_CARE;
  411.       } else
  412.     T->type = B_OR_OP;
  413.       break;
  414.     case '+':
  415.       T->subtype = '+';
  416.       if (*s == s[-1] || *s == '=') {
  417.     s++;
  418.     T->type = DONT_CARE;
  419.       } else
  420.     T->type = ADD_OP;
  421.       break;
  422.     case '~':
  423.       T->type = UNARY_OP;
  424.       T->subtype = '~';
  425.       break;
  426.     case ',':
  427.       T->type = COMMA;
  428.       T->subtype = ',';
  429.       break;
  430.     case '(':
  431.       T->type = LPAREN;
  432.       T->subtype = '(';
  433.       break;
  434.     case ')':
  435.       T->type = RPAREN;
  436.       T->subtype = ')';
  437.       break;
  438.     case '!':
  439.       T->subtype = '!';
  440.       if (*s == '=')
  441.     T->type = EQ_OP;
  442.       else
  443.     T->type = UNARY_OP;
  444.       break;
  445.     case '=':
  446.       T->subtype = '=';
  447.       if (*s == '=')
  448.     T->type = EQ_OP;
  449.       else
  450.     T->type = DONT_CARE;
  451.       break;
  452.     case '*':
  453.     case '/':
  454.     case '%':
  455.       T->subtype = s[-1];
  456.       if (*s == '=') {
  457.     s++;
  458.     T->type = DONT_CARE;
  459.       } else
  460.     T->type = MUL_OP;
  461.       break;
  462.     case '^':
  463.       T->subtype = '^';
  464.       if (*s == '=') {
  465.     s++;
  466.     T->type = DONT_CARE;
  467.       } else
  468.     T->type = B_XOR_OP;
  469.       break;
  470.     case '-':
  471.       T->subtype = '-';
  472.       if (*s == '-' || *s == '=' || *s == '>') {
  473.     s++;
  474.     T->type = DONT_CARE;
  475.       } else
  476.     T->type = ADD_OP;
  477.       break;
  478.     case '<':
  479.       if (tok_flags & INCLUDE_LINE) {
  480.     s = include_name(s, T);
  481.     break;
  482.       }
  483.       /* else fall through */
  484.     case '>':
  485.       T->subtype = s[-1];
  486.       T->type = REL_OP;
  487.       if (*s == s[-1]) {
  488.     s++;
  489.     T->type = SHIFT_OP;
  490.       }
  491.       if (*s == '=') {
  492.     s++;
  493.     if (T->type == REL_OP)
  494.       T->subtype = (T->subtype == '<' ? '(' : ')');
  495.     else
  496.       T->type = DONT_CARE;
  497.       }
  498.       break;
  499.     case '\'':
  500.       s = char_constant(s, T);
  501.       break;
  502.     case '"':
  503.       s = string_literal(s, T);
  504.       break;
  505.     case '[':
  506.     case ']':
  507.     case '{':
  508.     case '}':
  509.     case ';':
  510.     case ':':
  511.     case '?':
  512.       T->type = DONT_CARE;
  513.       break;
  514.     default:
  515.       T->type = UNKNOWN;
  516.     }
  517.   return s;
  518. }
  519.  
  520. /* print_token() -- write token |T| to the output file */
  521. void print_token(T)
  522.   TokenP T;
  523. {
  524.   if (T->type == STOP)
  525.     bugchk("STOP token in output stream?");
  526.   fputs(T->pre_ws, outf);
  527.   fputs(T->txt, outf);
  528.   if (T->flags & TRAIL_SPC)
  529.     fputc(' ', outf);
  530. }
  531.  
  532. /*
  533.    merge_tokens() -- Perform token pasting on Token's |T1| and |T2|. Returns
  534.    the resulting token.
  535. */
  536. TokenP merge_tokens(T1, T2)
  537.   TokenP T1, T2;
  538. {
  539.   TokenP T = alloc_token();
  540.   char *s, *t;
  541.  
  542.   T->pre_ws = strdup(T1->pre_ws);
  543.   T->txt = mallok(strlen(T1->txt) + strlen(T2->txt) + 1);
  544.   strcpy(T->txt, T1->txt);
  545.   strcat(T->txt, T2->txt);
  546.   t = xlate_token(T->txt, T);
  547.   if (*t != '\0') {
  548.     warning("Invalid token \"%s\" created by concatenation", t);
  549.     T->type = UNKNOWN;
  550.   }
  551.   return T;
  552. }
  553.  
  554. TokenP _one_token()
  555. {
  556.   register char *s = next_c, *t, *u;
  557.   int n;
  558.   TokenP T = alloc_token();
  559.  
  560.   t = suck_ws(s, &(T->pre_ws));
  561.   if (!t || !*t) {
  562.     T->txt = mallok(2);
  563.     T->txt[0] = '\n';
  564.     T->txt[1] = '\0';
  565.     T->type = EOL;
  566.     T->subtype = '\n';
  567.     next_c = t;
  568.     return T;
  569.   }
  570.   u = xlate_token(t, T);
  571.   n = u - t;
  572.   if (T->type == UNKNOWN && w_bad_chars)
  573.     error("Unrecognized character 0x%02x='%c'", *t, *t);
  574.   T->txt = mallok(n + 1);
  575.   strncpy(T->txt, t, n);
  576.   T->txt[n] = '\0';
  577.   next_c = u;
  578.   return T;
  579. }
  580.  
  581. void _tokenize_line()
  582. {
  583.   Token head;
  584.   TokenP T = &head;
  585.  
  586.   head.next = NULL;
  587.   do {
  588.     T = T->next = _one_token();
  589.   } while (T->type != EOL);
  590.   push_tlist(head.next);
  591. }
  592.  
  593. TokenP token()
  594. {
  595.   TokenP T;
  596.   register char *s;
  597.  
  598.   while (pushback_list) {
  599.     T = pushback_list;
  600.     pushback_list = T->next;
  601.     T->next = NULL;
  602.     if (T->type == UNMARK) {
  603.       Macro *M;
  604.  
  605.       M = lookup(T->txt, T->hashval);
  606.       if (!M)
  607.     bugchk("UNMARK on non-macro token %s", T->txt);
  608.       if (!(M->flags & MARKED))
  609.     bugchk("UNMARK on unmarked macro %s", T->txt);
  610.       M->flags ^= MARKED;
  611.       free_token(T);
  612.       continue;
  613.     } else {
  614.       return T;
  615.     }
  616.   }
  617.  
  618.   /*
  619.      if we get to here, the pushback list is empty, and we need to read in
  620.      another line
  621.   */
  622.   next_c = s = getline();
  623.   if (!s)
  624.     return mk_eof();
  625.   T = _one_token();
  626.   if (T->type == EOL) {
  627.     return T;
  628.   }
  629.   if (T->type != POUND || get_mode() & SLURP)
  630.     _tokenize_line();
  631.   return T;
  632. }
  633.  
  634. TokenP exp_token()
  635. {
  636.   TokenP T = token();
  637.   Macro *M;
  638.  
  639.   if (T->type == ID && !(T->flags & BLUEPAINT) && (M = lookup(T->txt, T->hashval))) {
  640.     expand(T, M);
  641.     return exp_token();
  642.   } else
  643.     return T;
  644. }
  645.