Ejemplo n.º 1
0
/* Reads a logical line into the output buffer.  Returns TRUE if there
   is more text left in the buffer.  */
bool
_cpp_read_logical_line_trad (cpp_reader *pfile)
{
  do
    {
      if (pfile->buffer->need_line && !_cpp_get_fresh_line (pfile))
	return false;
    }
  while (!_cpp_scan_out_logical_line (pfile, NULL) || pfile->state.skipping);

  return pfile->buffer != NULL;
}
Ejemplo n.º 2
0
/* Writes out the preprocessed file, handling spacing and paste
   avoidance issues.  */
void
_cpp_preprocess_dir_only (cpp_reader *pfile,
			  const struct _cpp_dir_only_callbacks *cb)
{
  struct cpp_buffer *buffer;
  const unsigned char *cur, *base, *next_line, *rlimit;
  cppchar_t c, last_c;
  unsigned flags;
  linenum_type lines;
  int col;
  source_location loc;

 restart:
  /* Buffer initialization ala _cpp_clean_line(). */
  buffer = pfile->buffer;
  buffer->cur_note = buffer->notes_used = 0;
  buffer->cur = buffer->line_base = buffer->next_line;
  buffer->need_line = false;

  /* This isn't really needed.  It prevents a compiler warning, though. */
  loc = pfile->line_table->highest_line;

  /* Scan initialization. */
  next_line = cur = base = buffer->cur;
  rlimit = buffer->rlimit;
  flags = DO_BOL;
  lines = 0;
  col = 1;

  for (last_c = '\n', c = *cur; cur < rlimit; last_c = c, c = *++cur, ++col)
    {
      /* Skip over escaped newlines. */
      if (__builtin_expect (c == '\\', false))
	{
	  const unsigned char *tmp = cur + 1;

	  while (is_nvspace (*tmp) && tmp < rlimit)
	    tmp++;
	  if (*tmp == '\r')
	    tmp++;
	  if (*tmp == '\n' && tmp < rlimit)
	    {
	      CPP_INCREMENT_LINE (pfile, 0);
	      lines++;
	      col = 0;
	      cur = tmp;
	      c = last_c;
	      continue;
	    }
	}

      if (__builtin_expect (last_c == '#', false) && !(flags & DO_SPECIAL))
	{
	  if (c != '#' && (flags & DO_BOL))
	  {
	    struct line_maps *line_table;

	    if (!pfile->state.skipping && next_line != base)
	      cb->print_lines (lines, base, next_line - base);

	    /* Prep things for directive handling. */
	    buffer->next_line = cur;
	    buffer->need_line = true;
	    _cpp_get_fresh_line (pfile);

	    /* Ensure proper column numbering for generated error messages. */
	    buffer->line_base -= col - 1;

	    _cpp_handle_directive (pfile, 0 /* ignore indented */);

	    /* Sanitize the line settings.  Duplicate #include's can mess
	       things up. */
	    line_table = pfile->line_table;
	    line_table->highest_location = line_table->highest_line;

	    /* The if block prevents us from outputing line information when
	       the file ends with a directive and no newline.  Note that we
	       must use pfile->buffer, not buffer. */
	    if (pfile->buffer->next_line < pfile->buffer->rlimit)
	      cb->maybe_print_line (pfile->line_table->highest_line);

	    goto restart;
	  }

	  flags &= ~DO_BOL;
	  pfile->mi_valid = false;
	}
      else if (__builtin_expect (last_c == '/', false) \
	       && !(flags & DO_SPECIAL) && c != '*' && c != '/')
	{
	  /* If a previous slash is not starting a block comment, clear the
	     DO_BOL flag.  */
	  flags &= ~DO_BOL;
	  pfile->mi_valid = false;
	}

      switch (c)
	{
	case '/':
	  if ((flags & DO_BLOCK_COMMENT) && last_c == '*')
	    {
	      flags &= ~DO_BLOCK_COMMENT;
	      c = 0;
	    }
	  else if (!(flags & DO_SPECIAL) && last_c == '/')
	    flags |= DO_LINE_COMMENT;
	  else if (!(flags & DO_SPECIAL))
	    /* Mark the position for possible error reporting. */
	    loc = linemap_position_for_column (pfile->line_table, col);

	  break;

	case '*':
	  if (!(flags & DO_SPECIAL))
	    {
	      if (last_c == '/')
		flags |= DO_BLOCK_COMMENT;
	      else
		{
		  flags &= ~DO_BOL;
		  pfile->mi_valid = false;
		}
	    }

	  break;

	case '\'':
	case '"':
	  {
	    unsigned state = (c == '"') ? DO_STRING : DO_CHAR;

	    if (!(flags & DO_SPECIAL))
	      {
		flags |= state;
		flags &= ~DO_BOL;
		pfile->mi_valid = false;
	      }
	    else if ((flags & state) && last_c != '\\')
	      flags &= ~state;

	    break;
	  }

	case '\\':
	  {
	    if ((flags & (DO_STRING | DO_CHAR)) && last_c == '\\')
	      c = 0;

	    if (!(flags & DO_SPECIAL))
	      {
		flags &= ~DO_BOL;
		pfile->mi_valid = false;
	      }

	    break;
	  }

	case '\n':
	  CPP_INCREMENT_LINE (pfile, 0);
	  lines++;
	  col = 0;
	  flags &= ~DO_LINE_SPECIAL;
	  if (!(flags & DO_SPECIAL))
	    flags |= DO_BOL;
	  break;

	case '#':
	  next_line = cur;
	  /* Don't update DO_BOL yet. */
	  break;

	case ' ': case '\t': case '\f': case '\v': case '\0':
	  break;

	default:
	  if (!(flags & DO_SPECIAL))
	    {
	      flags &= ~DO_BOL;
	      pfile->mi_valid = false;
	    }
	  break;
	}
    }

  if (flags & DO_BLOCK_COMMENT)
    cpp_error_with_line (pfile, CPP_DL_ERROR, loc, 0, "unterminated comment");

  if (!pfile->state.skipping && cur != base)
    {
      /* If the file was not newline terminated, add rlimit, which is
         guaranteed to point to a newline, to the end of our range.  */
      if (cur[-1] != '\n')
	{
	  cur++;
	  CPP_INCREMENT_LINE (pfile, 0);
	  lines++;
	}

      cb->print_lines (lines, base, cur - base);
    }

  _cpp_pop_buffer (pfile);
  if (pfile->buffer)
    goto restart;
}
Ejemplo n.º 3
0
/* Copies the next logical line in the current buffer (starting at
   buffer->cur) to the output buffer.  The output is guaranteed to
   terminate with a NUL character.  buffer->cur is updated.

   If MACRO is non-NULL, then we are scanning the replacement list of
   MACRO, and we call save_replacement_text() every time we meet an
   argument.  */
