/*****************************************************************************
*   "Irit" - the 3d polygonal solid modeller.				     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
*   Module to convert infix expression given as	ascii stream sequence into   *
* a binary tree, and evaluate it.					     *
*   All the objects are handled the same but the numerical one, which is     *
* moved as a RealType and not as an object (only internally within this	     *
* module) as it is frequently used and consumes much less memory this way.   *
*****************************************************************************/

#ifdef __MSDOS__
#include <alloc.h>
#include <graphics.h>
#endif /* __MSDOS__ */

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include "program.h"
#include "objects.h"
#include "allocatg.h"
#include "windowsg.h"
#include "inptprsg.h"
#include "inptprsl.h"
#include "overload.h"
#include "ctrl-brk.h"

#ifndef __MSDOS__
#include "xgraphic.h"
#endif /* __MSDOS__ */


static int IPGlblILastToken, IPGlblParseError;    /* Globals used by parser. */
char IPGlblCharData[LINE_LEN_LONG];	      /* Used for both parse & eval. */
static FileStackStruct FileStack[FILE_STACK_SIZE];    /* Include file stack. */
static int FileStackPtr = 0;

/* Operator preceeding parser stack, and stack pointer: */
static ParseTree *Stack[MAX_PARSER_STACK];
static int ParserStackPointer = 0;

/* Aliases list - simple macro substitution mechanizem. */
static AliasesStruct GlobalAliasList;

#ifdef DEBUG
    int MaxStackPointer = 0;		  /* Measure maximum depth of stack. */
#endif /* DEBUG */

static struct ParseTree *GenInputParseTree(void);
static ParseTree *OperatorPrecedence(void);
static int TestPreceeding(int Token1, int Token2);
static int GetToken(RealType *Data);
static int GetVarFuncToken(char *Token, RealType *Data);
static void FlushToEndOfExpr(int FlushStdin);
static void InptPrsrUnGetC(char c);
static char InptPrsrGetC(void);

/*****************************************************************************
*   Main module routine - generate parse tree and then tries to evaluate it. *
* Returns TRUE if succesfull, otherwise check IPGlblParseError/EvalError.    *
*****************************************************************************/
int InputParser(void)
{
    struct ParseTree *PTree;

    if (WasCtrlBrk || GlblFatalError) {
	GlblFatalError = WasCtrlBrk = FALSE;
	FlushToEndOfExpr(FALSE);	  /* Close all include files if any. */
	return TRUE;
    }

    PTree = GenInputParseTree();		     /* Generate parse tree. */

    if (IPGlblParseError == 0) {
#	ifdef DEBUG
	    fprintf(stderr, "\nInput generated Parse tree (Max stack = %d)\n",
							MaxStackPointer);
	    InptPrsrPrintTree(PTree, NULL);
	    fprintf(stderr, "\n");
#	endif /* DEBUG */
	if (InptPrsrTypeCheck(PTree, 0) == ERROR_EXPR) {   /* Type checking. */
	    InptPrsrFreeTree(PTree);		     /* Not needed any more. */
	    FlushToEndOfExpr(TRUE);/* Close all include files, & flush stdin.*/
	    return FALSE;
	}

	InptPrsrEvalTree(PTree, 0);			     /* Evaluate it. */
	if (IPGlblEvalError != 0) {
	    FlushToEndOfExpr(TRUE); /* Close include files, and flush stdin. */
	    return FALSE;
	}
    }
    else {
	FlushToEndOfExpr(TRUE); /* Close all include files, and flush stdin. */
	return FALSE;
    }

    InptPrsrFreeTree(PTree);			     /* Not needed any more. */

    return !(IPGlblParseError || IPGlblEvalError);
}

/*****************************************************************************
*   Routine to convert the expression from stream f into a binary tree.      *
* Algorithm: Using operator precedence with the following grammer:           *
* EXPR    ::= EXPR    |  EXPR + EXPR    |  EXPR - EXPR                       *
* EXPR    ::= EXPR    |  EXPR * EXPR    |  EXPR / EXPR                       *
* EXPR    ::= EXPR    |  EXPR ^ EXPR                                         *
* EXPR    ::= EXPR    |  EXPR , EXPR    |  EXPR = EXPR                       *
* EXPR    ::= NUMBER  |  -EXPR          |  (EXPR)        |  FUNCTION         *
* FUCTION ::= FUNC(EXPR , EXPR , ...)					     *
* Where FUNC might be function like arithmetics (SIN, COS etc.).	     *
* Note that FUNC might have more than one operand, seperated by ','.	     *
*                                                                            *
* Note the stream is terminated by semicolon character ';'.		     *
*                                                                            *
* Left associativity for +, -, *, /, ^.                                      *
* Precedence of operators is as usual:                                       *
*     <Highest> {unar minus}   {^}   {*, /}   {+, -} <Lowest>		     *
*                                                                            *
* Returns NULL if an error was found, and error is in IPGlblParseError       *
*****************************************************************************/
static ParseTree *GenInputParseTree(void)
{
    ParseTree *Root;
    int i;

    IPGlblILastToken = 0;	/* Used to hold last token read from stream. */
    IPGlblParseError = 0;			     /* No errors so far ... */

    Root = OperatorPrecedence();

    if (IPGlblParseError) {
	for (i=0; i<=ParserStackPointer; i++)/* Free partialy allocated tree.*/
	    InptPrsrFreeTree(Stack[i]);
	return (ParseTree *) NULL;				  /* Error ! */
    }
    else return Root;
}

