/*
 *  ADP (Another Data Processor) www.adp.la
 *  Copyright (C) 2010 Katsuhisa Ohfuji <katsuhisa@ohfuji.name>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA 02110-1301, USA.
 */

#ifndef ADP_EXECUTE_H
#define ADP_EXECUTE_H


struct GlobalContext {
	PHornBase	hbase;			/* HORN base */
	PHorns		topgoals;		/* 評価するゴール節配列 */
	size_t		goalidx;		/* 評価中のゴール節 */
	bool		compile_err;

	GlobalContext() : hbase(), topgoals(), goalidx(0), compile_err(false) {};
	GlobalContext( PHornBase &hbase_, PHorns &topgoals_) : hbase(hbase_), topgoals(topgoals_), goalidx(0), compile_err(false) {};

	bool compile( const string &fname, bool adpflg, bool existchk = true);
	bool compile( const char *src, bool adpflg);
	bool compile_body( CompileContext &c, bool adpflg);
	void recompile();
	bool prolog();
};
extern GlobalContext *gc;

struct PException {
	string	exception_class;
	string	message;
	string	where;
	
	void clear() { exception_class.clear(); message.clear(); }
	bool iserr() { return !exception_class.empty(); }
};


struct ExecContextRoot {
	bool					delflg;			/* call後にExecContextRootを解放してもよい。関数 ExecContextRootはdelflgをtrueで初期化している */
	ExecContext				*p;				/* 親 */
	const PPredicate		*pred;			/* 評価中の述語頭部（トップレベルの呼び出しでは0) */
	bool					firstflg;
	PObjectArray			*pobjs;			/* 確保したメモリリスト */

	ExecContext_Factory		*f;				/* コンテクストを生成したファクトリー関数ポインタ（リサイクル時に使用） */

	VLocal					*gl;			/* Goal側のローカル変数のポインタ */
	VLocal					*hl;			/* Horn側のローカル変数のポインタ */

	__forceinline void init(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) __attribute__((always_inline)) { 
		p = p_;
		f = (ExecContext_Factory*)f_;
		delflg = true;
		firstflg = true;
		pobjs = 0;
		pred = pred_;
		gl = hl = l;
	}
	__forceinline void init(const PPredicate *pred_, VLocal *l) __attribute__((always_inline)) { 
		delflg = true;
		firstflg = true;
		pred = pred_;
		gl = hl = l;
	}

	ExecContextRoot(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) { init(p_, f_, pred_, l); }

	// recreate　メモリマネージャレベルでのオブジェクトのリサイクル 
	virtual void recreate(const PPredicate *pred_, VLocal *l) { init(pred_, l); }
	virtual void recreate(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) { init(p_, f_, pred_, l); }
	// reinit　Executerレベルでのオブジェクトのリサイクル 
	virtual void reinit(const PPredicate *pred_, VLocal *l) { init(pred_, l); }
	virtual void reinit(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) { init(p_, f_, pred_, l); }

	virtual bool first(PException &excp) { return false; };
	virtual bool backtrack(PException &excp) { return false; };	
	virtual void cleanup() {};
	virtual bool call(PObjectArray *mtobjs, PException &excp);
	
	void assign( VLocal *local, size_t idx, const PObject *v) { (*local)[idx] = v; };

	virtual ~ExecContextRoot() {}

	const  PObject *geto(size_t idx) { return (*pred)[idx]->getval(this); }
	template <typename P> const P *get(size_t idx) { return dynamic_cast<const P*>((*pred)[idx]->getval(this)); }
	template <typename P> const P *at(size_t idx) { if ( pred->size() <= idx ) return 0; return dynamic_cast<const P*>((*pred)[idx]->getval(this)); }

	const char	*get_charp(size_t idx) {
		if ( pred->size() <= idx ) return 0;
		const PObject *obj = (*pred)[idx]->getval( this, gl);
		if ( obj == 0 ) return 0;
		return obj->c_str();
	}
	const char	*cnv_charp(size_t idx) {
		if ( pred->size() <= idx ) return 0;
		const PObject *obj = (*pred)[idx]->getval( this, gl);
		if ( obj == 0 ) return 0;
		if ( obj->c_str() ) return obj->c_str();

		string	str;
		if ( !obj->cnv_string(str) ) return 0;
		PString *po = pmm.newPString(pobjs,str);
		return po->c_str();
	}

