/* -----------------------------------------------------------------------------
 * scanner.cxx
 *
 *     SWIG1.1 tokenizer.
 *
 * Author(s) : David Beazley (beazley@cs.uchicago.edu)
 *
 * Copyright (C) 1998-2000.  The University of Chicago
 * Copyright (C) 1995-1998.  The University of Utah and The Regents of the
 *                           University of California.
 *
 * See the file LICENSE for information on usage and redistribution.
 * ----------------------------------------------------------------------------- */

char cvsroot_cscanner_c[] = "/cvsroot/SWIG/Source/CParse/cscanner.c,v 1.7 2004/01/15 22:46:04 cheetah Exp";

#include "cparse.h"
#include <../CParse/parser.h> /* Hack include to find correct parser.h */
#include <string.h>
#include <ctype.h>

#define  YYBSIZE  8192

typedef struct InFile {
  DOHFile *f;
  int     line_number;
  char   *in_file;
  struct InFile *prev;
} InFile;

InFile  *in_head;

DOHFile *LEX_in = 0;
static DOHString     *header = 0;
static DOHString     *comment = 0;
DOHString            *scanner_ccode = 0;            /* String containing C code */
static char          *yybuffer = 0;

static char    yytext[YYBSIZE];
static int     yylen = 0;
int            cparse_line = 1;
char          *cparse_file;
int            cparse_start_line = 0;
static  int    comment_start;
static  int    scan_init  = 0;
static  int    num_brace = 0;
static  int    last_brace = 0;
static  int    last_id = 0;
static  int    rename_active = 0;
        int    cparse_cplusplus;

/* -----------------------------------------------------------------------------
 * Swig_cparse_cplusplus()
 * ----------------------------------------------------------------------------- */

void
Swig_cparse_cplusplus(int v) {
  cparse_cplusplus = v;
}

/* ----------------------------------------------------------------------
 * locator()
 *
 * Support for locator strings.   These are strings of the form
 * @filename,line,id@ emitted by the SWIG preprocessor.  They
 * are primarily used for macro line number reporting 
 * ---------------------------------------------------------------------- */

typedef struct Locator {
  char           *filename;
  int             line_number;
  struct Locator *next;
} Locator;

static Locator *locs = 0;
  
static void
scanner_locator(String *loc) {
  int c;
  Locator *l;
  Seek(loc,1,SEEK_SET);
  c = Getc(loc);
  if (c == '@') {
    /* Empty locator.  We pop the last location off */
    if (locs) {
      cparse_file = locs->filename;
      cparse_line = locs->line_number;
      l = locs->next;
      free(locs);
      locs = l;
    }
    /*    Printf(stderr,"location: %s:%d\n",cparse_file,cparse_line);*/
    return;
  }

  /* We're going to push a new location */
  l = (Locator *) malloc(sizeof(Locator));
  l->filename = cparse_file;
  l->line_number = cparse_line;
  l->next = locs;
  locs = l;
  
  /* Now, parse the new location out of the locator string */
  {
    String *fn = NewString("");
    Putc(c,fn);
    
    while ((c = Getc(loc)) != EOF) {
      if ((c == '@') || (c == ',')) break;
      Putc(c,fn);
    }

    cparse_file = Swig_copy_string(Char(fn));
    Clear(fn);
  
    cparse_line = 1;
    /* Get the line number */
    while ((c = Getc(loc)) != EOF) {
      if ((c == '@') || (c == ',')) break;
      Putc(c,fn);
    }

    cparse_line = atoi(Char(fn));
    Clear(fn);

    /* Get the rest of it */
    while (( c= Getc(loc)) != EOF) {
      if (c == '@') break;
      Putc(c,fn);
    }
    /*  Printf(stderr,"location: %s:%d\n",cparse_file,cparse_line); */
    Delete(fn);
  }
}

/**************************************************************
 * scanner_init()
 *
 * Initialize buffers
 **************************************************************/

void scanner_init() {
  yybuffer = (char *) malloc(YYBSIZE);
  scan_init = 1;
  header = NewString("");
  comment = NewString("");
  scanner_ccode = NewString("");
}

/**************************************************************
 * scanner_file(FILE *f)
 *
 * Start reading from new file
 **************************************************************/
void scanner_file(DOHFile *f) {
  InFile *in;

  in = (InFile *) malloc(sizeof(InFile));
  in->f = f;
  in->in_file = cparse_file;
  in->line_number = 1;
  if (!in_head) in->prev = 0;
  else in->prev = in_head;
  in_head = in;
  LEX_in = f;
  cparse_line = 1;
}

/**************************************************************
 * scanner_close()
 *
 * Close current input file and go to next
 **************************************************************/

void scanner_close() {
  InFile *p;
  if (!in_head) return;
  Delete(LEX_in);
  p = in_head->prev;
  if (p != 0) {
    LEX_in = p->f;
    cparse_line = p->line_number;
    cparse_file = p->in_file;
  } else {
    LEX_in = 0;
  }
  free(in_head);
  in_head = p;
}