/*****************************************************************************
*  Routine to allocate new ParseTree expression node:			     *
*****************************************************************************/
ParseTree *MyExprMalloc(void)
{
    ParseTree *p;

    p = (ParseTree *) MyMalloc(sizeof(ParseTree), OTHER_TYPE);
    p -> Right = p -> Left = NULL;
    return p;
}

/*****************************************************************************
*  Routine to free one expression node:					     *
*****************************************************************************/
void MyExprFree(ParseTree *Ptr)
{
    MyFree((char *) Ptr, OTHER_TYPE);
}

/*****************************************************************************
*   Routine to actually parse using operator precedence:                     *
* Few Notes:                                                                 *
* 1. Parse the input with the help of GetToken routine. Input is redirected  *
*    using the FileStack.						     *
* 2. All tokens must be in the range of 0..999 as we use the numbers above   *
*    it (adding 1000) to deactivate them in the handle searching (i.e. when  *
*    they were reduced to sub.-expression).                                  *
* 3. Returns NULL pointer in case of an error (see Expr2TrG.h for errors     *
* 4. See "Compilers - principles, techniques and tools" by Aho, Sethi &      *
*    Ullman,   pages 207-210.                                                *
*****************************************************************************/
static ParseTree *OperatorPrecedence(void)
{
    int Token, LowHandle, Temp1, Temp2;
    RealType Data;

#   ifdef DEBUG
	MaxStackPointer = 0;
#   endif /* DEBUG */

    ParserStackPointer = 0;

    /* Push the start symbol on stack (node pointer points on tos): */
    Stack[ParserStackPointer] = MyExprMalloc();
    Stack[ParserStackPointer] -> NodeKind = TOKENSTART;
    Stack[ParserStackPointer] -> Right =
    Stack[ParserStackPointer] -> Left = NULL;

    Token = GetToken(&Data);      /* Get one look ahead token to start with. */

    do {
        if (IPGlblParseError) return NULL;

        Temp1 = ParserStackPointer;	   /* Find top active token (<1000). */
        while (Stack[Temp1] -> NodeKind >= 1000) Temp1--;
        /* Now test to see if the new token completes an handle: */
        if (TestPreceeding(Stack[Temp1] -> NodeKind, Token)) {
            switch (Token) {
		case CLOSPARA:
                    if (Stack[Temp1] -> NodeKind == OPENPARA) {
			MyExprFree(Stack[Temp1]);	 /* Free open paran. */
			/* If a parameter is introduced instead of function  */
			/* it will be reduced already against "(" and it     */
			/* probably was missspelled function...		     */
                        if (Stack[Temp1-1] -> NodeKind == PARAMETER+1000) {
			    strcpy(IPGlblCharData,
				    Stack[Temp1-1] -> U.PObj -> Name);
			    IPGlblParseError = IP_ERR_UndefFunc;
			    return NULL;
			}

                        switch (Stack[Temp1-1] -> NodeKind) {
			    case ARCCOS:  /* If it is of the form Func(Expr) */
			    case ARCSIN:  /* Then reduce it directly to that */
			    case ARCTAN2: /* function, else (default) reduce */
			    case ARCTAN:  /* to sub-expression.              */
			    case COS:
			    case EXP:
			    case FABS:
			    case LN:
			    case LOG:
			    case SIN:
			    case SQRT:
			    case TAN:
			    case CPOLY:
			    case AREA:
			    case VOLUME:
			    case TIME:

			    case VECTOR:
			    case ROTX:
			    case ROTY:
			    case ROTZ:
			    case TRANS:
			    case SCALE:
			    case BOX:
			    case GBOX:
			    case CONE:
			    case CYLIN:
			    case SPHERE:
			    case TORUS:
			    case PLANE:
			    case POLY:
			    case CROSSEC:
			    case SURFREV:
			    case EXTRUDE:
			    case LIST:
			    case LOAD:
			    case CONVEX:

			    case VIEW:
			    case DIR:
			    case CHDIR:
			    case NORMAL:
			    case INCLUDE:
			    case GDUMP:
			    case MDUMP:
			    case FREEOBJ:
			    case INTERACT:
			    case IFCOND:
			    case FORLOOP:
			    case PRHELP:
			    case PAUSE:
			    case ALIAS:
			    case BEEP:
			    case EDIT:
			    case LOGFILE:
			    case COLOR:

				if (ParserStackPointer-Temp1 != 1) {
				    UpdateCharError("",
					Stack[Temp1-1] -> NodeKind);
				    IPGlblParseError = IP_ERR_ParamFunc;
				    return NULL;
				}
				Stack[ParserStackPointer] -> NodeKind -= 1000;
				Stack[Temp1-1] -> NodeKind += 1000;
			        Stack[Temp1-1] -> Right =
						Stack[ParserStackPointer];
				ParserStackPointer -= 2;
				break;

			    case EXIT:   /* Special case of non param. func. */
			    case VARLIST:
			    case SYSTEM:

				if (ParserStackPointer-Temp1 == 1) {
				    UpdateCharError("",
					Stack[Temp1-1] -> NodeKind);
				    IPGlblParseError = IP_ERR_NoParamFunc;
				    return NULL;
				}
				Stack[Temp1-1] -> NodeKind += 1000;
			        Stack[Temp1-1] -> Right = NULL;
				ParserStackPointer --;
				break;

			    default:
				if (ParserStackPointer-Temp1 != 1) {
				    IPGlblParseError = IP_ERR_ParaMatch;
				    return NULL;
				}
                                Stack[Temp1] = Stack[ParserStackPointer--];
				break;
			}
                        Token = GetToken(&Data);       /* Get another token. */
                        continue;
		    }
		    else if (Stack[Temp1] -> NodeKind == TOKENSTART) {
			/* No match for this one! */
                        IPGlblParseError = IP_ERR_ParaMatch;
			return NULL;
		    }
		    break;
                case TOKENEND:
                    if (Stack[Temp1] -> NodeKind == TOKENSTART) {
                        if (ParserStackPointer != 1) {
                            IPGlblParseError = IP_ERR_WrongSyntax;
			    return NULL;
			}
			InptPrsrFreeTree(Stack[Temp1]);	  /* The TOKENSTART. */
			Stack[1] -> NodeKind -= 1000;
			return Stack[1];
		    }
		}

            Temp2 = Temp1-1;		  /* Find the lower bound of handle. */
            while (Stack[Temp2] -> NodeKind >= 1000) Temp2--;
            LowHandle = Temp2 + 1;
            if (LowHandle < 1) {                  /* No low bound was found. */
                IPGlblParseError = IP_ERR_WrongSyntax;
	        return NULL;			 /* We ignore data till now. */
            }
	    switch (ParserStackPointer - LowHandle + 1) {
		case 1: /* Its a scalar one - mark it as used (add 1000). */
		    switch (Stack[ParserStackPointer] -> NodeKind) {
			case NUMBER:
			case PARAMETER:
			case STRING:
		            Stack[ParserStackPointer] -> NodeKind += 1000;
			    break;
			default:
			    UpdateCharError("Found ",
				Stack[ParserStackPointer] -> NodeKind);
			    IPGlblParseError = IP_ERR_ParamExpect;
			    return NULL;
		    }
		    break;
		case 2: /* Its a monadic operator - create the subtree. */
		    switch (Stack[ParserStackPointer-1] -> NodeKind) {
		        case UNARMINUS:
		            Stack[ParserStackPointer-1] -> Right =
						Stack[ParserStackPointer];
		            Stack[ParserStackPointer] -> NodeKind -= 1000;
		            Stack[ParserStackPointer-1] -> NodeKind += 1000;
		            ParserStackPointer--;
		            break;
		        case OPENPARA:
			    IPGlblParseError = IP_ERR_ParaMatch;
			    return NULL;
		        default:
			    UpdateCharError("Found Operator ",
				Stack[ParserStackPointer-1] -> NodeKind);
			    IPGlblParseError = IP_ERR_OneOperand;
			    return NULL;
		    }
		    break;
		case 3: /* Its a diadic operator - create the subtree. */
		    switch (Stack[ParserStackPointer-1] -> NodeKind) {
		        case PLUS:
		        case MINUS:
		        case MULT:
		        case DIV:
		        case POWER:
		        case COMMA:
		        case EQUAL:
		        case COLON:
		            Stack[ParserStackPointer-1] -> Right =
                                  Stack[ParserStackPointer];
                            Stack[ParserStackPointer-1] -> Left =
                                  Stack[ParserStackPointer-2];
		            Stack[ParserStackPointer-2] -> NodeKind -= 1000;
		            Stack[ParserStackPointer] -> NodeKind -= 1000;
		            Stack[ParserStackPointer-1] -> NodeKind += 1000;
		            Stack[ParserStackPointer-2] =
						Stack[ParserStackPointer-1];
		            ParserStackPointer -= 2;
                            break;
                        default:
			    UpdateCharError("Found Operator ",
				Stack[ParserStackPointer-1] -> NodeKind);
			    IPGlblParseError = IP_ERR_TwoOperand;
			    return NULL;
		    }
		    break;
		default:
		    IPGlblParseError = IP_ERR_WrongSyntax;
		    return NULL;
	    }
        }
        else {		 /* Push that token on stack - it is not handle yet. */
	    Stack[++ParserStackPointer] = MyExprMalloc();

#	    ifdef DEBUG
		if (MaxStackPointer < ParserStackPointer)
		    MaxStackPointer = ParserStackPointer;
#	    endif /* DEBUG */

            if (ParserStackPointer == MAX_PARSER_STACK-1) {
                IPGlblParseError = IP_ERR_StackOV;
		return NULL;			 /* We ignore data till now. */
	    }
            Stack[ParserStackPointer] -> NodeKind = Token;
            Stack[ParserStackPointer] -> U.R = Data;  /* We might need that. */
	    Stack[ParserStackPointer] -> Right =
	    Stack[ParserStackPointer] -> Left = (ParseTree *) NULL;
	    if (Token == PARAMETER) {
		if ((Stack[ParserStackPointer] -> U.PObj =
				GetObject(IPGlblCharData)) == NULL) {
		    /* Its new one - allocate memory for it. */
		    Stack[ParserStackPointer] -> U.PObj =
			AllocObject(IPGlblCharData, UNDEF_OBJ, NULL);
		}
	    }
	    else
	    if (Token == STRING) {
		Stack[ParserStackPointer] -> U.PObj =
		    AllocObject("", STRING_OBJ, NULL);
		strcpy(Stack[ParserStackPointer] -> U.PObj -> U.Str,
		       IPGlblCharData);
	    }
            Token = GetToken(&Data);	   /* And get new token from stream. */
	}
    }
    while (TRUE);
}

