#define DO_NEG_NUMBERS	0

#if LEXTEST

#include "../h/cdefs.h"

#include <stdio.h>
#include <ctype.h>
#include <strings.h>

double strtod();

#define INTEGER		1
#define DOUBLE		2
#define STRING		3
#define LEFTPAREN	4
#define RIGHTPAREN	5
#define LEFTANGLE	6
#define RIGHTANGLE	7
#define EQUAL		8
#define IDENTIFIER	9
#define COMMA	       10
#define COLON	       11
#define QUESTION_MARK  12
#define ASSIGN	       13
#define PLUS	       14
#define MINUS	       15
#define TIMES	       16
#define DIVIDE	       17
#define ERR	      100

#define IBUFSIZE  1024
FILE* inputFd;
char inputBuf[IBUFSIZE];
char* inputBufPtr;
int lineno;
char errMsgBuf[256];
int errMsgSet = 0;

typedef struct {
	union {
	    double d;
	    int i;
	    char* s;
	 } u;
	int lineno;
} YYSTYPE;

YYSTYPE yylval;

static yylexinit();
static yylex();
static matchNumber();

#endif

#define TOKEN_ERR(s,d1,d2)			\
	sprintf (errMsgBuf, (s), (d1), (d2));	\
	errMsgSet = 1;				\
	token = ERR;

static char* copystr(s)
char *s;
{
	return (strcpy ((char*)malloc(strlen(s)+1), s));
}

static yylexinit(fp)
FILE *fp;
{
	inputFd = (fp ? fp : stdin);
	inputBuf[0] = '\0';
	inputBufPtr = inputBuf;
}

static yylex()
{
	register char* ibp;
	char strBuf[IBUFSIZE];
	char* newIbp;
	int negate = 0;
	int token;
	int inputOff = 0;

	ibp = inputBufPtr;
	do
	 { if (*ibp == '\0')
	    { if (!fgets (inputBuf, IBUFSIZE, inputFd))
	       { return (EOF);
	       }
	      else
	       { lineno++;
		 ibp = inputBuf;
		 if (strncmp (ibp, "#if 0", strlen("#if 0")) == 0)
		  { inputOff++;
		  }
		 else if (strncmp (ibp, "#endif", strlen("#endif")) == 0)
		  { if (inputOff)
		     { inputOff--;
		     }
		  }
	       }
	    }
	   if (!inputOff)
	    { while (isspace(*ibp))
	       { ibp++;
	       }
	    }
	   if (*ibp == '#' || inputOff)
	    { *ibp = '\0';
	    }
	 }
	while (*ibp == '\0' || inputOff);
	inputBufPtr = ibp;

#if DO_NEG_NUMBERS
	if (*ibp == '-')
	 { if (isdigit(ibp[1]) || ibp[1] == '.')
	    { negate = 1;
	      ibp++;
	    }
	 }
#endif
	if (isdigit(*ibp) || *ibp == '.')
	 { token = matchNumber (ibp, &newIbp, negate);
	 }
	else if (isalpha(*ibp) || *ibp == '_')
	 { char *cp, *sp;
	   for (cp=ibp, sp=strBuf; isalnum(*cp) || (*cp=='_'); )
	    { *sp++ = *cp++;
	    }
	   if (*cp == '.' 
#if DO_NEG_NUMBERS
	       || *cp == '-'
#endif
	      )
	    { TOKEN_ERR ("line %d: badly constructed indentifier\n",lineno,0);
	    }
	   else
	    { *sp = '\0';
	      yylval.u.s = copystr(strBuf);
	      newIbp = cp;
	      token = IDENTIFIER;
	    }
	 }
	else if (*ibp == '"')
	 { if (strtos (ibp, strBuf, IBUFSIZE, &newIbp) < 0)
	    { TOKEN_ERR ("line %d: non-terminated string\n", lineno, 0);
	    }
	   else
	    { yylval.u.s = copystr(strBuf);
	      token = STRING;
	    }
	 }
	else
	 { switch (*ibp)
	    { case ',':
	       { token = COMMA;
		 break;
	       }
	      case '(':
	       { token = LEFTPAREN;
		 break;
	       }
	      case ')':
	       { token = RIGHTPAREN;
		 break;
	       }
	      case '<':
	       { token = LEFTANGLE;
		 break;
	       }
	      case '>':
	       { token = RIGHTANGLE;
		 break;
	       }
	      case '=':
	       { token = EQUAL;
		 break;
	       }
	      case '?':
	       { token = QUESTION_MARK;
		 break;
	       }
	      case '+':
	       { token = PLUS;
		 break;
	       }
	      case '-':
	       { token = MINUS;
		 break;
	       }
	      case '*':
	       { token = TIMES;
		 break;
	       }
	      case '/':
	       { token = DIVIDE;
		 break;
	       }
	      case ':':
	       { if (ibp[1] == '=')
		  { token = ASSIGN;
		    ibp++;
		  }
		 else
		  { token = COLON;
		  }
		 break;
	       }
	      default:
	       { TOKEN_ERR ("line %d: illegal input char '%c'\n",lineno,*ibp);
	       }
	    }
	   newIbp = ibp+1;
	 }
	if (token != ERR)
	 { inputBufPtr = newIbp;
	   yylval.lineno = lineno;
	 }
	else
	 { for (ibp=inputBufPtr; !isspace(*ibp) && *ibp != '\0'; ibp++)
	      ;
	   inputBufPtr = ibp;
	 }
	return (token);
}