/**************************************************************
 * char nextchar()
 *
 * gets next character from input.
 * If we're in inlining mode, we actually retrieve a character
 * from inline_yybuffer instead.
 **************************************************************/

char nextchar() {
    int c = 0;

    while (LEX_in) {
      c = Getc(LEX_in);
      if (c == EOF) {
	scanner_close();
      } else {
	break;
      }
    }
    if (!LEX_in) return 0;
    if (yylen >= YYBSIZE) {
      Printf(stderr,"** FATAL ERROR.  Buffer overflow in scanner.cxx.\nReport this to swig-dev@cs.uchicago.edu.\n");
      exit (EXIT_FAILURE);
    }
    yytext[yylen] = c;
    yylen++;
    if (c == '\n') {
      cparse_line++;
    }
    return(c);
}

void retract(int n) {
  int i;
  for (i = 0; i < n; i++) {
    yylen--;
    if (yylen >= 0) {
      Ungetc(yytext[yylen],LEX_in);
      if (yytext[yylen] == '\n') {
	cparse_line--;
      }
    }
  }
  if (yylen < 0) yylen = 0;
}

/**************************************************************
 * start_inline(char *text, int line)
 *
 * This grabs a chunk of text and tries to inline it into
 * the current file.  (This is kind of wild, but cool when
 * it works).
 *
 * If we're already in inlining mode, we will save the code
 * as a new fragment.
 **************************************************************/

void start_inline(char *text, int line) {
  InFile *in;

  /* Save current state */
  in_head->line_number = cparse_line;
  in_head->in_file = cparse_file;

  in = (InFile *) malloc(sizeof(InFile));
  in->f = NewString(text);
  Seek(in->f,0,SEEK_SET);
  in->in_file = Swig_copy_string(cparse_file);
  in->line_number = line;
  in->prev = in_head;
  in_head = in;
  LEX_in = in->f;
  cparse_line = line;
}

/**************************************************************
 * yycomment(char *, int line)
 *
 * Inserts a comment into a documentation entry.
 **************************************************************/

/*
void yycomment(char *a, int b, int c) {
}
*/

/* -----------------------------------------------------------------------------
 * skip_balanced()
 *
 * Skips a piece of code enclosed in begin/end symbols such as '{...}' or
 * (...).  Ignores symbols inside comments or strings.
 * ----------------------------------------------------------------------------- */

void
skip_balanced(int startchar, int endchar) {
    char c;
    int  num_levels = 1;
    int  state = 0;
    char temp[2] = {0,0};
    int  start_line = cparse_line;

    Clear(scanner_ccode);
    Putc(startchar,scanner_ccode);
    temp[0] = (char) startchar;
    while (num_levels > 0) {
      c = nextchar();
      if (c == 0) {
	  Swig_error(cparse_file, start_line, "Missing '%c'. Reached end of input.\n", endchar);
	  return;
      }
      Putc(c,scanner_ccode);
      switch(state) {
      case 0:
	if (c == startchar) num_levels++;
	else if (c == endchar) num_levels--;
	    else if (c == '/') state = 10;
	else if (c == '\"') state = 20;
	else if (c == '\'') state = 30;
	break;
      case 10:
	if (c == '/') state = 11;
	else if (c == '*') state = 12;
	else state = 0;
	break;
      case 11:
	if (c == '\n') state = 0;
	else state = 11;
	break;
      case 12:
	if (c == '*') state = 13;
	break;
      case 13:
	if (c == '*') state = 13;
	else if (c == '/') state = 0;
	else state = 12;
	break;
      case 20:
	if (c == '\"') state = 0;
	else if (c == '\\') state = 21;
	break;
      case 21:
	state = 20;
	break;
      case 30:
	if (c == '\'') state = 0;
	else if (c == '\\') state = 31;
	break;
      case 31:
	state = 30;
	break;
      default:
	break;
      }
      yylen = 0;
    }
    if (endchar == '}') num_brace--;
    return;
}

/**************************************************************
 * void skip_decl(void)
 *
 * This tries to skip over an entire declaration.   For example
 *
 *  friend ostream& operator<<(ostream&, const char *s);
 *
 * or
 *  friend ostream& operator<<(ostream&, const char *s) { };
 *
 **************************************************************/

void skip_decl(void) {
  char c;
  int  done = 0;
  while (!done) {
    if ((c = nextchar()) == 0) {
      Swig_error(cparse_file,cparse_line,"Missing semicolon. Reached end of input.\n");
      return;
    }
    if (c == '{') {
      last_brace = num_brace;
      num_brace++;
      break;
    }
    yylen = 0;
    if (c == ';') done = 1;
  }
  if (!done) {
    while (num_brace > last_brace) {
      if ((c = nextchar()) == 0) {
	Swig_error(cparse_file,cparse_line,"Missing '}'. Reached end of input.\n");
	return;
      }
      if (c == '{') num_brace++;
      if (c == '}') num_brace--;
      yylen = 0;
    }
  }
}