/*****************************************************************************
*   Routine to test precedence of two tokens. returns 0, <0 or >0 according  *
* to comparison results:                                                     *
*****************************************************************************/
static int TestPreceeding(int Token1, int Token2)
{
    int Preced1, Preced2;

    if ((Token1 >= 1000) || (Token2 >= 1000))
	return FALSE;					 /* Ignore sub-expr. */

    switch (Token1) {
	case ARCCOS:
	case ARCSIN:
	case ARCTAN:
	case ARCTAN2:
	case COS:
	case EXP:
	case FABS:
	case LN:
	case LOG:
	case SIN:
	case SQRT:
	case TAN:
	case CPOLY:
	case AREA:
	case VOLUME:
	case TIME:

	case VECTOR:
	case ROTX:
	case ROTY:
	case ROTZ:
	case TRANS:
	case SCALE:
	case BOX:
	case GBOX:
	case CONE:
	case CYLIN:
	case SPHERE:
	case TORUS:
	case PLANE:
	case POLY:
	case CROSSEC:
	case SURFREV:
	case EXTRUDE:
	case LIST:
	case LOAD:
	case CONVEX:

	case EXIT:
	case VIEW:
	case DIR:
	case CHDIR:
	case NORMAL:
	case INCLUDE:
	case GDUMP:
	case MDUMP:
	case FREEOBJ:
	case INTERACT:
	case PAUSE:
	case IFCOND:
	case FORLOOP:
	case PRHELP:
	case VARLIST:
	case ALIAS:
	case BEEP:
	case EDIT:
	case SYSTEM:
	case LOGFILE:
	case COLOR:
	    Preced1 =130; break;
	case COMMA:
	case COLON:
	case EQUAL:
	    Preced1 = 30; break;
	case NUMBER:
	case PARAMETER:
	case STRING:
	    Preced1 =150; break;
	case PLUS:
	case MINUS:
	    Preced1 = 50; break;
	case MULT:
	case DIV:
	    Preced1 = 70; break;
	case POWER:
	    Preced1 = 90; break;
	case UNARMINUS:
	    Preced1 = 95; break;
	case OPENPARA:
	    Preced1 = 20; break;
	case CLOSPARA:
	    Preced1 =150; break;
	case TOKENSTART:
	case TOKENEND:
	    Preced1 = 10; break;
    }

    switch (Token2) {
	case ARCCOS:
	case ARCSIN:
	case ARCTAN:
	case ARCTAN2:
	case COS:
	case EXP:
	case FABS:
	case LN:
	case LOG:
	case SIN:
	case SQRT:
	case TAN:
	case CPOLY:
	case AREA:
	case VOLUME:
	case TIME:

	case VECTOR:
	case ROTX:
	case ROTY:
	case ROTZ:
	case TRANS:
	case SCALE:
	case BOX:
	case GBOX:
	case CONE:
	case CYLIN:
	case SPHERE:
	case TORUS:
	case PLANE:
	case POLY:
	case CROSSEC:
	case SURFREV:
	case EXTRUDE:
	case LIST:
	case LOAD:
	case CONVEX:

	case EXIT:
	case VIEW:
	case DIR:
	case CHDIR:
	case NORMAL:
	case INCLUDE:
	case GDUMP:
	case MDUMP:
	case FREEOBJ:
	case INTERACT:
	case PAUSE:
	case IFCOND:
	case FORLOOP:
	case PRHELP:
	case VARLIST:
	case ALIAS:
	case BEEP:
	case EDIT:
	case SYSTEM:
	case LOGFILE:
	case COLOR:
	    Preced2 =120; break;
	case COMMA:
	case COLON:
	case EQUAL:
	    Preced2 = 35; break;
	case NUMBER:
	case PARAMETER:
	case STRING:
	    Preced2 =140; break;
	case PLUS:
	case MINUS:
	    Preced2 = 40; break;
	case MULT:
	case DIV:
	    Preced2 = 60; break;
	case POWER:
	    Preced2 = 80; break;
	case UNARMINUS:
	    Preced2 =100; break;
	case OPENPARA:
	    Preced2 =140; break;
	case CLOSPARA:
	    Preced2 = 15; break;
	case TOKENSTART:
	case TOKENEND:
	    Preced2 =  0; break;
    }

    return Preced1-Preced2 > 0;
}

