/* node.cc */
/*
Copyright (C) 2003 Unique Software Designs

This file is part of the program "lambda".

The program "lambda" is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

The program "lambda" is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with "lambda"; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

A copy of the GNU General Public License may also be found at:
http://www.gnu.org/copyleft/gpl.html
*/
#if HAVE_CONFIG_H
#  include <config.h>
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "node.h"

#define REDUCE_VARS
//#undef REDUCE_VARS
#define USE_UNDER_SCORE_PREFIX_FOR_GENERATED_NAMES
#undef USE_UNDER_SCORE_PREFIX_FOR_GENERATED_NAMES

//const int MAX_RECURSE=200;
const int MAX_RECURSE=4000;

const arglst_node* definition_env = 0;

static int name_sequence = 1;
static int lambda_reduce_recurse_level = 0;
static int app_reduce_recurse_level = 0;
static int var_reduce_recurse_level = 0;

static char* newname(const char* oldname)
{
	char* nm = 0;
	char buf[128];
	if( !oldname ) oldname = "~";

#ifdef USE_UNDER_SCORE_PREFIX_FOR_GENERATED_NAMES
	sprintf( buf, "_%s_%d", oldname, name_sequence );
#else
	sprintf( buf, "%s#%d", oldname, name_sequence );
#endif
	name_sequence += 1;

	nm = new char[strlen(buf)+1];
	strcpy(nm,buf);
	return nm;
}

char* newstring(const char*os)
{
	char* ns;
	if(os) {
		ns = new char[strlen(os)+1];
		strcpy(ns,os);
	}else ns = 0;
	return ns;
};

//////////////////////////////////////////////////////////////////////
void node::
reset()
{
	name_sequence = 1;
	lambda_reduce_recurse_level = 0;
	app_reduce_recurse_level = 0;
	var_reduce_recurse_level = 0;
}

void node::
print(const alst_node*env,int inp) const
{
	printf("(%p:node)",this);
}

//////////////////////////////////////////////////////////////////////
// arg_node
arg_node::
arg_node(const char* nm, const exp_node* ex, Boolean import)
{
	this->name_v = newstring(nm);
	if( import ){
		if( ex ) value_v = (exp_node*)ex;
		else value_v = 0;
	}else{
		if( ex ) value_v = (exp_node*)ex->clone();
		else value_v = 0;
		if( value_v ) value_v->set_parent(this);
	}
}

arg_node::
arg_node(const arg_node& from)
{
	//*this = from;
	if( from.name_v ) {
		name_v = new char [strlen(from.name_v)+1];
		strcpy(name_v, from.name_v);
	}else name_v = 0;
	if( from.value_v )
		value_v = 
			(exp_node*)from.value_v->clone();
	else value_v = 0;
	if( value_v ) value_v->set_parent(this);
	parent_v = from.parent_v;
}

node* arg_node::
clone() const
{
	return (node*)new arg_node(*this);
}

arg_node::
~arg_node()
{
	if( name_v ) delete [] name_v;
	if( value_v ) delete value_v;
}

arg_node& arg_node::
operator =(const arg_node& from)
{
	if( this != &from ){
		this->~arg_node();
		if( from.name_v ) {
			name_v = new char [strlen(from.name_v)+1];
			strcpy(name_v, from.name_v);
		}else name_v = 0;
		if( from.value_v )
			value_v = 
				(exp_node*)from.value_v->clone();
		else value_v = 0;
		if( value_v ) value_v->set_parent(this);
		parent_v = from.parent_v;
	}
	return *this;
}

//Boolean arg_node::
//free_in(exp_node* exp)
//{
//	Boolean in = False;
//	in = exp->has_free(this);
//	return in;
//}

void arg_node::
print(const alst_node*env, int inp)const
{
	printf("%s", name_v ? name_v : "(null)" );
	if( value_v ) {
		printf(" = ");
		value_v->print(env,inp);
	}
}

void arg_node::
import_value(exp_node** val)
{
	if( value_v ) delete value_v;
	if( val ){
		value_v = *val;
		*val = 0;
	}else value_v = 0;
	if( value_v ) value_v->set_parent(this);
}

int arg_node::
operator == (const arg_node& cmp) const
{
	Boolean b1 = 
		!name_v && !cmp.name_v ||
		name_v && cmp.name_v && 0==strcmp(name_v, cmp.name_v);
	return b1;
}

int arg_node::
operator == (const char* cmp) const
{
	Boolean b1 = 
		!name_v && !cmp ||
		name_v && cmp && 0==strcmp(name_v, cmp);
	return b1;
}

/////////////////////////////////////////////////////////////////////
// exp_node's
const alst_node* exp_node::
match(const alst_node* env ) const
{
	const alst_node* match = 0;
	extern int match_alpha; //defined in parse.cc
	if( match_alpha )
	{
		exp_node* thisseq = this->seq_lambda();
		if( thisseq )
		{
			for(const alst_node* en=env; en; en = en->next() )
			{
				if( en->arg() && en->arg()->value() ){
					exp_node* thatseq =
						en->arg()->value()->seq_lambda();
					if(thatseq )
					{
						if( *thisseq==*thatseq )
						{
							match = en;
						}
						delete thatseq;
						if( match ) break;
					}
				}
			}
			delete thisseq;
		}
	}else{
		for(const alst_node* en=env; en; en = en->next() )
		{
			if( en->arg() && en->arg()->value() ){
				if( *this==*(en->arg()->value()) )
				{
					match = en;
					break;
				}
			}
		}
	}
	return match;
}

