
/* 
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/
#ident "$Header: scan.c 1.1 90/01/19 $@(#)scan.c	1.1 89/12/01"
#include "xs.h"
#include "names.h"
#include "stack.h"
#include "types.h"
#include "anal.h"
#include "mfile1.h"
#include "xdefs.h"

static int lastlen;
#define LRETURN(x) { yylval.node.t_off = lastoff;\
yylval.node.t_len = lastlen; lastoff += lastlen; lastlen = 0; return (x);}
#define GETCHAR()	(lastlen++,getc(fd_in))
#define UNGETC(x)  (ungetc(x, fd_in), lastlen--)
/* character-set translation */
# ifndef CCTRANS
# define CCTRANS(x) (x)
# endif

extern char *gettxt();

/* lexical actions */

# define A_ERR  0    /* illegal character */
# define A_LET  1    /* saw a letter */
# define A_DIG  2    /* saw a digit */
# define A_1C   3    /* return a single character */
# define A_STR  4    /* string */
# define A_CC   5    /* character constant */
# define A_BCD  6    /* GCOS BCD constant */
# define A_SL   7    /* saw a / */
# define A_DOT  8    /* saw a . */
# define A_PL   9    /* + */
# define A_MI  10    /* - */
# define A_EQ  11    /* = */
# define A_NOT 12    /* ! */
# define A_LT  13    /* < */
# define A_GT  14    /* > */
# define A_AND 16    /* & */
# define A_OR  17    /* | */
# define A_WS  18    /* whitespace (not \n) */
# define A_NL  19    /* \n */

/* character classes */

# define LEXLET 01
# define LEXDIG 02
# define LEXOCT 04
# define LEXHEX 010
# define LEXWS 020
# define LEXDOT 040

/* reserved word actions */

# define AR_TY 0     /* type word */
# define AR_RW 1     /* simple reserved word */
# define AR_CL 2     /* storage class word */
# define AR_S  3     /* struct */
# define AR_U  4     /* union */
# define AR_E  5     /* enum */
# define AR_A  6     /* asm */

/* text buffer */
# define LXTSZ BUFSIZ    /* lots of room for FLEXNAMES */
char yytext[LXTSZ];
char * lxgcp;

static int curlen;

# ifdef ibm

# define CSMASK 0377
# define CSSZ 256

# else

# define CSMASK 0177
# define CSSZ 128

# endif

short lxmask[CSSZ+1];

lxenter(s, m)
  register char *s;
  register short m;
{
  /* enter a mask into lxmask */
  register c;

  while( c= *s++ ) lxmask[c+1] |= m;
}


# define lxget(c,m) (lxgcp=yytext,lxmore(c,m))

lxmore(c, m)
  register c, m;
{
  register char *cp;

  *(cp = lxgcp) = c;
  while((c = GETCHAR()), lxmask[c+1] & m){
    if (cp < &yytext[LXTSZ-1])
      *++cp = c;
  }
  UNGETC(c);
  *(lxgcp = cp+1) = '\0';
}

