home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v92.tgz / v92.tar / v92 / src / preproc / bldtok.c next >
C/C++ Source or Header  |  1996-03-22  |  21KB  |  769 lines

  1. /*
  2.  * This file contains routines for building tokens out of characters from a
  3.  *  "character source". This source is the top element on the source stack.
  4.  */
  5. #include "::preproc:preproc.h"
  6. #include "::preproc:ptoken.h"
  7. #include <ctype.h>
  8.  
  9. /*
  10.  * Prototypes for static functions.
  11.  */
  12. hidden int           pp_tok_id  Params((char *s));
  13. hidden struct token *chck_wh_sp Params((struct char_src *cs));
  14. hidden struct token *pp_number  Params((noargs));
  15. hidden struct token *char_str   Params((int delim, int tok_id));
  16. hidden struct token *hdr_tok    Params((int delim, int tok_id,
  17.                                   struct char_src *cs));
  18.  
  19. int whsp_image = NoSpelling;    /* indicate what is in white space tokens */
  20. struct token *zero_tok;         /* token for literal 0 */
  21. struct token *one_tok;          /* token for literal 1 */
  22.  
  23. #include "::preproc:pproto.h"
  24.  
  25. /*
  26.  * IsWhSp(c) - true if c is a white space character.
  27.  */
  28. #define IsWhSp(c) (c == ' ' || c == '\n' || c == '\t' || c == '\v' || c == '\f')
  29.  
  30. /*
  31.  * AdvChar() - advance to next character from buffer, filling the buffer
  32.  *   if needed.
  33.  */
  34. #define AdvChar() \
  35.    if (++next_char == last_char) \
  36.       fill_cbuf();
  37.  
  38. static int line;                   /* current line number */
  39. static char *fname;                /* current file name */
  40. static struct str_buf tknize_sbuf; /* string buffer */
  41.  
  42. /*
  43.  * List of preprocessing directives and the corresponding token ids.
  44.  */
  45. static struct rsrvd_wrd pp_rsrvd[] = {
  46.    PPDirectives
  47.    {"if",      PpIf},
  48.    {"else",    PpElse},
  49.    {"ifdef",   PpIfdef},
  50.    {"ifndef",  PpIfndef},
  51.    {"elif",    PpElif},
  52.    {"endif",   PpEndif},
  53.    {"include", PpInclude},
  54.    {"define",  PpDefine},
  55.    {"undef",   PpUndef},
  56.    {"begdef",  PpBegdef},
  57.    {"enddef",  PpEnddef},
  58.    {"line",    PpLine},
  59.    {"error",   PpError},
  60.    {"pragma",  PpPragma},
  61.    {NULL, Invalid}};
  62.  
  63. /*
  64.  * init_tok - initialize tokenizer.
  65.  */
  66. novalue init_tok()
  67.    {
  68.    struct rsrvd_wrd *rw;
  69.    static int first_time = 1;
  70.  
  71.    if (first_time) {
  72.       first_time = 0;
  73.       init_sbuf(&tknize_sbuf); /* initialize string buffer */
  74.       /*
  75.        * install reserved words into the string table
  76.        */
  77.       for (rw = pp_rsrvd; rw->s != NULL; ++rw)
  78.          rw->s = spec_str(rw->s);
  79.  
  80.       zero_tok = new_token(PpNumber, spec_str("0"), "", 0);
  81.       one_tok = new_token(PpNumber, spec_str("1"), "", 0);
  82.       }
  83.    }
  84.  
  85. /*
  86.  * pp_tok_id - see if s in the name of a preprocessing directive.
  87.  */
  88. static int pp_tok_id(s)
  89. char *s;
  90.    {
  91.    struct rsrvd_wrd *rw;
  92.  
  93.    for (rw = pp_rsrvd; rw->s != NULL && rw->s != s; ++rw)
  94.       ;
  95.    return rw->tok_id;
  96.    }
  97.  
  98. /*
  99.  * chk_eq_sign - look ahead to next character to see if it is an equal sign.
  100.  *  It is used for processing -D options.
  101.  */
  102. int chk_eq_sign()
  103.    {
  104.    if (*next_char == '=') {
  105.       AdvChar();
  106.       return 1;
  107.       }
  108.    else
  109.       return 0;
  110.    }
  111.  
  112. /*
  113.  * chck_wh_sp - If the input is at white space, construct a white space token
  114.  *  and return it, otherwise return NULL. This function also helps keeps track
  115.  *  of preprocessor directive boundaries.
  116.  */
  117. static struct token *chck_wh_sp(cs)
  118. struct char_src *cs;
  119.    {
  120.    register int c1, c2;
  121.    struct token *t;
  122.    int tok_id;
  123.  
  124.    /*
  125.     * See if we are at white space or a comment.
  126.     */
  127.    c1 = *next_char;
  128.    if (!IsWhSp(c1) && (c1 != '/' || next_char[1] != '*'))
  129.       return NULL;
  130.  
  131.    /*
  132.     * Fine the line number of the current character in the line number
  133.     *  buffer, and correct it if we have encountered any #line directives.
  134.     */
  135.    line = cs->line_buf[next_char - first_char] + cs->line_adj;
  136.    if (c1 == '\n')
  137.       --line;      /* a new-line really belongs to the previous line */
  138.  
  139.    tok_id = WhiteSpace;
  140.    for (;;) {
  141.       if (IsWhSp(c1)) {
  142.          /*
  143.           * The next character is a white space. If we are retaining the
  144.           *  image of the white space in the token, copy the character to
  145.           *  the string buffer. If we are in the midst of a preprocessor
  146.           *  directive and find a new-line, indicate the end of the
  147.           *  the directive.
  148.           */
  149.          AdvChar();
  150.          if (whsp_image != NoSpelling)
  151.             AppChar(tknize_sbuf, c1);
  152.          if (c1 == '\n') {
  153.             if (cs->dir_state == Within)
  154.                tok_id = PpDirEnd;
  155.             cs->dir_state = CanStart;
  156.             if (tok_id == PpDirEnd)
  157.                break;
  158.             }
  159.          }
  160.       else if (c1 == '/' && next_char[1] == '*') {
  161.          /*
  162.           * Start of comment. If we are retaining the image of comments,
  163.           *  copy the characters into the string buffer.
  164.           */
  165.          if (whsp_image == FullImage) {
  166.             AppChar(tknize_sbuf, '/');
  167.             AppChar(tknize_sbuf, '*');
  168.             }
  169.          AdvChar();
  170.          AdvChar();
  171.  
  172.          /*
  173.           * Look for the end of the comment.
  174.           */
  175.          c1 = *next_char;
  176.          c2 = next_char[1];
  177.          while (c1 != '*' || c2 != '/') {
  178.             if (c1 == EOF)
  179.                 errfl1(fname, line, "eof encountered in comment");
  180.             AdvChar();
  181.             if (whsp_image == FullImage)
  182.                AppChar(tknize_sbuf, c1);
  183.             c1 = c2;
  184.             c2 = next_char[1];
  185.             }
  186.  
  187.          /*
  188.           * Determine if we are retaining the image of a comment, replacing
  189.           *  a comment by one space character, or ignoring comments.
  190.           */
  191.          if (whsp_image == FullImage) {
  192.             AppChar(tknize_sbuf, '*');
  193.             AppChar(tknize_sbuf, '/');
  194.             }
  195.          else if (whsp_image == NoComment)
  196.             AppChar(tknize_sbuf, ' ');
  197.          AdvChar();
  198.          AdvChar();
  199.          }
  200.       else
  201.          break;         /* end of white space */
  202.       c1 = *next_char;
  203.       }
  204.  
  205.    /*
  206.     * If we are not retaining the image of white space, replace it all
  207.     *  with one space character.
  208.     */
  209.    if (whsp_image == NoSpelling)
  210.       AppChar(tknize_sbuf, ' ');
  211.  
  212.    t = new_token(tok_id, str_install(&tknize_sbuf), fname, line);
  213.  
  214.    /*
  215.     * Look ahead to see if a ## operator is next.
  216.     */
  217.    if (*next_char == '#' && next_char[1] == '#')
  218.       if (tok_id == PpDirEnd)
  219.          errt1(t, "## expressions must not cross directive boundaries");
  220.       else {
  221.          /*
  222.           * Discard white space before a ## operator.
  223.           */
  224.          free_t(t);
  225.          return NULL;
  226.          }
  227.    return t;
  228.    }
  229.  
  230. /*
  231.  * pp_number - Create a token for a preprocessing number (See ANSI C Standard
  232.  *  for the syntax of such a number).
  233.  */
  234. static struct token *pp_number()
  235.    {
  236.    register int c;
  237.  
  238.    c = *next_char;
  239.    for (;;) {
  240.       if (c == 'e' || c == 'E') {
  241.          AppChar(tknize_sbuf, c);
  242.          AdvChar();
  243.          c = *next_char;
  244.          if (c == '+' || c == '-') {
  245.             AppChar(tknize_sbuf, c);
  246.             AdvChar();
  247.             c = *next_char;
  248.             }
  249.          }
  250.       else if (isdigit(c) || c == '.' || islower(c) || isupper(c) || c == '_') {
  251.          AppChar(tknize_sbuf, c);
  252.          AdvChar();
  253.          c = *next_char;
  254.          }
  255.       else {
  256.          return new_token(PpNumber, str_install(&tknize_sbuf), fname, line);
  257.          }
  258.       }
  259.    }
  260.  
  261. /*
  262.  * char_str - construct a token for a character constant or string literal.
  263.  */
  264. static struct token *char_str(delim, tok_id)
  265. int delim;
  266. int tok_id;
  267.    {
  268.    register int c;
  269.  
  270.    for (c = *next_char; c != EOF && c != '\n' &&  c != delim; c = *next_char) {
  271.       AppChar(tknize_sbuf, c);
  272.       if (c == '\\') {
  273.          c = next_char[1];
  274.          if (c == EOF || c == '\n')
  275.             break;
  276.          else {
  277.             AppChar(tknize_sbuf, c);
  278.             AdvChar();
  279.             }
  280.          }
  281.       AdvChar();
  282.       }
  283.    if (c == EOF)
  284.       errfl1(fname, line, "End-of-file encountered within a literal");
  285.    if (c == '\n')
  286.       errfl1(fname, line, "New-line encountered within a literal");
  287.    AdvChar();
  288.    return new_token(tok_id, str_install(&tknize_sbuf), fname, line);
  289.    }
  290.  
  291. /*
  292.  * hdr_tok - create a token for an #include header. The delimiter may be
  293.  *  > or ".
  294.  */
  295. static struct token *hdr_tok(delim, tok_id, cs)
  296. int delim;
  297. int tok_id;
  298. struct char_src *cs;
  299.    {
  300.    register int c;
  301.  
  302.    line = cs->line_buf[next_char - first_char] + cs->line_adj;
  303.    AdvChar();
  304.  
  305.    for (c = *next_char; c != delim; c = *next_char) {
  306.       if (c == EOF)
  307.          errfl1(fname, line,
  308.             "End-of-file encountered within a header name");
  309.       if (c == '\n')
  310.          errfl1(fname, line,
  311.             "New-line encountered within a header name");
  312.       AppChar(tknize_sbuf, c);
  313.       AdvChar();
  314.       }
  315.    AdvChar();
  316.    return new_token(tok_id, str_install(&tknize_sbuf), fname, line);
  317.    }
  318.  
  319. /*
  320.  * tokenize - return the next token from the character source on the top
  321.  *  of the source stack.
  322.  */
  323. struct token *tokenize()
  324.    {
  325.    struct char_src *cs;
  326.    struct token *t1, *t2;
  327.    register int c;
  328.    int tok_id;
  329.  
  330.  
  331.    cs = src_stack->u.cs;
  332.  
  333.    /*
  334.     * Check to see if the last call left a token from a look ahead.
  335.     */
  336.    if (cs->tok_sav != NULL) {
  337.       t1 = cs->tok_sav;
  338.       cs->tok_sav = NULL;
  339.       return t1;
  340.       }
  341.  
  342.    if (*next_char == EOF)
  343.       return NULL;
  344.  
  345.    /*
  346.     * Find the current line number and file name for the character
  347.     *  source and check for white space.
  348.     */
  349.    line = cs->line_buf[next_char - first_char] + cs->line_adj;
  350.    fname = cs->fname;
  351.    if ((t1 = chck_wh_sp(cs)) != NULL)
  352.        return t1;
  353.  
  354.    c = *next_char;  /* look at next character */
  355.    AdvChar();
  356.  
  357.    /*
  358.     * If the last thing we saw in this character source was white space
  359.     *  containing a new-line, then we must look for the start of a
  360.     *  preprocessing directive.
  361.     */
  362.    if (cs->dir_state == CanStart) {
  363.       cs->dir_state = Reset;
  364.       if  (c == '#' && *next_char != '#') {
  365.          /*
  366.           * Assume we are within a preprocessing directive and check
  367.           *  for white space to discard.
  368.           */
  369.          cs->dir_state = Within;
  370.          if ((t1 = chck_wh_sp(cs)) != NULL)
  371.             if (t1->tok_id == PpDirEnd) {
  372.                /*
  373.                 * We found a new-line, this is a null preprocessor directive.
  374.                 */
  375.                cs->tok_sav = t1;
  376.                AppChar(tknize_sbuf, '#');
  377.                return new_token(PpNull, str_install(&tknize_sbuf), fname, line);
  378.                }
  379.             else
  380.                free_t(t1);  /* discard white space */
  381.          c = *next_char;
  382.          if (islower(c) || isupper(c) || c == '_') {
  383.             /*
  384.              * Tokenize the identifier following the #
  385.              */
  386.             t1 = tokenize();
  387.             if ((tok_id = pp_tok_id(t1->image)) == Invalid) {
  388.                /*
  389.                 * We have a stringizing operation, not a preprocessing
  390.                 *  directive.
  391.                 */
  392.                cs->dir_state = Reset;
  393.                cs->tok_sav = t1;
  394.                AppChar(tknize_sbuf, '#');
  395.                return new_token('#', str_install(&tknize_sbuf), fname, line);
  396.                }
  397.             else {
  398.                t1->tok_id = tok_id;
  399.                if (tok_id == PpInclude) {
  400.                   /*
  401.                    * A header name has to be tokenized specially. Find
  402.                    *  it, then save the token.
  403.                    */
  404.                   if ((t2 = chck_wh_sp(cs)) != NULL)
  405.                      if (t2->tok_id == PpDirEnd)
  406.                         errt1(t2, "file name missing from #include");
  407.                      else
  408.                         free_t(t2);
  409.                   c = *next_char;
  410.                   if (c == '"')
  411.                      cs->tok_sav = hdr_tok('"', StrLit, cs);
  412.                   else if (c == '<')
  413.                      cs->tok_sav = hdr_tok('>', PpHeader, cs);
  414.                   }
  415.                /*
  416.                 * Return the token indicating the kind of preprocessor
  417.                 *  directive we have started.
  418.                 */
  419.                return t1;
  420.                }
  421.             }
  422.          else
  423.             errfl1(fname, line,
  424.                "# must be followed by an identifier or keyword");
  425.          }
  426.       }
  427.  
  428.    /*
  429.     * Check for literals containing wide characters.
  430.     */
  431.    if (c == 'L') {
  432.       if (*next_char == '\'') {
  433.          AdvChar();
  434.          t1 = char_str('\'', LCharConst);
  435.          if (t1->image[0] == '\0')
  436.             errt1(t1, "invalid character constant");
  437.          return t1;
  438.          }
  439.       else if (*next_char == '"') {
  440.          AdvChar();
  441.          return char_str('"', LStrLit);
  442.          }
  443.       }
  444.  
  445.    /*
  446.     * Check for identifier.
  447.     */
  448.    if (islower(c) || isupper(c) || c == '_') {
  449.       AppChar(tknize_sbuf, c);
  450.       c = *next_char;
  451.       while (islower(c) || isupper(c) || isdigit(c) || c == '_') {
  452.          AppChar(tknize_sbuf, c);
  453.          AdvChar();
  454.          c = *next_char;
  455.          }
  456.       return new_token(Identifier, str_install(&tknize_sbuf), fname, line);
  457.      }
  458.  
  459.    /*
  460.     * Check for number.
  461.     */
  462.    if (isdigit(c)) {
  463.       AppChar(tknize_sbuf, c);
  464.       return pp_number();
  465.       }
  466.   
  467.    /*
  468.     * Check for character constant.
  469.     */
  470.    if (c == '\'') {
  471.       t1 = char_str(c, CharConst);
  472.       if (t1->image[0] == '\0')
  473.          errt1(t1, "invalid character constant");
  474.       return t1;
  475.       }
  476.  
  477.    /*
  478.     * Check for string constant.
  479.     */
  480.    if (c == '"')
  481.       return char_str(c, StrLit);
  482.  
  483.    /*
  484.     * Check for operators and punctuation. Anything that does not fit these
  485.     *  categories is a single character token.
  486.     */
  487.    AppChar(tknize_sbuf, c);
  488.    switch (c) {
  489.       case '.':
  490.          c = *next_char;
  491.          if (isdigit(c)) {
  492.             /*
  493.              * Number
  494.              */
  495.             AppChar(tknize_sbuf, c);
  496.             AdvChar();
  497.             return pp_number();
  498.             }
  499.          else if (c == '.' && next_char[1] == '.') {
  500.             /*
  501.              *  ...
  502.              */
  503.             AdvChar();
  504.             AdvChar();
  505.             AppChar(tknize_sbuf, '.');
  506.             AppChar(tknize_sbuf, '.');
  507.             return new_token(Ellipsis, str_install(&tknize_sbuf), fname, line);
  508.             }
  509.          else
  510.             return new_token('.', str_install(&tknize_sbuf), fname, line);
  511.  
  512.       case '+':
  513.          c = *next_char;
  514.          if (c == '+') {
  515.             /*
  516.              *  ++
  517.              */
  518.             AppChar(tknize_sbuf, '+');
  519.             AdvChar();
  520.             return new_token(Incr, str_install(&tknize_sbuf), fname, line);
  521.             }
  522.          else if (c == '=') {
  523.             /*
  524.              *  +=
  525.              */
  526.             AppChar(tknize_sbuf, '=');
  527.             AdvChar();
  528.             return new_token(PlusAsgn, str_install(&tknize_sbuf), fname, line);
  529.             }
  530.          else
  531.             return new_token('+', str_install(&tknize_sbuf), fname, line);
  532.  
  533.       case '-':
  534.          c = *next_char;
  535.          if (c == '>') {
  536.             /*
  537.              *  ->
  538.              */
  539.             AppChar(tknize_sbuf, '>');
  540.             AdvChar();
  541.             return new_token(Arrow, str_install(&tknize_sbuf), fname, line);
  542.             }
  543.          else if (c == '-') {
  544.             /*
  545.              *  --
  546.              */
  547.             AppChar(tknize_sbuf, '-');
  548.             AdvChar();
  549.             return new_token(Decr, str_install(&tknize_sbuf), fname, line);
  550.             }
  551.          else if (c == '=') {
  552.             /*
  553.              *  -=
  554.              */
  555.             AppChar(tknize_sbuf, '=');
  556.             AdvChar();
  557.             return new_token(MinusAsgn, str_install(&tknize_sbuf), fname,
  558.                line);
  559.             }
  560.          else
  561.             return new_token('-', str_install(&tknize_sbuf), fname, line);
  562.  
  563.       case '<':
  564.          c = *next_char;
  565.          if (c == '<') {
  566.             AppChar(tknize_sbuf, '<');
  567.             AdvChar();
  568.             if (*next_char == '=') {
  569.                /*
  570.                 *  <<=
  571.                 */
  572.                AppChar(tknize_sbuf, '=');
  573.                AdvChar();
  574.                return new_token(LShftAsgn, str_install(&tknize_sbuf), fname,
  575.                   line);
  576.                }
  577.             else
  578.                /*
  579.                 *  <<
  580.                 */
  581.                return new_token(LShft, str_install(&tknize_sbuf), fname, line);
  582.             }
  583.          else if (c == '=') {
  584.             /*
  585.              *  <=
  586.              */
  587.             AppChar(tknize_sbuf, '=');
  588.             AdvChar();
  589.             return new_token(Leq, str_install(&tknize_sbuf), fname, line);
  590.             }
  591.          else
  592.             return new_token('<', str_install(&tknize_sbuf), fname, line);
  593.  
  594.       case '>':
  595.          c = *next_char;
  596.          if (c == '>') {
  597.             AppChar(tknize_sbuf, '>');
  598.             AdvChar();
  599.             if (*next_char == '=') {
  600.                /*
  601.                 *  >>=
  602.                 */
  603.                AppChar(tknize_sbuf, '=');
  604.                AdvChar();
  605.                return new_token(RShftAsgn, str_install(&tknize_sbuf), fname,
  606.                   line);
  607.                }
  608.             else
  609.                /*
  610.                 *  >>
  611.                 */
  612.                return new_token(RShft, str_install(&tknize_sbuf), fname, line);
  613.             }
  614.          else if (c == '=') {
  615.             /*
  616.              *  >=
  617.              */
  618.             AppChar(tknize_sbuf, '=');
  619.             AdvChar();
  620.             return new_token(Geq, str_install(&tknize_sbuf), fname, line);
  621.             }
  622.          else
  623.             return new_token('>', str_install(&tknize_sbuf), fname, line);
  624.  
  625.       case '=':
  626.          if (*next_char == '=') {
  627.             /*
  628.              *  ==
  629.              */
  630.             AppChar(tknize_sbuf, '=');
  631.             AdvChar();
  632.             return new_token(Equal, str_install(&tknize_sbuf), fname, line);
  633.             }
  634.          else
  635.             return new_token('=', str_install(&tknize_sbuf), fname, line);
  636.  
  637.       case '!':
  638.          if (*next_char == '=') {
  639.             /*
  640.              *  !=
  641.              */
  642.             AppChar(tknize_sbuf, '=');
  643.             AdvChar();
  644.             return new_token(Neq, str_install(&tknize_sbuf), fname, line);
  645.             }
  646.          else
  647.             return new_token('!', str_install(&tknize_sbuf), fname, line);
  648.  
  649.       case '&':
  650.          c = *next_char;
  651.          if (c == '&') {
  652.             /*
  653.              *  &&
  654.              */
  655.             AppChar(tknize_sbuf, '&');
  656.             AdvChar();
  657.             return new_token(And, str_install(&tknize_sbuf), fname, line);
  658.             }
  659.          else if (c == '=') {
  660.             /*
  661.              *  &=
  662.              */
  663.             AppChar(tknize_sbuf, '=');
  664.             AdvChar();
  665.             return new_token(AndAsgn, str_install(&tknize_sbuf), fname, line);
  666.             }
  667.          else
  668.             return new_token('&', str_install(&tknize_sbuf), fname, line);
  669.  
  670.       case '|':
  671.          c = *next_char;
  672.          if (c == '|') {
  673.             /*
  674.              *  ||
  675.              */
  676.             AppChar(tknize_sbuf, '|');
  677.             AdvChar();
  678.             return new_token(Or, str_install(&tknize_sbuf), fname, line);
  679.             }
  680.          else if (c == '=') {
  681.             /*
  682.              *  |=
  683.              */
  684.             AppChar(tknize_sbuf, '=');
  685.             AdvChar();
  686.             return new_token(OrAsgn, str_install(&tknize_sbuf), fname, line);
  687.             }
  688.          else
  689.             return new_token('|', str_install(&tknize_sbuf), fname, line);
  690.  
  691.       case '*':
  692.          if (*next_char == '=') {
  693.             /*
  694.              *  *=
  695.              */
  696.             AppChar(tknize_sbuf, '=');
  697.             AdvChar();
  698.             return new_token(MultAsgn, str_install(&tknize_sbuf), fname, line);
  699.             }
  700.          else
  701.             return new_token('*', str_install(&tknize_sbuf), fname, line);
  702.  
  703.       case '/':
  704.          if (*next_char == '=') {
  705.             /*
  706.              *  /=
  707.              */
  708.             AppChar(tknize_sbuf, '=');
  709.             AdvChar();
  710.             return new_token(DivAsgn, str_install(&tknize_sbuf), fname, line);
  711.             }
  712.          else
  713.             return new_token('/', str_install(&tknize_sbuf), fname, line);
  714.  
  715.       case '%':
  716.          if (*next_char == '=') {
  717.             /*
  718.              *  &=
  719.              */
  720.             AppChar(tknize_sbuf, '=');
  721.             AdvChar();
  722.             return new_token(ModAsgn, str_install(&tknize_sbuf), fname, line);
  723.             }
  724.          else
  725.             return new_token('%', str_install(&tknize_sbuf), fname, line);
  726.  
  727.       case '^':
  728.          if (*next_char == '=') {
  729.             /*
  730.              *  ^=
  731.              */
  732.             AppChar(tknize_sbuf, '=');
  733.             AdvChar();
  734.             return new_token(XorAsgn, str_install(&tknize_sbuf), fname, line);
  735.             }
  736.          else
  737.             return new_token('^', str_install(&tknize_sbuf), fname, line);
  738.  
  739.       case '#':
  740.          /*
  741.           * Token pasting or stringizing operator.
  742.           */
  743.          if (*next_char == '#') {
  744.             /*
  745.              *  ##
  746.              */
  747.             AppChar(tknize_sbuf, '#');
  748.             AdvChar();
  749.             t1 =  new_token(PpPaste, str_install(&tknize_sbuf), fname, line);
  750.             }
  751.          else
  752.             t1 = new_token('#', str_install(&tknize_sbuf), fname, line);
  753.  
  754.          /*
  755.           * The operand must be in the same preprocessing directive.
  756.           */
  757.          if ((t2 = chck_wh_sp(cs)) != NULL)
  758.             if (t2->tok_id == PpDirEnd)
  759.               errt2(t2, t1->image,
  760.                " preprocessing expression must not cross directive boundary");
  761.             else
  762.                free_t(t2);
  763.          return t1;
  764.  
  765.       default:
  766.          return new_token(c, str_install(&tknize_sbuf), fname, line);
  767.       }
  768.    }
  769.