/*****************************************************************************
*  Routine to update the character error message according to StrMsg & Token *
*****************************************************************************/
void UpdateCharError(char *StrMsg, int Token)
{
    char *TokenChar = NULL;

    if (Token > 1000) Token -= 1000;

    switch (Token) {
	case ARCSIN:
	case ARCCOS:
	case ARCTAN:
	case ARCTAN2:
	case COS:
	case EXP:
	case FABS:
	case LN:
	case LOG:
	case SIN:
	case SQRT:
	case TAN:
	case CPOLY:
	case AREA:
	case VOLUME:
	case TIME:
            TokenChar = NumFuncTable[Token-NUM_FUNC_OFFSET].FuncName;
	    break;
	case VECTOR:
	case ROTX:
	case ROTY:
	case ROTZ:
	case TRANS:
	case SCALE:
	case BOX:
	case GBOX:
	case CONE:
	case CYLIN:
	case SPHERE:
	case TORUS:
	case PLANE:
	case POLY:
	case CROSSEC:
	case SURFREV:
	case EXTRUDE:
	case LIST:
	case LOAD:
	case CONVEX:
            TokenChar = ObjFuncTable[Token-OBJ_FUNC_OFFSET].FuncName;
            break;
	case EXIT:
	case VIEW:
        case DIR:
	case CHDIR:
	case NORMAL:
	case INCLUDE:
	case GDUMP:
	case MDUMP:
	case FREEOBJ:
	case INTERACT:
	case PAUSE:
	case IFCOND:
	case FORLOOP:
	case PRHELP:
	case VARLIST:
	case ALIAS:
	case BEEP:
	case EDIT:
	case SYSTEM:
	case LOGFILE:
	case COLOR:
            TokenChar = GenFuncTable[Token-GEN_FUNC_OFFSET].FuncName;
            break;
	case PLUS:
            TokenChar = "+";
            break;
	case MINUS:
            TokenChar = "-";
            break;
	case MULT:
            TokenChar = "*";
            break;
	case DIV:
            TokenChar = "/";
            break;
	case POWER:
            TokenChar = "^";
	    break;
	case UNARMINUS:
            TokenChar = "(Unar) -";
	    break;
	case COMMA:
	    TokenChar = ",";
	    break;
	case EQUAL:
            TokenChar = "=";
	    break;
	case COLON:
            TokenChar = ":";
	    break;
	default:
            sprintf(IPGlblCharData, "%s - Token %d\n", StrMsg, Token);
            return;
    }
    sprintf(IPGlblCharData, "%s%s", StrMsg, TokenChar);
}