struct lxdope
{
  short lxch;   /* the character */
  short lxact;  /* the action to be performed */
  short lxtok;  /* the token number to be returned */
  short lxval;  /* the value to be returned */
} lxdope[] =
{
  '$',  A_ERR,  0,    0,  /* illegal characters go here... */
  '_',  A_LET,  0,    0,  /* letters point here */
  '0',  A_DIG,  0,    0,  /* digits point here */
  ' ',  A_WS,   0,    0,  /* whitespace goes here */
  '\n',  A_NL,  0,    0,
  '"',  A_STR,  0,    0,  /* character string */
  '\'',  A_CC,  0,    0,  /* character constant */
  '`',  A_BCD,  0,    0,  /* GCOS BCD constant */
  '(',  A_1C,  LP,    0,
  ')',  A_1C,  RP,    0,
  '{',  A_1C,  LC,    0,
  '}',  A_1C,  RC,    0,
  '[',  A_1C,  LB,    0,
  ']',  A_1C,  RB,    0,
  '*',  A_1C,  MUL,   MUL,
  '?',  A_1C,  QUEST, 0,
  ':',  A_1C,  COLON, 0,
  '+',  A_PL,  PLUS,  PLUS,
  '-',  A_MI,  MINUS, MINUS,
  '/',  A_SL,  DIVOP, DIV,
  '%',  A_1C,  DIVOP, MOD,
  '&',  A_AND, AND,   AND,
  '|',  A_OR,  OR,    OR,
  '^',  A_1C,  ER,    ER,
  '!',  A_NOT, UNOP,  NOT,
  '~',  A_1C,  UNOP,  COMPL,
  ',',  A_1C,  CM,    CM,
  ';',  A_1C,  SM,    0,
  '.',  A_DOT, STROP, DOT,
  '<',  A_LT,  RELOP, LT,
  '>',  A_GT,  RELOP, GT,
  '=',  A_EQ,  ASSIGN,ASSIGN,
  -1,  A_1C,   0,     0,
};

struct lxdope *lxcp[CSSZ+1];
lxinit()
{
  lineno = 1;
  lastoff = 0;
  lxtitle();
}

