#ifndef EXPRESSION_H
#define EXPRESSION_H

/** **********************************************************************
** Module:  Expression \file expr.h
** Description: This module implements the Expression abstraction.
**  Several types of expressions are supported: identifiers,
**  literals, operations (arithmetic, logical, array indexing,
**  etc.), function calls, and query expressions.  Every expression
**  is marked with a type.
** Constants:
**  EXPRESSION_NULL     - the null expression
**  LITERAL_E       - a real literal with the value 2.7182...
**  LITERAL_EMPTY_SET   - a set literal representing the empty set
**  LITERAL_INFINITY    - a numeric literal representing infinity
**  LITERAL_PI      - a real literal with the value 3.1415...
**  LITERAL_ZERO        - an integer literal representing 0
**
************************************************************************/

/*
 * This software was developed by U.S. Government employees as part of
 * their official duties and is not subject to copyright.
 *
 * $Log: expr.h,v $
 * Revision 1.4  1997/01/21 19:17:11  dar
 * made C++ compatible
 *
 * Revision 1.3  1994/11/10  19:20:03  clark
 * Update to IS
 *
 * Revision 1.2  1993/10/15  18:48:24  libes
 * CADDETC certified
 *
 * Revision 1.6  1993/02/16  03:21:31  libes
 * fixed numerous confusions of type with return type
 * fixed implicit loop variable type declarations
 * improved errors
 *
 * Revision 1.5  1993/01/19  22:44:17  libes
 * *** empty log message ***
 *
 * Revision 1.4  1992/08/27  23:39:59  libes
 * *** empty log message ***
 *
 * Revision 1.3  1992/08/18  17:12:41  libes
 * rm'd extraneous error messages
 *
 * Revision 1.2  1992/06/08  18:06:24  libes
 * prettied up interface to print_objects_when_running
 */

/*************/
/* constants */
/*************/

#define EXPRESSION_NULL (Expression)0

/*****************/
/* packages used */
/*****************/

#include <scl_export.h>
#include <math.h>
#include "expbasic.h"   /* get basic definitions */

#ifndef MAXINT
#define MAXINT (~(1 << 31))
#endif

/************/
/* typedefs */
/************/

typedef enum {
    OP_AND,         OP_ANDOR,
    OP_ARRAY_ELEMENT,   OP_CONCAT,
    OP_DIV,         OP_DOT,         OP_EQUAL,
    OP_EXP,         OP_GREATER_EQUAL,   OP_GREATER_THAN,
    OP_GROUP,
    OP_IN,          OP_INST_EQUAL,      OP_INST_NOT_EQUAL,
    OP_LESS_EQUAL,      OP_LESS_THAN,       OP_LIKE,
    OP_MINUS,       OP_MOD,         OP_NEGATE,
    OP_NOT,         OP_NOT_EQUAL,       OP_OR,
    OP_PLUS,        OP_REAL_DIV,        OP_SUBCOMPONENT,
    OP_TIMES,       OP_XOR,         OP_UNKNOWN,
    OP_LAST /**< must be last - used only to size tables */
} Op_Code;

typedef struct Qualified_Attr   Qualified_Attr;
typedef struct Expression_ * Expression;
typedef Expression      Ary_Expression, One_Of_Expression, Identifier,
        Literal;
typedef struct Query_    *   Query;
typedef One_Of_Expression   Function_Call;
typedef Ary_Expression      Ternary_Expression, Binary_Expression,
        Unary_Expression;
typedef Literal         Aggregate_Literal, Integer_Literal,
        Logical_Literal, Real_Literal, String_Literal,
        Binary_Literal;

/****************/
/* modules used */
/****************/

#include "entity.h"
#include "type.h"
#include "variable.h"
#include "alg.h"
#include "scope.h"

/***************************/
/* hidden type definitions */
/***************************/

/* expression types */

struct Qualified_Attr {
    struct Expression_ * complex;   /**< complex entity instance */
    Symbol * entity;
    Symbol * attribute;
};

struct Op_Subexpression {
    Op_Code op_code;
    Expression op1;
    Expression op2;
    Expression op3;
};

