/* This file is part of the Project Athena Zephyr Notification System.
 * It is one of the source files comprising zwgc, the Zephyr WindowGram
 * client.
 *
 *      Created by:     Marc Horowitz <marc@athena.mit.edu>
 *
 *      $Id: b33ea6d3a52717724267941fe8337168cbd63f3d $
 *
 *      Copyright (c) 1989 by the Massachusetts Institute of Technology.
 *      For copying and distribution information, see the file
 *      "mit-copyright.h".
 */

#include <sysdep.h>

#if (!defined(lint) && !defined(SABER))
static const char rcsid_eval_c[] = "$Id: b33ea6d3a52717724267941fe8337168cbd63f3d $";
#endif

#include <zephyr/mit-copyright.h>

/****************************************************************************/
/*                                                                          */
/*                      Code to evaluate an expression:                     */
/*                                                                          */
/****************************************************************************/

#include <zephyr/zephyr.h>
#include "new_memory.h"
#include "node.h"
#include "eval.h"
#include "substitute.h"
#include "port.h"
#include "buffer.h"
#include "regexp.h"
#include "formatter.h"
#include "text_operations.h"
#include "zwgc.h"
#include "variables.h"

/****************************************************************************/
/*                                                                          */
/*                Code to deal with string/boolean conversion:              */
/*                                                                          */
/****************************************************************************/

/*
 *  Internal Routine:
 *
 *    int string_to_bool(string str)
 *       Effects: Returns true iff the string str represents true.
 *                True is represented by any string which is equal to
 *                "true" when case is disregraded.
 */

#define  string_to_bool(str)      (!strcasecmp(str,"true"))

/*
 *  Internal Routine:
 *
 *    string bool_to_string(int bool)
 *       Effects: Returns a string representive for the C boolean bool.
 *                (In C, true == non-zero)  I.e.,
 *                string_to_bool(bool_to_string(x)) == !!x.
 *                The string returned is on the heap & must be freed
 *                eventually.
 */

static string
bool_to_string(int bool)
{
    return(bool ? string_Copy("TRUE") : string_Copy("FALSE"));
}

/*
 *    int eval_bool_expr(Node *expr)
 *        Modifies: dict
 *        Requires: expr is a proper expression or NULL.  (see node.c)
 *        Effects: Evaluates expr to its boolean value which is returned.
 *                 NULL is defined to have the boolean value true.
 */

int
eval_bool_expr(Node *expr)
{
    string temp;
    int result;

    if (!expr)
      return(1);

    temp = eval_expr(expr);
    result = string_to_bool(temp);
    free(temp);

    return(result);
}

/****************************************************************************/
/*                                                                          */
/*                      Code to evaluate an expression:                     */
/*                                                                          */
/****************************************************************************/

/*
 *    string eval_expr(Node *expr)
 *        Modifies: dict
 *        Requires: expr is a proper expression (NOT NULL).  (see node.c)
 *        Effects: Evaluates expr to its string value which is returned.
 *                 The returned string is on the heap and must be freed
 *                 eventually.
 */