/*****************************************************************************
*   Routine to get the next token out of the expression.                     *
* Gets the expression in S, and current position in i.                       *
* Returns the next token found, set data to the returned value (if any),     *
* and update i to one char ofter the new token found.                        *
*   Note that in minus sign case, it is determined whether it is monadic or  *
* diadic minus by the last token - if the last token was operator or '('     *
* it is monadic minus.                                                       *
*****************************************************************************/
static int GetToken(RealType *Data)
{
    int i, RetVal = 0;
    char c;

    while (isspace(c = InptPrsrGetC()));	       /* Skip white blanks. */

    if (c == '"') {		  /* Its a string token - read up to next ". */
	i = 0;
	while ((IPGlblCharData[i] = InptPrsrGetC()) != '"') {
	    if (IPGlblCharData[i] == '\\') /* Its escape char. for next one: */
		IPGlblCharData[i] = InptPrsrGetC();
	    i++;
	}
	IPGlblCharData[i] = 0;
	RetVal = STRING;
    }
    else if (isalpha(c)) {		  /* Is it a variable/function name? */
	if (islower(c)) IPGlblCharData[i=0] = toupper(c);
	else IPGlblCharData[i=0] = c;

	while (isalpha(c = InptPrsrGetC()) || isdigit(c) || c == '_')
	    if (islower(c)) IPGlblCharData[++i] = toupper(c);
	    else IPGlblCharData[++i] = c;
	IPGlblCharData[++i] = 0;
	InptPrsrUnGetC(c);

	if (strlen(IPGlblCharData) >= OBJ_NAME_LEN) {
	    RetVal = TOKENERROR;
	    IPGlblParseError = IP_ERR_NameTooLong;
	}
	else {
	    RetVal = GetVarFuncToken(IPGlblCharData, Data);
	}
    }
    else if (isdigit(c) || (c == '.')) {	      /* Is it numeric data? */
	IPGlblCharData[i=0] = c;

	while (isdigit(c = InptPrsrGetC()) || (c == '.') ||
					(c == 'e') || (c == 'E') || (c == 'e'))
	    IPGlblCharData[++i] = c;
	/* Handle the special case of negative exponent ("111.111E-22"). */
	if (c == '-' && (IPGlblCharData[i] == 'e' ||
			 IPGlblCharData[i] == 'E')) {
	    IPGlblCharData[++i] = c;
	    while (isdigit(c = InptPrsrGetC()) || (c == '.'))
		IPGlblCharData[++i] = c;
	}
	IPGlblCharData[++i] = 0;

	InptPrsrUnGetC(c);

#	ifdef DOUBLE
	    sscanf(IPGlblCharData, "%lf", Data);
#	else
	    sscanf(IPGlblCharData, "%f", Data);
#	endif /* DOUBLE */

        RetVal = NUMBER;
    }
    else switch (c) {
	case '+': RetVal = PLUS; break;
	case '-': switch (IPGlblILastToken) {
		       case 0:	      /* If first token (no last token yet). */
		       case PLUS:
		       case MINUS:
		       case MULT:
		       case DIV:
		       case POWER:
		       case COMMA:
		       case EQUAL:
		       case COLON:
		       case UNARMINUS:
		       case OPENPARA:
			   RetVal = UNARMINUS; break;
		       default:
                           RetVal = MINUS; break;
		  }
		  break;
	case '*': RetVal = MULT; break;
	case '/': RetVal = DIV; break;
	case '^': RetVal = POWER; break;;
	case '(': RetVal = OPENPARA; break;
	case ')': RetVal = CLOSPARA; break;
	case '=': RetVal = EQUAL; break;
	case ',': RetVal = COMMA; break;
	case ':': RetVal = COLON; break;
	case ';': RetVal = TOKENEND; break;	       /* End of expression! */
	default:
	    RetVal = TOKENERROR;
	    IPGlblParseError = IP_ERR_UndefToken;
            break;
    }

    IPGlblILastToken = RetVal;

    return RetVal;
}