void exp_node::
symbolic_print(const alst_node*env,int inp)const
{
	if( inp&node::DO_PRINT_SYM ){
		exp_node* exp = (exp_node*)this->clone();
		if( exp ){
			exp_node* pexp = exp->extract_defs(env);
			if( pexp ){
				pexp->print(env,inp);
				if( pexp != exp ) delete pexp;
			}else{
				exp->print(env,inp);
			}
			delete exp;
		}else{
			this->print(env,inp);
		}
	}else{
		this->print(env,inp);
	}
}

exp_node* exp_node::
seq_lambda() const
{
	exp_node* result;
	int seq = 1;
	result = seq_lambda(&seq);
	return result;
}

/////////////////////////////////////////////////////////////////////
// var_node
var_node::
var_node(const char* nm)
{
	if( nm ){
		name_v = new char[strlen(nm)+1];
		strcpy(name_v,nm);
	}else nm = 0;
}

var_node::
var_node(const var_node& from)
{
	//*this = from;
	if( from.name_v ) {
		name_v = new char [strlen(from.name_v)+1];
		strcpy(name_v, from.name_v);
	}else name_v = 0;
	parent_v = 0;//from.parent_v;
}

Boolean var_node::
has_free(const arg_node* argr, const alst_node* env) const
{
//	const exp_node* exp=this;
	#ifndef REDUCE_VARS
		exp = reduce_value(env);
		//if( exp != this )
		if( VAR!=exp->op() )
			return exp->has_free(argr, env);
	#endif
#undef MATCH_NAME
#ifdef MATCH_NAME
	//if( argr ) return argr->operator==(name_v);
	if( argr ) return *argr==(exp->name());
#else
	if( argr ){
		const alst_node* bd = this->bind(env);
		if( bd && (bd->arg()==argr))
		{
			return True;
		}
	}
#endif
	return False;
}

arglst_node* var_node::
bind(const alst_node* env) const
{
	//arg_node* arg = 0;
	//if( !this->name() ) return arg;
	//if( ref_arg_v ) return;
	if( !this->name() ) return 0;
#if 0
	for( node* par = parent_v; par; par = par->parent() )
	{
		if( LAM==par->op() && par->arg() && par->arg()->name() ){
			if( 0==strcmp(par->arg()->name(),
				          this->name()))
			{
				arg = par->arg();
				break;
			}
		}
//		if( arg ) break;
	}
#endif
	//if( !arg ) {
		alst_node* lst;
		for( lst=(arglst_node*)env;
			lst; lst=(arglst_node*)lst->next() )
		{
			if( lst->arg() && lst->arg()->name() ){
				if( 0==strcmp(lst->arg()->name(),
					          this->name()))
				{
					//arg = lst->arg();
					break;
				}
			}
		}
	//}
	////if( arg ){ref_arg_v = arg;if( def_arg_v ) delete def_arg_v;def_arg_v = 0;}
	return (arglst_node*)lst;
}

void var_node::
print(const alst_node*env,int inp) const
{
	printf("%s", name() ? name() : "(null-var)" );
}

var_node& var_node::
operator =(const var_node& from)
{
	if( name_v ) delete [] name_v;
	if( from.name_v ) {
		name_v = new char [strlen(from.name_v)+1];
		strcpy(name_v, from.name_v);
	}else name_v = 0;
	if( this != &from ){
		parent_v = from.parent_v;
	}
	return *this;
}

void var_node::
set_name(const char* nm)
{
	if( name_v == nm ) return;
	if( name_v ) delete [] name_v;
	name_v = nm?new char[strlen(nm)+1]:0;
	if( name_v && nm ) strcpy(name_v,nm);
}

exp_node* var_node::
reduce_value(const alst_node* env, int inp, int* outp,
	const alst_node** pnenv)
{
	exp_node* exp = this;
	exp_node* val;

	val = exp;
	int reduced = 0;
	int cnt = 0;
	int max_loops = (inp & DO_REDUCE_FULLY) ? 2 : 0;
	do {
		do {
			env = val->bind(env);
			if( !val ) goto end;
			arg_node* barg = env ? env->arg() : 0;
			exp_node* nval = barg ? barg->value() : 0;
			if( !nval ) break;
			if( val == nval ) goto end;
			if( val == nval ) break;
			val = nval;
			reduced = 1;
		} while (env && val && VAR==val->op() && val!=this );
		env = definition_env;
		cnt++;
	} while (reduced && cnt < max_loops);
end:
	if( val ) exp = val;
	if( pnenv ){
		//if( env ) env = env->next();
		//*pnenv = (alst_node*)env;
		*pnenv = definition_env;
	}
	return exp;
}