	bool size_check_and_alloc_array(size_t siz, PException &excp, PArray **alloc_ary ) {
		if ( siz < pred->size() ) return RERR_argment_number(excp, siz);
		if ( siz >= pred->size() ) {
			const PVeriable *v = dynamic_cast<const PVeriable*>(pred->back());
			if ( (!v || !v->isargs()) ) {
				if ( siz != pred->size() ) return RERR_argment_number(excp, siz);
			} else {
				if ( !(*gl)[v->idx] ) {
					PArray *ary = pmm.newPArray(pobjs, siz - pred->size() + 1, &pnil, true);
					(*gl)[v->idx] = ary;
					*alloc_ary = ary;
				}
			}
		}
		return true;
	}

	 template < typename P > bool unifyResultArray(size_t vidx, const P *po, PException &excp, string *label) {
		try {
			size_t	idx = vidx - pred->size() + 1;
			const PVeriable *v = dynamic_cast<const PVeriable*>(pred->back());
			if ( !v || !v->isargs() ) return RERR_argment_number(excp, vidx + 1);
			PArray *ary = const_cast<PArray*>(dynamic_cast<const PArray *>((*gl)[v->idx]));
			if ( !ary ) return RERR_result_type( excp);
			if ( idx >= ary->size() ) return false;
			if ( (*ary)[idx] == &pnil ) {
				ary->set(idx,po,label);
			} else {
				assert( idx < ary->size());
				return (*ary)[idx]->unify(*po, this);	
			}
		} catch (...) {
			return RERR_not_enough_memory(excp);
		}
		return true;
	}

	 template < typename P > bool unify(size_t vidx, const P *po, PException &excp, string *label = 0, PArray *alloc_ary = 0) {
		// 最適化
		size_t	idx = vidx - pred->size() + 1;
		if ( alloc_ary != 0 && idx < alloc_ary->size() ) {
			alloc_ary->set(idx,po,label);
			return true;
		}
		if ( vidx >= pred->size() ) {
			return unifyResultArray<P>( vidx, po, excp, label);
		} else {
			const PObject *v = (*pred)[vidx];
			if ( v->isargs() ) {
				return unifyResultArray<P>( vidx, po, excp, label);
			} else {
				return v->unify( *po, this);
			}
		}
		return true;
	}

	bool setmessage(PException &excp, const char *klass, const char *msg = 0) { 
		if ( klass ) excp.exception_class = klass;
		if ( msg ) excp.message = msg;
		if ( pred ) {
			stringstream	str;
			pred->recompile(str);
			excp.where = str.str();
		}
		return false;
	}

	bool INTERNAL_cut(PException &excp)
		{ return setmessage(excp, "!"); }
	bool INTERNAL_ERROR_sql_hash_result(PException &excp)
		{ return setmessage(excp, "Internal error sql hash result."); }
	//bool INTERNAL_eof(PException &excp)
	//	{ return setmessage(excp, "$"); }
	//bool INTERNAL_next(PException &excp)
	//	{ return setmessage(excp, "?!"); }
	//bool INTERNAL_next_backtrack(PException &excp)
	//	{ return setmessage(excp, "??"); }

	bool RERR_nothing_match_predicate(PException &excp)
		{ return setmessage(excp, "RuntimeError",  "Nothing match predicate."); }
	bool RERR_argment_number(PException &excp, size_t expnum)
		{	string str = cformat("Argment number error. expcect %d (Caller is %d).", expnum, pred->size());
			return setmessage(excp, "RuntimeError",  str.c_str()); }
	bool RERR_argment_type(PException &excp, size_t idx)
		{	string str = cformat("Argment type error(%d). ", idx);
			return setmessage(excp, "RuntimeError",  str.c_str()); }
	bool RERR_range(PException &excp, PINTEGER src, size_t dest)
		{	string str = cformat("range error %lld. range is %d. ", src, dest);
			return setmessage(excp, "RuntimeError",  str.c_str()); }
	bool RERR_no_key_err(PException &excp, const string &key)
		{	string str = cformat("No key error(%s). ", key.c_str());
			return setmessage(excp, "RuntimeError",  str.c_str()); }