/* This function is called when a backslash is found in a string */
static void get_escape() {
  int result = 0;
  int state = 0;
  int c;

  while(1) {
    c = nextchar();
    if (c == 0) break;
    switch(state) {
    case 0:
      if (c == 'n') {
	yytext[yylen-1] = '\n';
	return;
      }
      if (c == 'r') {
	yytext[yylen-1] = '\r';
	return;
      }
      if (c == 't') {
	yytext[yylen-1] = '\t';
	return;
      }
      if (c == 'a') {
	yytext[yylen-1] = '\a';
	return;
      }
      if (c == 'b') {
	yytext[yylen-1] = '\b';
	return;
      }
      if (c == 'f') {
	yytext[yylen-1] = '\f';
	return;
      }
      if (c == '\\') {
	yytext[yylen-1] = '\\';
	return;
      }
      if (c == 'v') {
	yytext[yylen-1] = '\v';
	return;
      }
      if (c == 'e') {
	yytext[yylen-1] = '\033';
	return;
      }
      if (c == '\'') {
	yytext[yylen-1] = '\'';
	return;
      }
      if (c == '\"') {
	yytext[yylen-1] = '\"';
	return;
      }
      if (c == '\n') {
	yylen--;
	return;
      }
      if (isdigit(c)) {
	state = 10;
	result = (c-'0');
      }
      else if (c == 'x') {
	state = 20;
      } else {
	yytext[yylen-1] = '\\';
	yytext[yylen] = c;
	yylen++;
	return;
      }
      break;
    case 10:
      if (!isdigit(c)) {
	retract(1);
	yytext[yylen-1] = (char) result;
	return;
      }
      result = (result << 3) + (c - '0');
      yylen--;
      break;
    case 20:
      if (!isxdigit(c)) {
	retract(1);
	yytext[yylen-1] = (char) result;
	return;
      }
      if (isdigit(c))
	result = (result << 4) + (c - '0');
      else
	result = (result << 4) + (10 + tolower(c) - 'a');
      yylen--;
      break;
    }
  }
  return;
}

/**************************************************************
 * int yylook()
 *
 * Lexical scanner.
 * See Aho,Sethi, and Ullman,  pg. 106
 **************************************************************/