exp_node* var_node::
reduce_number()
{
	exp_node* exp = 0;
	if( name() )
	{
		int is_number = 1;
		for(const char*pc = name();*pc!='\0';pc++)
		{
			int ch = *pc;
			if( ch < '0' || ch > '9' )
			{
				is_number = 0;
				break;
			}
		}
		if( is_number )
		{
			arg_node* arg1 = new arg_node("m",0);
			arg_node* arg2 = new arg_node("n",0);
			exp_node *bdy = new var_node("n");
			int number = atoi((const char*)name());
			for( ; number > 0; number -= 1 )
			{
				var_node* lft = new var_node("m");
				bdy = new app_node(lft,bdy,True);
			}
			lam_node* lam2 = new lam_node(arg2,bdy,True);
			lam_node* lam1 = new lam_node(arg1,lam2,True);
			exp = lam1;
		}
	}
	return exp;
}

exp_node* var_node::
reduce(const alst_node* env, int inp,int* outp)
{
	var_reduce_recurse_level++;
	if( var_reduce_recurse_level > MAX_RECURSE ){
		printf("\nvar_reduce_recurse_level %d [",
			var_reduce_recurse_level);
		this->print();
		printf ("]\n");
		var_reduce_recurse_level--;
		if( outp ) *outp |= node::RECURSE_LVL;
		return this;
	}
	const alst_node* nenv = 0;
	exp_node* exp = this->reduce_value(env, inp, outp, &nenv);
	if( exp != this )
	{
		exp = (exp_node*)exp->clone();
		if( nenv && (APP==exp->op() || LAM==exp->op())
		&& (inp&DO_REDUCE_FULLY)!=0 )
		{
			exp_node* nexp = exp->reduce_vars(nenv,inp,outp);
			if( nexp && nexp != exp )
			{
				delete exp;
				exp = nexp;
			}
		}
	}
	exp_node* num = reduce_number();
	if( num )
	{
		if( exp != this ){
			delete exp;
			exp = num;
		}else{
			exp = num;
		}
	}
	var_reduce_recurse_level--;
	return exp;
}

void var_node::
rename(arg_node* arg, const char* newname, alst_node* env)
{
	arglst_node* lst = this->bind(env);
	arg_node* barg = lst ? lst->arg() : 0;
	if( barg && barg==arg ) this->set_name(newname);
}

int var_node::
operator == (const exp_node& cmp) const
{
	Boolean b1 = False;
	if( VAR!=cmp.op() ) return b1;
	b1 = !name() && !cmp.name() ||
		name() && cmp.name() && 0==strcmp(name(), cmp.name());
	return b1;
}

exp_node* var_node::
extract(const char* nm, int inp) const
{
	if( name() && nm && 0==strcmp(nm,name()) )
	{
		return (exp_node*)new var_node("I");
	}else
	if( nm )
	{
		exp_node* lft = new var_node("K");
		exp_node* rgt = new var_node(name());
		return (exp_node*)new app_node(lft,rgt,True);
	}else{
		return (exp_node*)new var_node(name());
	}
}

exp_node* var_node::
seq_lambda(int* pseq) const
{
	return (exp_node*)clone();
}

//////////////////////////////////////////////////////////////////////
// lam_node
lam_node::
lam_node(arg_node* arg, exp_node* bdy, Boolean import)
{
	arg_v = 0;
	body_v = 0;
	set_arg(arg,import);
	set_body(bdy,import);
}

lam_node::
lam_node(const lam_node& from)
{
	//*this = from;
	if( from.arg_v )
		arg_v = (arg_node*)from.arg_v->clone();
	else arg_v = 0;
	if( arg_v ) arg_v->set_parent(this);
	if( from.body_v )
		body_v = (exp_node*)from.body_v->clone();
	else body_v = 0;
	if( body_v ) body_v->set_parent(this);
	parent_v = 0;//from.parent_v;
}

lam_node::
~lam_node()
{
	if( arg_v ) delete arg_v;
	if( body_v ) delete body_v;
}

Boolean lam_node::
has_free(const arg_node* argr, const alst_node* env) const
{
	stack_frame nenv(arg_v,env);
	if( arg_v && arg_v->operator ==(*argr) ) return False;
	if( body_v ) return body_v->has_free(argr, (alst_node*)&nenv);
	return False;
}

void lam_node::
print(const alst_node*env, int inp) const
{
	int printed = 0;
	if( (inp&DO_PRINT_SYM) )
	{
		if( env )
		{
			const alst_node* alst = this->match(env);
			if( alst ){
				printf("%s", alst->arg()->name());
				printed = 1;
			}
		}
		if( !printed )
		{
			int value = 0;
			if( LAM==body()->op()
			&& arg()
			&& arg()->name()
			&& body()
			&& body()->arg()
			&& body()->arg()->name()
			&& body()->body())
			{
				const char* name1 = arg()->name();
				const char* name2 = body()->arg()->name();
				for( exp_node* numbody = body()->body();
					numbody; numbody = numbody->right() )
				{
					if( APP==numbody->op()
					&& numbody->left()
					&& VAR==numbody->left()->op()
					&& numbody->left()->name()
					&& 0==strcmp(name1,numbody->left()->name())
					){
						value += 1;
					}else
					if( VAR==numbody->op()
					&&  numbody->name()
					&&  0==strcmp(name2,numbody->name())
					){
						break;
					}else{
						value = -1;
						break;
					}
				}
				if( value >= 0 )
				{
					printf( "%d",value);
					printed = 1;
				}
			}else
			if( arg()
			&& arg()->name()
			&& body()
			&& VAR==body()->op()
			&& 0==strcmp(arg()->name(), body()->name()) )
			{
				printf( "I");
				printed = 1;
			}
		}
	}
	if( !printed )
	{
		printf("^");
		//if( arg() ) arg()->print(env,inp);
		if( arg() && arg()->name() ) printf("%s",arg()->name());
		else printf("(null-arg)");
		if( arg() && arg()->value() ){
			printf("[");
			arg()->value()->print(env,inp);
			printf("]");
		}
		printf(".");
		if( body() ) body()->print(env,inp);
		else printf("(null-body)");
	}
}