struct Query_ {
    Variable local;
    Expression aggregate;   /**< set from which to test */
    Expression expression;  /**< logical expression */
    struct Scope_ * scope;
};

struct Funcall {
    struct Scope_ * function; /**< can also be an entity because entities can be called as functions */
    Linked_List list;
};

union expr_union {
    int integer;
    float real;
    char * attribute;   /**< inverse .... for 'attr' */
    char * binary;
    int logical;
    bool boolean;
    struct Query_ * query;
    struct Funcall funcall;

    /* if etype == aggregate, list of expressions */
    /* if etype == funcall, 1st element of list is funcall or entity */
    /*  remaining elements are parameters */
    Linked_List list;   /**< aggregates (bags, lists, sets, arrays) or lists for oneof expressions */
    Expression expression;  /**< derived value in derive attrs, or
                             * initializer in local vars, or
                             * enumeration tags
                             * or oneof value */
    struct Scope_ * entity; /**< used by subtype exp, group expr
                              * and self expr, some funcall's and any
                              * expr that results in an entity */
    Variable variable;  /**< attribute reference */
};

/** The difference between 'type' and 'return_type' is illustrated
 * by "func(a)".  Here, 'type' is Type_Function while 'return_type'
 * might be Type_Integer (assuming func returns an integer). */
struct Expression_ {
    Symbol symbol;      /**< contains things like funcall names, string names, binary values, enumeration names */
    Type type;
    Type return_type;   /**< type of value returned by expression */
    struct Op_Subexpression e;
    union expr_union u;
};

/** indexed by the op enumeration values */
struct EXPop_entry {
    char * token;       /**< literal token, e.g., "<>" */
    Type( *resolve ) PROTO( ( Expression, struct Scope_ * ) );
};

/********************/
/* global variables */
/********************/

#ifdef EXPRESSION_C
#include "defstart.h"
#else
#include "decstart.h"
#endif /*EXPRESSION_C*/

GLOBAL SCL_EXPRESS_EXPORT struct EXPop_entry EXPop_table[OP_LAST];

GLOBAL SCL_EXPRESS_EXPORT Expression   LITERAL_E       INITIALLY( EXPRESSION_NULL );
GLOBAL SCL_EXPRESS_EXPORT Expression   LITERAL_INFINITY    INITIALLY( EXPRESSION_NULL );
GLOBAL SCL_EXPRESS_EXPORT Expression   LITERAL_PI      INITIALLY( EXPRESSION_NULL );
GLOBAL SCL_EXPRESS_EXPORT Expression   LITERAL_ZERO        INITIALLY( EXPRESSION_NULL );
GLOBAL SCL_EXPRESS_EXPORT Expression   LITERAL_ONE;

GLOBAL SCL_EXPRESS_EXPORT Error    ERROR_bad_qualification         INITIALLY( ERROR_none );
GLOBAL SCL_EXPRESS_EXPORT Error    ERROR_integer_expression_expected   INITIALLY( ERROR_none );
GLOBAL SCL_EXPRESS_EXPORT Error    ERROR_implicit_downcast         INITIALLY( ERROR_none );
GLOBAL SCL_EXPRESS_EXPORT Error    ERROR_ambig_implicit_downcast       INITIALLY( ERROR_none );

GLOBAL SCL_EXPRESS_EXPORT struct freelist_head EXP_fl;
GLOBAL SCL_EXPRESS_EXPORT struct freelist_head OP_fl;
GLOBAL SCL_EXPRESS_EXPORT struct freelist_head QUERY_fl;
GLOBAL SCL_EXPRESS_EXPORT struct freelist_head QUAL_ATTR_fl;

#include "de_end.h"

/******************************/
/* macro function definitions */
/******************************/

#define EXP_new()   (struct Expression_ *)MEM_new(&EXP_fl)
#define EXP_destroy(x)  MEM_destroy(&EXP_fl,(Freelist *)(Generic)x)
#define OP_new()    (struct Op_Subexpression *)MEM_new(&OP_fl)
#define OP_destroy(x)   MEM_destroy(&OP_fl,(Freelist *)(Generic)x)
#define QUERY_new() (struct Query_ *)MEM_new(&QUERY_fl)
#define QUERY_destroy(x) MEM_destroy(&QUERY_fl,(Freelist *)(Generic)x)
#define QUAL_ATTR_new() (struct Qualified_Attr *)MEM_new(&QUAL_ATTR_fl)
#define QUAL_ATTR_destroy(x) MEM_destroy(&QUAL_ATTR_fl,(Freelist *)(Generic)x)