bool
_cpp_scan_out_logical_line (cpp_reader *pfile, cpp_macro *macro)
{
  bool result = true;
  cpp_context *context;
  const uchar *cur;
  uchar *out;
  struct fun_macro fmacro;
  unsigned int c, paren_depth = 0, quote;
  enum ls lex_state = ls_none;
  bool header_ok;
  const uchar *start_of_input_line;

  fmacro.buff = NULL;

  quote = 0;
  header_ok = pfile->state.angled_headers;
  CUR (pfile->context) = pfile->buffer->cur;
  RLIMIT (pfile->context) = pfile->buffer->rlimit;
  pfile->out.cur = pfile->out.base;
  pfile->out.first_line = pfile->line;
  /* start_of_input_line is needed to make sure that directives really,
     really start at the first character of the line.  */
  start_of_input_line = pfile->buffer->cur;
 new_context:
  context = pfile->context;
  cur = CUR (context);
  check_output_buffer (pfile, RLIMIT (context) - cur);
  out = pfile->out.cur;

  for (;;)
    {
      if (!context->prev
	  && cur >= pfile->buffer->notes[pfile->buffer->cur_note].pos)
	{
	  pfile->buffer->cur = cur;
	  _cpp_process_line_notes (pfile, false);
	}
      c = *cur++;
      *out++ = c;

      /* Whitespace should "continue" out of the switch,
	 non-whitespace should "break" out of it.  */
      switch (c)
	{
	case ' ':
	case '\t':
	case '\f':
	case '\v':
	case '\0':
	  continue;

	case '\n':
	  /* If this is a macro's expansion, pop it.  */
	  if (context->prev)
	    {
	      pfile->out.cur = out - 1;
	      _cpp_pop_context (pfile);
	      goto new_context;
	    }

	  /* Omit the newline from the output buffer.  */
	  pfile->out.cur = out - 1;
	  pfile->buffer->cur = cur;
	  pfile->buffer->need_line = true;
	  pfile->line++;

	  if ((lex_state == ls_fun_open || lex_state == ls_fun_close)
	      && !pfile->state.in_directive
	      && _cpp_get_fresh_line (pfile))
	    {
	      /* Newlines in arguments become a space, but we don't
		 clear any in-progress quote.  */
	      if (lex_state == ls_fun_close)
		out[-1] = ' ';
	      cur = pfile->buffer->cur;
	      continue;
	    }
	  goto done;

	case '<':
	  if (header_ok)
	    quote = '>';
	  break;
	case '>':
	  if (c == quote)
	    quote = 0;
	  break;

	case '"':
	case '\'':
	  if (c == quote)
	    quote = 0;
	  else if (!quote)
	    quote = c;
	  break;

	case '\\':
	  /* Skip escaped quotes here, it's easier than above.  */
	  if (*cur == '\\' || *cur == '"' || *cur == '\'')
	    *out++ = *cur++;
	  break;

	case '/':
	  /* Traditional CPP does not recognize comments within
	     literals.  */
	  if (!quote && *cur == '*')
	    {
	      pfile->out.cur = out;
	      cur = copy_comment (pfile, cur, macro != 0);
	      out = pfile->out.cur;
	      continue;
	    }
	  break;

	case '_':
	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
	case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
	case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
	case 's': case 't': case 'u': case 'v': case 'w': case 'x':
	case 'y': case 'z':
	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
	case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
	case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
	case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
	case 'Y': case 'Z':
	  if (!pfile->state.skipping && (quote == 0 || macro))
	    {
	      cpp_hashnode *node;
	      uchar *out_start = out - 1;

	      pfile->out.cur = out_start;
	      node = lex_identifier (pfile, cur - 1);
	      out = pfile->out.cur;
	      cur = CUR (context);

	      if (node->type == NT_MACRO
		  /* Should we expand for ls_answer?  */
		  && (lex_state == ls_none || lex_state == ls_fun_open)
		  && !pfile->state.prevent_expansion)
		{
		  /* Macros invalidate MI optimization.  */
		  pfile->mi_valid = false;
		  if (! (node->flags & NODE_BUILTIN)
		      && node->value.macro->fun_like)
		    {
		      maybe_start_funlike (pfile, node, out_start, &fmacro);
		      lex_state = ls_fun_open;
		      fmacro.line = pfile->line;
		      continue;
		    }
		  else if (!recursive_macro (pfile, node))
		    {
		      /* Remove the object-like macro's name from the
			 output, and push its replacement text.  */
		      pfile->out.cur = out_start;
		      push_replacement_text (pfile, node);
		      lex_state = ls_none;
		      goto new_context;
		    }
		}
	      else if (macro && (node->flags & NODE_MACRO_ARG) != 0)
		{
		  /* Found a parameter in the replacement text of a
		     #define.  Remove its name from the output.  */
		  pfile->out.cur = out_start;
		  save_replacement_text (pfile, macro, node->value.arg_index);
		  out = pfile->out.base;
		}
	      else if (lex_state == ls_hash)
		{
		  lex_state = ls_predicate;
		  continue;
		}
	      else if (pfile->state.in_expression
		       && node == pfile->spec_nodes.n_defined)
		{
		  lex_state = ls_defined;
		  continue;
		}
	    }
	  break;

	case '(':
	  if (quote == 0)
	    {
	      paren_depth++;
	      if (lex_state == ls_fun_open)
		{
		  if (recursive_macro (pfile, fmacro.node))
		    lex_state = ls_none;
		  else
		    {
		      lex_state = ls_fun_close;
		      paren_depth = 1;
		      out = pfile->out.base + fmacro.offset;
		      fmacro.args[0] = fmacro.offset;
		    }
		}
	      else if (lex_state == ls_predicate)
		lex_state = ls_answer;
	      else if (lex_state == ls_defined)
		lex_state = ls_defined_close;
	    }
	  break;

	case ',':
	  if (quote == 0 && lex_state == ls_fun_close && paren_depth == 1)
	    save_argument (&fmacro, out - pfile->out.base);
	  break;

	case ')':
	  if (quote == 0)
	    {
	      paren_depth--;
	      if (lex_state == ls_fun_close && paren_depth == 0)
		{
		  cpp_macro *m = fmacro.node->value.macro;

		  m->used = 1;
		  lex_state = ls_none;
		  save_argument (&fmacro, out - pfile->out.base);

		  /* A single zero-length argument is no argument.  */
		  if (fmacro.argc == 1
		      && m->paramc == 0
		      && out == pfile->out.base + fmacro.offset + 1)
		    fmacro.argc = 0;

		  if (_cpp_arguments_ok (pfile, m, fmacro.node, fmacro.argc))
		    {
		      /* Remove the macro's invocation from the
			 output, and push its replacement text.  */
		      pfile->out.cur = (pfile->out.base
					     + fmacro.offset);
		      CUR (context) = cur;
		      replace_args_and_push (pfile, &fmacro);
		      goto new_context;
		    }
		}
	      else if (lex_state == ls_answer || lex_state == ls_defined_close)
		lex_state = ls_none;
	    }
	  break;

	case '#':
	  if (cur - 1 == start_of_input_line
	      /* A '#' from a macro doesn't start a directive.  */
	      && !pfile->context->prev
	      && !pfile->state.in_directive)
	    {
	      /* A directive.  With the way _cpp_handle_directive
		 currently works, we only want to call it if either we
		 know the directive is OK, or we want it to fail and
		 be removed from the output.  If we want it to be
		 passed through (the assembler case) then we must not
		 call _cpp_handle_directive.  */
	      pfile->out.cur = out;
	      cur = skip_whitespace (pfile, cur, true /* skip_comments */);
	      out = pfile->out.cur;

	      if (*cur == '\n')
		{
		  /* Null directive.  Ignore it and don't invalidate
		     the MI optimization.  */
		  pfile->buffer->need_line = true;
		  pfile->line++;
		  result = false;
		  goto done;
		}
	      else
		{
		  bool do_it = false;

		  if (is_numstart (*cur)
		      && CPP_OPTION (pfile, lang) != CLK_ASM)
		    do_it = true;
		  else if (is_idstart (*cur))
		    /* Check whether we know this directive, but don't
		       advance.  */
		    do_it = lex_identifier (pfile, cur)->is_directive;

		  if (do_it || CPP_OPTION (pfile, lang) != CLK_ASM)
		    {
		      /* This is a kludge.  We want to have the ISO
			 preprocessor lex the next token.  */
		      pfile->buffer->cur = cur;
		      _cpp_handle_directive (pfile, false /* indented */);
		      result = false;
		      goto done;
		    }
		}
	    }

	  if (pfile->state.in_expression)
	    {
	      lex_state = ls_hash;
	      continue;
	    }
	  break;

	default:
	  break;
	}

      /* Non-whitespace disables MI optimization and stops treating
	 '<' as a quote in #include.  */
      header_ok = false;
      if (!pfile->state.in_directive)
	pfile->mi_valid = false;

      if (lex_state == ls_none)
	continue;

      /* Some of these transitions of state are syntax errors.  The
	 ISO preprocessor will issue errors later.  */
      if (lex_state == ls_fun_open)
	/* Missing '('.  */
	lex_state = ls_none;
      else if (lex_state == ls_hash
	       || lex_state == ls_predicate
	       || lex_state == ls_defined)
	lex_state = ls_none;

      /* ls_answer and ls_defined_close keep going until ')'.  */
    }

 done:
  if (fmacro.buff)
    _cpp_release_buff (pfile, fmacro.buff);

  if (lex_state == ls_fun_close)
    cpp_error_with_line (pfile, CPP_DL_ERROR, fmacro.line, 0,
			 "unterminated argument list invoking macro \"%s\"",
			 NODE_NAME (fmacro.node));
  return result;
}
Ejemplo n.º 4
0
/* Lex a token into pfile->cur_token, which is also incremented, to
   get diagnostics pointing to the correct location.

   Does not handle issues such as token lookahead, multiple-include
   optimization, directives, skipping etc.  This function is only
   suitable for use by _cpp_lex_token, and in special cases like
   lex_expansion_token which doesn't care for any of these issues.

   When meeting a newline, returns CPP_EOF if parsing a directive,
   otherwise returns to the start of the token buffer if permissible.
   Returns the location of the lexed token.  */
