#ifndef COMDEFS_H
#define COMDEFS_H


/*
 comdefs.h

 XIDEK: Extensible Script Language Development Kit
 Common code.
 Modified to support complex arithmetic.

 Copyright (c) 1996-2002 Parsifal Software.
 All Rights Reserved.

 For further information about this program or the AnaGram
 parser generator, please contact:

    Parsifal Software
    http://www.parsifalsoft.com
    info@parsifalsoft.com
    +1-800-879-2577, Voice/Fax +1-508-358-2564
    P.O. Box 219
    Wayland, MA 01778
    USA
*/

#ifndef AG_PLACEMENT_DELETE_REQUIRED
#if _MSC_VER >= 1200 || __INTEL_COMPILER
#define AG_PLACEMENT_DELETE_REQUIRED 1
#endif
#endif

#include "agstr.h"
#include "agdict.h"

// Forward references for classes declared in this file:
class Value;
class Dataset;
class ErrorMessage;
class ErrorDiagnostic;
class FileLocation;

/*** Interpreter interface specifications************************************/

// Apply a script to a dataset
Value interpret(const char *, Dataset &);

// ErrorMessage class is used to report interpreter errors
class ErrorMessage {
  const AgString msg;
public:
  ErrorMessage(const AgString &m);
  const char *message() const;
};

class ErrorDiagnostic {
  const AgString msg;
public:
  ErrorDiagnostic(const AgString &m);
  const char *message() const;
};

/*** Data Encapsulation Classes *********************************************/

template <class Object>
class ValueWrapper {
  Object object;
  friend class Value;
  void *operator new(size_t, void *p) {return p;}
  void operator delete(void *) {}
#ifdef AG_PLACEMENT_DELETE_REQUIRED
  void operator delete(void *, void *) { }
#endif
  ValueWrapper(const Object &o) : object(o) {}
  ValueWrapper(const ValueWrapper<Object> &o) : object(o.object) {}
  ~ValueWrapper() {}
  operator Object &() {return object;}
};

// FileLocation class is used by parsers to track line and column numbers
class FileLocation {
public:
  const unsigned char *pointer;
  int line, column;
  FileLocation(const unsigned char *p = NULL, int l = 1, int c = 1)
  : pointer(p), line(l), column(c) {}
};

/*
 The Value class is used for arithmetic in interpreters. It overrides all
 arithmetic operators. Integer arithmetic is performed using longs.
 All values are stored as doubles.
*/
class Value {
protected:
  // Nested type definition
  enum Type {
    uninitType,
    integerType,
    realType,
    stringType,
    pointerType,
    complexType,
  };
  Type type;
  union {
    double value;
    struct {                                  // Complex value
      double real, imag;
    } part;
    Value *pointer;
    // Make sure there is enough space
    char stringSpace[sizeof(ValueWrapper<AgString>)];
    AgStringBase *string;
  };
public:
  // Constructors
  Value();
  Value(int x);
  Value(long x);
  Value(double x, Type t = realType);
  Value(const AgString &s);
  Value(Value *p);
  Value(double x, double y);
  Value(const Value &);

  // Destructor
  ~Value();

  // Error checking functions
  void assertInitialized() const;
  void assertInteger() const;
  void assertScalar() const;
  void assertString() const;
  void assertPointer() const;
  void assertComplex() const;

  int isComplex() const;

  // Data access functions
  double getDouble() const {assertScalar(); return value;}
  long getLong() const {assertInteger(); return (long) value;}
  AgString &getString() const {assertString(); return *(AgString *) &value;}
  Value &deref();

  AgString asString() const;
  AgString asLiteral() const;

  int isDefined() const {return type != uninitType;}
  int isTrue() const;
  int isFalse() const {return !isTrue();}

  // Type conversion functions
  Value &makeInteger();                            // Force type to be integer
  Value &makeReal();                                  // Force type to be real