lam_node& lam_node::
operator =(const lam_node& from)
{
	if( this!=&from ){
		this->~lam_node();
		if( from.arg_v )
			arg_v = (arg_node*)from.arg_v->clone();
		else arg_v = 0;
		if( arg_v ) arg_v->set_parent(this);
		if( from.body_v )
			body_v = (exp_node*)from.body_v->clone();
		else body_v = 0;
		if( body_v ) body_v->set_parent(this);
		parent_v = 0;//from.parent_v;
	}
	return *this;
}

exp_node* lam_node::
reduce(const alst_node* env, int inp, int* outp)
{
	exp_node* exp = this;
	stack_frame nenv(this->arg_v, (arglst_node*)env );
	if(inp&DO_TRACE){
		for(int idx=0;idx<lambda_reduce_recurse_level;idx++)printf(".");
		printf("L:  ");
		symbolic_print(env, inp);
		printf("\n");
		exp_node* par;
		for(par=this;par->parent();par=(exp_node*)par->parent() );
		par->symbolic_print(env,inp);
		printf("\n");
	}
	lambda_reduce_recurse_level++;
	if( lambda_reduce_recurse_level > MAX_RECURSE ){
		printf("\nlambda_reduce_recurse_level %d\n",
			lambda_reduce_recurse_level);
		lambda_reduce_recurse_level--;
		if( outp ) *outp |= node::RECURSE_LVL;
		return this;
	}
	body_v->set_parent(this);
	if( arg_v && arg_v->name() && '&'==arg_v->name()[0] )
	{
		inp &= ~DO_STEP;
		inp &= ~DO_REDUCE_BODY;
	}
	if( arg_v && arg_v->value() && body_v ){
		body_v->resolve_name_clash(arg_v,&nenv);
		//old delet -- body_v->bind(nenv);
		//
		if( arg_v->name() && '$'==arg_v->name()[0] )
		{
			inp &= ~DO_STEP;
			inp |= DO_REDUCE_BODY;
		}
		if( !(inp&DO_STEP) ){
			int toutp = outp ? *outp : 0;
			exp = body_v;
			exp = body_v->reduce_vars((alst_node*)&nenv,inp,outp);
			if( exp!=body_v ) set_body(0,True);
			arg_v->import_value((exp_node*)0);
int cnt = 0;
			do {
				exp_node* nexp = 0;
				nexp = exp->reduce((alst_node*)&nenv,inp,&toutp);
				if( nexp && exp != nexp ){
					if( exp==body_v ) body_v = 0;
					delete exp;
					exp = nexp;
				}
				if( toutp && toutp&node::RECURSE_LVL ) break;
				if( toutp & node::BETA_ETA_DONE){
					toutp &= ~node::BETA_ETA_DONE;
				}else break;
cnt++;
			} while( exp );
			if( outp ) *outp = toutp;
if( cnt > 1 )
{
printf("[%d.%d]",cnt,lambda_reduce_recurse_level);
}
		}else{
			exp = body_v->reduce_vars((alst_node*)&nenv,inp,outp);
		}
		if( body_v && body_v!=exp ) delete body_v;
		this->body_v = 0;
		if( exp ) exp->set_parent(parent_v);
		//if( parent() ) delete this;
	}else if( body_v ){
		exp_node* nexp = eta_reduce( &nenv,inp,outp);
		if( exp!=nexp ){
			if( exp != this ) delete exp;
			exp = nexp;
		}else{
			int toutp = outp ? *outp : 0;
			if( inp&DO_REDUCE_BODY ){
				exp_node* nd =
					body_v->reduce((alst_node*)&nenv,inp,&toutp);
				if( nd ) set_body(nd,True);
			}else{
				exp_node* nd =
					body_v->reduce_vars((alst_node*)&nenv,inp,outp);
				if( nd ) set_body(nd,True);
			}
			if( !(inp&DO_STEP) || !(toutp&node::BETA_ETA_DONE) ){
				nexp = eta_reduce( &nenv,inp, &toutp);
				if( exp!=nexp ){
					if( exp != this ) delete exp;
					exp = nexp;
				}
			}
			if( outp ) *outp = toutp;
		}
	}
	lambda_reduce_recurse_level--;
	if(0 && inp&DO_TRACE ){
		for(int idx=0;idx<lambda_reduce_recurse_level;idx++)printf(".");
		printf("L> ");
		//exp->print(env, inp);
		exp->set_parent(this->parent());
		exp_node* par;
		for(par=exp;par->parent();par=(exp_node*)par->parent() );
		par->symbolic_print(env,inp);
		printf("\n");
	}
	return exp;
}

