/* Copyright (C) 1995 Bjoern Beutel. */

/* Description. =============================================================*/

/* This module defines the data type "value_t", and many
 * operations to build, modify, and print such values.
 * There are six different types of values:
 * symbol, string, list, record, number and index. */

/* Constants. ===============================================================*/

/* Some standard symbols. */
enum {NIL_SYMBOL, YES_SYMBOL, NO_SYMBOL, 
      SYMBOL_SYMBOL, STRING_SYMBOL, NUMBER_SYMBOL, LIST_SYMBOL, RECORD_SYMBOL};

enum {SYMBOL_MAX = 8192}; /* Symbols are in the range of 0..SYMBOL_MAX - 1. */

/* Types. ===================================================================*/

typedef u_short_t cell_t;
/* A value is stored in one or more cells.
 * Use this type if you want to allocate memory (pools etc.) for values. */ 

typedef cell_t *value_t; /* Reference to a Malaga values by this type. */
typedef cell_t symbol_t;

typedef enum 
{INTERNAL_ORDER, ALPHABETIC_ORDER, DEFINITION_ORDER} attribute_order_t;
/* INTERNAL_ORDER is the order in which attributes are stored internally.
 * ALPHABETIC_ORDER means the alphabetic order of the attribute names.
 * DEFINITION_ORDER is the order in which the attributes are defined in the 
 * symbol file. */

/* Variables. ===============================================================*/

extern string_t (*values_get_symbol_name)( symbol_t symbol );
/* Return the name of "symbol".
 * This is a callback function that must be set externally. */

extern value_t (*values_get_atoms)( symbol_t symbol );
/* Return the list of atoms of SYMBOL.
 * This is a callback function that must be set externally. */

extern value_t *value_stack;
/* The value stack contains static values and local values. 
 * The location of the VALUE_STACK-vector may change when the stack size
 * must increase. */

extern int_t top; 
/* The index of the first unused item on VALUE_STACK.
 * You may only read or decrease this variable! */

extern attribute_order_t attribute_order;
/* The order in which attributes in a record are printed.
 * Used by "value_readable". */

/* Module initialisation. ===================================================*/

extern void init_values( void );
/* Initialise this module. */

extern void terminate_values( void );
/* Terminate this module. */

/* Value operations. ========================================================*/

extern value_t new_value( value_t value );
/* Allocate space for VALUE and copy it.
 * Use "free" to free the space occupied by this value. */

extern value_t copy_value_to_pool( pool_t value_pool, 
                                   value_t value, 
                                   int_t *index );
/* Copy VALUE to the pool VALUE_POOL and store its index in *INDEX. */

extern int_t length_of_value( value_t value );
/* Return the length of VALUE in cells. */

extern symbol_t get_value_type( value_t value );
/* Return the type of VALUE. Depending of the type, the result value may be
 * SYMBOL_SYMBOL, STRING_SYMBOL, NUMBER_SYMBOL, LIST_SYMBOL, RECORD_SYMBOL. */

extern void push_value( value_t value );
/* Stack effects: (nothing) -> VALUE. */

extern void insert_value( int_t n, value_t value );
/* Stack effects: VALUE1...VALUE_N -> VALUE VALUE1...VALUE_N. */

/* Symbol operations. =======================================================*/

extern symbol_t value_to_symbol( value_t value );
/* VALUE must be a symbol.
 * Return VALUE as a symbol. */

extern void push_symbol_value( symbol_t symbol );
/* Stack effects: (nothing) -> NEW_SYMBOL.
 * NEW_SYMBOL is SYMBOL converted to a Malaga value. */

/* String operations. =======================================================*/

extern string_t value_to_string( value_t value );
/* VALUE must be a string value.
 * Return the value of VALUE as a C style string. */

extern void push_string_value( string_t string_start, string_t string_end );
/* Stack effects: (nothing) -> NEW_STRING.
 * NEW_STRING is the string starting at STRING_START as a Malaga value.
 * If STRING_END != NULL, it marks the end of the string. */

extern void concat_string_values( void );
/* Stack effects: STRING1 STRING2 -> NEW_STRING.
 * NEW_STRING is the concatenation of STRING1 and STRING2. */

/* Record operations. =======================================================*/