int yylook(void) {

    int      state;
    int      c = 0;

    state = 0;
    yylen = 0;
    while(1) {

/*	printf("State = %d\n", state);   */
	switch(state) {

	case 0 :
	  if((c = nextchar()) == 0) return (0);

	  /* Process delimeters */

	  if (c == '\n') {
	    state = 0;
	    yylen = 0;
	    /*	    last_id = 0;*/
	  } else if (isspace(c) || (c=='\\')) {
	    state = 0;
	    yylen = 0;
	    /*	    last_id = 0; */
	  }

	  else if ((isalpha(c)) || (c == '_')) state = 7;
	  else if (c == '$') state = 75;

	  /* Look for single character symbols */

	  else if (c == '(') return (LPAREN);
	  else if (c == ')') return (RPAREN);
	  else if (c == ';') return (SEMI);
	  else if (c == ',') return (COMMA);
	  else if (c == '*') return (STAR);
	  else if (c == '}') {
	    num_brace--;
	    if (num_brace < 0) {
	      Swig_error(cparse_file, cparse_line, "Syntax error. Extraneous '}'\n");
	      state = 0;
	      num_brace = 0;
	    } else {
	      return (RBRACE);
	    }
	  }
	  else if (c == '{') {
	    last_brace = num_brace;
	    num_brace++;
	    return (LBRACE);
	  }
	  else if (c == '=') return (EQUAL);
	  else if (c == '+') return (PLUS);
          else if (c == '-') return (MINUS);
	  else if (c == '&') {
	    state = 300;
	  } 
	  else if (c == '|') {
	    state = 301;
	  }
	  else if (c == '^') return (XOR);
          else if (c == '<') state = 60;
	  else if (c == '>') state = 61;
	  else if (c == '~') {
	    return (NOT);
	  }
          else if (c == '!') return (LNOT);
	  else if (c == '\\') {
	    state = 99;
	  }
  	  else if (c == '[') return (LBRACKET);
	  else if (c == ']') return (RBRACKET);

	  /* Look for multi-character sequences */

	  else if (c == '/') state = 1;    /* Comment (maybe) */
	  else if (c == '\"') state = 2;   /* Possibly a string */
	  else if (c == '#') state = 3;    /* CPP */
	  else if (c == '%') state = 4;    /* Directive */
	  else if (c == '@') state = 4;    /* Objective C keyword */
	  else if (c == ':') state = 5;    /* maybe double colon */
	  else if (c == '0') state = 83;   /* An octal or hex value */
	  else if (c == '\'') state = 9;   /* A character constant */
	  else if (c == '.') state = 100;  /* Maybe a number, maybe just a period */
	  else if (c == '`') {
	    state = 200; /* Back-tick type */
	    yylen = 0;
	  }
	  else if (isdigit(c)) state = 8;  /* A numerical value */

	  else state = 99;
	  break;
	case 1:  /*  Comment block */
	  if ((c = nextchar()) == 0) return(0);
	  if (c == '/') {
	    comment_start = cparse_line;
	    Clear(comment);
	    state = 10;        /* C++ style comment */
	  } else if (c == '*') {
	    comment_start = cparse_line;
	    Clear(comment);
	    state = 12;   /* C style comment */
	  } else {
	    retract(1);
	    return(SLASH);
	  }
	  break;
	case 300: /* & or && */
	  if ((c = nextchar()) == 0) return(AND);
	  if (c == '&') return(LAND);
	  else {
	    retract(1);
	    return(AND);
	  }

	case 301: /* | or || */
	  if ((c = nextchar()) == 0) return(OR);
	  if (c == '|') return(LOR);
	  else {
	    retract(1);
	    return(OR);
	  }
	case 10:  /* C++ style comment */
	  if ((c = nextchar()) == 0) {
	    Swig_error(cparse_file,-1, "Unterminated comment detected.\n");
	    return 0;
	  }
	  if (c == '\n') {
	    Putc(c,comment);
	    /* Add the comment to documentation */
	    /*	    yycomment(Char(comment),comment_start, column_start);*/
	    yylen = 0;
	    state = 0;
	  } else {
	    state = 10;
	    Putc(c,comment);
	    yylen = 0;
	  }
	  break;

	case 12: /* C style comment block */
	  if ((c = nextchar()) == 0) {
	    Swig_error(cparse_file,-1,"Unterminated comment detected.\n");
	    return 0;
	  }
	  if (c == '*') {
	    state = 13;
	  } else {
	    Putc(c,comment);
	    yylen = 0;
	    state = 12;
	  }
	  break;
	case 13: /* Still in C style comment */
	  if ((c = nextchar()) == 0) {
	    Swig_error(cparse_file,-1,"Unterminated comment detected.\n");
	    return 0;
	  }
	  if (c == '*') {
	    Putc(c,comment);
	    state = 13;
	  } else if (c == '/') {

	    /* Look for locator markers */
	    {
	      char *loc = Char(comment);
	      if (Len(comment)) {
		if ((*loc == '@') && (*(loc+Len(comment)-1) == '@')) {
		  /* Locator */
		  scanner_locator(comment);
		}
	      }
	    }
	    /*	    yycomment(Char(comment),comment_start,column_start); */
	    yylen = 0;
	    state = 0;
	  } else {
	    Putc('*',comment);
	    Putc(c,comment);
	    yylen = 0;
	    state = 12;
	  }
	  break;

	case 2: /* Processing a string */
	  if ((c = nextchar()) == 0) {
	    Swig_error(cparse_file,-1, "Unterminated string detected.\n");
	    return 0;
	  }
	  if (c == '\"') {
	    yytext[yylen-1] = 0;
	    yylval.id = Swig_copy_string(yytext+1);
	    return(STRING);
	  } else if (c == '\\') {
	    yylen--;
	    get_escape();
	    break;
	  } else state = 2;
	  break;

	case 3: /* a CPP directive */
	  if (( c= nextchar()) == 0) return 0;
	  if (c == '\n') {
	    retract(1);
	    yytext[yylen] = 0;
	    yylval.id = yytext;
	    return(POUND);
	  }
	  break;

	case 4: /* A wrapper generator directive (maybe) */
	  if (( c= nextchar()) == 0) return 0;
	  if (c == '{') {
	    state = 40;   /* Include block */
	    Clear(header);
	    cparse_start_line = cparse_line;
	  } else if ((isalpha(c)) || (c == '_')) state = 7;
	  else if (c == '}') {
	    Swig_error(cparse_file,cparse_line, "Misplaced %%}.\n");
	    return 0;
	  } else {
	    retract(1);
	    return(MODULO);
	  }
	  break;

	case 40: /* Process an include block */
	  if ((c = nextchar()) == 0) {
	    Swig_error(cparse_file,-1, "Unterminated include block detected.\n");
	    return 0;
	  }
	  yylen = 0;
	  if (c == '%') state = 41;
	  else {
	    Putc(c,header);
	    yylen = 0;
	    state = 40;
	  }
	  break;
	case 41: /* Still processing include block */
	  if ((c = nextchar()) == 0) {
	    Swig_error(cparse_file,-1, "Unterminated include block detected.\n");
	    return 0;
	  }
	  if (c == '}') {
	    yylval.str = NewString(header);
	    return(HBLOCK);
	  } else {
	    Putc('%',header);
	    Putc(c,header);
	    yylen = 0;
	    state = 40;
	  }
	  break;

	case 5: /* Maybe a double colon */

	  if (( c= nextchar()) == 0) return 0;
	  if ( c == ':') {
	    state = 51;
	  } else {
	    retract(1);
	    return COLON;
	  }
	  break;
	case 51: /* Maybe a ::*, ::~, or :: */
	  if (( c = nextchar()) == 0) return 0;
	  if (c == '*') {
	    return DSTAR;
	  } else if (c == '~') {
	    return DCNOT;
	  } else if (isspace(c)) {
	    /* Keep scanning ahead.  Might be :: * or :: ~ */
	  } else {
	    retract(1);
	    if (!last_id) {
	      retract(2);
	      return NONID;
	    } else {
	      return DCOLON;
	    }
	  }
	  break;

	case 60: /* shift operators */
	  if ((c = nextchar()) == 0) return (0);
	  if (c == '<') return LSHIFT;
	  else {
	    retract(1);
	    return LESSTHAN;
	  }
	  break;
	case 61:
	  if ((c = nextchar()) == 0) return (0);
	  if (c == '>') return RSHIFT;
	  else {
	    retract(1);
            return GREATERTHAN;
	  }
	  break;
	case 7: /* Identifier */
	  if ((c = nextchar()) == 0) return(0);
	  if (isalnum(c) || (c == '_') || (c == '.') || (c == '$')) {
	    state = 7;
	  } else {
	    retract(1);
	    return(ID);
	  }
	  break;
	case 75: /* Special identifier $*/
	  if ((c = nextchar()) == 0) return(0);
	  if (isalnum(c) || (c == '_') || (c == '*') || (c == '&')) {
	    state = 7;
	  } else {
	    retract(1);
	    return(ID);
	  }
	  break;

	case 8: /* A numerical digit */
	  if ((c = nextchar()) == 0) return(0);
	  if (c == '.') {state = 81;}
	  else if ((c == 'e') || (c == 'E')) {state = 86;}
	  else if ((c == 'f') || (c == 'F')) {
	     return(NUM_FLOAT);
	  }
	  else if (isdigit(c)) { state = 8;}
	  else if ((c == 'l') || (c == 'L')) {
	    state = 87;
	  } else if ((c == 'u') || (c == 'U')) {
	    state = 88;
	  } else {
	      retract(1);
	      return(NUM_INT);
	    }
	  break;
	case 81: /* A floating pointer number of some sort */
	  if ((c = nextchar()) == 0) return(0);
	  if (isdigit(c)) state = 81;
	  else if ((c == 'e') || (c == 'E')) state = 82;
          else if ((c == 'f') || (c == 'F') || (c == 'l') || (c == 'L')) {
	    return(NUM_FLOAT);
	  } else {
	    retract(1);
	    return(NUM_FLOAT);
	  }
	  break;
	case 82:
	  if ((c = nextchar()) == 0) return(0);
	  if ((isdigit(c)) || (c == '-') || (c == '+')) state = 86;
	  else {
	    retract(2);
	    yytext[yylen-1] = 0;
	    return(NUM_INT);
	  }
	  break;
	case 83:
	  /* Might be a hexidecimal or octal number */
	  if ((c = nextchar()) == 0) return(0);
	  if (isdigit(c)) state = 84;
	  else if ((c == 'x') || (c == 'X')) state = 85;
	  else if (c == '.') state = 81;
	  else if ((c == 'l') || (c == 'L')) {
	    state = 87;
	  } else if ((c == 'u') || (c == 'U')) {
	    state = 88;
	  } else {
	    retract(1);
	    return(NUM_INT);
	  }
	  break;
	case 84:
	  /* This is an octal number */
	  if ((c = nextchar()) == 0) return (0);
	  if (isdigit(c)) state = 84;
	  else if ((c == 'l') || (c == 'L')) {
	    state = 87;
	  } else if ((c == 'u') || (c == 'U')) {
	    state = 88;
	  } else {
	    retract(1);
	    return(NUM_INT);
	  }
	  break;
	case 85:
	  /* This is an hex number */
	  if ((c = nextchar()) == 0) return (0);
	  if ((isdigit(c)) || (c=='a') || (c=='b') || (c=='c') ||
	      (c=='d') || (c=='e') || (c=='f') || (c=='A') ||
	      (c=='B') || (c=='C') || (c=='D') || (c=='E') ||
	      (c=='F'))
	    state = 85;
	  else if ((c == 'l') || (c == 'L')) {
	    state = 87;
	  } else if ((c == 'u') || (c == 'U')) {
	    state = 88;
	  } else {
	    retract(1);
	    return(NUM_INT);
	  }
	  break;

	case 86:
	  /* Rest of floating point number */

	  if ((c = nextchar()) == 0) return (0);
	  if (isdigit(c)) state = 86;
          else if ((c == 'f') || (c == 'F') || (c == 'l') || (c == 'L')) {
	    return(NUM_FLOAT);
	  } else {
	    retract(1);
	    return(NUM_FLOAT);
	  }
	  /* Parse a character constant. ie. 'a' */
	  break;

	case 87 :
	  /* A long integer of some sort */
	  if ((c = nextchar()) == 0) return (NUM_LONG);
	  if ((c == 'u') || (c == 'U')) {
	    return(NUM_ULONG);
	  } else if ((c == 'l') || (c == 'L')) {
	    state = 870;
	  } else {
	    retract(1);
	    return(NUM_LONG);
	  }
	  break;

	case 870:
	  if ((c = nextchar()) == 0) return (NUM_LONGLONG);
	  if ((c == 'u') || (c == 'U')) {
	    return (NUM_ULONGLONG);
	  } else {
	    retract(1);
	    return(NUM_LONGLONG);
	  }

	case 88:
	  /* An unsigned integer of some sort */
	  if ((c = nextchar()) == 0) return (NUM_UNSIGNED);
	  if ((c == 'l') || (c == 'L')) {
	    state = 880;
	  } else {
	    retract(1);
	    return(NUM_UNSIGNED);
	  }
	  break;

	case 880:
	  if ((c = nextchar()) == 0) return (NUM_ULONG);
	  if ((c == 'l') || (c == 'L')) return (NUM_ULONGLONG);
	  else {
	    retract(1);
	    return(NUM_ULONG);
	  }
	  
	case 9:
	  if ((c = nextchar()) == 0) return (0);
	  if (c == '\\') {
	    yylen--;
	    get_escape();
	  } else if (c == '\'') {
	    yytext[yylen-1] = 0;
	    yylval.str = NewString(yytext+1);
	    if (yylen == 2) {
	      Swig_error(cparse_file, cparse_line, "Empty character constant\n");
	    }
	    return(CHARCONST);
	  }
	  break;

	case 100:
	  if ((c = nextchar()) == 0) return (0);
	  if (isdigit(c)) state = 81;
	  else {
	    retract(1);
	    return(PERIOD);
	  }
	  break;
	case 200:
	  if ((c = nextchar()) == 0) return (0);
	  if (c == '`') {
	    yytext[yylen-1] = 0;
	    yylval.type = NewString(yytext);
	    return(TYPE_RAW);
	  }
	  break;

	default:
	  Swig_error(cparse_file, cparse_line, "Illegal character '%c'=%d.\n",c,c);
	  state = 0;
	  return(ILLEGAL);
	}
    }
}