exp_node* lam_node::
eta_reduce(const alst_node* env,int inp,int* outp)
{
	// assume stack frame already set>
	exp_node* exp = this;
	if( !body_v ) return exp;
	if( !(inp&DO_STEP) || !outp || !(*outp&node::BETA_ETA_DONE))
	{
		if(APP==body_v->op() && !(inp&DO_APP_ORDER) ){
			// check for  eta reduction
			if( body()->left() )
			{
#ifdef REDUCE_VARS
				exp_node* nd = body_v->left()->reduce_vars(env,inp,outp);
				if( nd ) ((app_node*)body())->set_left(nd,True);
#endif
				if( body_v->right() && arg_v
					&& *arg_v == body_v->right()->name()
					&& !body_v->left()->has_free(arg_v, env) )
				{
					// do eta reduction
					exp = this->body_v->export_left();
					//this->body_v = 0; // right_v memory leak?
					set_body(0,true);
					if( outp ) *outp |=
						(node::BETA_ETA_DONE | node::ETA_DONE);
					if( !(inp&DO_STEP ) ){
						exp_node* nexp = exp->reduce(env,inp,outp);
						if( nexp && exp != nexp ){
							if( exp ) delete exp;
							exp = nexp;
						}
					}
				}
			}
		}
	}
	return exp;
}

exp_node* lam_node::
reduce_vars(const alst_node* env,int inp,int* outp)
{
	stack_frame nenv(this->arg_v, (arglst_node*)env );

	exp_node* nd = body_v->reduce_vars((alst_node*)&nenv,inp,outp);
	if( nd ) set_body(nd,True);

	return this;
}

//assumes name clashes resolved already
void lam_node::
rename(arg_node* arg, const char* nname, alst_node* env)
{
	stack_frame nenv(this->arg_v, (arglst_node*)env );
	if( body_v ) body_v->rename(arg,nname,&nenv);
}

void lam_node::
resolve_name_clash( arg_node* outer_arg,alst_node* env)
{
	stack_frame nenv(this->arg_v, (arglst_node*)env );
	if( outer_arg->value() ){
		arg_node* inner_arg = this->arg();
		exp_node* inner_body = this->body();
		if( inner_arg && inner_body
			&& !(inner_arg==outer_arg))
		{
			Boolean b1 = outer_arg->value()
				->has_free(inner_arg, (alst_node*)&nenv);
			Boolean b2 =
				inner_body->has_free(outer_arg, (alst_node*)&nenv);
			if( b1 && b2 )
			{
				char* nm = newname(inner_arg->name());
				body_v->rename(inner_arg,nm,(alst_node*)&nenv);
				inner_arg->set_name(nm);
				delete [] nm;
			}
		}
		if( body_v && body_v->has_free(outer_arg, &nenv) )
			body_v->resolve_name_clash(outer_arg, &nenv);
	}
}

void lam_node::
set_arg(arg_node* nd, Boolean import)
{
	if( arg_v == nd ) return;
	if( arg_v ) delete arg_v;
	if( import ) arg_v = nd;
	else if( nd ) arg_v = (arg_node*)nd->clone();
	else arg_v = 0;
	if( arg_v ) arg_v->set_parent(this);
}

void lam_node::
set_body(exp_node* nd, Boolean import)
{
	if( body_v == nd ) return;
	if( body_v ) delete body_v;
	if( import ) body_v = nd;
	else if( nd ) body_v = (exp_node*)nd->clone();
	else body_v = 0;
	if( body_v ) body_v->set_parent(this);
}

int lam_node::
operator == (const exp_node& cmp) const
{
	Boolean b1 = False;
	if( LAM!=cmp.op() ) return b1;
	if( !arg() || !cmp.arg() ) return b1;
	if( !body() || !cmp.body() ) return b1;
	b1 = *arg() == *(cmp.arg()) 
		&& *body() == *(cmp.body());
	return b1;
}

exp_node* lam_node::
extract_defs(const alst_node* env)
{
	exp_node* exp = this;
	const alst_node* alst = this->match(env);
	if( !alst ){
		if( body() ) {
			exp_node* nb = body()->extract_defs(env);
			if( nb )
				set_body(nb,True);
			alst = this->match(env);
			if( alst )
				 exp = new var_node(alst->arg()->name());
		}
	}else{
		exp = new var_node(alst->arg()->name());
	}
	return exp;
}

exp_node* lam_node::
extract(const char* nm, int inp) const
{
	exp_node* exp = 0;
	if( body() && arg() )
	{
		exp_node* bdy = body()->extract(arg()->name(),inp);
		if( bdy && nm )
		{
			exp = bdy->extract(nm,inp);
			delete bdy;
		} else {
			exp = bdy;
		}
	}
	return exp;
}