#define EXPget_name(e)          ((e)->symbol.name)
#define ENUMget_name(e)         ((e)->symbol.name)

#define BIN_EXPget_operator(e)      ARY_EXPget_operator(e)
#define BIN_EXPget_first_operand(e) ARY_EXPget_operand(e)

#define UN_EXPget_operator(e)       ARY_EXPget_operator(e)
#define UN_EXPget_operand(e)        ARY_EXPget_operand(e)

#define FCALLput_parameters(expr,parms) ((e)->u.funcall.list = (parms))
#define FCALLget_parameters(e)      ((e)->u.funcall.list)
#define FCALLput_function(expr,func)    ((e)->u.funcall.function = (func))
#define FCALLget_function(e)        ((e)->u.funcall.function)
/** assumes the function is not an entity-function! */
#define FCALLget_algorithm(e)       ((e)->u.funcall.function->u.function->body)

#define INT_LITget_value(e)     ((e)->u.integer)
#define LOG_LITget_value(e)     ((e)->u.logical)
#define REAL_LITget_value(e)        ((e)->u.real)
#define STR_LITget_value(e)     ((e)->symbol.name)
#define STR_LITput_value(e,s)       ((e)->symbol.name = (s))
#define BIN_LITget_value(e)     ((e)->u.binary)
#define AGGR_LITget_value(e)        ((e)->u.list)
#define EXPget_type(e)          ((e)->type)
#define ARY_EXPget_operand(e)       ((e)->e.op1)
#define ARY_EXPget_operator(e)      ((e)->e.op_code)
#define BIN_EXPget_operand(e)       ((e)->e.op2)
#define TERN_EXPget_second_operand(e)   ((e)->e.op2)
#define TERN_EXPget_third_operand(e)    ((e)->e.op3)
#define QUERYget_variable(e)        ((e)->u.query->local)
#define QUERYget_source(e)      ((e)->u.query->aggregate)
#define QUERYget_discriminant(e)    ((e)->u.query->expression)
#define ONEOFget_list(e)        ((e)->u.list)

/***********************/
/* function prototypes */
/***********************/

extern SCL_EXPRESS_EXPORT Expression   EXPcreate PROTO( ( Type ) );
extern SCL_EXPRESS_EXPORT Expression   EXPcreate_simple PROTO( ( Type ) );
extern SCL_EXPRESS_EXPORT Expression   EXPcreate_from_symbol PROTO( ( Type, Symbol * ) );
extern SCL_EXPRESS_EXPORT Expression   UN_EXPcreate PROTO( ( Op_Code, Expression ) );
extern SCL_EXPRESS_EXPORT Expression   BIN_EXPcreate PROTO( ( Op_Code, Expression, Expression ) );
extern SCL_EXPRESS_EXPORT Expression   TERN_EXPcreate PROTO( ( Op_Code, Expression, Expression, Expression ) );
extern SCL_EXPRESS_EXPORT Expression   QUERYcreate PROTO( ( Symbol *, Expression ) );
extern SCL_EXPRESS_EXPORT void     EXPinitialize PROTO( ( void ) );
extern SCL_EXPRESS_EXPORT Type     EXPtype PROTO( ( Expression, struct Scope_ * ) );
extern SCL_EXPRESS_EXPORT int      EXPget_integer_value PROTO( ( Expression ) );

/********************/
/* inline functions */
/********************/

#if supports_inline_functions || defined(EXPRESSION_C)

static_inline
int
OPget_number_of_operands( Op_Code op ) {
    if( ( op == OP_NEGATE ) || ( op == OP_NOT ) ) {
        return 1;
    } else if( op == OP_SUBCOMPONENT ) {
        return 3;
    } else {
        return 2;
    }
}

#endif /* supports_inline_functions || defined(EXPRESSION_C) */

#endif /*EXPRESSION_H*/