	bool RERR_NOTSUPPORT_EXPRESSION(PException &excp)		
		{ return setmessage(excp, "RuntimeError",  "ADP does not support this expression."); }
	bool RERR_result_type(PException &excp)		
		{ return setmessage(excp, "RuntimeError",  "Result type error."); }
	bool RERR_predicate_name(PException &excp)		
		{ return setmessage(excp, "RuntimeError",  "Predicate_name error."); }
	bool RERR_File_Operation(PException &excp)		
		{ return setmessage(excp, "RuntimeError",  "File operation error."); }
	bool RERR_File_Open(PException &excp)		
		{ return setmessage(excp, "RuntimeError",  "File open error."); }
	bool RERR_File_Read(PException &excp)		
		{ return setmessage(excp, "RuntimeError",  "File read error."); }
	bool RERR_File_Write(PException &excp)		
		{ return setmessage(excp, "RuntimeError",  "File write error."); }
	bool RERR_File_Format(PException &excp)		
		{ return setmessage(excp, "RuntimeError",  "File format error."); }
	bool RERR_DB_Cannot_Used_Quote_Char(PException &excp)
		{ return setmessage(excp, "RuntimeError",  "Quote caractor cannot be used in table keyword."); }
	bool RERR_not_enough_memory(PException &excp)
		{ return setmessage(excp, "RuntimeError",  "Not enough memory."); }
	bool RERR_regex_format_error(PException &excp)		
		{ return setmessage(excp, "RuntimeError",  "Regex format error."); }
	bool RERR_System_Call(PException &excp)		
		{ return setmessage(excp, "RuntimeError",  "System call error."); }
	bool RERR_Communication(PException &excp)		
		{ return setmessage(excp, "RuntimeError",  "Commnication error."); }
	bool RERR_Createthread(PException &excp)		
		{ return setmessage(excp, "RuntimeError",  "Createthread error."); }
	bool RERR_Compile(PException &excp)		
		{ return setmessage(excp, "RuntimeError",  "Compile error."); }

	bool RERR_Convert(PException &excp, size_t pos)
		{	string str = cformat("Convert error. the position is %d. ", pos);
			return setmessage(excp, "RuntimeError",  str.c_str()); }

	bool ASSERT_assert_number_of_times_unificate_error(PException &excp)
		{ return setmessage(excp, "Assertion", "NumberOfTimesUnificateError"); }

	bool RERR(PException &excp, string &msg)
		{ return setmessage(excp, "RuntimeError",  msg.c_str()); }

	//bool EXCEPT_nothing_match_regex(PException &excp)		
	//	{ return setmessage(excp, "Exception",     "Nothing match regex."); }


	// Expression functions 
	PObject *add(const PObject *args0, const PObject *args1, PObjectArray *pojbs) {
		// 試しの最適化
		const PINTEGER *a1 = args0->c_integer(this);
		const PINTEGER *a2 = args1->c_integer(this);
		if ( a1 && a2 ) {
			return pmm.newPInteger( pobjs, *a1 + *a2);
		}
		return args0->getval(this)->add(*args1->getval(this),pobjs);
	}

	PObject *sub(const PObject *args0, const PObject *args1, PObjectArray *pojbs) {
		// 試しの最適化
		const PINTEGER *a1 = args0->c_integer(this);
		const PINTEGER *a2 = args1->c_integer(this);
		if ( a1 && a2 ) {
			return pmm.newPInteger( pobjs, *a1 - *a2);
		}
		return args0->getval(this)->sub(*args1->getval(this),pobjs);
	}

	PObject *mul(const PObject *args0, const PObject *args1, PObjectArray *pojbs) {
		// 試しの最適化
		const PINTEGER *a1 = args0->c_integer(this);
		const PINTEGER *a2 = args1->c_integer(this);
		if ( a1 && a2 ) {
			return pmm.newPInteger( pobjs, *a1 * *a2);
		}
		return args0->getval(this)->mul(*args1->getval(this),pobjs);
	}

	PObject *div(const PObject *args0, const PObject *args1, PObjectArray *pojbs) {
		// 試しの最適化
		const PINTEGER *a1 = args0->c_integer(this);
		const PINTEGER *a2 = args1->c_integer(this);
		if ( a1 && a2 ) {
			return pmm.newPInteger( pobjs, *a1 / *a2);
		}
		return args0->getval(this)->div(*args1->getval(this),pobjs);
	}