static matchNumber(s, newS, negate)
register char *s;
char **newS;
int negate;
{
	int ival;
	int token = 0;
	double dval;

	if (*s == '0')
	 { if (s[1] == 'x' || s[1] == 'X')
	    { ival = strtol (&s[2], newS, 16);
	      token = INTEGER;
	    }
	   else if (s[1] == 'o' || s[1] == 'O')
	    { ival = strtol (&s[2], newS, 8);
	      token = INTEGER;
	    }
	   else if (s[1] == 'b' || s[1] == 'B')
	    { ival = strtol (&s[2], newS, 2);
	      token = INTEGER;
	    }
	   else
	    { ival = strtol (s, newS, 8);
	      if (**newS == '.' || **newS == 'e' || **newS == 'E')
	       { dval = strtod (s, newS);
		 token = DOUBLE;
	       }
	      else
	       { token = INTEGER;
	       }
	    }
	 }
	else if (isdigit(*s))
	 { ival = strtol (s, newS, 10);
	   if (**newS == '.' || **newS == 'e' || **newS == 'E')
	    { dval = strtod (s, newS);
	      token = DOUBLE;
	    }
	   else
	    { token = INTEGER;
	    }
	 }
	else if ((*s == '.') && (isdigit(s[1])))
	 { dval = strtod (s, newS);
	   token = DOUBLE;
	 }
	if (!token || s == *newS || isalpha(**newS) || **newS == '.'
#if DO_NEG_NUMBERS
		   ||**newS == '-' 
#endif
	   )
	 { TOKEN_ERR ("line %d: badly constructed number\n", lineno, 0);
	 }
	else if (token == DOUBLE)
	 { yylval.u.d = (negate ? -dval : dval);
	 }
	else 
	 { yylval.u.i = (negate ? -ival : ival);
	 }
	return (token);
}

#if LEXTEST

char **argvec;