/*****************************************************************************
*   Routine to test alpha Token for match with one of the defined functions  *
* and returns that Token function if found one. otherwise it is assumed to   *
* be a variable (new or old).						     *
* Note that although the search is linear, It is extremely fast to add new   *
* functions - simply add its token, its entry here, and in the parser itself.*
*****************************************************************************/
static int GetVarFuncToken(char *Token, RealType *Data)
{
    int i;
    char c;

    if (strcmp("COMMENT", Token) == 0) {
	/* Get first nonspace char after the COMMENT key word: */
	while (isspace(c = InptPrsrGetC()));
	/* And read the input until this char appear again (end of comment): */
	while (c != InptPrsrGetC());

	return GetToken(Data);		       /* Return next token instead. */
    }

    for (i=0; i<NumFuncTableSize; i++)		 /* Is it Numeric function ? */
	if (strcmp(NumFuncTable[i].FuncName, Token) == 0)
	    return(NumFuncTable[i].FuncToken);
    for (i=0; i<ObjFuncTableSize; i++)		  /* Is it Object function ? */
	if (strcmp(ObjFuncTable[i].FuncName, Token) == 0)
	    return(ObjFuncTable[i].FuncToken);
    for (i=0; i<GenFuncTableSize; i++)		 /* Is it General function ? */
	if (strcmp(GenFuncTable[i].FuncName, Token) == 0)
	    return(GenFuncTable[i].FuncToken);

    for (i=0; i<ConstantTableSize; i++)	   /* Replace constant by its value. */
	if (strcmp(ConstantTable[i].FuncName, Token) == 0) {
	    sprintf(Token, "%lg", ConstantTable[i].Value);
	    *Data = ConstantTable[i].Value;
	    return NUMBER;
	}

    return PARAMETER;   /* If not a function - it is assumed to be variable. */
}