  double magnitudeSquared() const;
  double magnitude() const;
  double getReal() const {assertComplex(); return part.real;}
  double getImag() const {assertComplex(); return part.imag;}

  AgString complexToString() const;

  // Miscellaneous functions
  Value &setValue(const Value &);
  int hash(int) const;

  Value idiv(const Value &divisor);
  Value rdiv(const Value &divisor);

  // Operator overrides
  const Value &operator = (const Value &);
  const Value &operator += (const Value &);
  const Value &operator -= (const Value &);
  const Value &operator *= (const Value &);
  const Value &operator /= (const Value &);
  const Value &operator %= (const Value &);
  const Value &operator &= (const Value &);
  const Value &operator |= (const Value &);
  const Value &operator ^= (const Value &);
  const Value &operator <<= (const Value &);
  const Value &operator >>= (const Value &);

  Value operator + (const Value &x) const {return Value(*this) += x;}
  Value operator - (const Value &x) const {return Value(*this) -= x;}
  Value operator * (const Value &x) const {return Value(*this) *= x;}
  Value operator / (const Value &x) const {return Value(*this) /= x;}
  Value operator % (const Value &x) const {return Value(*this) %= x;}
  Value operator & (const Value &x) const {return Value(*this) &= x;}
  Value operator | (const Value &x) const {return Value(*this) |= x;}
  Value operator ^ (const Value &x) const {return Value(*this) ^= x;}
  Value operator << (const Value &x) const {return Value(*this) <<= x;}
  Value operator >> (const Value &x) const {return Value(*this) >>= x;}

  // Unary operators
  Value operator -() const;
  Value operator !() const {
    return Value(isFalse());
  }
  Value operator ~() const {
    assertInteger();
    return Value(~(long) value);
  }

  // Comparison operators
  // All other inqualities are implemented in terms of the less than operator
  int operator < (const Value &v) const;
  int operator > (const Value &v) const {
    return v < *this;
  }
  int operator <= (const Value &v) const {
    return !(v < *this);
  }
  int operator >= (const Value &v) const {
    return !(*this < v);
  }
  int operator == (const Value &v) const;
  int operator != (const Value &v) const {
    return !(*this == v);
  }

  // Autoincrement, Autodecrement operators
  Value operator ++();
  Value operator --();
  Value operator ++(int);
  Value operator --(int);
};

// Externally defined functions
Value pow(const Value &, const Value &);
inline int agABTHash(const Value &v, int startValue = 0) {return v.hash(startValue);}

/*
 The Dataset class consists of a reference to a dictionary and an array of
 data values. The dictionary associates a unique integer with each distinct
 variable name. The integer is the index in the data array of the value of
 the named variable. Different scripts can operate on the same Dataset
 object.
*/
class Dataset {
public:
  AgDictionary<AgString> &dictionary;                    // Indexes variable names
  AgStack<Value> data;                         // Contains values of variables

  // Constructors
  Dataset(AgDictionary<AgString> &d);
  Dataset(const Dataset &d);

  // Destructor
  ~Dataset() {}                                               // Nothing to do

  // Get value
  Value &value(const AgString &n);    // returns reference to value named by n

  int size() const {return dictionary.size();}
  // Operator overloads
  Value &operator [] (int x);
};

/*** Support Classes ********************************************************/

// Function object definitions

struct FunctionObject {
  void notDefined();
  virtual Value oneArg(const Value &) {notDefined(); return Value();}
  virtual Value twoArgs(const Value &, const Value &) {notDefined(); return Value();}
};

// Function table definitions

struct FunctionDescriptor {
  const char *name;
  int argCount;
  FunctionObject *function;
};

extern const FunctionDescriptor functionTable[];

int idFunction(const AgString &name, int argCount);
Value callFunction(const AgString &name, AgStack<Value> &args);
Value callFunction(int k, AgStack<Value> &args);

// External functions

// What was thought to be an octal number turns out to be decimal.
// Undo the octal representation and make it decimal.
long makeDecimal(long octal);

#endif