exp_node* lam_node::
seq_lambda(int* pseq) const
{
	exp_node* result = (exp_node*)clone();
	if( pseq && result && result->arg() && result->body() )
	{
		char nmbuf[50];
		sprintf(nmbuf,"~~%d", *pseq);
		*pseq = *pseq +1;
		char* nm = nmbuf;
		if( result->body()->arg() ) {
			// resolve name clashe
			arg_node* arg = result->arg();
			if(arg && arg->name())
			{
				var_node va(nm);
				arg_node ta(arg->name(),&va,false);
				stack_frame st(&ta,0);
				result->body()->resolve_name_clash(&ta,&st);
			}
		}
		stack_frame nenv(result->arg(), (arglst_node*)NULL );
		result->body()->rename(result->arg(),nm,&nenv);
		result->arg()->set_name(nm);
		exp_node* bdy = result->body()->seq_lambda(pseq);
		if( bdy )
		{
			((lam_node*)result)->set_body(bdy,true);
		}
	}
	return result;
}

//////////////////////////////////////////////////////////////////////
// app_node
app_node::
app_node(exp_node* lft,
				   exp_node* rgt,
				   Boolean import)
{
	left_v = 0;
	right_v = 0;
	set_left(lft,import);
	set_right(rgt,import);
}

app_node::
app_node(const app_node& from)
{
	//*this = from;
	if( from.left_v )
		left_v = (exp_node*)from.left_v->clone();
	else left_v = 0;
	if( left_v ) left_v->set_parent(this);
	if( from.right_v )
		right_v =
			(exp_node*)from.right_v->clone();
	else right_v = 0;
	if( right_v) set_parent(this);
	parent_v = from.parent_v;
}
	
app_node::
~app_node()
{
	if( left_v ) delete left_v;
	if( right_v ) delete right_v;
}

Boolean app_node::
has_free(const arg_node* argr, const alst_node* env) const
{
	Boolean has = False;
	if( left_v ) has =  has || left_v->has_free(argr,env);
	if( right_v ) has = has || right_v->has_free(argr,env);
	return has;
}

void app_node::
print(const alst_node*env,int inp) const
{
	if( inp&DO_PRINT_BRIEF ){
		int left_paren = 1;
		int right_paren = 1;
		int left_right_paren = 0;
		int left_match = 0;
		int right_match = 0;
		int left_right_match = 0;
		if( left() ){
			if( inp&DO_PRINT_SYM )
				left_match = left()->match(env) ? 1 : 0;
			if( VAR==left()->op() || APP==left()->op() || left_match )
			{
				left_paren = 0;
			}
			if( APP==left()->op() && left()->right() )
			{
				if( inp&DO_PRINT_SYM )
					left_right_match =
						left()->right()->match(env) ? 1 : 0;
				if( VAR!=left()->right()->op() && !left_right_match )
					left_right_paren = 1;
			}
			if( left_paren ) printf("(");
			if( left() ) left()->print(env,inp);
			else printf("(null-right)");
			if( left_paren ) printf(")");
		}else printf("()");
		if( right() ){
			if( inp&DO_PRINT_SYM )
				right_match = right()->match(env) ? 1 : 0;
			if( VAR==right()->op() || right_match ) right_paren = 0;
			if( right_paren ) printf("(");
			else if( !left_paren && !left_right_paren )
				printf(" ");
			if( right() ) right()->print(env,inp);
			else printf("(null-right)");
			if( right_paren ) printf(")");
		}else printf("()");
	}else{
		if( left() ){
			if( VAR!=left()->op()||left()->value() ) printf("(");
			if( left() ) left()->print(env,inp);
			else printf("(null-right)");
			if( VAR!=left()->op()||left()->value() ) printf(")");
		}else printf("()");
		if( right() ){
			if( VAR!=right()->op()||right()->value() ) printf("(");
			else printf(" ");
			if( right() ) right()->print(env,inp);
			else printf("(null-right)");
			if( VAR!=right()->op()||right()->value() ) printf(")");
		}else printf("()");
	}
}

app_node& app_node::
operator =(const app_node& from)
{
	if( this!=&from ){
		this->~app_node();
		if( from.left_v )
			left_v = (exp_node*)from.left_v->clone();
		else left_v = 0;
		if( left_v ) left_v->set_parent(this);
		if( from.right_v )
			right_v = (exp_node*)from.right_v->clone();
		else right_v = 0;
		if( right_v ) right_v->set_parent(this);
		parent_v = from.parent_v;
	}
	return *this;
}

void app_node::
rename(arg_node* arg, const char* newname, alst_node* env)
{
	if( left_v ) left_v->rename(arg,newname,env);
	if( right_v ) right_v->rename(arg,newname,env);
}

void app_node::
resolve_name_clash( arg_node* outer_arg,alst_node* env)
{
	if( left_v ) left_v->resolve_name_clash(outer_arg,env);
	if( right_v ) right_v->resolve_name_clash(outer_arg,env);
}