extern value_t get_attribute( value_t record, symbol_t attribute );
/* Return the value of ATTRIBUTE in the record RECORD 
 * or NULL if it doesn't exist. */

extern void build_record( int_t n );
/* Stack effects: ATTR1 VALUE1 ... ATTR_N VALUE_N -> NEW_RECORD.
 * NEW_RECORD looks like [ATTR1: VALUE1, ..., ATTR_N: VALUE_N]. */

extern void join_records( void );
/* Stack effects: RECORD1 RECORD2 -> NEW_RECORD.
 * NEW_RECORD contains all attributes of RECORD1 and RECORD2, and 
 * their associated values. If an attribute has different values in RECORD1
 * and RECORD2, the value in RECORD2 will be taken. */

extern void select_attribute( symbol_t attribute );
/* Stack effects: RECORD -> NEW_RECORD.
 * NEW_RECORD contains ATTRIBUTE and its value in RECORD. */

extern void select_attributes( void );
/* Stack effects: RECORD LIST -> NEW_RECORD.
 * NEW_RECORD contains all attribute-value pairs of RECORD whose attributes
 * are in LIST. */

extern void remove_attribute( symbol_t attribute );
/* Stack effects: RECORD -> NEW_RECORD.
 * NEW_RECORD contains all attribute-value pairs of RECORD but the one with
 * attribute ATTRIBUTE. */

extern void remove_attributes( void );
/* Stack effects: RECORD LIST -> NEW_RECORD.
 * NEW_RECORD contains all attribute-value pairs of RECORD but the ones
 * whose attributes are in LIST. */

extern void replace_attribute( symbol_t attribute );
/* Stack effects: RECORD VALUE -> NEW_RECORD.
 * NEW_RECORD is equal to RECORD, only the value of ATTRIBUTE is replaced
 * by VALUE. RECORD must contain ATTRIBUTE. */

/* List operations. =========================================================*/

extern int_t get_list_length( value_t list );
/* Return the number of elements in the list LIST. */

extern value_t get_element( value_t list, int_t n );
/* Return the N-th element of the list LIST,
 * or NULL, if that element doesn't exist.
 * If N is positive, elements will be counted from the left border.
 * If it's negative, elements will be counted from the right border. */

extern void build_list( int_t n );
/* Stack effects: VALUE1 ... VALUE_N -> NEW_LIST.
 * NEW_LIST looks like <VALUE1, ..., VALUE_N>. */

extern int_t decompose_list( void );
/* Stack effects: LIST -> VALUE1 ... VALUE_N.
 * VALUE1 ... VALUE_N are the elements of LIST.
 * Return N. */

extern void concat_lists( void );
/* Stack effects: LIST1 LIST2 -> NEW_LIST.
 * NEW_LIST is the concatenation of LIST1 and LIST2. */

extern void get_list_difference( void );
/* Stack effects: LIST1 LIST2 -> NEW_LIST.
 * NEW_LIST contains the list difference of LIST1 and LIST2:
 * An element that appears M times in LIST1 and N times in LIST2 
 * appears M - N times in NEW_LIST. */

extern void get_set_difference( void );
/* Stack effects: LIST1 LIST2 -> NEW_LIST.
 * NEW_LIST contains the set difference of LIST1 and LIST2.
 * Each element of LIST1 is in NEW_LIST if it is not in LIST2. */

extern void intersect_lists( void );
/* Stack effects: LIST1 LIST2 -> NEW_LIST.
 * NEW_LIST contains the list intersection of LIST1 and LIST2.
 * Each element that appears M times in LIST1 and N times in LIST2
 * appears min(M, N) times in NEW_LIST. */

extern void remove_element( int_t n );
/* Stack effects: LIST -> NEW_LIST.
 * NEW_LIST is LIST without element at index N.
 * If N is positive, the elements will be counted from the left border;
 * if N is negative, they will be counted from the right border.
 * If LIST contains less than abs(N) elements, then NEW_LIST = LIST. */

extern void remove_elements( int_t n );
/* Stack effects: LIST -> NEW_LIST.
 * NEW_LIST is LIST without abs(N) elements.
 * If N is positive, the elements will be cut from the left border,
 * if N is negative, they will be cut from the list's right border.
 * If LIST contains less than abs(N) elements, then NEW_LIST = <>. */