/*****************************************************************************
*   Routine to reset the aliases buffer to a known state.		     *
*****************************************************************************/
void AliasReset(void)
{
    int i;

    for (i=0; i<NUM_OF_ALIASES; i++)
	GlobalAliasList.Aliases[i].Name = NULL;
}

/*****************************************************************************
*   Routine to update (insert, delete or print) the global alias list	     *
*****************************************************************************/
void AliasEdit(char *Name, char *Value)
{
    int i;
    char s[LINE_LEN];

    if (strlen(Name) == 0) {		    /* Print all defined alias list. */
	WndwInputWindowPutStrFS("Alias List:", RED, TRUE);
	for (i=0; i<NUM_OF_ALIASES; i++)
	    if (GlobalAliasList.Aliases[i].Name != NULL) {
		sprintf(s, "Alias \"%s\" - \"%s\"",
			GlobalAliasList.Aliases[i].Name,
			GlobalAliasList.Aliases[i].Value);
		WndwInputWindowPutStrFS(s, RED, FALSE);
	    }
	return;
    }

    if (strlen(Value) == 0) {			  /* Its a delete operation. */
	for (i=0; i<NUM_OF_ALIASES; i++)
	    if (stricmp(Name, GlobalAliasList.Aliases[i].Name) == 0) break;
	if (i<NUM_OF_ALIASES) {	     /* Found alias to delete, so delete it. */
	    MyFree(GlobalAliasList.Aliases[i].Name, OTHER_TYPE);
	    MyFree(GlobalAliasList.Aliases[i].Value, OTHER_TYPE);
	    GlobalAliasList.Aliases[i].Name = NULL;
	}
	else WndwInputWindowPutStr("Alias not found, ignored", RED);
    }
    else {/* Its an insert operation - test for old one, otherwise make new. */
	for (i=0; i<NUM_OF_ALIASES; i++)
	    if (stricmp(Name, GlobalAliasList.Aliases[i].Name) == 0) break;
	if (i<NUM_OF_ALIASES) {	     /* Found alias to replace - replace it. */
	    MyFree(GlobalAliasList.Aliases[i].Value, OTHER_TYPE);
	    GlobalAliasList.Aliases[i].Value =
		MyMalloc(strlen(Value) + 1, OTHER_TYPE);
	    strcpy(GlobalAliasList.Aliases[i].Value, Value);
	}
	else {			   /* Find empty slot and insert as new one. */
	    for (i=0; i<NUM_OF_ALIASES; i++)
		if (GlobalAliasList.Aliases[i].Name == NULL) break;
	    if (i<NUM_OF_ALIASES) {	       /* Found empty slot - use it. */
		GlobalAliasList.Aliases[i].Name =
		    MyMalloc(strlen(Name) + 1, OTHER_TYPE);
		strcpy(GlobalAliasList.Aliases[i].Name, Name);
		GlobalAliasList.Aliases[i].Value =
		    MyMalloc(strlen(Value) + 1, OTHER_TYPE);
		strcpy(GlobalAliasList.Aliases[i].Value, Value);
	    }
	    else WndwInputWindowPutStr("Aliases buffer full, ignored", RED);
	}
    }
}

/*****************************************************************************
*   Routine to expand aliases of the given line using the global defined     *
* alias list as saved in GlobalAliasList.				     *
*****************************************************************************/
void AliasExpand(char *Line)
{
    int i, j, OldSize, NewSize, DiffSize, Count = 0;
    char *alias;

    for (i=0; i<NUM_OF_ALIASES; i++)
	if (GlobalAliasList.Aliases[i].Name != NULL) {
	    do {
		if ((alias = strstr(Line, GlobalAliasList.Aliases[i].Name)) !=
		    NULL) {
		    if (Count++ > 100) {
			WndwInputWindowPutStr("Alias expansion - 100 expansions reached created, aborted:",
				RED);
			return;
		    }
		    OldSize = strlen(GlobalAliasList.Aliases[i].Name);
		    NewSize = strlen(GlobalAliasList.Aliases[i].Value);
		    DiffSize = NewSize - OldSize;
		    if (DiffSize + strlen(Line) > LINE_LEN_LONG - 1) {
			WndwInputWindowPutStr("Alias expansion created too long line, aborted:",
				RED);
			return;
		    }
		    /* Expand/shrink line space according to Name/Value sizes*/
		    if (DiffSize != 0)
			if (NewSize > OldSize) {
			    movmem(alias, &alias[DiffSize], strlen(alias) + 1);
			}
			else {
			    movmem(&alias[-DiffSize], alias,
				strlen(&alias[-DiffSize]) + 1);
			}
		    /* And copy the Value instead of name into line: */
		    for (j=0; j<NewSize; j++) alias[j] =
			GlobalAliasList.Aliases[i].Value[j];
		}
	    }
	    while (alias != NULL);
	}

}