exp_node* app_node::
reduce(const alst_node* env, int inp,int* outp)
{
	exp_node* exp = this;
	app_reduce_recurse_level += 1;
	if( app_reduce_recurse_level > MAX_RECURSE ){
		printf("\napp_reduce_recurse_level %d\n",
			app_reduce_recurse_level);
		app_reduce_recurse_level--;
		if( outp ) *outp |= node::RECURSE_LVL;
		return this;
	}
	
	if( left_v ){
		int ninp = inp & ~DO_REDUCE_BODY;
		left_v->set_parent(this);
		exp_node* nd = left_v->reduce(env,ninp,outp);
		set_left(nd,True);
		if( outp && *outp&node::RECURSE_LVL ){
			app_reduce_recurse_level--;
			return exp;
		}
		if( inp&node::DO_STEP && outp && (*outp&node::BETA_ETA_DONE) )
		{
			app_reduce_recurse_level--;
			return exp;
		}
	}
	if( left_v && APP==left_v->op() ){
		exp_node* nd = 0;
		int ninp = inp & ~DO_REDUCE_BODY;
		left_v->set_parent(this);
		if( left_v ) nd = left_v->reduce(env,ninp,outp);
		set_left(nd, True);
		if( outp && *outp&node::RECURSE_LVL )
		{
			app_reduce_recurse_level--;
			return exp;
		}
		if( inp&node::DO_STEP && outp && (*outp&node::BETA_ETA_DONE) )
		{
			app_reduce_recurse_level--;
			return exp;
		}
		if( right_v ){
			nd = 0;
			right_v->set_parent(this);
#ifdef REDUCE_VARS
			nd = right_v->reduce_vars(env,inp,outp);
			set_right(nd,True);
#endif
		}
	}
	if( left_v && LAM==left_v->op() )
	{
		int app_inp = inp;
		if( left_v->arg()
		&&  left_v->arg()
		&&  left_v->arg()->name() )
		{
			if('@'==left_v->arg()->name()[0] )
			{
				app_inp |= DO_APP_ORDER;
			}else
			if( '#'==left_v->arg()->name()[0] )
			{
				app_inp &= ~DO_APP_ORDER;
			}
		}
		if( right_v )
		{
			Boolean has = False;
			right_v->set_parent(this);
			if( app_inp&DO_APP_ORDER ){
				arg_node* an = left_v->arg();
				exp_node* bd = left_v->body();
				stack_frame nenv(an, (arglst_node*)env );
				has = bd && an && bd->has_free(an, &nenv);
			}
			if( (app_inp&DO_APP_ORDER) && has ){
				int ninp = inp & ~DO_REDUCE_BODY;
				set_right(right_v->reduce(env, ninp, outp),True);
				if( outp && (*outp&node::RECURSE_LVL) )
				{
					app_reduce_recurse_level--;
					return exp;
				}
				if( (inp&node::DO_STEP)
					&& outp && (*outp&node::BETA_ETA_DONE) )
				{
					app_reduce_recurse_level--;
					return exp;
				}
			}else{
#ifdef REDUCE_VARS
				set_right(right_v->reduce_vars(env,inp,outp),True);
				if( outp && (*outp&node::RECURSE_LVL) )
				{
					app_reduce_recurse_level--;
					return exp;
				}
#endif
			}
		}
		left_v->arg()->import_value(&right_v);
		right_v = 0;
		left_v->set_parent(this);
		exp = left_v->reduce(env,inp,outp);
		if( left_v && left_v!=exp ) delete left_v;
		left_v = 0;
		if( exp ) exp->set_parent(this);
		if( outp ) *outp |= node::BETA_ETA_DONE;
	}else{
		if( right_v ){
			right_v->set_parent(this);
#ifdef REDUCE_VARS
			set_right(right_v->reduce_vars(env,inp,outp),True);
#endif
			set_right(right_v->reduce(env,inp,outp),True);

			#if 0
				if( right_v && APP==right_v->op()
					&& right_v->left()
					&& LAM==right_v->left()->op()
				){
					set_right(right_v->reduce(env,inp,outp),True);//OLD
				}
			#endif
		}
	}
	app_reduce_recurse_level -= 1;
	return exp;
}

exp_node* app_node::
reduce_vars(const alst_node* env,int inp,int* outp)
{
	if( left_v ){
		left_v->set_parent(this);
		exp_node* exp = left_v->reduce_vars(env,inp,outp);
		set_left(exp,True);
  		if( outp && (*outp&node::RECURSE_LVL) )
  		{
			if( right_v ) right_v->set_parent(this);
  			return this;
  		}
	}
	if( right_v ){
		right_v->set_parent(this);
		exp_node* exp = right_v->reduce_vars(env,inp,outp);
		set_right(exp,True);
	}
	return this;
}

void app_node::
set_left(exp_node* nd, Boolean import)
{
	if( left_v == nd ) return;
	if( left_v ) delete left_v;
	if( import ) left_v = nd;
	else if( nd ) left_v = (exp_node*)nd->clone();
	else left_v = 0;
	if( left_v ) left_v->set_parent(this);
}

void app_node::
set_right(exp_node* nd, Boolean import)
{
	if( right_v==nd ) return;
	if( right_v ) delete right_v;
	if( import ) right_v = nd;
	else if( nd ) right_v = (exp_node*)nd->clone();
	else right_v = 0;
	if( right_v ) right_v->set_parent(this);
}

int app_node::
operator == (const exp_node& cmp) const
{
	Boolean b1 = False;
	if( APP!=cmp.op() ) return b1;
	if( !left() || !cmp.left() ) return b1;
	if( !right() || !cmp.right() ) return b1;
	b1 = *left() == *(cmp.left()) 
		&& *right() == *(cmp.right());
	return b1;
}