	PObject *mod(const PObject *args0, const PObject *args1, PObjectArray *pojbs) {
		// 試しの最適化
		const PINTEGER *a1 = args0->c_integer(this);
		const PINTEGER *a2 = args1->c_integer(this);
		if ( a1 && a2 ) {
			return pmm.newPInteger( pobjs, *a1 % *a2);
		}
		return args0->getval(this)->mod(*args1->getval(this),pobjs);
	}

	PObject *_and(const PObject *args0, const PObject *args1, PObjectArray *pojbs) {
		const PINTEGER *a1 = args0->c_integer(this);
		const PINTEGER *a2 = args1->c_integer(this);
		if ( a1 && a2 ) {
			return pmm.newPInteger( pobjs, *a1 & *a2);
		}
		return 0;
	}

	PObject *_or(const PObject *args0, const PObject *args1, PObjectArray *pojbs) {
		const PINTEGER *a1 = args0->c_integer(this);
		const PINTEGER *a2 = args1->c_integer(this);
		if ( a1 && a2 ) {
			return pmm.newPInteger( pobjs, *a1 | *a2);
		}
		return 0;
	}

	PObject *_xor(const PObject *args0, const PObject *args1, PObjectArray *pojbs) {
		const PINTEGER *a1 = args0->c_integer(this);
		const PINTEGER *a2 = args1->c_integer(this);
		if ( a1 && a2 ) {
			return pmm.newPInteger( pobjs, *a1 ^ *a2);
		}
		return 0;
	}

};


struct PredicateCache {
	PINTEGER					hash;
	const PObject				*key;
	long long					result;
	vector<const PPredicate*>	values;
	PObjectArray				objs;
	
	PredicateCache() : hash(0),key(0),result(0),values(), objs() {}
	PredicateCache( PINTEGER hash_, const PObject *key_ ) : hash(0),key(0),result(0),values(), objs() {
		hash = hash_;
		key = key_->clone(&objs);
	}
	PredicateCache( const PredicateCache &src) { copy(src); }
	PredicateCache &operator=(const PredicateCache &src) { copy(src); return *this; }
	void copy( const PredicateCache &src) {
		hash = src.hash;
		if ( src.key == 0 ) {
			key = 0;
		} else {
			key = src.key->clone(&objs);
		}
		result = src.result;
		for( vector<const PPredicate*>::const_iterator i = src.values.begin(); i < src.values.end(); i++ ) {
			values.push_back(dynamic_cast<const PPredicate*>((*i)->clone(&objs)));
		}
	}
	~PredicateCache() {
		for_each(objs.begin(), objs.end(), PMMDeleteObject());
	}
	
};
typedef map<PINTEGER, PredicateCache>	pcmap;
typedef pair<PINTEGER, PredicateCache>	pcpair;

extern	pcmap						pccache;// 述語評価キャッシュ

struct ExecContextBase :  public ExecContextRoot {
	VLocal					backup;			/* ローカル変数のバックアップ */

	PObjectArray			objs;			/* 確保したメモリリスト（バックトラックでクリアされる変数） */


	PredicateCache			*cache;			/* 述語評価キャッシュ */
	size_t					ccount;			/* キャッシュ評価カウンタ */
	bool					record;			/* キャッシュレコードモードフラグ */

	const PPredicate		*pred_a;		/* 評価中の述語頭部（トップレベルの呼び出しでは0) getvalしたもの */

	__forceinline void init() __attribute__((always_inline)) { 
		delflg = false;
		cache = 0;
		ccount = 0;
		record = false;
		pred_a = 0;
		backup = *gl; // 受け渡し引数のバックアップ
		if ( !objs.empty() ) {
			for_each(objs.begin(), objs.end(), PMMDeleteObject());
			objs.clear();
		}
	};	
public:
	ExecContextBase() : ExecContextRoot(0,0,0,0), backup(), objs() {
		pobjs = &objs;
		delflg = false;
		cache = 0;
		ccount = 0;
		record = false;
		pred_a = 0;
		//gl = &backup;	// トップレベル呼出しはbackupをローカル変数領域とする 
						// マルチスレッド動作時の整合性を保つため、gl = ExecContext.hlocalとする 
	}
	ExecContextBase(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l), backup(), objs() {
		pobjs = &objs;
		init();
	}
	// recreate　メモリマネージャレベルでのオブジェクトのリサイクル 
	virtual void recreate(const PPredicate *pred_, VLocal *l) { ExecContextRoot::init(pred_, l); init(); }
	virtual void recreate(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) { ExecContextRoot::init(p_, f_, pred_, l); init(); }
	// reinit　Executerレベルでのオブジェクトのリサイクル 
	virtual void reinit(const PPredicate *pred_, VLocal *l) { ExecContextRoot::init(pred_, l); init(); }
	virtual void reinit(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) { ExecContextRoot::init(p_, f_, pred_, l); init(); }