struct tokenVal {
	int token;
	char *str;
  } testTokenList[] = 
{ 
/* line 1 */

  INTEGER, "1234", 
  DOUBLE,  "1234",
  STRING,  "\"\"hi there\n",
  IDENTIFIER, "I12dent", 

/* line 2 */

  INTEGER, "0",
  INTEGER, "0xff",
  INTEGER, "0o77",
  INTEGER, "0o77",
  INTEGER, "0b1010",
#if DO_NEG_NUMBERS
  INTEGER, "0",
  INTEGER, "-2",
  INTEGER, "-0o777",
#else
  MINUS, 0,
  INTEGER, "0",
  MINUS, 0,
  INTEGER, "2",
  MINUS, 0,
  INTEGER, "0o777",
#endif
  INTEGER, "0xff",
  INTEGER, "0b10101",
#if DO_NEG_NUMBERS
  INTEGER, "-987",
#else
  MINUS, 0,
  INTEGER, "987",
#endif
  INTEGER, "987",

/* line 3 */

  DOUBLE, "0.1",
  DOUBLE, "0.1",
  DOUBLE, "123.456",
  DOUBLE, "1e-4",
  DOUBLE, "1e4",
  DOUBLE, "1e4",
  DOUBLE, "1e4",
  DOUBLE, "1.1e4",
  DOUBLE, "123",

/* line 4 */


  ERR, 0,
  ERR, 0,
  ERR, 0,
  ERR, 0,
#if DO_NEG_NUMBERS
  ERR, 0,
  ERR, 0,
#else
  DOUBLE, "1e4",
  MINUS, 0,
  INTEGER, "777",
  MINUS, 0,
#endif
  ERR, 0,
  ERR, 0,
  ERR, 0,
  ERR, 0,
#if DO_NEG_NUMBERS
  ERR, 0,
#else
  INTEGER, "0",
  MINUS, 0,
#endif
  ERR, 0,
  ERR, 0,
  ERR, 0,
#if DO_NEG_NUMBERS
  ERR, 0,
  ERR, 0,
#else
  DOUBLE, "1",
  MINUS, 0,
  MINUS, 0,
  ERR, 0,
#endif

/* line 5 */

  COLON, 0,
  QUESTION_MARK, 0,
  LEFTANGLE, 0,
  RIGHTANGLE, 0,
  LEFTPAREN, 0,
  RIGHTPAREN, 0,
  EQUAL, 0,
  LEFTANGLE, 0,
  RIGHTANGLE, 0,
  LEFTPAREN, 0,
  RIGHTPAREN, 0,
  EQUAL, 0,

  LEFTPAREN, 0,
  INTEGER, "1",
  RIGHTPAREN, 0,
  DOUBLE, "2.3",
  EQUAL, 0,
  STRING, "hi",
  LEFTANGLE, 0,
  INTEGER, "7",
  INTEGER, "8",
  RIGHTANGLE, 0,
  EQUAL, 0,
  LEFTANGLE, 0,
  LEFTPAREN, 0,
  DOUBLE, "7.0",
  RIGHTPAREN, 0,
  RIGHTANGLE, 0,
  IDENTIFIER, "field3",
  EQUAL, 0,
  IDENTIFIER, "foo",
  LEFTANGLE, 0,
  INTEGER, "1",
  RIGHTANGLE, 0,
  LEFTANGLE, 0,
  IDENTIFIER, "id1", 
  IDENTIFIER, "id2", 
  RIGHTANGLE, 0,
  
};

YYSTYPE *testTokenVals;

char* tokenStr(token)
int token;
{
	static struct {
		 int token;
		 char* name; } tokenStrings[] = 
	 { INTEGER,	"INTEGER",
	   DOUBLE,	"DOUBLE",
	   STRING,	"STRING",
	   LEFTPAREN,	"LEFTPAREN",
	   RIGHTPAREN,	"RIGHTPAREN",
	   LEFTANGLE,	"LEFTANGLE",
	   RIGHTANGLE,	"RIGHTANGLE",
	   EQUAL,	"EQUAL",
	   IDENTIFIER,	"IDENTIFIER",
	   COMMA,	"COMMA",
	   ERR,		"ERR",
	 };
	int k;

	for (k=0; k<ARRAY_SIZE(tokenStrings); k++)
	 { if (token == tokenStrings[k].token)
	    { return (tokenStrings[k].name);
	    }
	 }
	return ("**BOGUS**");
}