cpp_token *
_cpp_lex_direct (cpp_reader *pfile)
{
  cppchar_t c;
  cpp_buffer *buffer;
  const unsigned char *comment_start;
  cpp_token *result = pfile->cur_token++;

 fresh_line:
  result->flags = 0;
  buffer = pfile->buffer;
  if (buffer->need_line)
    {
      if (!_cpp_get_fresh_line (pfile))
	{
	  result->type = CPP_EOF;
	  if (!pfile->state.in_directive)
	    {
	      /* Tell the compiler the line number of the EOF token.  */
	      result->line = pfile->line;
	      result->flags = BOL;
	    }
	  return result;
	}
      if (!pfile->keep_tokens)
	{
	  pfile->cur_run = &pfile->base_run;
	  result = pfile->base_run.base;
	  pfile->cur_token = result + 1;
	}
      result->flags = BOL;
      if (pfile->state.parsing_args == 2)
	result->flags |= PREV_WHITE;
    }
  buffer = pfile->buffer;
 update_tokens_line:
  result->line = pfile->line;

 skipped_white:
  if (buffer->cur >= buffer->notes[buffer->cur_note].pos
      && !pfile->overlaid_buffer)
    {
      _cpp_process_line_notes (pfile, false);
      result->line = pfile->line;
    }
  c = *buffer->cur++;
  result->col = CPP_BUF_COLUMN (buffer, buffer->cur);

  switch (c)
    {
    case ' ': case '\t': case '\f': case '\v': case '\0':
      result->flags |= PREV_WHITE;
      skip_whitespace (pfile, c);
      goto skipped_white;

    case '\n':
      pfile->line++;
      buffer->need_line = true;
      goto fresh_line;

    case '0': case '1': case '2': case '3': case '4':
    case '5': case '6': case '7': case '8': case '9':
      result->type = CPP_NUMBER;
      lex_number (pfile, &result->val.str);
      break;

    case 'L':
      /* 'L' may introduce wide characters or strings.  */
      if (*buffer->cur == '\'' || *buffer->cur == '"')
	{
	  lex_string (pfile, result, buffer->cur - 1);
	  break;
	}
      /* Fall through.  */

    case '_':
    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
    case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
    case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
    case 's': case 't': case 'u': case 'v': case 'w': case 'x':
    case 'y': case 'z':
    case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
    case 'G': case 'H': case 'I': case 'J': case 'K':
    case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
    case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
    case 'Y': case 'Z':
      result->type = CPP_NAME;
      result->val.node = lex_identifier (pfile, buffer->cur - 1);

      /* Convert named operators to their proper types.  */
      if (result->val.node->flags & NODE_OPERATOR)
	{
	  result->flags |= NAMED_OP;
	  result->type = result->val.node->directive_index;
	}
      break;

    case '\'':
    case '"':
      lex_string (pfile, result, buffer->cur - 1);
      break;

    case '/':
      /* A potential block or line comment.  */
      comment_start = buffer->cur;
      c = *buffer->cur;
      
      if (c == '*')
	{
	  if (_cpp_skip_block_comment (pfile))
	    cpp_error (pfile, CPP_DL_ERROR, "unterminated comment");
	}
      else if (c == '/' && (CPP_OPTION (pfile, cplusplus_comments)
			    || CPP_IN_SYSTEM_HEADER (pfile)))
	{
	  /* Warn about comments only if pedantically GNUC89, and not
	     in system headers.  */
	  if (CPP_OPTION (pfile, lang) == CLK_GNUC89 && CPP_PEDANTIC (pfile)
	      && ! buffer->warned_cplusplus_comments)
	    {
	      cpp_error (pfile, CPP_DL_PEDWARN,
			 "C++ style comments are not allowed in ISO C90");
	      cpp_error (pfile, CPP_DL_PEDWARN,
			 "(this will be reported only once per input file)");
	      buffer->warned_cplusplus_comments = 1;
	    }

	  if (skip_line_comment (pfile) && CPP_OPTION (pfile, warn_comments))
	    cpp_error (pfile, CPP_DL_WARNING, "multi-line comment");
	}
      else if (c == '=')
	{
	  buffer->cur++;
	  result->type = CPP_DIV_EQ;
	  break;
	}
      else
	{
	  result->type = CPP_DIV;
	  break;
	}

      if (!pfile->state.save_comments)
	{
	  result->flags |= PREV_WHITE;
	  goto update_tokens_line;
	}

      /* Save the comment as a token in its own right.  */
      save_comment (pfile, result, comment_start, c);
      break;

    case '<':
      if (pfile->state.angled_headers)
	{
	  lex_string (pfile, result, buffer->cur - 1);
	  break;
	}

      result->type = CPP_LESS;
      if (*buffer->cur == '=')
	buffer->cur++, result->type = CPP_LESS_EQ;
      else if (*buffer->cur == '<')
	{
	  buffer->cur++;
	  IF_NEXT_IS ('=', CPP_LSHIFT_EQ, CPP_LSHIFT);
	}
      else if (*buffer->cur == '?' && CPP_OPTION (pfile, cplusplus))
	{
	  buffer->cur++;
	  IF_NEXT_IS ('=', CPP_MIN_EQ, CPP_MIN);
	}
      else if (CPP_OPTION (pfile, digraphs))
	{
	  if (*buffer->cur == ':')
	    {
	      buffer->cur++;
	      result->flags |= DIGRAPH;
	      result->type = CPP_OPEN_SQUARE;
	    }
	  else if (*buffer->cur == '%')
	    {
	      buffer->cur++;
	      result->flags |= DIGRAPH;
	      result->type = CPP_OPEN_BRACE;
	    }
	}
      break;

    case '>':
      result->type = CPP_GREATER;
      if (*buffer->cur == '=')
	buffer->cur++, result->type = CPP_GREATER_EQ;
      else if (*buffer->cur == '>')
	{
	  buffer->cur++;
	  IF_NEXT_IS ('=', CPP_RSHIFT_EQ, CPP_RSHIFT);
	}
      else if (*buffer->cur == '?' && CPP_OPTION (pfile, cplusplus))
	{
	  buffer->cur++;
	  IF_NEXT_IS ('=', CPP_MAX_EQ, CPP_MAX);
	}
      break;

    case '%':
      result->type = CPP_MOD;
      if (*buffer->cur == '=')
	buffer->cur++, result->type = CPP_MOD_EQ;
      else if (CPP_OPTION (pfile, digraphs))
	{
	  if (*buffer->cur == ':')
	    {
	      buffer->cur++;
	      result->flags |= DIGRAPH;
	      result->type = CPP_HASH;
	      if (*buffer->cur == '%' && buffer->cur[1] == ':')
		buffer->cur += 2, result->type = CPP_PASTE;
	    }
	  else if (*buffer->cur == '>')
	    {
	      buffer->cur++;
	      result->flags |= DIGRAPH;
	      result->type = CPP_CLOSE_BRACE;
	    }
	}
      break;

    case '.':
      result->type = CPP_DOT;
      if (ISDIGIT (*buffer->cur))
	{
	  result->type = CPP_NUMBER;
	  lex_number (pfile, &result->val.str);
	}
      else if (*buffer->cur == '.' && buffer->cur[1] == '.')
	buffer->cur += 2, result->type = CPP_ELLIPSIS;
      else if (*buffer->cur == '*' && CPP_OPTION (pfile, cplusplus))
	buffer->cur++, result->type = CPP_DOT_STAR;
      break;

    case '+':
      result->type = CPP_PLUS;
      if (*buffer->cur == '+')
	buffer->cur++, result->type = CPP_PLUS_PLUS;
      else if (*buffer->cur == '=')
	buffer->cur++, result->type = CPP_PLUS_EQ;
      break;

    case '-':
      result->type = CPP_MINUS;
      if (*buffer->cur == '>')
	{
	  buffer->cur++;
	  result->type = CPP_DEREF;
	  if (*buffer->cur == '*' && CPP_OPTION (pfile, cplusplus))
	    buffer->cur++, result->type = CPP_DEREF_STAR;
	}
      else if (*buffer->cur == '-')
	buffer->cur++, result->type = CPP_MINUS_MINUS;
      else if (*buffer->cur == '=')
	buffer->cur++, result->type = CPP_MINUS_EQ;
      break;

    case '&':
      result->type = CPP_AND;
      if (*buffer->cur == '&')
	buffer->cur++, result->type = CPP_AND_AND;
      else if (*buffer->cur == '=')
	buffer->cur++, result->type = CPP_AND_EQ;
      break;

    case '|':
      result->type = CPP_OR;
      if (*buffer->cur == '|')
	buffer->cur++, result->type = CPP_OR_OR;
      else if (*buffer->cur == '=')
	buffer->cur++, result->type = CPP_OR_EQ;
      break;

    case ':':
      result->type = CPP_COLON;
      if (*buffer->cur == ':' && CPP_OPTION (pfile, cplusplus))
	buffer->cur++, result->type = CPP_SCOPE;
      else if (*buffer->cur == '>' && CPP_OPTION (pfile, digraphs))
	{
	  buffer->cur++;
	  result->flags |= DIGRAPH;
	  result->type = CPP_CLOSE_SQUARE;
	}
      break;

    case '*': IF_NEXT_IS ('=', CPP_MULT_EQ, CPP_MULT); break;
    case '=': IF_NEXT_IS ('=', CPP_EQ_EQ, CPP_EQ); break;
    case '!': IF_NEXT_IS ('=', CPP_NOT_EQ, CPP_NOT); break;
    case '^': IF_NEXT_IS ('=', CPP_XOR_EQ, CPP_XOR); break;
    case '#': IF_NEXT_IS ('#', CPP_PASTE, CPP_HASH); break;

    case '?': result->type = CPP_QUERY; break;
    case '~': result->type = CPP_COMPL; break;
    case ',': result->type = CPP_COMMA; break;
    case '(': result->type = CPP_OPEN_PAREN; break;
    case ')': result->type = CPP_CLOSE_PAREN; break;
    case '[': result->type = CPP_OPEN_SQUARE; break;
    case ']': result->type = CPP_CLOSE_SQUARE; break;
    case '{': result->type = CPP_OPEN_BRACE; break;
    case '}': result->type = CPP_CLOSE_BRACE; break;
    case ';': result->type = CPP_SEMICOLON; break;

      /* @ is a punctuator in Objective-C.  */
    case '@': result->type = CPP_ATSIGN; break;

    case '$':
    case '\\':
      {
	const uchar *base = --buffer->cur;

	if (forms_identifier_p (pfile, true))
	  {
	    result->type = CPP_NAME;
	    result->val.node = lex_identifier (pfile, base);
	    break;
	  }
	buffer->cur++;
      }

    default:
      create_literal (pfile, result, buffer->cur - 1, 1, CPP_OTHER);
      break;
    }

  return result;
}