text: /*
  ** c_expr.cpp
  ** Console commands dealing with mathematical expressions
  **
  **---------------------------------------------------------------------------
  ** Copyright 1998-2006 Randy Heit
  ** All rights reserved.
  **
  ** Redistribution and use in source and binary forms, with or without
  ** modification, are permitted provided that the following conditions
  ** are met:
  **
  ** 1. Redistributions of source code must retain the above copyright
  **    notice, this list of conditions and the following disclaimer.
  ** 2. Redistributions in binary form must reproduce the above copyright
  **    notice, this list of conditions and the following disclaimer in the
  **    documentation and/or other materials provided with the distribution.
  ** 3. The name of the author may not be used to endorse or promote products
  **    derived from this software without specific prior written permission.
  **
  ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  **---------------------------------------------------------------------------
  **
  */
  
  // HEADER FILES ------------------------------------------------------------
  
  #include <stdlib.h>
  #include <math.h>
  #include <string.h>
  
  #include "c_dispatch.h"
  #include "c_cvars.h"
  #include "cmdlib.h"
  #include "printf.h"
  
  // MACROS ------------------------------------------------------------------
  
  // TYPES -------------------------------------------------------------------
  
  enum EProductionType
  {
  	PROD_String, PROD_Double
  };
  
  struct FProduction
  {
  	EProductionType Type;
  };
  
  struct FStringProd : public FProduction
  {
  	char Value[1];
  };
  
  struct FDoubleProd : public FProduction
  {
  	double Value;
  };
  
  struct FProducer
  {
  	char Token[4];
  	FProduction *(*DoubleProducer) (FDoubleProd *prod1, FDoubleProd *prod2);
  	FProduction *(*StringProducer) (FStringProd *prod1, FStringProd *prod2);
  };
  
  // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
  
  // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
  
  bool IsFloat (const char *str);
  
  // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
  
  static FProduction *ParseExpression (FCommandLine &argv, int &parsept);
  static const char *CIsNum (const char *str);
  static FStringProd *NewStringProd (const char *str);
  static FStringProd *NewStringProd (size_t len);
  static FDoubleProd *NewDoubleProd (double val);
  static FStringProd *DoubleToString (FProduction *prod);
  static FDoubleProd *StringToDouble (FProduction *prod);
  void MaybeStringCoerce (FProduction *&prod1, FProduction *&prod2);
  void MustStringCoerce (FProduction *&prod1, FProduction *&prod2);
  void DoubleCoerce (FProduction *&prod1, FProduction *&prod2);
  
  FProduction *ProdAddDbl  (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdAddStr  (FStringProd *prod1, FStringProd *prod2);
  FProduction *ProdSubDbl  (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdMulDbl  (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdDivDbl  (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdModDbl  (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdPowDbl  (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdLTDbl   (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdLTEDbl  (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdGTDbl   (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdGTEDbl  (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdEqDbl   (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdNeqDbl  (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdXorDbl  (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdAndDbl  (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdOrDbl   (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdLAndDbl (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdLOrDbl  (FDoubleProd *prod1, FDoubleProd *prod2);
  FProduction *ProdLTStr   (FStringProd *prod1, FStringProd *prod2);
  FProduction *ProdLTEStr  (FStringProd *prod1, FStringProd *prod2);
  FProduction *ProdGTStr   (FStringProd *prod1, FStringProd *prod2);
  FProduction *ProdGTEStr  (FStringProd *prod1, FStringProd *prod2);
  FProduction *ProdEqStr   (FStringProd *prod1, FStringProd *prod2);
  FProduction *ProdNeqStr  (FStringProd *prod1, FStringProd *prod2);
  
  // EXTERNAL DATA DECLARATIONS ----------------------------------------------
  
  // PUBLIC DATA DEFINITIONS -------------------------------------------------
  
  // PRIVATE DATA DEFINITIONS ------------------------------------------------
  
  static FProducer Producers[] =
  {
  	{ "+",   ProdAddDbl, ProdAddStr },
  	{ "-",   ProdSubDbl, NULL },
  	{ "*",   ProdMulDbl, NULL },
  	{ "/",   ProdDivDbl, NULL },
  	{ "%",   ProdModDbl, NULL },
  	{ "^",   ProdPowDbl, NULL },
  	{ "<",   ProdLTDbl,  ProdLTStr },
  	{ "<=",  ProdLTEDbl, ProdLTEStr },
  	{ ">",   ProdGTDbl,  ProdGTStr },
  	{ ">=",  ProdGTEDbl, ProdGTEStr },
  	{ "=",   ProdEqDbl,  ProdEqStr },
  	{ "==",  ProdEqDbl,  ProdEqStr },
  	{ "!=",  ProdNeqDbl, ProdNeqStr },
  	{ "<>",  ProdNeqDbl, ProdNeqStr },
  	{ "xor", ProdXorDbl, NULL },
  	{ "&",   ProdAndDbl, NULL },
  	{ "|",   ProdOrDbl,  NULL },
  	{ "&&",  ProdLAndDbl, NULL },
  	{ "||",  ProdLOrDbl, NULL }
  };
  
  // CODE --------------------------------------------------------------------
  
  //==========================================================================
  //
  // ParseExpression
  //
  // Builds a production from an expression. The supported syntax is LISP-like
  // but without parentheses.
  //
  //==========================================================================
  
  static FProduction *ParseExpression (FCommandLine &argv, int &parsept)
  {
  	if (parsept >= argv.argc())
  		return NULL;
  
  	const char *token = argv[parsept++];
  	FProduction *prod1 = NULL, *prod2 = NULL, *prod3 = NULL;
  
  	if (IsFloat (token))
  	{
  		return NewDoubleProd (atof(token));
  	}
  	else if (stricmp (token, "true") == 0)
  	{
  		return NewDoubleProd (1.0);
  	}
  	else if (stricmp (token, "false") == 0)
  	{
  		return NewDoubleProd (0.0);
  	}
  	else
  	{
  		for (size_t i = 0; i < countof(Producers); ++i)
  		{
  			if (strcmp (Producers[i].Token, token) == 0)
  			{
  				prod1 = ParseExpression (argv, parsept);
  				prod2 = ParseExpression (argv, parsept);
  				if (prod1 == NULL || prod2 == NULL)
  				{
  					goto missing;
  				}
  				if (Producers[i].StringProducer == NULL)
  				{
  					DoubleCoerce (prod1, prod2);
  				}
  				else if (Producers[i].DoubleProducer == NULL)
  				{
  					MustStringCoerce (prod1, prod2);
  				}
  				else
  				{
  					MaybeStringCoerce (prod1, prod2);
  				}
  				if (prod1->Type == PROD_String)
  				{
  					prod3 = Producers[i].StringProducer ((FStringProd *)prod1, (FStringProd *)prod2);
  				}
  				else
  				{
  					prod3 = Producers[i].DoubleProducer ((FDoubleProd *)prod1, (FDoubleProd *)prod2);
  				}
  				goto done;
  			}
  		}
  		if (strcmp ("!", token) == 0)
  		{
  			prod1 = ParseExpression (argv, parsept);
  			if (prod1 == NULL)
  			{
  				goto missing;
  			}
  			if (prod1->Type == PROD_String)
  			{
  				prod1 = StringToDouble (prod1);
  			}
  			prod3 = NewDoubleProd (!static_cast<FDoubleProd *>(prod1)->Value);
  			goto done;
  		}
  		return NewStringProd (token);
  	}
  
  missing:
  	Printf ("Missing argument to %s\n", token);
  
  done:
  	if (prod2 != NULL) M_Free (prod2);
  	if (prod1 != NULL) M_Free (prod1);
  	return prod3;
  }
  
  //==========================================================================
  //
  // IsFloat
  //
  //==========================================================================
  
  bool IsFloat (const char *str)
  {
  	const char *pt;
  	
  	if (*str == '+' || *str == '-')
  		str++;
  
  	if (*str == '.')
  	{
  		pt = str;
  	}
  	else
  	{
  		pt = CIsNum (str);
  		if (pt == NULL)
  			return false;
  	}
  	if (*pt == '.')
  	{
  		pt = CIsNum (pt+1);
  		if (pt == NULL)
  			return false;
  	}
  	if (*pt == 'e' || *pt == 'E')
  	{
  		pt++;
  		if (*pt == '+' || *pt == '-')
  			pt++;
  		pt = CIsNum (pt);
  	}
  	return pt != NULL && *pt == 0;
  }
  
  //==========================================================================
  //
  // IsNum
  //
  //==========================================================================
  
  static const char *CIsNum (const char *str)
  {
  	const char *start = str;
  
  	while (*str)
  	{
  		if (*str >= '0' && *str <= '9')
  			str++;
  		else
  			break;
  	}
  
  	return (str > start) ? str : NULL;
  }
  
  //==========================================================================
  //
  // NewStringProd (from a string)
  //
  //==========================================================================
  
  static FStringProd *NewStringProd (const char *str)
  {
  	FStringProd *prod = (FStringProd *)M_Malloc (sizeof(FStringProd)+strlen(str));
  	prod->Type = PROD_String;
  	strcpy (prod->Value, str);
  	return prod;
  }
  
  //==========================================================================
  //
  // NewStringProd (from a length)
  //
  //==========================================================================
  
  static FStringProd *NewStringProd (size_t len)
  {
  	FStringProd *prod = (FStringProd *)M_Malloc (sizeof(FStringProd)+len);
  	prod->Type = PROD_String;
  	prod->Value[0] = 0;
  	return prod;
  }
  
  //==========================================================================
  //
  // NewDoubleProd
  //
  //==========================================================================
  
  static FDoubleProd *NewDoubleProd (double val)
  {
  	FDoubleProd *prod = (FDoubleProd *)M_Malloc (sizeof(FDoubleProd));
  	prod->Type = PROD_Double;
  	prod->Value = val;
  	return prod;
  }
  
  //==========================================================================
  //
  // DoubleToString
  //
  //==========================================================================
  
  static FStringProd *DoubleToString (FProduction *prod)
  {
  	char buf[128];
  	FStringProd *newprod;
  
  	mysnprintf (buf, countof(buf), "%g", static_cast<FDoubleProd *>(prod)->Value);
  	newprod = NewStringProd (buf);
  	M_Free (prod);
  	return newprod;
  }
  
  //==========================================================================
  //
  // StringToDouble
  //
  //==========================================================================
  
  static FDoubleProd *StringToDouble (FProduction *prod)
  {
  	FDoubleProd *newprod;
  	
  	newprod = NewDoubleProd (atof (static_cast<FStringProd *>(prod)->Value));
  	M_Free (prod);
  	return newprod;
  }
  
  //==========================================================================
  //
  // MaybeStringCoerce
  //
  // If one of the parameters is a string, convert the other to a string.
  //
  //==========================================================================
  
  void MaybeStringCoerce (FProduction *&prod1, FProduction *&prod2)
  {
  	if (prod1->Type == PROD_String)
  	{
  		if (prod2->Type == PROD_Double)
  		{
  			prod2 = DoubleToString (prod2);
  		}
  	}
  	else if (prod2->Type == PROD_String)
  	{
  		prod1 = DoubleToString (prod1);
  	}
  }
  
  //==========================================================================
  //
  // MustStringCoerce
  //
  // Ensures that both parameters are strings
  //
  //==========================================================================
  
  void MustStringCoerce (FProduction *&prod1, FProduction *&prod2)
  {
  	if (prod1->Type == PROD_Double)
  	{
  		prod1 = DoubleToString (prod1);
  	}
  	if (prod2->Type == PROD_Double)
  	{
  		prod2 = DoubleToString (prod2);
  	}
  }
  
  //==========================================================================
  //
  // DoubleCoerce
  //
  // Ensures that both parameters are doubles
  //
  //==========================================================================
  
  void DoubleCoerce (FProduction *&prod1, FProduction *&prod2)
  {
  	if (prod1->Type == PROD_String)
  	{
  		prod1 = StringToDouble (prod1);
  	}
  	if (prod2->Type == PROD_String)
  	{
  		prod2 = StringToDouble (prod2);
  	}
  }
  
  //==========================================================================
  //
  // ProdAddDbl
  //
  //==========================================================================
  
  FProduction *ProdAddDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd (prod1->Value + prod2->Value);
  }
  
  //==========================================================================
  //
  // ProdAddStr
  //
  //==========================================================================
  
  FProduction *ProdAddStr (FStringProd *prod1, FStringProd *prod2)
  {
  	size_t len = strlen (prod1->Value) + strlen (prod2->Value) + 1;
  	FStringProd *prod = NewStringProd (len);
  	strcpy (prod->Value, prod1->Value);
  	strcat (prod->Value, prod2->Value);
  	return prod;
  }
  
  //==========================================================================
  //
  // ProdSubDbl
  //
  //==========================================================================
  
  FProduction *ProdSubDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd (prod1->Value - prod2->Value);
  }
  
  //==========================================================================
  //
  // ProdMulDbl
  //
  //==========================================================================
  
  FProduction *ProdMulDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd (prod1->Value * prod2->Value);
  }
  
  //==========================================================================
  //
  // ProdDivDbl
  //
  //==========================================================================
  
  FProduction *ProdDivDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd (prod1->Value / prod2->Value);
  }
  
  //==========================================================================
  //
  // ProdModDbl
  //
  //==========================================================================
  
  FProduction *ProdModDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd (fmod (prod1->Value, prod2->Value));
  }
  
  //==========================================================================
  //
  // ProdPowDbl
  //
  //==========================================================================
  
  FProduction *ProdPowDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd (pow (prod1->Value, prod2->Value));
  }
  
  //==========================================================================
  //
  // ProdLTDbl
  //
  //==========================================================================
  
  FProduction *ProdLTDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd (prod1->Value < prod2->Value);
  }
  
  //==========================================================================
  //
  // ProdLTEDbl
  //
  //==========================================================================
  
  FProduction *ProdLTEDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd (prod1->Value <= prod2->Value);
  }
  
  //==========================================================================
  //
  // ProdGTDbl
  //
  //==========================================================================
  
  FProduction *ProdGTDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd (prod1->Value > prod2->Value);
  }
  
  //==========================================================================
  //
  // ProdGTEDbl
  //
  //==========================================================================
  
  FProduction *ProdGTEDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd (prod1->Value >= prod2->Value);
  }
  
  //==========================================================================
  //
  // ProdEqDbl
  //
  //==========================================================================
  
  FProduction *ProdEqDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd (prod1->Value == prod2->Value);
  }
  
  //==========================================================================
  //
  // ProdNeqDbl
  //
  //==========================================================================
  
  FProduction *ProdNeqDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd (prod1->Value != prod2->Value);
  }
  
  //==========================================================================
  //
  // ProdLTStr
  //
  //==========================================================================
  
  FProduction *ProdLTStr (FStringProd *prod1, FStringProd *prod2)
  {
  	return NewDoubleProd (stricmp (prod1->Value, prod2->Value) < 0);
  }
  
  //==========================================================================
  //
  // ProdLTEStr
  //
  //==========================================================================
  
  FProduction *ProdLTEStr (FStringProd *prod1, FStringProd *prod2)
  {
  	return NewDoubleProd (stricmp (prod1->Value, prod2->Value) <= 0);
  }
  
  //==========================================================================
  //
  // ProdGTStr
  //
  //==========================================================================
  
  FProduction *ProdGTStr (FStringProd *prod1, FStringProd *prod2)
  {
  	return NewDoubleProd (stricmp (prod1->Value, prod2->Value) > 0);
  }
  
  //==========================================================================
  //
  // ProdGTEStr
  //
  //==========================================================================
  
  FProduction *ProdGTEStr (FStringProd *prod1, FStringProd *prod2)
  {
  	return NewDoubleProd (stricmp (prod1->Value, prod2->Value) >= 0);
  }
  
  //==========================================================================
  //
  // ProdEqStr
  //
  //==========================================================================
  
  FProduction *ProdEqStr (FStringProd *prod1, FStringProd *prod2)
  {
  	return NewDoubleProd (stricmp (prod1->Value, prod2->Value) == 0);
  }
  
  //==========================================================================
  //
  // ProdNeqStr
  //
  //==========================================================================
  
  FProduction *ProdNeqStr (FStringProd *prod1, FStringProd *prod2)
  {
  	return NewDoubleProd (stricmp (prod1->Value, prod2->Value) != 0);
  }
  
  //==========================================================================
  //
  // ProdXorDbl
  //
  //==========================================================================
  
  FProduction *ProdXorDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd ((double)((int64_t)prod1->Value ^ (int64_t)prod2->Value));
  }
  
  //==========================================================================
  //
  // ProdAndDbl
  //
  //==========================================================================
  
  FProduction *ProdAndDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd ((double)((int64_t)prod1->Value & (int64_t)prod2->Value));
  }
  
  //==========================================================================
  //
  // ProdOrDbl
  //
  //==========================================================================
  
  FProduction *ProdOrDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd ((double)((int64_t)prod1->Value | (int64_t)prod2->Value));
  }
  
  //==========================================================================
  //
  // ProdLAndDbl
  //
  //==========================================================================
  
  FProduction *ProdLAndDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd ((double)((int64_t)prod1->Value && (int64_t)prod2->Value));
  }
  
  //==========================================================================
  //
  // ProdLOrDbl
  //
  //==========================================================================
  
  FProduction *ProdLOrDbl (FDoubleProd *prod1, FDoubleProd *prod2)
  {
  	return NewDoubleProd ((double)((int64_t)prod1->Value || (int64_t)prod2->Value));
  }
  
  
  //==========================================================================
  //
  //
  //
  //==========================================================================
  
  //==========================================================================
  //
  // CCMD test
  //
  // If <expr> is non-zero, execute <true cmd>.
  // If <expr> is zero, execute [false cmd] if specified.
  // 
  //==========================================================================
  
  CCMD (test)
  {
  	int parsept = 1;
  	FProduction *prod = ParseExpression (argv, parsept);
  
  	if (prod == NULL || parsept >= argv.argc())
  	{
  		Printf ("Usage: test <expr> <true cmd> [false cmd]\n");
  	}
  	else
  	{
  		if (prod->Type == PROD_String)
  		{
  			prod = StringToDouble (prod);
  		}
  
  		if (static_cast<FDoubleProd *>(prod)->Value != 0.0)
  		{
  			AddCommandString (argv[parsept]);
  		}
  		else if (++parsept < argv.argc())
  		{
  			AddCommandString (argv[parsept]);
  		}
  	}
  	if (prod != NULL)
  	{
  		M_Free (prod);
  	}
  }
  
  //==========================================================================
  //
  // CCMD eval
  //
  // Evaluates an expression and either prints it to the console or stores
  // it in an existing cvar.
  //
  //==========================================================================
  
  CCMD (eval)
  {
  	if (argv.argc() >= 2)
  	{
  		int parsept = 1;
  		FProduction *prod = ParseExpression (argv, parsept);
  
  		if (prod != NULL)
  		{
  			if (parsept < argv.argc())
  			{
  				FBaseCVar *var = FindCVar (argv[parsept], NULL);
  				if (var == NULL)
  				{
  					Printf ("Unknown variable %s\n", argv[parsept]);
  				}
  				else
  				{
  					UCVarValue val;
  
  					if (prod->Type == PROD_Double)
  					{
  						val.Float = (float)static_cast<FDoubleProd *>(prod)->Value;
  						var->SetGenericRep (val, CVAR_Float);
  					}
  					else
  					{
  						val.String = static_cast<FStringProd *>(prod)->Value;
  						var->SetGenericRep (val, CVAR_String);
  					}
  				}
  			}
  			else
  			{
  				if (prod->Type == PROD_Double)
  				{
  					Printf ("%g\n", static_cast<FDoubleProd *>(prod)->Value);
  				}
  				else
  				{
  					Printf ("%s\n", static_cast<FStringProd *>(prod)->Value);
  				}
  			}
  			M_Free (prod);
  			return;
  		}
  	}
  
  	Printf ("Usage: eval <expression> [variable]\n");
  }
  