static char UnGetChar;

/*****************************************************************************
*   Routine to control all getchar in this module and echo it if requested   *
* Note it handles the FileStack and decrease it if end of file was found.    *
*****************************************************************************/
static char InptPrsrGetC(void)
{
    static char Line[LINE_LEN_LONG] = "";
    static int LineLength = 0, LineCount = 0;
    char c;
#ifndef __MSDOS__
    int i;
#endif /* __MSDOS__ */

    if (UnGetChar == 0) {		       /* One level of unget char... */
	if (LineCount < LineLength) {	 /* Is there anything in local Line? */
	}
	else do {
	    if (FileStackPtr == 0) {
		WndwInputWindowGetStr(Line, LINE_LEN_LONG);
		LineCount = 0;
	    }
	    else {
		sprintf(Line, "%s > ", FileStack[FileStackPtr-1].Name);
		LineCount = strlen(Line);
		if (fgets(&Line[LineCount], LINE_LEN_LONG-20,
					FileStack[FileStackPtr-1].f) == NULL) {
		    /* Its end of file - close it and update stack. */
		    Line[0] = 0;
		    fclose(FileStack[--FileStackPtr].f);
		}
	    }
	    AliasExpand(&Line[LineCount]);    /* Expand the aliases in line. */

	    /* Line len. changes by Wndw routine - strip off CR/LF/TAB.      */
#ifdef __MSDOS__
	    WndwInputWindowPutStr(Line, NO_COLOR);
#else
	    if (FileStackPtr != 0)		 /* Input was from keyboard? */
		WndwInputWindowPutStr(Line, NO_COLOR);
	    {
		for (i=0; i<strlen(Line); i++)
		    if (Line[i] == TAB) Line[i] = ' ';	/* Strip off tabs... */
		for (i=strlen(Line); isspace(Line[--i]););   /* Strip CR/LF. */
		Line[i+1] = 0;
	    }
#endif /* __MSDOS__ */
	    LineLength = strlen(Line);            
	} while (LineCount >= LineLength);
	c = Line[LineCount++];
	if (c == '#') {			  /* Its a comment - skip that line. */
            c = ' ';				   /* Must return something. */
            LineCount = LineLength;    /* Force next time to fetch new line. */
	}
#	ifdef DEBUG
	    fprintf(stderr, "%c", c);
#	endif /* DEBUG */
    }
    else {
	c = UnGetChar;
	UnGetChar = 0;
    }

    return c;
}

/*****************************************************************************
*   Routine to unget one char						     *
*****************************************************************************/
static void InptPrsrUnGetC(char c)
{
    UnGetChar = c;
}

/*****************************************************************************
*   Routine to read data up to the next end of expression marker - ';'.	     *
*****************************************************************************/
static void FlushToEndOfExpr(int FlushStdin)
{
    if (FileStackPtr > 0)	/* Close all the open files - back to stdin. */
	while (FileStackPtr) fclose(FileStack[--FileStackPtr].f);
    else if (FlushStdin && IPGlblILastToken != TOKENEND)
		while (InptPrsrGetC() != ';');

}

/*****************************************************************************
*   Routine to push new file to read on the FileStack from INCLUDE command:  *
*****************************************************************************/
void FileInclude(char *FileName)
{
    int i;
    FILE *f;
    char s[LINE_LEN], c;

    if (FileStackPtr < FILE_STACK_SIZE) {
	if (strrchr(FileName, '.') == NULL)	        /* If no '.' in name */
	{      /* (nor in its path - actually a bug, but I'll skip that...). */
	    strcat(FileName, ".irt");
	}
	if ((f = fopen(FileName, "r")) != NULL) {
	    FileStack[FileStackPtr].f = f;
	    for (i=strlen(FileName)-1;		   /* Isolate the file name. */
		 i > 0 && (c = FileName[i]) != '\\' && c != '/' && c != ':';
		 i--);
	    if (i > 0) i++;
	    strncpy(FileStack[FileStackPtr].Name, &FileName[i],
							FILE_NAME_LEN-1);
	    FileStackPtr++;		 /* Now next char is from that file! */
	}
	else {
	    sprintf(s, "Cannt open file %s - ignored\n", FileName);
	    WndwInputWindowPutStr(s, RED);
	}
    }
    else WndwInputWindowPutStr("File nesting too deep - ignored\n", RED);
}

/*****************************************************************************
*   Routine to return parsing error if happen one, zero	elsewhere	     *
*****************************************************************************/
int InptPrsrParseError(char **Message)
{
    int	Temp;

    *Message = IPGlblCharData;
    Temp = IPGlblParseError;
    IPGlblParseError = 0;
    return Temp;
}