	void record_cache(bool result, PException &excp) {
		if ( excp.iserr() ) { 
			pccache.erase(cache->hash);	// エラー時はキャッシュをクリアする（エラーのキャッシュは行わない）
			return;
		}
		if ( !result ) return;
		if ( record ) {
			const PObject *p = pred->getval( this, gl);
			cache->values.push_back(dynamic_cast<PPredicate*>(p->clone(&cache->objs)));
			cache->result++;
		}
	}

	bool play_cache() {
		if ( ccount < cache->result ) {
			// 代入的なことをする
			for (size_t i = 0; i < pred->size(); i++ ) {
				const PObject *arg = get<PObject>(i);
				const PObject *v = (*cache->values[ccount])[i];
				if ( typeid(*arg) == typeid(PEVeriable) && typeid(*v) != typeid(PVeriable) ) {
					arg->unify(*v, this);
				}
			}
			ccount++;
			return true;
		} else {
			return false;
		}
	}

	virtual void cleanup() { 
		cache = 0;
		ccount = 0;
		record = false;
		pred_a = 0;
		if ( !objs.empty() ) {
			for_each(objs.begin(), objs.end(), PMMDeleteObject());
			objs.clear();
		}
	}
	virtual bool call(PObjectArray *mtobjs, PException &excp);

	virtual ~ExecContextBase() {
		if ( !objs.empty() ) {
			for_each(objs.begin(), objs.end(), PMMDeleteObject());
		}
	}
};

struct SubstitutePair {
	size_t	idxh;
	size_t	idxg;
	SubstitutePair() : idxh(0), idxg(0) {}
	SubstitutePair( size_t idxh_, size_t idxg_) : idxh(idxh_), idxg(idxg_) {}
};

// オブジェクトの破棄
struct MyDeleteEC {
	template <typename T> void operator() (T* ptr) const {
		if ( ptr ) ptr->f->del(ptr);
	}
};
struct ExecContext_Pipe;

struct ExecContext : public ExecContextBase {
	VLocal					hlocal;			/* ホーン節側ローカル */

	ArrayNoexpand<SubstitutePair>	substitutepair;	/* 代入最適化用 */

	const PGoal				*goal;		/* 評価中のゴール */
	size_t					vcnt;		/* 変数スタックのサイズ、p == 0 のときのみ有効 */
	const PPredicate		*g;			/* 評価中の述語 */

	PHorns					*horns;			/* 評価中のHORN base */
	bool					skipnamechk;	/* 述語名のチェックが必要か？フラグ */
	size_t					hornidx;		/* 評価中のHORN baseのインディックス */
	size_t					goalidx;		/* 評価中のgoalidx(body->upGoal(goalidx) */

	PObjectArray			*lastpobjs;		/* 最後に確保したメモリリスト */

	ExecContextRootArrayNE	ecs;			/* 評価中の述語のコンテクスト */
	size_t					nextidx;		/* next述語の位置 */
	ExecContext_Pipe		*pipe;			/* 一番外のpipe（Chain用） */
	bool					trueflg;		/* true終了フラグ（catch節に到達) */ 
	string					ppoutbuf;		/* pp out buffer */
	bool					nobacktrack;	/* バックトラックしないフラグ */
	bool					last_result;	/* 最新の述語の評価結果（result_returnフラグが追加述語の評価後のみ有効） */

	__forceinline void init() __attribute__((always_inline)) { 
		goal = 0;
		hl = &hlocal;			// ローカル変数エリア
		// ecs,substitutepairのreserveはfirst,backtrackで行う 
		horns = 0;
		skipnamechk = false;
		hornidx = 0;
		goalidx = 0;
		lastpobjs = &objs;
		nextidx = (size_t)-1;
		pipe = 0;
		trueflg = false;
		substitutepair.reserve(pred->size());
		nobacktrack = false;
		last_result = false;
	};