string
eval_expr(Node *expr)
{
    int opcode = expr->opcode;
    int bool_result = 0;
    string first, second;
    char *result = NULL;
    string *text_ptr;

    /*
     * Dispatch based on the opcode of the top node in the expression:
     */
    switch (opcode) {
      case STRING_CONSTANT_OPCODE:
	return(string_Copy(expr->d.string_constant));

      case VARREF_OPCODE:
	return(string_Copy(var_get_variable(expr->d.string_constant)));

      case BUFFER_OPCODE:
	return(string_Copy(buffer_to_string()));

	/*
	 * Handle unary expressions:
	 */
      case NOT_OPCODE:
      case SUBSTITUTE_OPCODE:
      case PROTECT_OPCODE:
      case VERBATIM_OPCODE:
      case STYLESTRIP_OPCODE:
      case GETENV_OPCODE:
      case UPCASE_OPCODE:
      case DOWNCASE_OPCODE:
      case ZVAR_OPCODE:
      case GET_OPCODE:
	first = eval_expr(expr->d.nodes.first);

	switch (opcode) {
	  case NOT_OPCODE:
	    result = bool_to_string(! string_to_bool(first));
	    break;

	  case SUBSTITUTE_OPCODE:
	    result = substitute(var_get_variable, first);
	    break;

	  case PROTECT_OPCODE:
	    result=protect(first);
	    break;

	  case VERBATIM_OPCODE:
	    return(verbatim(first,0));

	  case STYLESTRIP_OPCODE:
	    return(stylestrip(first));

	  case GETENV_OPCODE:
	    result = getenv(first);
	    if (!result)
	      result = string_Copy("");
	    else
	      result = string_Copy(result);
	    break;

	  case UPCASE_OPCODE:
	    return(string_Upcase(first));

	  case DOWNCASE_OPCODE:
	    return(string_Downcase(first));

	  case ZVAR_OPCODE:
	    result = ZGetVariable(first);
	    if (!result)
	      result = string_Copy("");
	    else
	      result = string_Copy(result);
	    break;

	  case GET_OPCODE:
	    result = read_from_port(first);
	    break;
	}
	free(first);
	break;

	/*
	 * Handle binary operators:
	 */
      case PLUS_OPCODE:
      case AND_OPCODE:
      case OR_OPCODE:
      case EQ_OPCODE:
      case NEQ_OPCODE:
      case REGEQ_OPCODE:
      case REGNEQ_OPCODE:
	first = eval_expr(expr->d.nodes.first);
	second = eval_expr(expr->d.nodes.second);

	switch (opcode) {
	  case PLUS_OPCODE:
	    result = string_Concat(first, second);
	    free(first);
	    free(second);
	    return(result);

	  case AND_OPCODE:
	    bool_result = string_to_bool(first) && string_to_bool(second);
	    break;

	  case OR_OPCODE:
	    bool_result = string_to_bool(first) || string_to_bool(second);
	    break;

	  case EQ_OPCODE:
	    bool_result = string_Eq(first, second);
	    break;

	  case NEQ_OPCODE:
	    bool_result = string_Neq(first, second);
	    break;

	  case REGEQ_OPCODE:
	    bool_result = ed_regexp_match_p(first, second);
	    break;

	  case REGNEQ_OPCODE:
	    bool_result = !ed_regexp_match_p(first, second);
	    break;
	}
	free(first);
	free(second);
	result = bool_to_string(bool_result);
	break;

	/*
	 * Handle text-manipulation operators:
	 */
      case LANY_OPCODE:    case RANY_OPCODE:  
      case LBREAK_OPCODE:  case RBREAK_OPCODE:
      case LSPAN_OPCODE:   case RSPAN_OPCODE:
	first = eval_expr(expr->d.nodes.first);
	second = eval_expr(expr->d.nodes.second);
	text_ptr = &first;

	switch (opcode) {
	  case LANY_OPCODE:
	    result = lany(text_ptr, second);
	    break;

	  case RANY_OPCODE:  
	    result = rany(text_ptr, second);
	    break;

	  case LBREAK_OPCODE:
	    result = lbreak(text_ptr, string_to_character_class(second));
	    break;

	  case RBREAK_OPCODE:
	    result = rbreak(text_ptr, string_to_character_class(second));
	    break;

	  case LSPAN_OPCODE:
	    result = lspan(text_ptr, string_to_character_class(second));
	    break;

	  case RSPAN_OPCODE:
	    result = rspan(text_ptr, string_to_character_class(second));
	    break;
	}

	if (expr->d.nodes.first->opcode == VARREF_OPCODE)
	  var_set_variable(expr->d.nodes.first->d.string_constant, first);
	free(first);
	free(second);
	break;

#ifdef DEBUG
      default:
	printf("zwgc: internal error: attempt to evaluate the following non-expression:\n");  fflush(stdout);
	node_display(expr);
	printf("\n\n");
	exit(2);
#endif
    }

    return(result);
}