extern void extract_elements( int_t n );
/* Stack effects: LIST -> NEW_LIST.
 * NEW_LIST is LIST with only abs(N) elements.
 * If N is positive, the elements will be taken from the left border,
 * if N is negative, they will be taken from the list's right border.
 * If LIST contains less than abs(N) elements, then NEW_LIST = LIST. */

extern void replace_element( int_t n );
/* Stack effects: LIST VALUE -> NEW_LIST.
 * NEW_LIST is LIST, but its N-th element is replaced by VALUE.
 * If N is negative, count from the right end.
 * LIST must contain at least N elements. */

extern void convert_list_to_set( void );
/* Stack effects: LIST -> NEW_LIST.
 * NEW_LIST contains all elements of LIST, but multiple appearances
 * of one value are reduced to a single appearance.
 * That means, NEW_LIST is LIST converted to a set. */

/* Number operations. =======================================================*/

extern double value_to_double( value_t value );
/* Return the value of VALUE which must be a number value. */

extern int_t value_to_int( value_t value );
/* Return the value of VALUE which must be an integral number value. */

extern void push_number_value( double number );
/* Stack effects: (nothing) -> NEW_NUMBER.
 * NEW_NUMBER is NUMBER as a Malaga value. */

/* Type dependent Malaga operations. ========================================*/

extern void dot_operation( void );
/* Stack effects: VALUE1 VALUE2 -> NEW_VALUE.
 * NEW_VALUE is VALUE1 "." VALUE2 or NULL, if that value doesn't exist.
 * The actual operation depends on the type of the values. */

extern void plus_operation( void );
/* Stack effects: VALUE1 VALUE2 -> NEW_VALUE.
 * NEW_VALUE is VALUE1 "+" VALUE2. 
 * The actual operation depends on the type of the values. */

extern void minus_operation( void );
/* Stack effects: VALUE1 VALUE2 -> NEW_VALUE.
 * NEW_VALUE is VALUE1 "-" VALUE2. 
 * The actual operation depends on the type of the values. */

extern void asterisk_operation( void );
/* Stack effects: VALUE1 VALUE2 -> NEW_VALUE.
 * NEW_VALUE is VALUE1 "*" VALUE2. 
 * The actual operation depends on the type of the values. */

extern void slash_operation( void );
/* Stack effects: VALUE1 VALUE2 -> NEW_VALUE.
 * NEW_VALUE is VALUE1 "/" VALUE2. 
 * The actual operation depends on the type of the values. */

extern void unary_minus_operation( void );
/* Stack effects: VALUE -> NEW_VALUE.
 * NEW_VALUE is "-" VALUE.
 * The actual operation depends on the type of the value. */

/* Functions for value paths. ===============================================*/

extern value_t get_value_part( value_t value, value_t path );
/* Return the value part of VALUE that is specified by the path PATH. 
 * If that value part does not exist, return NULL. */

extern void build_path( int_t n );
/* Stack effects: VALUE1 ... VALUE_N -> NEW_LIST.
 * NEW_LIST is a path which contains VALUE1, ..., VALUE_N. 
 * VALUE1, ..., VALUE_N must be numbers, symbols or lists of numbers and 
 * symbols. If a value is a list, the elements of this list are inserted into
 * NEW_LIST instead of the value itself. */

extern void modify_value_part( void (*modifier)( void ) );
/* Stack effects: VALUE PATH MOD_VALUE -> NEW_VALUE.
 * NEW_VALUE is VALUE, but the part that is described by PATH is 
 * modified. PATH must be a list of symbols and numbers <E1, E2, .. , E_N>.
 * They will be used as nested attributes and indexes, so the part of VALUE
 * that is actually modified is OLD_VALUE := VALUE.E1.E2..E_N. 
 * If this part does not exist, an error will be reported. Else the function 
 * MODIFIER will be called on OLD_VALUE and MOD_VALUE. 
 * The value returned by MODIFIER will be entered in VALUE in place of
 * OLD_VALUE. */

extern void right_value( void );
/* Stack effects: LEFT_VALUE RIGHT_VALUE -> RIGHT_VALUE.
 * A modifier for "modify_value_part". */