	ExecContext(PGoal &goal_, size_t vcnt_) : ExecContextBase(),
			hlocal(), substitutepair(), vcnt(vcnt_), g(0), ecs() {
			goal = &goal_;
			hlocal.assign( vcnt, 0);	// ローカル変数エリアの確保			
			hl = &hlocal;			// ローカル変数エリア
			gl = hl;
			ecs.assign(goal->size(), 0);
			substitutepair.reserve(vcnt);
			horns = 0;
			skipnamechk = false;
			hornidx = 0;
			goalidx = 0;
			lastpobjs = &objs;
			nextidx = (size_t)-1;
			pipe = 0;
			trueflg = false;
			nobacktrack = false;
			last_result = false;
	}

	ExecContext(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextBase(p_, f_, pred_, l),
			hlocal(), substitutepair(), vcnt(0), g(0), ecs() { 
				init();
	}


	// recreate　メモリマネージャレベルでのオブジェクトのリサイクル 
	virtual void recreate(const PPredicate *pred_, VLocal *l) { ExecContextRoot::init(pred_, l); ExecContextBase::init(); init(); }
	virtual void recreate(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) { ExecContextRoot::init(p_,f, pred_, l); ExecContextBase::init(); init(); }
	// reinit　Executerレベルでのオブジェクトのリサイクル 
	virtual void reinit(const PPredicate *pred_, VLocal *l) { ExecContextRoot::init(pred_, l); ExecContextBase::init(); init(); }
	virtual void reinit(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) { ExecContextRoot::init(p_,f, pred_, l); ExecContextBase::init(); init(); }

	virtual bool first(PException &excp);
	virtual bool backtrack(PException &excp);
	bool	execute(PException &excp, bool forward);

	virtual ~ExecContext() {
		for_each( ecs.begin(), ecs.end(), MyDeleteObject());
	}

	inline const PPredicate	*getPredicate(size_t gidx) {
		const PObject *p = goal->getPredicate(gidx);
		return p->getPredicate(hl);
	}

	const string *getPredicateName(bool &docache) { // docacheは、呼出し元でtrueで初期化すること！
		const PObject *p = pred->getNamePtr();
		return p->getPredicateName(docache, this->p->hl);
	}

	const PPredicate	*newCatchCaller(PException &excp) {
		PException excp_;
		string	nspace_("sys");
		PString *name_ = pmm.newPString( pobjs, "catch");
		PString *klass_ = pmm.newPString( pobjs, excp.exception_class);
		PString *message_ = pmm.newPString( pobjs, excp.message);
		PString *where_ = pmm.newPString( pobjs, excp.where);
		PPredicate *catch_pred = pmm.newPPredicate(pobjs, nspace_, name_, klass_, message_, where_);
		return catch_pred;			
	}

	inline void setppoutbuf();
};

class ExecContext_Factory {
	ExecContextRootArray		eobjs;
	ExecContext_FactoryFunction	f;
	MyLockObject				eclock;
public:
	ExecContext_Factory( ExecContext_FactoryFunction f_ ) : eobjs(), f(f_) {InitMyLockObject( &eclock);}
	ExecContextRoot *create(ExecContext *p_) {
		ExecContextRoot *ec = 0;
		{ MyLockSection lock(eclock);
			if ( !eobjs.empty() ) {
				ec = eobjs.back();
				eobjs.pop_back();
			}
		}
		if ( ec ) {
			ec->recreate(p_, this, p_->g, p_->hl);
		} else {
			ec = (*f)( p_, this, p_->g, p_->hl);	// new している
		}
		return ec;
	}
	void del(ExecContextRoot *ec) {
		ec->cleanup();
		MyLockSection lock(eclock);
		eobjs.push_back(ec);
	}
	~ExecContext_Factory() {
		for_each( eobjs.begin(), eobjs.end(), MyDeleteObject());
		DeleteMyLockObject( &eclock);
	}	
};

void init_ExecContext_Factory_Map(void);
ExecContext_Factory *search_ExecContext_Factory(const string *name, const string &nspace);
bool check_ExecContext_Factory(const string *name);

struct ExecContext_Cut : public ExecContextRoot {
	ExecContext_Cut(ExecContext *p_,  void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) { delflg = false; }
	virtual bool first(PException &excp) { 
			for ( size_t i = 0; i < p->goalidx; i++ ) {
				ExecContextRoot	*e = p->ecs[i];
				if ( e ) {
					e->f->del(e);
					p->ecs[i] = 0;
				}
			}
			if ( p->goalidx >= p->goal->size() ) {
				p->delflg = true;
			}
			return true;
	}

