/* Provide various operations on values (Create new expressions from old ones) */
/* These could probably be made externally accessible (it would be useful) */
#include "evalint.h"
#include "operations.h"
#include "variables.h"
#include <string.h>

/* Duplicate value */
value id(expr)
register value expr;
{
    switch (expr->type)
    {
	case cste:
	    return(cst(expr->cte));
	case var:
	    return(dup_var(&expr->vr));
	case fonc:
	    return(apply_func(expr->fn.func, id(expr->fn.arg)));
	case op:
	    return(dup_op(expr, id(expr->opr.arg1), id(expr->opr.arg2)));
    }
}

/* Create new expression which is the sum of the two others */
value add(arg1, arg2)
value arg1, arg2;
{
    register value node;

    if (arg1 && arg2 && ALLOC_VALUE(node))
    {
	node->type = op;
	node->opr.op = plus;
	node->opr.arg1 = arg1;
	node->opr.arg2 = arg2;

	return(node);
    }
    if (arg1) free_expr(arg1);
    if (arg2) free_expr(arg2);

    return(NULL);
}

value sub(arg1, arg2)
value arg1, arg2;
{
    register value node;

    if (arg1 && arg2 && ALLOC_VALUE(node))
    {
	node->type = op;
	node->opr.op = minus;
	node->opr.arg1 = arg1;
	node->opr.arg2 = arg2;

	return(node);
    }
    if (arg1) free_expr(arg1);
    if (arg2) free_expr(arg2);

    return(NULL);
}

value mult(arg1, arg2)
value arg1, arg2;
{
    register value node;

    if (arg1 && arg2 && ALLOC_VALUE(node))
    {
	node->type = op;
	node->opr.op = times;
	node->opr.arg1 = arg1;
	node->opr.arg2 = arg2;

	return(node);
    }
    if (arg1) free_expr(arg1);
    if (arg2) free_expr(arg2);

    return(NULL);
}

value div(arg1, arg2)
value arg1, arg2;
{
    register value node;

    if (arg1 && arg2 && ALLOC_VALUE(node))
    {
	node->type = op;
	node->opr.op = divide;
	node->opr.arg1 = arg1;
	node->opr.arg2 = arg2;

	return(node);
    }
    if (arg1) free_expr(arg1);
    if (arg2) free_expr(arg2);

    return(NULL);
}

value topow(arg1, arg2)
value arg1, arg2;
{
    register value node;

    if (arg1 && arg2 && ALLOC_VALUE(node))
    {
	node->type = op;
	node->opr.op = power;
	node->opr.arg1 = arg1;
	node->opr.arg2 = arg2;

	return(node);
    }
    if (arg1) free_expr(arg1);
    if (arg2) free_expr(arg2);

    return(NULL);
}

/* Apply function to value */
value apply_func(func, arg)
FUNCTION func;
value arg;
{
    register value node;

    if (arg && ALLOC_VALUE(node))
    {
	node->type = fonc;
	node->fn.func =  func;
	node->fn.arg = arg;

	return(node);
    }
    if (arg) free_expr(arg);

    return(NULL);
}

/* Create a constant expression */
value cst(c)
double c;
{
    register value node;

    if (ALLOC_VALUE(node))
    {
	node->type = cste;
	node->cte = c;
    }
    return(node);
}

/* Make an expression of the form 'x' */
value dup_var(v)
variable *v;
{
    register value node;

    if (ALLOC_VALUE(node))
    {
	node->type = var;
	node->vr = *v;
	if (node->vr.name = ALLOCMEM(strlen(v->name) + 1))
	{
	    strcpy(node->vr.name, v->name);
	    return(node);
	}
	FREE_VALUE(node);
    }
    return(NULL);
}

/* Create an expression of type a /+-* b */
value dup_op(expr, arg1, arg2)
value expr, arg1, arg2;
{
    register value node;

    if (arg1 && arg2 && ALLOC_VALUE(node))
    {
	node->type = op;
	node->opr.op = expr->opr.op;
	node->opr.arg1 = arg1;
	node->opr.arg2 = arg2;

	return(node);
    }
    if (arg1) free_expr(arg1);
    if (arg2) free_expr(arg2);

    return(NULL);
}

/* Are two expressions identical ? */
int same(expr1, expr2)
register value expr1, expr2;
{
    if (expr1->type != expr2->type) return(FALSE);
    else
	switch(expr1->type)
	{
	    case cste:
		return(expr1->cte == expr2->cte);
	    case var:
		return(strcmp(expr1->vr.name, expr2->vr.name) == 0);
	    case fonc:
		return(expr1->fn.func == expr2->fn.func &&
		       same(expr1->fn.arg, expr2->fn.arg));
	    case op:
		return(expr1->opr.op == expr2->opr.op &&
		       same(expr1->opr.arg1, expr2->opr.arg1) &&
		       same(expr1->opr.arg2, expr2->opr.arg2));
	}
}