static int check_typedef = 0;

void scanner_check_typedef() {
  check_typedef = 1;
}

void scanner_ignore_typedef() {
  check_typedef = 0;
}

void scanner_last_id(int x) {
  /*  printf("Setting last_id = %d\n", x); */
  last_id = x;
}

void scanner_clear_rename() {
  rename_active = 0;
}

static int next_token = 0;
void scanner_next_token(int tok) {
  next_token = tok;
}

/**************************************************************
 * int yylex()
 *
 * Gets the lexene and returns tokens.
 *************************************************************/

int yylex(void) {

    int   l;

    if (!scan_init) {
      scanner_init();
    }

    if (next_token) {
      l = next_token;
      next_token = 0;
      return l;
    }
    /*    Printf(stdout,"%d\n", last_id);*/
    l = yylook();

    if (l == NONID) {
      last_id = 1;
    } else {
      last_id = 0;
    }
    /*
    yytext[yylen]= 0;
    Printf(stdout,"%d  '%s' %d\n", l, yytext, last_id);
    */

    /* We got some sort of non-white space object.  We set the start_line
       variable unless it has already been set */

    if (!cparse_start_line) {
      cparse_start_line = cparse_line;
    }

    /* Copy the lexene */

    yytext[yylen] = 0;
    switch(l) {

    case NUM_INT:
    case NUM_FLOAT:
    case NUM_ULONG:
    case NUM_LONG:
    case NUM_UNSIGNED:
    case NUM_LONGLONG:
    case NUM_ULONGLONG:
      if (l == NUM_INT) yylval.dtype.type = T_INT;
      if (l == NUM_FLOAT) yylval.dtype.type = T_DOUBLE;
      if (l == NUM_ULONG) yylval.dtype.type = T_ULONG;
      if (l == NUM_LONG) yylval.dtype.type = T_LONG;
      if (l == NUM_UNSIGNED) yylval.dtype.type = T_UINT;
      if (l == NUM_LONGLONG) yylval.dtype.type = T_LONGLONG;
      if (l == NUM_ULONGLONG) yylval.dtype.type = T_ULONGLONG;
      yylval.dtype.val = NewString(yytext);
      yylval.dtype.bitfield = 0;
      yylval.dtype.throws = 0;
      return(l);
      break;
      
    case ID:

	if (yytext[0] != '%') {
	  /* Look for keywords now */
	  
	  if (strcmp(yytext,"int") == 0) {
	    yylval.type = NewSwigType(T_INT);
	    return(TYPE_INT);
	  }
	  if (strcmp(yytext,"double") == 0) {
	    yylval.type = NewSwigType(T_DOUBLE);
	    return(TYPE_DOUBLE);
	  }
	  if (strcmp(yytext,"void") == 0) {
	    yylval.type = NewSwigType(T_VOID);
	    return(TYPE_VOID);
	  }
	  if (strcmp(yytext,"char") == 0) {
	    yylval.type = NewSwigType(T_CHAR);
	    return(TYPE_CHAR);
	  }
	  if (strcmp(yytext,"short") == 0) {
	    yylval.type = NewSwigType(T_SHORT);
	    return(TYPE_SHORT);
	  }
	  if (strcmp(yytext,"long") == 0) {
	    yylval.type = NewSwigType(T_LONG);
	    return(TYPE_LONG);
	  }
	  if (strcmp(yytext,"float") == 0) {
	    yylval.type = NewSwigType(T_FLOAT);
	    return(TYPE_FLOAT);
	  }
	  if (strcmp(yytext,"signed") == 0) {
	    yylval.type = NewSwigType(T_INT);
	    return(TYPE_SIGNED);
	  }
	  if (strcmp(yytext,"unsigned") == 0) {
	    yylval.type = NewSwigType(T_UINT);
	    return(TYPE_UNSIGNED);
	  }
	  if (strcmp(yytext,"bool") == 0) {
	    yylval.type = NewSwigType(T_BOOL);
	    return(TYPE_BOOL);
	  }
	  /* C++ keywords */
	  
	  if (cparse_cplusplus) {
	    if (strcmp(yytext,"class") == 0) return(CLASS);
	    if (strcmp(yytext,"private") == 0) return(PRIVATE);
	    if (strcmp(yytext,"public") == 0) return(PUBLIC);
	    if (strcmp(yytext,"protected") == 0) return(PROTECTED);
	    if (strcmp(yytext,"friend") == 0) return(FRIEND);
	    if (strcmp(yytext,"virtual") == 0) return(VIRTUAL);
	    if (strcmp(yytext,"operator") == 0) {
	      String *s = NewString("operator");
	      int c;
	      int state = 0;
	      int sticky = 0;
	      int isconversion = 0;
	      int count = 0;
	      while ((c = nextchar())) {
		if (((c == '(') || (c == ';')) && state) {
		  retract(1);
		  break;
		}
		count++;
		if (!isspace(c)) {
		  if ((!state) && (isalpha(c))) isconversion = 1;
		  if (!state && !sticky) Putc(' ',s);
		  Putc(c,s);
		  sticky = 0;
		  state = 1;
		} else {
		  if (!sticky) Putc(' ',s);
		  sticky = 1;
		}
	      }
	      Chop(s);
	      yylval.str = s;
	      while(Replaceall(s,"[ ", "["));
	      if (isconversion) {
		String *ns = Swig_symbol_string_qualify(s,0);
		yylval.str = ns;
	      }
	      if (isconversion && !rename_active) {
		char *t = Char(s) + 9;
		if (!((strcmp(t,"new") == 0) || (strcmp(t,"delete") == 0) 
		      || (strcmp(t,"new[]") == 0) || (strcmp(t,"delete[]") == 0))) {
		  /*		  retract(strlen(t));*/
		  retract(count);
		  return COPERATOR;
		}
	      }
	      return(OPERATOR);
	    }
	    if (strcmp(yytext,"throw") == 0) return(THROW);
	    if (strcmp(yytext,"try") == 0) return (yylex());
	    if (strcmp(yytext,"catch") == 0) return (CATCH);
	    if (strcmp(yytext,"inline") == 0) return(yylex());
	    if (strcmp(yytext,"mutable") == 0) return(yylex());
	    if (strcmp(yytext,"explicit") == 0) return(yylex());
	    if (strcmp(yytext,"export") == 0) return(yylex());
	    if (strcmp(yytext,"typename") == 0) return (TYPENAME);
	    if (strcmp(yytext,"template") == 0) {
	      yylval.ivalue = cparse_line;
	      return(TEMPLATE);
	    }
	    if (strcmp(yytext,"delete") == 0) {
	      return(DELETE);
	    }
	    if (strcmp(yytext,"using") == 0) {
	      return(USING);
	    }
	    if (strcmp(yytext,"namespace") == 0) {
	      return(NAMESPACE);
	    }
	  } else {
	    if (strcmp(yytext,"class") == 0) {
	      Swig_warning(WARN_PARSE_CLASS_KEYWORD,cparse_file,cparse_line, "class keyword used, but not in C++ mode.\n");
	    }
	  }
	  
	  /* Objective-C keywords */
#ifdef OBJECTIVEC
	  if ((ObjC) && (yytext[0] == '@')) {
	    if (strcmp(yytext,"@interface") == 0) return (OC_INTERFACE);
	    if (strcmp(yytext,"@end") == 0) return (OC_END);
	    if (strcmp(yytext,"@public") == 0) return (OC_PUBLIC);
	    if (strcmp(yytext,"@private") == 0) return (OC_PRIVATE);
	    if (strcmp(yytext,"@protected") == 0) return (OC_PROTECTED);
	    if (strcmp(yytext,"@class") == 0) return(OC_CLASS);
	    if (strcmp(yytext,"@implementation") == 0) return(OC_IMPLEMENT);
	    if (strcmp(yytext,"@protocol") == 0) return(OC_PROTOCOL);
	  }
#endif
	  
	  /* Misc keywords */
	  
	  if (strcmp(yytext,"extern") == 0) return(EXTERN);
	  if (strcmp(yytext,"const") == 0) return(CONST);
	  if (strcmp(yytext,"static") == 0) return(STATIC);
	  if (strcmp(yytext,"struct") == 0) return(STRUCT);
	  if (strcmp(yytext,"union") == 0) return(UNION);
	  if (strcmp(yytext,"enum") == 0) return(ENUM);
	  if (strcmp(yytext,"sizeof") == 0) return(SIZEOF);
	  
	  if (strcmp(yytext,"typedef") == 0) {
	    yylval.ivalue = 0;
	    return(TYPEDEF);
	  }
	  
	  /* Ignored keywords */
	  
	  if (strcmp(yytext,"volatile") == 0) return(VOLATILE);
	  
	  /* SWIG directives */
	} else {
	  if (strcmp(yytext,"%module") == 0) return(MODULE);
	  if (strcmp(yytext,"%insert") == 0) return(INSERT);
	  if (strcmp(yytext,"%name") == 0) return(NAME);
	  if (strcmp(yytext,"%rename") == 0) {
	    rename_active = 1;
	    return(RENAME);
	  }
	  if (strcmp(yytext,"%namewarn") == 0) {
	    rename_active = 1;
	    return (NAMEWARN);
	  }
	  if (strcmp(yytext,"%includefile") == 0) return(INCLUDE);
	  if (strcmp(yytext,"%val") == 0) {
	    Swig_warning(WARN_DEPRECATED_VAL, cparse_file, cparse_line, "%%val directive deprecated (ignored).\n");
	    return (yylex());
	  }
	  if (strcmp(yytext,"%out") == 0) {
	    Swig_warning(WARN_DEPRECATED_OUT, cparse_file, cparse_line, "%%out directive deprecated (ignored).\n");
	    return(yylex());
	  }
	  if (strcmp(yytext,"%constant") == 0) return(CONSTANT);
	  if (strcmp(yytext,"%typedef") == 0) {
	    yylval.ivalue = 1;
	    return(TYPEDEF);
	  }
	  if (strcmp(yytext,"%native") == 0) return(NATIVE);
	  if (strcmp(yytext,"%pragma") == 0) return(PRAGMA);
	  if (strcmp(yytext,"%extend") == 0) return(EXTEND);
	  if (strcmp(yytext,"%fragment") == 0) return(FRAGMENT);
	  if (strcmp(yytext,"%inline") == 0) return(INLINE);
	  if (strcmp(yytext,"%typemap") == 0) return(TYPEMAP);
	  if (strcmp(yytext,"%feature") == 0) return(FEATURE);
	  if (strcmp(yytext,"%except") == 0) return(EXCEPT);
	  if (strcmp(yytext,"%importfile") == 0) return(IMPORT);
	  if (strcmp(yytext,"%echo") == 0) return(ECHO);
	  if (strcmp(yytext,"%apply") == 0) return(APPLY);
	  if (strcmp(yytext,"%clear") == 0) return(CLEAR);
	  if (strcmp(yytext,"%types") == 0) return(TYPES);
	  if (strcmp(yytext,"%parms") == 0) return(PARMS);
	  if (strcmp(yytext,"%varargs") == 0) return(VARARGS);
	  if (strcmp(yytext,"%template") == 0) return (SWIGTEMPLATE);
	  if (strcmp(yytext,"%warn") == 0) return(WARN);
	}
	/* Have an unknown identifier, as a last step, we'll do a typedef lookup on it. */

        /* Need to fix this */
	if (check_typedef) {
	  if (SwigType_istypedef(yytext)) {
	    yylval.type = NewString(yytext);
	    return(TYPE_TYPEDEF);
	  }
	}
	yylval.id = Swig_copy_string(yytext);
	last_id = 1;
	return(ID);
    case POUND:
      return yylex();
    default:
      return(l);
    }
}