	virtual bool backtrack(PException &excp) {
		p->nobacktrack = true;
		return false;
	}
};

struct ExecContext_Fail : public ExecContextRoot {
	ExecContext_Fail(ExecContext *p_,  void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		return false;
	}
};

struct ExecContext_Raise : public ExecContextRoot {
	ExecContext_Raise(ExecContext *p_,  void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( pred->size() < 1 ) return RERR_argment_number(excp, 1);
		const PString *klass = at<PString>(0);
		const PString *msg = at<PString>(1);
		if ( klass ) return setmessage(excp, klass->c_str(), msg->c_str() );
		return false;
	}
};

struct ExecContext_Next : public ExecContextRoot {
	ExecContext_Next(ExecContext *p_,  void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l){ delflg = false; }
	virtual void recreate(const PPredicate *pred_, VLocal *l) { ExecContextRoot::init(pred_, l); delflg = false; }
	virtual void recreate(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) { ExecContextRoot::init(p_,f,pred_, l); delflg = false; }
	virtual void reinit(const PPredicate *pred_, VLocal *l) { ExecContextRoot::init(pred_, l); delflg = false; }
	virtual void reinit(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) { ExecContextRoot::init(p_,f,pred_, l); delflg = false; }
	virtual bool first(PException &excp) { 
		if ( p->goalidx == 0 ) return false;
		p->nextidx = p->goalidx;
		return false;
	}
	virtual bool backtrack(PException &excp) {
		// 次のNextからのバックトラック
		// 次のNextの次へ行く
		if ( p->nextidx != p->goalidx && p->nextidx != -1) {
			p->goalidx = p->nextidx;
			return true;
		} else {
			p->nextidx = -1;	// 後の為にリセットしておく
		}
		return false;
	}
};

struct ExecContext_Catch : public ExecContextRoot {
	ExecContext_Catch(ExecContext *p_,  void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		p->trueflg = true;
		return true;
	}
};


struct ExecContext_LastResult : public ExecContextRoot {
	ExecContext_LastResult(ExecContext *p_,  void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		const PInteger  *result = pmm.newPInteger(pobjs, p->last_result ? -1 : 0);
		return (*pred)[0]->unify( *result, this);
	}
};

struct ExecContext_Pipe : public ExecContext {
	bool			pipe;	// true スレッド初期化必要 / false 初期化済み
	THREADHANDLE	th;		// スレッドハンドラ
	
	volatile bool	fl;
	volatile bool	fr;
	bool			done;	// 終了フラグ（thのWaitでよい？） 
	PObjectArray	mtobjs;	// マルチスレッド用確保したメモリリスト 
	size_t			goalidx;// 評価中のgoalidx(body->upGoal(goalidx) 
	PException		excp;	// 例外 
	ExecContext		*d;		// コピー先	（ややこしいですが、別のpipeから上書きされる） 
		
	ExecContext_Pipe(ExecContext *p_,  void *f_, const PPredicate *pred_, VLocal *l) : ExecContext(p_, f_, pred_, l){  pipe = false; }
	virtual void recreate(const PPredicate *pred_, VLocal *l) { ExecContextBase::recreate(pred_, l); pipe = false; }
	virtual void recreate(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) { ExecContextBase::recreate(p_,f,pred_, l);  pipe = false; }
	virtual void reinit(const PPredicate *pred_, VLocal *l) { ExecContextBase::reinit(pred_, l); pipe = true; }
	virtual void reinit(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) { ExecContextBase::reinit(p_,f,pred_, l); pipe = true; }
	virtual bool first(PException &excp);
	virtual bool backtrack(PException &excp);
	virtual void cleanup() { 
		ecs.clear();
		do {
			done = true;
			fl = true;
		} while ( th && wait_thread(th));
		destory_thread(th);
		th = 0;
		ExecContext::cleanup(); 
	}
	virtual ~ExecContext_Pipe() { ecs.clear(); cleanup(); }
	bool prefetch_backtrack(PException &excp);
};


#endif