lxinit_all()
{
  register struct lxdope *p;
  register i;
  register char *cp;
  /* set up character classes */

  lxenter( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", LEXLET );
  lxenter( "0123456789", LEXDIG );
  lxenter( "0123456789abcdefABCDEF", LEXHEX );
  /* \013 should become \v someday; \013 is OK for ASCII and EBCDIC */
  lxenter( " \t\r\f\013", LEXWS );
  lxenter( "01234567", LEXOCT );
  lxmask['.'+1] |= LEXDOT;

  /* make lxcp point to appropriate lxdope entry for each character */

  /* initialize error entries */

  for( i= 0; i<=CSSZ; ++i )
    lxcp[i] = lxdope;

  /* make unique entries */

  for( p=lxdope; ; ++p ){
    lxcp[p->lxch+1] = p;
    if( p->lxch < 0 ) break;
  }

  /* handle letters, digits, and whitespace */
  /* by convention, first, second, and third places */

  cp = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  while( *cp ) lxcp[*cp++ + 1] = &lxdope[1];
  cp = "123456789";
  while( *cp ) lxcp[*cp++ + 1] = &lxdope[2];
  cp = "\t\r\f\013";
  while( *cp ) lxcp[*cp++ + 1] = &lxdope[3];
}

int lxmatch;  /* character to be matched in char or string constant */

lxstr()
{
  /* match a string */

  register c;
  register int cnt = 0;
  char *cp = yytext;

  while((c = GETCHAR()) != '\"'){
    switch(c){

    case EOF:
      uerror( gettxt("xs:47","lxstr() unexpected EOF") );
      break;

    case '\n':
      uerror( gettxt("xs:48","newline in string") );
      ++lineno;
      break;

    case '\\':
      if ((c = GETCHAR()) == '\n' )
        ++lineno;
      cnt += 2;
      if (cnt >= sizeof(yytext)){
      	werror(gettxt("xs:49","string too long - truncated to %d chars"), cnt - 2);
      	while(GETCHAR() != '\"');
      	break;
      }
      *cp++ = '\\';
      *cp++ = c;
      continue;
    default:
      if (++cnt >= sizeof(yytext)){
        werror(gettxt("xs:50","string too long - truncated to %d chars."), cnt - 1);
      	while(GETCHAR() != '\"');
        break;
      }
      *cp++ = c;
      cnt++;
      continue;
    }
    break;
  }
  *cp++ = '\0';
  yylval.node.t_name = na_store(yytext);
}

/*
 * Evaluate a character constant
 */
lxcc()
{
  register i = 0;
  register c;
  register val;

  while ((c = GETCHAR()) != '\''){
    switch(c){
    case EOF:
      uerror(gettxt("xs:51","unexpected EOF"));
      break;
    case '\n':
      uerror(gettxt("xs:52","newline in character constant"));
      lineno++;
      break;
    case '\\':
      switch (c = GETCHAR()){
      case '\n':
        lineno++;
        continue;
      default:
        val = c;
        goto mkcc;

      case 'n':
        val = '\n';
        goto mkcc;

      case 'r':
        val = '\r';
        goto mkcc;

      case 'b':
        val = '\b';
        goto mkcc;

      case 't':
        val = '\t';
        goto mkcc;

      case 'f':
        val = '\f';
        goto mkcc;

      case 'v':
        val = '\013';
        goto mkcc;

      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
        val = c-'0';
        c = GETCHAR();  /* try for 2 */
        if( lxmask[c+1] & LEXOCT ){
          val = (val<<3) | (c-'0');
          c = GETCHAR();  /* try for 3 */
          if( lxmask[c+1] & LEXOCT ){
            val = (val<<3) | (c-'0');
          }
          else UNGETC(c);
        }
        else UNGETC(c);

        goto mkcc1;
      }
    default:
      val =c;
mkcc:
            
      val = c;
      val = CCTRANS(val);
mkcc1:
      if (i == 0)
        lastcon = val;
      else{
# ifdef MAKECC
        MAKECC( val, i );
# else
# ifdef RTOLBYTES
        lastcon <<= SZCHAR;
        lastcon |= (val&BITMASK(SZCHAR));
# else
        lastcon &= BITMASK(SZCHAR*i);
        lastcon |= val << (SZCHAR*i);
# endif
# endif
      }
      i++;
      continue;
    }
    break;
  }
  if (i == 0)
    uerror(gettxt("xs:53","empty character constant"));
  if (i > (SZINT/SZCHAR))
    uerror(gettxt("xs:54","too many characters in character constant"));

}

lxcom()
{
  register c;
  /* saw a /*: process a comment */

  for(;;){

    switch( c = GETCHAR() ){

    case EOF:
      uerror( gettxt("xs:55","lxcom() unexpected EOF") );
      lastlen--;
      return;

    case '\n':
      ++lineno;

    default:
      continue;

    case '*':
      if( (c = GETCHAR()) == '/' ) return;
      else UNGETC(c);
      continue;

    }
  }
}

yylex()
{
  curlen = 0;

  for(;;){

    register lxchar;
    register struct lxdope *p;
    register struct symtab *sp;
    register char *cp;
    register id;
    TYPEP tp;

    switch( (p = lxcp[(lxchar = GETCHAR())+1])->lxact ){

onechar:
      UNGETC(lxchar);

    case A_1C:
      /* eat up a single character, and return an opcode */

      yylval.node.t_val = p->lxval;
      LRETURN( p->lxtok );

    case A_ERR:
      uerror( gettxt("xs:56","illegal character: %03o (octal)"), lxchar );
      break;

    case A_LET:
      /* collect an identifier, and check for reserved word */
      lxget( lxchar, LEXLET|LEXDIG );

      /* 0 means some kind of low level error, >0 reserved,
      ** <0 means id
      */
      if( (lxchar=lxres()) > 0 ) LRETURN( lxchar );
      if( lxchar== 0 ) continue;

      if (!is_null_type(tp = types_check(cp = na_store(yytext)))){
	if (is_typedef){
	  int c;
	  lxget(' ', LEXWS);
	  c = GETCHAR();
	  UNGETC(c);
	  if (isalpha(c) || c == '_'){
            yylval.node.t_type = tp;
            LRETURN( TYPE );
	  } else {
	    yylval.node.t_name = cp;
	    LRETURN(NAME);
 	  }
	} else {
	  yylval.node.t_type = tp;
	  LRETURN(TYPE);
	}
      }
      yylval.node.t_name = cp;
      LRETURN( NAME );

    case A_DIG:
      /* collect a digit string, then look at last one... */
      lastcon = 0;
      yylval.node.t_val = 0;  /* assume it's an int (not LONG) */
      lxget( lxchar, LEXDIG );
      switch( lxchar = GETCHAR() )
      {

      case 'x':
      case 'X':
        if( yytext[0] != '0' || yytext[1] != '\0' )
          uerror( gettxt("xs:57","illegal hex constant") );
        lxmore( lxchar, LEXHEX );
        /* convert the value */
        {
          for( cp = yytext+2; *cp; ++cp ){
            /* this code won't work for all character sets,
            ** but seems ok for ascii and ebcdic
            */
            lastcon <<= 4;
            if( isdigit( *cp ) )
              lastcon += *cp-'0';
            else if( isupper( *cp ) )
              lastcon += *cp - 'A'+ 10;
            else
              lastcon += *cp - 'a'+ 10;
#if SZINT != SZLONG
            if (lastcon >= (1L << SZINT))
                yylval.node.t_val = 1;
#endif
          }
        }
        goto islong;

      case '.':
        lxmore( lxchar, LEXDIG );

getfp:
        if( (lxchar = GETCHAR()) == 'e' || lxchar == 'E' ){
           /* exponent */
        case 'e':
        case 'E':
          if( (lxchar = GETCHAR()) == '+' || lxchar == '-' ){
            *lxgcp++ = 'e';
          } else {
            UNGETC(lxchar);
            lxchar = 'e';
          }
          lxmore( lxchar, LEXDIG );
          /* now have the whole thing... */
        } else {
            /* no exponent */
          UNGETC(lxchar);
        }
        errno = 0;
        dcon = FP_ATOF(yytext);
        if (errno)
            werror(gettxt("xs:58","float/double constant out of range"));
        LRETURN( FCON );

      default:
        UNGETC(lxchar);
        if( yytext[0] == '0' ) {
          /* convert in octal */
          for( cp = yytext+1; *cp; ++cp )
          {
            if (*cp > '7')
              werror(gettxt("xs:59","bad octal digit"));
            lastcon <<= 3;
            lastcon += *cp - '0';
#if SZINT != SZLONG
            if (lastcon >= (1L << SZINT))
                yylval.node.t_val = 1;
#endif
          }
          goto islong;
        } else {
          /* convert in decimal */
          for( cp = yytext; *cp; ++cp ) {
            lastcon = lastcon*10 + *cp - '0';
#if SZINT != SZLONG
            if (lastcon >= (1L << (SZINT-1)))
                yylval.node.t_val = 1;
#endif
          }
        }

islong:
        /* finally, look for trailing L or l */
        if( (lxchar=GETCHAR()) == 'L' || lxchar == 'l' )
            yylval.node.t_val = 1;
        else UNGETC(lxchar);
        LRETURN( ICON );
      }

    case A_DOT:
      /* a dot: if followed by a digit, floating point */
      lxchar = GETCHAR();
      if( lxmask[lxchar+1] & LEXDIG ) {
        UNGETC(lxchar);
        lxget( '.', LEXDIG );
        goto getfp;
      }
      goto onechar;

    case A_STR:
      /* string constant */
      lxstr();
      LRETURN( STRING );

    case A_CC:
      /* character constant */
      lastcon = 0;
      lxcc();
      yylval.node.t_val = 0;
      LRETURN( ICON );

    case A_BCD:
      {
        register i;
        register j;
        for( i=0; i<LXTSZ; ++i ){
          if( ( j = GETCHAR() ) == '`' )
            break;
          if( j == '\n' ) {
            uerror(
            gettxt("xs:60","newline in BCD constant") );
            break;
          }
          yytext[i] = j;
        }
        yytext[i] = '\0';
        if( i>6 )
          uerror(gettxt("xs:61","BCD constant exceeds 6 characters") );
# ifdef gcos
        else
          strtob( yytext, &lastcon, i );
        lastcon >>= 6*(6-i);
# else
        if( yytext[0] == 'u' ){
          yylval.node.t_val = UOP0 + yytext[1]-'0';
          LRETURN( UNOP );
        }
        uerror( gettxt("xs:62","gcos BCD constant illegal") );
# endif
        LRETURN( ICON );
      }

    case A_SL:
      /* / */
      if( (lxchar = GETCHAR()) != '*' ) goto onechar;
      lxcom();
    case A_WS:
      continue;

    case A_NL:
      ++lineno;
      lxtitle();
      continue;

    case A_NOT:
      /* ! */
      if( (lxchar = GETCHAR()) != '=' ) goto onechar;
      yylval.node.t_val = NE;
      LRETURN( EQUOP );

    case A_MI:
      /* - */
      if( (lxchar = GETCHAR()) == '-' ) {
        yylval.node.t_val = DECR;
        LRETURN( INCOP );
      }
      if( lxchar != '>' )
        goto onechar;
      yylval.node.t_val=STREF;
      LRETURN( STROP );

    case A_PL:
      /* + */
      if( (lxchar = GETCHAR()) != '+' ) goto onechar;
      yylval.node.t_val = INCR;
      LRETURN( INCOP );

    case A_AND:
      /* & */
      if( (lxchar = GETCHAR()) != '&' ) goto onechar;
      LRETURN( yylval.node.t_val = ANDAND );

    case A_OR:
      /* | */
      if( (lxchar = GETCHAR()) != '|' ) goto onechar;
      LRETURN( yylval.node.t_val = OROR );

    case A_LT:
      /* < */
      if( (lxchar = GETCHAR()) == '<' ){
        yylval.node.t_val = LS;
        LRETURN( SHIFTOP );
      }
      if( lxchar != '=' ) goto onechar;
      yylval.node.t_val = LE;
      LRETURN( RELOP );

    case A_GT:
      /* > */
      if( (lxchar = GETCHAR()) == '>' ){
        yylval.node.t_val = RS;
        LRETURN(SHIFTOP );
      }
      if( lxchar != '=' ) goto onechar;
      yylval.node.t_val = GE;
      LRETURN( RELOP );

    case A_EQ:
      /* = */
      if( (lxchar = GETCHAR()) == '=' ){
        yylval.node.t_val = EQ;
        LRETURN( EQUOP );
      }

      goto onechar;


    default:
      cerror( gettxt("xs:63","yylex error, character %03o (octal)"), lxchar );

    }

    /* ordinarily, repeat here... */
    cerror( gettxt("xs:64","out of switch in yylex") );

  }

}

struct lxrdope {    /* dope for reserved, in alphabetical order */
    char *lxrch;    /* name of reserved word */
    short lxract;   /* reserved word action */
    short lxrval;   /* value to be returned */
} lxrdope[] = {

  "asm",       AR_A,   0,
  "auto",      AR_CL,  AUTO,
  "break",     AR_RW,  BREAK,
  "char",      AR_TY,  CHAR,
  "case",      AR_RW,  CASE,
  "continue",  AR_RW,  CONTINUE,
  "double",    AR_TY,  DOUBLE,
  "default",   AR_RW,  DEFAULT,
  "do",        AR_RW,  DO,
  "extern",    AR_CL,  EXTERN,
  "else",      AR_RW,  ELSE,
  "enum",      AR_E,  ENUM,
  "for",       AR_RW,  FOR,
  "float",     AR_TY,  FLOAT,
  "fortran",   AR_CL,  FORTRAN,
  "goto",      AR_RW,  GOTO,
  "if",        AR_RW,  IF,
  "int",       AR_TY,  INT,
  "long",      AR_TY,  LONG,
  "return",    AR_RW,  RETURN,
  "register",  AR_CL,  REGISTER,
  "switch",    AR_RW,  SWITCH,
  "struct",    AR_S,   0,
  "sizeof",    AR_RW,  SIZEOF,
  "short",     AR_TY,  SHORT,
  "static",    AR_CL,  STATIC,
  "typedef",   AR_CL,  TYPEDEF,
  "unsigned",  AR_TY,  UNSIGNED,
  "union",     AR_U,   0,
  "void",      AR_TY,  UNDEF, /* tymerge adds FTN */
  "while",     AR_RW,  WHILE,
  "",          0,      0,  /* to stop the search */
}; /* lxdope[] */

lxres()
{
  /* check to see of yytext is reserved; if so,
  /* do the appropriate action and return */
  /* otherwise, return -1 */

  register c, ch;
  register struct lxrdope *p;

  ch = yytext[0];

  if (!islower(ch))
    return -1;

  switch(ch){
  case 'a':
    c=0; break;
  case 'b':
    c=2; break;
  case 'c':
    c=3; break;
  case 'd':
    c=6; break;
  case 'e':
    c=9; break;
  case 'f':
    c=12; break;
  case 'g':
    c=15; break;
  case 'i':
    c=16; break;
  case 'l':
    c=18; break;
  case 'r':
    c=19; break;
  case 's':
    c=21; break;
  case 't':
    c=26; break;
  case 'u':
    c=27; break;
  case 'v':
    c=29; break;
  case 'w':
    c=30; break;
  default:
    return -1;
  } /* switch */
  for( p= lxrdope+c; p->lxrch[0] == ch; ++p ){
    if( !strcmp( yytext, p->lxrch ) ){
       /* match */
      switch( p->lxract ){

      case AR_TY:
        /* type word */
        yylval.node.t_type = types_systype(p->lxrval);
        return( TYPE );

      case AR_RW:
        /* ordinary reserved word */
        yylval.node.t_val = p->lxrval;
        if (yylval.node.t_val == SIZEOF)
          is_sizeof = TRUE;
        return yylval.node.t_val;

      case AR_CL:
        /* class word */
	if (!is_typedef)
	  is_typedef = (yytext[0] == 't' && strcmp(yytext, "typedef") == 0);
        yylval.node.t_val = p->lxrval;
        yylval.node.t_type = types_class(p->lxrval);
        return( CLASS );

      case AR_S:
        /* struct */
        yylval.node.t_val = STRUCT;
	no_type = TRUE;
        return( STRUCT );

      case AR_U:
        /* union */
        yylval.node.t_val = -STRUCT;
	no_type = TRUE;
        return( STRUCT );

      case AR_E:
        /* enums */
	no_type = TRUE;
        return( yylval.node.t_val = ENUM );

      case AR_A:
        /* asm */
        lxget( ' ', LEXWS );
        if( GETCHAR() != '(' ) goto badasm;
        lxget( ' ', LEXWS );
        if( GETCHAR() != '"' ) goto badasm;
        while ((c = GETCHAR()) != '"')
          if( c=='\n' || c==EOF ) goto badasm;
        lxget( ' ', LEXWS );
        if( GETCHAR() != ')' ) goto badasm;
	return(ASM);
badasm:
        uerror( gettxt("xs:65","bad asm construction") );
        return( 0 );

      default:
        cerror( gettxt("xs:66","bad AR_?? action") );
      }
    }
  }
  return( -1 );
}

lxtitle()
{
  /* called after a newline; set linenumber and file name */

  register c, val;
  register char *cp;

  for(;;){
     /* might be several such lines in a row */
    if( (c = GETCHAR()) != '#' ){
      if( c != EOF ) UNGETC(c);
      return;
    }

    lxget( ' ', LEXWS );
    c = GETCHAR();

    if (!isdigit(c)){
      while((c = GETCHAR()) != EOF && c != '\n');
      lineno++;
      continue;
    }
    for( val = 0; isdigit(c); c=GETCHAR() ){
      val = val*10+ c - '0';
    }
    UNGETC(c);
    lineno = val;
    lxget( ' ', LEXWS );
    if( (c = GETCHAR()) != '\n' ){
      for( cp = ftitle; c != '\n'; c = GETCHAR(),++cp ){
        *cp = c;
      }
      *cp = '\0';
    }
  }
}