main(argc, argv)
int argc;
char **argv;
{
	int token;
	int automatic = 1;
	int k, j;
	int lastLine;

	argvec = argv;
	argc--; argv++;
	while (argc > 0)
	 { if (strcmp(*argv, "-i") == 0)
	    { automatic = 0;
	    }
	   else
	    { goto usage;
	    }
	   argc--; argv++;
	   continue;
usage:
	   fprintf (stderr, "Usage: %s [-i]\n", argvec[0]);
	   exit (-1);
	 }

	if (automatic)
	 { FILE *fp;
	   if ((fp = fopen ("lextestData", "r")) == 0)
	    { fprintf (stderr, "Can't open file lextestData\n");
	      exit (-1);
	    }
	   yylexinit(fp);

	   /* start by computing the necessary lexical values */

	   testTokenVals = (YYSTYPE*)malloc(sizeof(YYSTYPE)*
					    ARRAY_SIZE(testTokenList));
	   for (k=0; k<ARRAY_SIZE(testTokenList); k++)
	    { switch (testTokenList[k].token)
	       { case STRING:
		 case IDENTIFIER:
		  { testTokenVals[k].u.s = testTokenList[k].str;
		    break;
		  }
		 case DOUBLE:
		  { testTokenVals[k].u.d = strtod(testTokenList[k].str, 0);
		    break;
		  }
		 case INTEGER:
		  { char *str = testTokenList[k].str;
		    int negate = 0;

		    if (str[0] == '-')
		     { negate = 1;
		       str++;
		     }
		    if (str[0] == '0' && str[1] == 'b')
		     { testTokenVals[k].u.i = strtol(&str[2], 0, 2);
		     }
		    else if (str[0] == '0' && str[1] == 'o')
		     { testTokenVals[k].u.i = strtol(&str[2], 0, 8);
		     }
		    else if (str[0] == '0' && str[1] == 'x')
		     { testTokenVals[k].u.i = strtol(&str[2], 0, 16);
		     }
		    else
		     { testTokenVals[k].u.i = strtol(str, 0, 10);
		     }
		    if (negate)
		     { testTokenVals[k].u.i *= -1;
		     }
		  }
		 break;
	       }
	    }

	   lastLine = 0;
	   for (k=0; (token = yylex()) != EOF; k++)
	    { /* j counts the tokens from the start of the line */
	      if (lastLine != lineno)
	       { lastLine = lineno;
		 j = 1;
	       }
	      else
	       { j++;
	       }
	      if (k >= ARRAY_SIZE(testTokenList))
	       { printf ("Too many tokens in test file\n");
		 exit (-1);
	       }
	      if (token != testTokenList[k].token)
	       { printf ("Token number %d, line %d:\n", j, lineno);
		 printf ("Expecting %s, got %s\n", 
			  tokenStr(testTokenList[k].token), tokenStr(token));
		 exit (-1);
	       }
	      switch (token)
	       { case INTEGER:
		  { if (yylval.u.i != testTokenVals[k].u.i)
		     { printf ("Token number %d, line %d:\n", j, lineno);
		       printf ("INTEGER: Expecting value %d, got %d\n", 
				testTokenVals[k].u.i, yylval.u.i);
		       exit (-1);
		     }
		    break;
		  }
		 case DOUBLE:
		  { if (yylval.u.d != testTokenVals[k].u.d)
		     { printf ("Token number %d, line %d:\n", j, lineno);
		       printf ("DOUBLE: Expecting value %g, got %g\n", 
				testTokenVals[k].u.d, yylval.u.d);
		       exit (-1);
		     }
		    break;
		  }
		 case IDENTIFIER:
		  { if (strcmp (yylval.u.s, testTokenVals[k].u.s) != 0)
		     { printf ("Token number %d, line %d:\n", j, lineno);
		       printf ("IDENTIFIER: Expecting value %s, got %s\n", 
				testTokenVals[k].u.s, yylval.u.s);
		       exit (-1);
		     }
		    break;
		  }
		 case STRING:
		  { if (strcmp (yylval.u.s, testTokenVals[k].u.s) != 0)
		     { printf ("Token number %d, line %d:\n", j, lineno);
		       printf ("STRING: Expecting value %s, got %s\n", 
				testTokenVals[k].u.s, yylval.u.s);
		       exit (-1);
		     }
		    break;
		  }
	       }
	    }	      
	   if (k != ARRAY_SIZE(testTokenList))
	    { printf ("Not enough tokens in test file\n");
	      exit (-1);
	    }
	   printf ("\nLexical ROUTINES OK\n\n");
	 }
	else
	 { yylexinit(0);
	    
	   while ((token = yylex()) != EOF)
	    { switch (token)
	       { case INTEGER:
		  { printf ("INTEGER %d\n", yylval.u.i);	
		    break;
		  }
		 case DOUBLE:
		  { printf ("DOUBLE %g\n", yylval.u.d);	
		    break;
		  }
		 case STRING:
		  { printf ("STRING %s\n", yylval.u.s);	
		    break;
		  }
		 case IDENTIFIER:
		  { printf ("IDENTIFIER %s\n", yylval.u.s);	
		    break;
		  }
		 default:
		  { printf ("%s\n", tokenStr(token));	
		    break;
		  }
	       }
	    }
	 }
}

#endif
