/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/

#include <dxconfig.h>


#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <dx/dx.h>
#include "_compute.h"
#include "_compparse.h"

/* If this char is 0, return 0, else space on */
#define input() (*_dxfccsptr++)
#define unput() (_dxfccsptr--)

#define MAXTOKEN	200


static char *_dxfccsbuf;
static char *_dxfccsptr;





/*
 * these are key words used for operators (NOT functions!)
 */
static struct keytab
{
    char *name;
    int value;
} keytab [] =
{
    { "cross",		T_CROSS },
    { "dot",		T_DOT },
    { "mod",		T_MOD }
};

_dxfccinput(char *buffer)
{
#if defined(_CCDEBUG)
    extern int _dxfccdebug;
#endif

    _dxfccsbuf = buffer;
    _dxfccsptr = _dxfccsbuf;

#if defined(_CCDEBUG)
    _dxfccdebug = 1;
#endif
}

int
_dxfcclexerror()
{
    return (_dxfccsptr - _dxfccsbuf);
}

/*
 * analyze the input and return the next lexical token
 */
static int lastToken = T_EOF;
#define LEX_RETURN(t) return (lastToken = (t));

int
_dxfcclex()
{
    int c;
    char _dxfcctext[MAXTOKEN];
    int _dxfccleng;
    int i;
    double d;
    int    isDouble;

    for (;;) {
	_dxfccleng = 0;

	c = input();

	switch (c) {
	case 0:
	    LEX_RETURN(T_EOF);
	case '(':
	    LEX_RETURN(T_LPAR);
	case ')':
	    LEX_RETURN(T_RPAR);
	case '{':
	    LEX_RETURN(T_LBRA);
	case '}':
	    LEX_RETURN(T_RBRA);
	case '[':
	    LEX_RETURN(T_LSQB);
	case ']':
	    LEX_RETURN(T_RSQB);
	case ',':
	    LEX_RETURN(T_COMMA);
	case '|':
	    c = input();
	    if (c == '|')
		LEX_RETURN(T_LOR);
	    unput();
	    LEX_RETURN('|');
	case '&':
	    c = input();
	    if (c == '&')
		LEX_RETURN(T_LAND);
	    unput();
	    LEX_RETURN('&');
	case '!':
	    c = input();
	    if (c == '=')
		LEX_RETURN(T_NE);
	    unput();
	    LEX_RETURN(T_LNOT);
	case '<':
	    c = input();
	    if (c == '=')
		LEX_RETURN(T_LE);
	    unput();
	    LEX_RETURN(T_LT);
	case '>':
	    c = input();
	    if (c == '=')
		LEX_RETURN(T_GE);
	    unput();
	    LEX_RETURN(T_GT);
	case '=':
	    c = input();
	    if (c == '=')
		LEX_RETURN(T_EQ);
	    unput();
	    LEX_RETURN(T_ASSIGN);
	case '?':
	    LEX_RETURN(T_QUEST);
	case ':':
	    LEX_RETURN(T_COLON);
	case ';':
	    LEX_RETURN(T_SEMI);
	case '+':
	    LEX_RETURN(T_PLUS);
	case '-':
	    LEX_RETURN(T_MINUS);
	case '*':
	    c = input();
	    if (c == '*')
		LEX_RETURN(T_EXP);
	    unput();
	    LEX_RETURN(T_TIMES);
	case '/':
	    LEX_RETURN(T_DIV);
	case '^':
	    LEX_RETURN(T_EXP);
	case '%':
	    LEX_RETURN(T_MOD);
	case '$':			/* input variables */
	    c = input();
	    while (isdigit(c)) {
		_dxfcctext[_dxfccleng++] = c;
		c = input();
	    }
	    _dxfcctext[_dxfccleng] = '\0';
	    _dxfcclval.i = atoi(_dxfcctext);
	    unput();
	    if (_dxfcclval.i >= MAX_INPUTS) {
		DXSetError(ERROR_BAD_PARAMETER, "#12070",
			 _dxfcclval.i, MAX_INPUTS);
		LEX_RETURN(ERROR);
	    }
	    else {
		LEX_RETURN(T_INPUT);
	    }
	case ' ':			/* white space */
	case '\t':
	    break;
	case '\'':
	    {
		int i=0;
	        while (c = input()) {
		    if (c == '\'') { 
			if (!(c = input()) || (c != '\'')) {
			    unput();
			    c = '\'';
			    break;
			}
		    }
		    _dxfcclval.s[i++] = c;
		}
		_dxfcclval.s[i] = '\0';
		if (c != '\'') {
		    DXSetError(ERROR_BAD_PARAMETER, "Compute string has unmatched '");
		    LEX_RETURN(ERROR);
		}
	    }
	    LEX_RETURN(T_STRING);
	case '.':			/* logical or float */
	    if (lastToken != T_INPUT && lastToken != T_RPAR && lastToken != T_NAME) {
		c = input();
		if (isdigit(c)) {
		    _dxfcctext[_dxfccleng++] = '.';
		    goto fraction;
		}
		unput();
	    }
	    LEX_RETURN(T_PERIOD);
	case '0':	/* octal or hex input */
	    _dxfcctext[_dxfccleng++] = c;
	    c = input();
	    /* Process hex input.  if there are more than 8 hex digits
	     * (non-zero), then this won't fit in a 32 bit int. 
	     */
	    if (c == 'x' || c == 'X') {
		do {
		    _dxfcctext[_dxfccleng++] = c;
		    c = input();
		} while (isxdigit(c));
		_dxfcctext[_dxfccleng] = '\0';
		if (_dxfccleng - strspn(_dxfcctext, "0x") > 8) {
		    DXSetError(ERROR_BAD_PARAMETER, "#12080", _dxfcctext);
		    LEX_RETURN(ERROR);
		}
		_dxfcclval.i = strtol(_dxfcctext, NULL, 0);
	    }
	    /* 
	     * Process octal, becoming float if we get ., e, or E
	     * Note that we don't use strtol here because that doesn't
	     * understand the C sense of '8' and '9' as octal digits.
	     * If the we are about to multiply this number by 8, and it
	     * is greater than or equal to 4Gig >> 3, then it's an error.
	     */
	    else {
		_dxfcclval.i = 0;
		while (isdigit(c)) {
		    if (c == '8' || c == '9')
			DXWarning ("bad octal digit `%c'", c);
		    _dxfcctext[_dxfccleng++] = c;
		    /* Check overflow (including carying the high bit of 8&9) */
		    if ((_dxfcclval.i + ((c - '0') >> 3)) >= 0x20000000) {
			DXSetError(ERROR_BAD_PARAMETER, "#12081", _dxfcctext);
			LEX_RETURN(ERROR);
		    }
		    _dxfcclval.i = _dxfcclval.i * 8 + (c - '0');
		    c = input();
		}
		if (c == '.') {
		    goto fraction;
		}
		else if (c == 'e' || c == 'E') 
		    goto exponent;
	    }
	    /*
	     * Got something we didn't understand, quit (already converted)
	     */
	    unput();
	    LEX_RETURN(T_INT);
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9': /* Decimal input */
	    isDouble = 0;
	    do {
		_dxfcctext[_dxfccleng++] = c;
		c = input();
	    } while (isdigit(c));
	    if (c == '.') {
		goto fraction;
	    }
	    else if (c == 'e' || c == 'E')
		goto exponent;
	    else if (c == 'd' || c == 'D') {
		c = 'e';
		isDouble = 1;
		goto exponent;
	    }
	    unput();
	    _dxfcctext[_dxfccleng] = '\0';
	    if (strlen(_dxfcctext) > 10 || 
		(strlen(_dxfcctext) == 10 && strcmp(_dxfcctext, "2147483647") > 0)) {
		DXSetError(ERROR_BAD_PARAMETER, "#12082", _dxfcctext);
		LEX_RETURN(ERROR);
	    }
	    _dxfcclval.i = strtol(_dxfcctext, NULL, 0);
	    LEX_RETURN(T_INT);

fraction:
	    do {
		_dxfcctext[_dxfccleng++] = c;
		c = input();
		if (c == 'e' || c == 'E')
		    goto exponent;
		else if (c == 'd' || c == 'D')
		{
		    c = 'e';
		    isDouble = 1;
		    goto exponent;
		}
	    } while (isdigit(c));
	    unput();
	    _dxfcctext[_dxfccleng] = '\0';
	    d = atof(_dxfcctext);
	    if ((d > 0 && d > DXD_MAX_FLOAT) ||
		    (d < 0 && d < -DXD_MAX_FLOAT)) {
		DXSetError(ERROR_BAD_PARAMETER, "#12085", _dxfcctext);
		LEX_RETURN(ERROR);
	    }
	    else if ((d > 0 && d < DXD_MIN_FLOAT) ||
		    (d < 0 && d > -DXD_MIN_FLOAT)) {
		DXSetError(ERROR_BAD_PARAMETER, "#12085", _dxfcctext);
		LEX_RETURN(ERROR);
	    }
	    _dxfcclval.f = d;
	    LEX_RETURN(T_FLOAT);
exponent:
	    /* Jam the 'e' in place and get the next thing.  If it's the sign,
	     * shove it in too.  Then, suck up all digits.
	     */
	    _dxfcctext[_dxfccleng++] = c;
	    c = input();
	    if (c == '+' || c == '-') {
		_dxfcctext[_dxfccleng++] = c;
		c = input();
	    }
	    while (isdigit(c)) {
		_dxfcctext[_dxfccleng++] = c;
		c = input();
	    }
	    unput();
	    _dxfcctext[_dxfccleng] = '\0';
	    d = atof(_dxfcctext);
	    if (!isDouble) {
		if ((d > 0 && d > DXD_MAX_FLOAT) ||
			(d < 0 && d < -DXD_MAX_FLOAT)) {
		    DXSetError(ERROR_BAD_PARAMETER, "#12085", _dxfcctext);
		    LEX_RETURN(ERROR);
		}
		else if ((d > 0 && d < DXD_MIN_FLOAT) ||
			(d < 0 && d > -DXD_MIN_FLOAT)) {
		    DXSetError(ERROR_BAD_PARAMETER, "#12085", _dxfcctext);
		    LEX_RETURN(ERROR);
		}
		_dxfcclval.f = d;
		LEX_RETURN(T_FLOAT);
	    }
	    else {
		_dxfcclval.d = d;
		LEX_RETURN(T_DOUBLE);
	    }
	default:
	    /*
	     * most likely an identifier
	     */
	    if (isalpha(c)) {
		do {
		    _dxfcctext[_dxfccleng++] = c;
		    c = input();
		} while(isalpha(c) || isdigit(c) || c == '_');
		unput();
		_dxfcctext[_dxfccleng] = '\0';

		/* check for keywords */
		for (i=0; i < sizeof(keytab) / sizeof(keytab[0]); i++)
		    if (strcmp(keytab[i].name, _dxfcctext) == 0)
			LEX_RETURN(keytab[i].value);

		strncpy (_dxfcclval.s, _dxfcctext, MAX_PARSE_STRING_SIZE);
		LEX_RETURN(T_NAME);
	    }
	    else {
		DXSetError(ERROR_BAD_PARAMETER, "#12095", c);
		LEX_RETURN(ERROR);
	    }
	}
    }
}