/* Functions for list/record iteration. =====================================*/

extern value_t get_first_item( value_t value );
/* If VALUE is a list, then return its first element (or NULL).
 * If VALUE is a record, then return its first attribute (or NULL). */

extern value_t get_next_item( value_t value, value_t item );
/* If VALUE is a list, and ELEMENT one of its elements,
 * then NEW_ELEMENT is the successor of ELEMENT (or NULL).
 * If VALUE is a record, and ELEMENT one of its attributes,
 * then NEW_ELEMENT is the next attribute in VALUE (or NULL). */

extern void get_first_element( void );
/* Stack effects: VALUE -> NEW_VALUE.
 * If VALUE is a list, then NEW_VALUE is its first element (or NULL).
 * If VALUE is a record, then NEW_VALUE is its first attribute (or NULL).
 * If VALUE is a number, then NEW_VALUE is NULL (if VALUE == 0),
 * 1 (if VALUE > 0) or -1 (if VALUE < 0). */

extern void get_next_element( int_t stack_index );
/* Stack effects: (nothing) -> (nothing).
 * VALUE1 is VALUE_STACK[ INDEX - 1 ], VALUE2 is VALUE_STACK[ INDEX ].
 * VALUE_STACK[ INDEX ] will be set to NEW_VALUE.
 * VALUE2 must be the result of an application of "get_first_element" or 
 * "get_next_element" on VALUE1.
 * If VALUE1 is a list, and VALUE2 one of its elements,
 * then NEW_VALUE is the successor of VALUE2 (or NULL).
 * If VALUE1 is a record, and VALUE2 one of its attributes,
 * then NEW_VALUE is the next attribute in VALUE1 (or NULL).
 * If VALUE1 is a positive number, and VALUE2 a number smaller than
 * VALUE1, then NEW_VALUE is VALUE2 + 1.
 * If VALUE1 is a negative number, and VALUE2 a number greater than
 * VALUE1, then NEW_VALUE is VALUE2 - 1. */

/* Functions to compare values. =============================================*/

extern int_t compare_atom_lists( value_t atoms1, value_t atoms2 );
/* Compare atom lists ATOMS1 and ATOMS2.
 * Return -1 if ATOMS1 < ATOMS2.
 * Return 0 if ATOMS1 == ATOMS2.
 * Return 1 if ATOMS1 > ATOMS2. */

extern bool_t values_equal( value_t value1, value_t value2 );
/* Return a truth value indicating whether VALUE1 and VALUE2 are equal.
 * VALUE1 an VALUE2 must be of same type or one of them must be nil.
 * Refer to documentation to see what "equal" in Malaga really means. */

extern bool_t values_congruent( value_t value1, value_t value2 );
/* Return a truth value indicating whether VALUE1 and VALUE2 have
 * at least one element in common.
 * VALUE1 and VALUE2 must both be symbols or lists. */

extern bool_t value_in_value( value_t value1, value_t value2 );
/* Return bool value saying if VALUE1 is element or attribute of VALUE2.
 * VALUE2 must be a list or a record.
 * If VALUE2 is a record, then VALUE1 must be a symbol. */

/* Functions to convert values to text. =====================================*/

extern symbol_t *get_hidden_attributes( void );
/* Get a SYMBOL_MAX-terminated vector of the currently hidden attributes. 
 * The vector must be freed after use. */

extern void add_hidden_attribute( symbol_t attribute );
/* Add ATTRIBUTE to the list of currently hidden attributes. */

extern void remove_hidden_attribute( symbol_t attribute );
/* Remove ATTRIBUTE from the list of currently hidden attributes. */

extern void clear_hidden_attributes( void );
/* Clear the list of currently hidden attributes. */

extern char_t *value_to_readable( value_t value, 
                                  bool_t full_value,
                                  int_t indent );
/* Return VALUE in a format readable for humans. 
 * If FULL_VALUE == TRUE, show all attributes, even those that are hidden.
 * If INDENT >= 0, format value, i.e. print each element of a list or record
 * on a line of its own. Assume the value is indented by INDENT columns.
 * The result must be freed after use. */

/* End of file. =============================================================*/