exp_node* app_node::
extract_defs(const alst_node* env)
{
	exp_node* exp = this;
	const alst_node* alst = match(env);
	if( !alst ){
		if( left() ){
			exp_node* nl = left()->extract_defs(env);
			if( nl )
				set_left( nl, True);
		}
		if( right() ){
			exp_node* nr = right()->extract_defs(env);
			if( nr )
				set_right( nr, True);
		}
		alst = match(env);
		if( alst )
			exp = new var_node(alst->arg()->name());
	}else{
		exp = new var_node(alst->arg()->name());
	}
	return exp;
}

exp_node* app_node::
extract(const char* nm,int inp) const
{
	exp_node* exp = 0;
	exp_node* lft = 0;
	exp_node* rgt = 0;
	exp_node* exp1 = 0;
	//if( left() && right() && nm && (inp&DO_EXTRACT_ETA) )
	if( left() && right() && nm )
	{
		arg_node* argr = new arg_node(nm,0);
		arglst_node env(argr,0, True);
		if( !this->has_free(argr, &env) )
		{
			if( inp&DO_EXTRACT_APP )
			{
				if( left() ) lft = left()->extract(nm,inp);
				if( right() ) rgt = right()->extract(nm,inp);
				exp1 = (exp_node*)new app_node(
					new var_node("S"),lft,True);
				exp = (exp_node*)new app_node(exp1,rgt,True);
			}else{
				//exp = (exp_node*)new app_node(new var_node("K"),
				//	(exp_node*)this->clone(),True);
				exp1 = this->extract(0,inp);
				exp = (exp_node*)new app_node(
					new var_node("K"), exp1, True);
			}
		}else
		if( !left()->has_free(argr, &env)
			&& VAR==right()->op()
			&& right()->name()
			&& (inp&DO_EXTRACT_ETA)
			&& 0==strcmp( nm, right()->name()) )
		{
			exp = (exp_node*)left()->clone();
		}
	}
	if( !exp )
	{
		if( left() ) lft = left()->extract(nm,inp);
		if( right() ) rgt = right()->extract(nm,inp);
		if( nm )
		{
			exp1 = (exp_node*)new app_node(
				new var_node("S"),lft,True);
			exp = (exp_node*)new app_node(exp1,rgt,True);
		}else{
			exp = (exp_node*)new app_node(lft,rgt,True);
		}
	}
	return exp;
}

exp_node* app_node::
seq_lambda(int* pseq) const
{
	app_node* result = NULL;
	exp_node* lft = NULL;
	exp_node* rgt = NULL;
	if( left() )
	{
		lft = left()->seq_lambda(pseq);
	}
	if( right() )
	{
		rgt = right()->seq_lambda(pseq);
	}
	result = new app_node(lft,rgt);
	return result;
}

//////////////////////////////////////////////////////////////////////
// alst_node's
arglst_node::
arglst_node( arg_node* arg,arglst_node* next, Boolean import)
{
	if( import) arg_v=arg;
	else if( arg ) arg_v = (arg_node*)arg->clone();
	else arg_v = 0;
	if( import ) next_v=next;
	else if( next ) next_v = (arglst_node*)next->clone();
	else next_v = 0;
}

arglst_node::
arglst_node(const arglst_node& from) {
	if( from.arg_v )
		arg_v = (arg_node*)from.arg_v->clone();
	else arg_v = 0;
	if( from.next_v )
		next_v = (arglst_node*)from.next_v->clone();
	else arg_v = 0;
}


arglst_node::
~arglst_node()
{
	if( arg_v ) delete arg_v;
	if( next_v ) delete next_v;
}
alst_node* arglst_node::
add(arg_node* anode,Boolean import)
{
	arglst_node* retval = this;
	arglst_node* where = 0;
	if( !anode ) return where;

	for( arglst_node* lst=this;
		lst; lst=(arglst_node*)lst->next() )
	{
		if( lst->arg() && lst->arg()->name() ){
			if( 0==strcmp(anode->name(),
					      lst->arg_v->name()))
			{
				where = lst;
				where->set_arg(anode, import);
				break;
			}
		}
	}

	if( !where){
		where = new arglst_node(anode,0,import);
		where->next_v = this;
		retval = where;
	}

	return (alst_node*)retval;
}

arg_node* arglst_node::
find(arg_node* anode)
{
	arglst_node* where = 0;
	if( !anode ) return 0;
	arglst_node* last = 0;
	arg_node* arg = 0;

	for( arglst_node* lst=this;
		lst; last = lst, lst=(arglst_node*)lst->next() )
	{
		if( lst->arg() && lst->arg()->name() ){
			if( 0==strcmp(anode->name(),
					      lst->arg_v->name()))
			{
				where = lst;
				break;
			}
		}
	}
	if( where ) arg = where->arg_v;
	return arg;
}

void arglst_node::
set_arg(arg_node* nd, Boolean import)
{
	if( arg_v == nd ) return;
	if( arg_v ) delete arg_v;
	if( import ) arg_v = nd;
	else if( nd ) arg_v = (arg_node*)nd->clone();
	else arg_v = 0;
	if( arg_v ) arg_v->set_parent(this);
}

void arglst_node::
list()
{
	for(alst_node* pnode=(alst_node*)this;pnode; pnode=pnode->next() )
	{
		if( pnode->arg() ) pnode->arg()->print();
		printf("\n");
	}
}


syntax highlighted by Code2HTML, v. 0.9.1