/*
 *  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.
 */
#include "adp.h"

extern ExecContext_Factory newExecContext_Catch;
extern ExecContext_Factory newExecContext_Next;
extern ExecContext_Factory newExecContext_Pipe;



bool ExecContextRoot::call(PObjectArray *, PException &excp) { 
	bool result;
	if ( firstflg ) {
		firstflg = false;
		result = first(excp);
		if ( result == false && !excp.iserr() ) {
			if ( pred->getOpt().first_true ) {
				return ASSERT_assert_number_of_times_unificate_error(excp);
			}
		}
	} else {
		result = backtrack(excp);
		if ( result == true ) {
			if ( pred->getOpt().backtrack_false ) {
				return ASSERT_assert_number_of_times_unificate_error(excp);
			}
		}
	}
	if ( !pred->opt.result_return || excp.iserr() ) {
		return result;
	} else {
		p->last_result = result;
		return true;
	}
}


bool ExecContextBase::call(PObjectArray *mtobjs, PException &excp) { 
	bool result;
	if ( firstflg ) {
		firstflg = false;
		// キャッシュ
		if ( pred->opt.cachable ) {
			pred_a = dynamic_cast<const PPredicate*>(pred->getval( this, gl));
			PINTEGER	hash = pred_a->chash();
			pcmap::iterator	i = pccache.find(hash);
			if ( i != pccache.end() && i->second.key->cmatch(pred_a) ) {
				// 見つかった！
				ccount = 0;
				cache = &i->second;
				record = false;
				result = play_cache();
				if ( !result ) {
					if ( p ) { (*gl) = backup; } // 代入を元に戻す
					cleanup();
				}
				if ( !pred->opt.result_return ) {
					return result;
				} else {
					p->last_result = result;
					return true;
				}
			} else {
				// 見つからなかった
				pccache[hash] = PredicateCache( hash, pred_a);
				pcmap::iterator	i = pccache.find(hash);
				cache = &i->second;
				ccount = 0;
				record = true;
			}
			result = first(excp);
			record_cache(result,excp);	// 結果をキャッシュに保存する
		} else {
			result = first(excp);
		}
		if ( result == false && !excp.iserr() ) {
			if ( pred->getOpt().first_true ) {
				if ( p ) { (*gl) = backup; } // 代入を元に戻す
				cleanup();
				return ASSERT_assert_number_of_times_unificate_error(excp);
			}
		}
	} else {
		assert( gl->size() == backup.size() );
		*gl = backup;	// 保存していた変数の復元
		if ( !objs.empty() ) {
			if ( mtobjs ) {
				// 平行動作用に一時的に保持していたメモリを確保する 
				// Arrayクラスに移動専用のメソッドを追加した方が良い 
				for_each(objs.begin(), objs.end(), MoveObject(*mtobjs));	
			} else {
				for_each(objs.begin(), objs.end(), PMMDeleteObject());
			}
			objs.clear();
		}
		if ( cache != 0 && !record ) {
			result = play_cache();
			if ( !result ) { 
				if ( p ) { (*gl) = backup; } // 代入を元に戻す
				cleanup();
			}
			if ( !pred->opt.result_return ) {
				return result;
			} else {
				p->last_result = result;
				return true;
			}
		}
		result = backtrack(excp);
		if ( pred->opt.cachable ) record_cache(result,excp);	// 結果をキャッシュに保存する
		if ( result == true ) {
			if ( pred->getOpt().backtrack_false ) {
				if ( p ) { (*gl) = backup; } // 代入を元に戻す
				cleanup();
				return ASSERT_assert_number_of_times_unificate_error(excp);
			}
		}
	}
	if ( !result ) {
		if ( p ) { (*gl) = backup; } // 代入を元に戻す
		cleanup();
	}
	if ( !pred->opt.result_return || excp.iserr() ) {
		return result;
	} else {
		p->last_result = result;
		return true;
	}
}


// Pipeスレッド関数
thread_return __stdcall pipe_thread( void *args )
{
	thread_count++;	
	ExecContext_Pipe	*pargs = (ExecContext_Pipe*)args;
	pargs->prefetch_backtrack(pargs->excp);

	MyLog( cformat( "tid=%8x:pipe_thread_wait:%p\n", GetCurrentThreadId(), pargs).c_str(), "pipe");

	while ( pargs->fl == false ) mySleep(0);
	pargs->fl = false;

	pargs->ecs.clear();

	// 平行動作用に一時的に保持していたメモリを開放する 
	for_each( pargs->mtobjs.begin(), pargs->mtobjs.end(), PMMDeleteObject());
	pargs->mtobjs.clear();

	pargs->done = true;
	//SetEvent(pargs->ehr);
	pargs->fr = true;

	MyLog( cformat( "tid=%8x:pipe_thread_exit:%p\n", GetCurrentThreadId(), pargs).c_str(), "pipe");

	thread_count--;
	return 0;
}


bool ExecContext_Pipe::first(PException &excp)
{
	MyLog( cformat( "tid=%8x:first:%p(pipe=%d)\n", GetCurrentThreadId(), this, pipe).c_str(), "pipe");

	if ( pipe ) {
		// サブスレッド側の処理（こちらは実行されない） 
		return false;
	} else {
		// コンテクストの保存
		hlocal = p->hlocal;
		substitutepair = p->substitutepair;
		goal = p->goal;
		vcnt = p->vcnt;
		g = p->g;
		lastpobjs = p->lastpobjs;
		ecs = p->ecs;
		goalidx = p->goalidx - 1;

		// ローカル変数の書き換え
		for ( int i = goalidx; i >= 0; i-- ) {
			ExecContextRoot* ec = ecs[i];
			if ( ec ) {
				if ( ec->gl == p->gl ) {
					ec->gl = hl;
				}
				if ( ec->hl == p->hl ) {
					ec->hl = hl;
				}
				if ( ec == p->pipe ) break;
			}
		}

		// コピーバック先の初期化
		d = p;

		// pipeidxの更新
		if ( p->pipe ) {
			p->pipe->d = this;	// 前のpipeのコピーバックの設定 
		}
		p->pipe = this;

		// フラグ関係の初期化
		fl = false;
		fr = false;
		pipe = true;
		done = false;

		if ( !start_thread( &th, pipe_thread, this) ) return RERR_Createthread(excp);
		return true;
	}
}

bool ExecContext_Pipe::prefetch_backtrack(PException &excp)
{
	MyLog( cformat( "tid=%8x:prefetch_backtrack:%p\n", GetCurrentThreadId(), this).c_str(), "pipe");


	// サブスレッド側の処理
	bool forward = false;

	while ( !done ) {
		ExecContextRoot*		ec = 0;
		ExecContext_Factory*	f = 0;
		if ( !forward ) {	// バックトラック 
			ec = ecs[goalidx];	// キャッシュがあるはず 
			g = getPredicate(goalidx); // 呼び出し側の述語のプリフィックスをみる 
			if ( ec == 0 || (g != 0 && g->opt.only_one_call == true) ) {
				if ( goalidx == 0 ) {
					// 本メソッドではnext述語の処理は行わない 
					return false;
				} else {
					--goalidx;
				}
				continue; // ループの先頭へ 
			}
			f = ec->f;
			// backtrack
		} else {
			// 自身のpipe述語まで到達した。 
			if ( this == ecs[goalidx] ) {
				// backtrackにつなげる 

				// コ・スレッド待ち
				MyLog( cformat( "tid=%8x:prefetch_backtrack_wait:%p \n", GetCurrentThreadId(), this).c_str(), "pipe");

				while ( fl == false ) mySleep(0);
				fl = false;

				MyLog( cformat( "tid=%8x:prefetch_backtrack:%p done=%d\n", GetCurrentThreadId(), this, done).c_str(), "pipe");

				if ( done ) return false;

				// コンテクストのコピーバック
				d->hlocal = hlocal;
				d->substitutepair = substitutepair;
				//d->goal = goal;
				//d->vcnt = vcnt;
				//d->g = g;
				//d->lastpobjs = lastpobjs;
				for ( size_t i = 0; i < goalidx; i++ ) {
					d->ecs[i] = ecs[i];
				}

				// 平行動作用に一時的に保持していたメモリを開放する 
				// pipeが連鎖している場合は、連鎖先へ渡す
				if ( d != p ) {
					ExecContext_Pipe *pipe = dynamic_cast<ExecContext_Pipe*>(d);
					pipe->mtobjs.insert( pipe->mtobjs.end(), mtobjs.begin(), mtobjs.end());
					mtobjs.clear();
				} else {
					for_each( mtobjs.begin(), mtobjs.end(), PMMDeleteObject());
					mtobjs.clear();
				}

				// コ・スレッドの継続
				fr = true;

				--goalidx;
				forward = false;
				continue;
			}
			// 通常のフォワードシーケンス処理 
			g = getPredicate(goalidx);
			if ( g == 0 ) return RERR_predicate_name(excp);	// 後のcatch節に行かなければならない？ 
			f = g->factory;
			if ( f == 0 ) {
				bool cache = true;
				const string *name = g->getNamePtr()->getPredicateName(cache, hl);
				f = search_ExecContext_Factory(name, g->nspace);
				if ( cache ) g->factory = f; // Nameが変項の時はg->factoryに保存できない 
			}
			if ( (ec = ecs[goalidx]) == 0 ) {
				ec = f->create(this);
				ecs[goalidx] = ec;
			} else if ( f != ec->f ) {
				// リサイクルで使う述語は別もの
				//delete ecs[goalidx];
				ec->f->del(ec);
				ec = f->create(this);
				ecs[goalidx] = ec;
			} else {
				ec->reinit(g, hl);	// リサイクルする
			}
			// 一時変数の保存場所の検索
			if ( ec->pobjs == 0 ) {
				if ( lastpobjs == 0 ) {
					for ( size_t i = goalidx - 1; i < goalidx; i-- ) {
						if ( ecs[i] ) {
							lastpobjs = ecs[i]->pobjs;
							break;
						}
					}
					if ( lastpobjs == 0 ) lastpobjs = &objs;
				}
				ec->pobjs = lastpobjs;
			} else {
				lastpobjs = ec->pobjs;
			}

			// first
		}
	
		if ( f == &newExecContext_Next ) {	// prefetch時は、Next述語までバックトラックしたら終了とする。 
			if ( goalidx == 0 ) {
				return false;
			} else {
				--goalidx;
			}
		} else if ( (forward = ec->call(&mtobjs, excp)) == true ) {
			if ( ec->delflg ) {	// This code helps performance up. 
				ec->f->del(ec);
				ecs[goalidx] = 0;
				lastpobjs = 0;
			}
			goalidx++;
			assert( !excp.iserr() );
		} else {
			if ( nobacktrack ) {
				return false;
			}
			ec->firstflg = false;
			if ( ec->pobjs == lastpobjs ) lastpobjs = 0;
			// 例外処理
			if ( excp.iserr() ) {
				// 本メソッドでは例外の処理は行わない。 （リターン後、backtrackの後の親のexecuteで拾う）
				return false;
			} else {
				if ( goalidx == 0 ) {
					return false;
				} else {
					--goalidx;
				}
			}
		}
	}
	return false;
}


bool ExecContext_Pipe::backtrack(PException &excp)
{
	MyLog( cformat( "tid=%8x:backtrack:%p\n", GetCurrentThreadId(), this).c_str(), "pipe");

	// メインスレッド側の処理
	// pipe == ture時のfirstが呼び出されるまで待つ 
	MyLog( cformat( "tid=%8x:backtrack_wait:%p\n", GetCurrentThreadId(), this).c_str(), "pipe");
	if ( done ) {
		if ( this->excp.iserr() ) excp = this->excp;
		MyLog( cformat( "tid=%8x:backtrack_done_true:%p\n", GetCurrentThreadId(), this).c_str(), "pipe");
		return false;
	}

	fl = true;
	while ( fr == false ) mySleep(0);
	fr = false;

	if ( done == false ) {
		MyLog( cformat( "tid=%8x:backtrack_done_false:%p\n", GetCurrentThreadId(), this).c_str(), "pipe");
		return true;
	} else {
		if ( this->excp.iserr() ) excp = this->excp;
		MyLog( cformat( "tid=%8x:backtrack_done_true:%p\n", GetCurrentThreadId(), this).c_str(), "pipe");
		return false;
	}
}


inline void ExecContext::setppoutbuf()
{
	if ( p ) {
		p->ppoutbuf += ppoutbuf;
	} else {
		if ( !ppoutbuf.empty() ) {
			print_header();
			fwrite( ppoutbuf.c_str(), ppoutbuf.size(), 1, stdout);
     	}
	}
	
}

bool ExecContext::execute(PException &excp, bool forward)
{
	while ( true ) {
		ExecContextRoot*  ec = 0;
		ExecContext_Factory*  f = 0;
		if ( !forward ) {	// バックトラック
			ec = ecs[goalidx];	// キャッシュがあるはず
			g = getPredicate(goalidx); // 呼び出し側の述語のプリフィックスをみる
			if ( ec == 0 || (g != 0 && g->opt.only_one_call == true) ) {
				if ( goalidx == 0 ) {
					if ( nextidx != -1 ) {
						goalidx = nextidx + 1;
						forward = true;
						if ( goalidx >= goal->size() ) {
							--goalidx;
							setppoutbuf();
							// goal->nobacktrackがtrueのときはecsの全てのオブジェクトをdelするが元々オブジェクトがない場合のループなのでdelは不要 
							return true;
						}
					} else {
						return false;
					}
				} else {
					--goalidx;
				}
				continue; // ループの先頭へ
			}
			f = ec->f;
			// backtrack
		} else {
			g = getPredicate(goalidx);
			if ( g == 0 ) return RERR_predicate_name(excp);	// 後のcatch節に行かなければならない？
			f = g->factory;
			if ( f == 0 ) {
				bool cache = true;
				const string *name = g->getNamePtr()->getPredicateName(cache, hl);
				f = search_ExecContext_Factory(name, g->nspace);
				if ( cache ) g->factory = f; // Nameが変項の時はg->factoryに保存できない
			}
			if ( (ec = ecs[goalidx]) == 0 ) {
				ec = f->create(this);
				ecs[goalidx] = ec;
			} else if ( f != ec->f ) {
				// リサイクルで使う述語は別もの
				//delete ecs[goalidx];
				ec->f->del(ec);
				ec = f->create(this);
				ecs[goalidx] = ec;
			} else {
				ec->reinit(g, hl);	// リサイクルする
			}
			// 一時変数の保存場所の検索
			if ( ec->pobjs == 0 ) {
				if ( lastpobjs == 0 ) {
					for ( size_t i = goalidx - 1; i < goalidx; i-- ) {
						if ( ecs[i] ) {
							lastpobjs = ecs[i]->pobjs;
							break;
						}
					}
					if ( lastpobjs == 0 ) lastpobjs = &objs;
				}
				ec->pobjs = lastpobjs;
			} else {
				lastpobjs = ec->pobjs;
			}

			// first
		}
	
		if ( f == &newExecContext_Catch ) {
			--goalidx;
			setppoutbuf();
			return true;
		} else if ( (forward = ec->call(0, excp)) == true ) {
			ec->firstflg = false;
			if ( ec->delflg ) {	// This code helps performance up. 
				ec->f->del(ec);
				ecs[goalidx] = 0;
				lastpobjs = 0;
			}
			goalidx++;
			assert( !excp.iserr() );
		} else {
			if ( nobacktrack ) return false;
			ec->firstflg = false;
			size_t backidx = goalidx;
			if ( ec->pobjs == lastpobjs ) lastpobjs = 0;
			// 例外処理
			if ( excp.iserr() ) {
				const PPredicate*  catch_pred = newCatchCaller(excp);
				bool result = false;
				int	push_backcnt = 0;
				while ( goalidx < goal->size() && result == false ) {
					const PPredicate*  p = getPredicate(goalidx);
					if ( p && p->ncmp("catch") ) {
						VLocal	backup(*hl);
						result = p->unify( *catch_pred, this, hl, hl);
						if ( !result ) {
							*hl = backup;
						}
					}
					if ( goalidx < ecs.size() ) {
						ExecContextRoot	*e = ecs[goalidx];
						if ( e ) {
							e->f->del(e);
							ecs[goalidx] = 0;
						}
					} else {
						ecs.push_back(0);
						push_backcnt++;
					}
					++goalidx;
				}
				if ( result ) {
					excp.clear();
					forward = true;
				} else {
					while ( push_backcnt-- > 0) {
						ecs.pop_back();
					}
					return false;	// 例外を呼び出し元に伝播
				}
			} else {
				if ( goalidx == 0 ) {
					if ( nextidx != -1 ) {
						goalidx = nextidx + 1;
						forward = true;
					} else {
						return false;
					}
				} else {
					--goalidx;
				}
			}
		}
		if ( goalidx >= goal->size() ) {
			--goalidx;
			setppoutbuf();
			return true;
		}
	}
	return false;
}


bool ExecContext::first(PException &excp)
{
	if ( pred->horns != 0 ) {
		horns = pred->horns;
		skipnamechk = true;
	} else {
		bool		  cache = true;
		const string *name;
		if ( (name = getPredicateName(cache)) == 0 ) return RERR_nothing_match_predicate(excp);		
		horns = gc->hbase.upHorns(*name);
		if ( cache ) {
			pred->horns = horns;
		}
		skipnamechk = true;
		if ( horns == 0 && !name->empty() ) {
			horns = gc->hbase.upHorns(string(""));
			skipnamechk = false;
		}
		if ( !pred->nspace.empty() ) {
			skipnamechk = false;
		}
		if ( horns == 0 ) return RERR_nothing_match_predicate(excp);		
	}
	// ホーン節の検索 
	while ( true ) {
		goal = 0;
		PHorn	&horn = horns->upHorn(hornidx);
		hl->assign( horn.getVcnt(), 0);
		substitutepair.clear();
		if ( horn.upHorn(*pred, *this, skipnamechk) ) {
			goal = &horn.getGoal();
			if ( goal->empty() ) {
				if ( goal->nobacktrack == true ) delflg = true;
				return true;
			}
			goalidx = 0;			// 述語検索関係の変数の初期化 
			nextidx = (size_t)-1;	// 述語検索関係の変数の初期化 
			ppoutbuf.clear();		// 特別に初期化が必要なもの 
			size_t s = goal->size();
			ecs.assign(s,0);		// メモリの予約 
			bool flg = execute(excp, true);
			if ( flg ) {
				// 実行後の代入処理
				for ( Array<SubstitutePair>::iterator i = substitutepair.begin(); i < substitutepair.end(); i++ ) {
					const PObject *g = (*gl)[(*i).idxg];
					if ( g ) {
						g->unify( *(*hl)[(*i).idxh], this, gl, hl);
						flg = g->unify( *(*hl)[(*i).idxh], this->p, gl, hl);
						if ( !flg ) break;
					} else {
						const PObject *n = (*hl)[(*i).idxh];
						if ( n ) { 
							(*gl)[(*i).idxg] = n->getval(this->p, hl);
						}
					}
				}
				if ( flg ) {
					if ( goal->nobacktrack == true ) {
						// ↓変数の受け渡しをclone/deleteで行っているが、objs間の移動の方が効率はよくないか？（検索が入るのでこちらの方が早い？）  
						// バグフィックス 
						for ( size_t i = 0; i < pred->arglist.size(); i++ ) {
							const PVeriable *v = dynamic_cast<const PVeriable*>((*pred)[i]);
							if ( v ) {
								const PObject *p = (*gl)[v->idx];
								if ( p ) (*gl)[v->idx] = p->getval(this->p,gl)->clone(&this->p->objs);
							}
						}
						// バグフィックス(開放処理をここで行う 
						for (size_t i = 0; i < ecs.size(); i++ ) {
							ExecContextRoot *e = ecs[i];
							if ( e ) {
								e->f->del(e);
								ecs[i] = 0;
							}
						}

						delflg = true;
					}
					return true;
				}
			}
			if ( flg == false && excp.iserr() ) { break; } // 例外処理の伝播 
			if ( ++hornidx >= horns->size() ) {
				PHorns	*p = gc->hbase.upHorns(string(""));
				if ( p == 0 || p == horns ) break;
				horns = p;
				skipnamechk = false;
				hornidx = 0;
			}
			(*gl) = backup;	// 保存していた変数の復元 
		} else {
			if ( ++hornidx >= horns->size() ) {
				PHorns	*p = gc->hbase.upHorns(string(""));
				if ( p == 0 || p == horns ) break;
				horns = p;
				skipnamechk = false;
				hornidx = 0;
			}
			(*gl) = backup;	// 保存していた変数の復元 
		}
	}
	return false;
}

bool ExecContext::backtrack(PException &excp)
{
	bool flg = false;

	if ( horns == 0 ) return false;
	if ( goal != 0 && goal->nobacktrack ) return false;
	
	// 一般ホーン節の評価 
	while ( true ) {
		// 評価中のゴールがあればそちらを再評価する 
		if ( goal != 0 && !goal->empty() ) {
			flg = execute(excp, flg);
		}
		// ゴールの評価が成功すれば代入を行う（成功終了）。 
		if ( flg  ) {
			// 実行後の代入処理
			for ( Array<SubstitutePair>::iterator i = substitutepair.begin(); i < substitutepair.end(); i++ ) {
				const PObject *g = (*gl)[(*i).idxg];
				if ( g ) {
					g->unify( *(*hl)[(*i).idxh], this, gl, hl);
					flg = g->unify( *(*hl)[(*i).idxh], this, gl, hl);
					if ( !flg ) break;
				} else {
					const PObject *n = (*hl)[(*i).idxh];
					if ( n ) { 
						(*gl)[(*i).idxg] = n->getval(this, hl);
					}
				}
			}
			if ( flg ) {
				if ( goal->nobacktrack == true ) {
					// ↓変数の受け渡しをclone/deleteで行っているが、objs間の移動の方が効率はよくないか？（検索が入るのでこちらの方が早い？） 
					// バグフィックス
					for ( size_t i = 0; i < pred->arglist.size(); i++ ) {
						const PVeriable *v = dynamic_cast<const PVeriable*>((*pred)[i]);
						if ( v ) {
							const PObject *p = (*gl)[v->idx];
							if ( p ) (*gl)[v->idx] = p->getval(this->p,gl)->clone(&this->p->objs);
						}
					}
					// バグフィックス(開放処理をここで行う
					for (size_t i = 0; i < ecs.size(); i++ ) {
						ExecContextRoot *e = ecs[i];
						if ( e ) {
							e->f->del(e);
							ecs[i] = 0;
						}
					}
					delflg = true;
				}
				return true;
			}
		}
		if ( flg == false && excp.iserr() ) { break; } // 例外処理の伝播
		if ( flg == false && nobacktrack ) { break; } // cut
		if ( ++hornidx >= horns->size() ) {
			PHorns	*p = gc->hbase.upHorns(string(""));
			if ( p == 0 || p == horns ) break;
			horns = p;
			skipnamechk = false;
			hornidx = 0;
		}
		// 別のホーン節の検索
		goal = 0;
		PHorn	&horn = horns->upHorn(hornidx);
		hl->assign( horn.getVcnt(), 0);
		(*gl) = backup;	// 保存していた変数の復元
		substitutepair.clear();
		if ( horn.upHorn(*pred, *this, skipnamechk) ) {
			goal = &horn.getGoal();
			if ( goal->empty() ) {
				if ( goal->nobacktrack == true ) delflg = true;
				return true;
			}
			goalidx = 0;			// 述語検索関係の変数の初期化
			nextidx = (size_t)-1;	// 述語検索関係の変数の初期化
			ppoutbuf.clear();		// 特別に初期化が必要なもの
			size_t s = goal->size();
			ecs.assign(s, 0);		// メモリの予約
			flg = true;
			pobjs = &objs;
		}
	}

	return false;
}


bool GlobalContext::compile( const string &fname, bool adpflg, bool existchk) {
	CompileContext	c(fname.c_str(), existchk);
	return compile_body( c, adpflg);
	
}

bool GlobalContext::compile( const char *src, bool adpflg) {
	CompileContext	c(src);
	return compile_body( c, adpflg);
}

bool GlobalContext::compile_body( CompileContext &c, bool adpflg)
{
	bool	adpcode;
	bool	adpexpcode = false;
	int		rawmode = 1; // 1:awpescape <%=  2:htmlescape <%=h  3:row <%=r  4:urlencode <%=u
	string	out;

	adpcode = adpflg;
	// UTF-8 BOM 対応
	if ( !c.eof() && *c == '\xEF' ) {
		c.next_noskip();
		if ( !c.eof() && *c == '\xBB' ) {
			c.next_noskip();
			if ( !c.eof() && *c == '\xBF' ) {
				c.next_noskip();
			} else {
				c.err("C0024: unexpected charactor '\xBB' ");
			}
		} else {
			c.err("C0025: unexpect charactor '\xEF' ");
		}
	}
	while ( !c.eof() && *c == '#' ) {	// 先頭行の最初の文字が#の場合、行コメントとみなす。
		c.newline();
	}
	while ( !c.eof() ) {
		if ( (adpcode == true || adpexpcode == true) && *c == '%' ) {
			c.next_noskip();
			if ( *c == '>' ) {
				c.next_noskip();
				if ( *c == '\r' ) c.next_noskip();
				if ( *c == '\n' ) c.next_noskip();
				adpcode = false;
				adpexpcode = false;
			} else {
				c.push_back();
			}
		}
		if ( adpcode == false && adpexpcode == false && *c == '<' ) {
			c.next_noskip();
			if ( *c == '%' ) {	// 差し込み
				if ( !out.empty() && (strcmp(out.c_str(), "\n") && strcmp(out.c_str(), "\r") && strcmp(out.c_str(), "\r\n")) ) {
					PGoal	goal;
					c.vary.clear();
					goal.make_prt_code(out);
					topgoals.addGoal(goal, c.vary);
					out.clear();
				}
				c.next_noskip();
				if ( *c == '=' ) {			// 式の差し込み
					adpexpcode = true;
					c.next_noskip();
					switch ( *c ) {
						case 'u':
							c.next();
							rawmode = 4;
							break;
						case 'r':
							c.next();
							rawmode = 3;
							break;
						case 'h':
							c.next();
							rawmode = 2;
							break;
						default:
							rawmode = 1;
					}
				} else {
					adpcode = true;			// コードの差し込み
				}
			} else if ( *c == '@' ) {		// PPの差し込み(include)
				c.next();
				string filename;
				bool	incflg = false;
				while ( !c.eof() ) {
					if ( *c == '@' ) {
						c.next_noskip();
						if ( *c == '>' ) {
							incflg = true;
							break; // include したファイルから再度評価を行う
						} else {
							filename.push_back('@');
						}
					}
					filename.push_back(*c);
					c.next_noskip();
				}
				if ( incflg ) {
					c.next_noskip();
					c.include_file( filename.c_str(), true, first_src_base_path.c_str());
					continue;
				}
			} else {
				c.push_back();
			}
		}
		if ( adpexpcode ) {
			PGoal	goal;
			c.vary.clear();
			switch ( rawmode ) {
				case 1:
					goal.compileOutcode_awpescape(c);
					break;
				case 2:
					goal.compileOutcode_htmlescape(c);
					break;
				case 3:
					goal.compileOutcode_raw(c);
				case 4:
					goal.compileOutcode_urlencode(c);
					break;
			}
			topgoals.addGoal(goal, c.vary);
			out.clear();
			if ( *c != '%' ) c.next();	// スキップ
		} else if ( adpcode ) {	// ADP コード
			bool cflg = false;
			c.skip();
			if ( hbase.isMyObject(*c) ) {
				hbase.compile(c, false);
				cflg = true;
			}
			c.skip();
			PGoal	goal;
			if ( goal.isMyObject(*c) ) {
				c.vary.clear();
				goal.compile(c, false);
				topgoals.addGoal(goal, c.vary);
				cflg = true;
			}
//				if ( *c == '%' ) {	// これを入れるとdebug002でエラーとなる
//					cflg = true;
//				}
			if ( cflg == false ) {
				c.err( cformat( "C0026: unexpected charactor '%c' ", *c));
				c.next();
			}
		} else if ( !c.eof() ) {
			out.push_back(*c);
			c.next_noskip();
		}
	}
	if ( !out.empty() && (strcmp(out.c_str(), "\n") && strcmp(out.c_str(), "\r") && strcmp(out.c_str(), "\r\n")) ) {
		PGoal	goal;
		c.vary.clear();
		goal.make_prt_code(out);
		topgoals.addGoal(goal, c.vary);
	}

	if ( !c.result ) {
		for ( vector<string>::iterator i = c.errmsg.begin(); i < c.errmsg.end(); i++ ) {
			cerr << *i << endl;
		}
		compile_err = true;
		return false;
	}
	return c.result;
}


void GlobalContext::recompile()
{
	if ( compiletrace ) {
		hbase.recompile(cerr);
		topgoals.recompile(cerr);
		cerr << endl;
	}
}


bool GlobalContext::prolog() {
	if ( compile_err ) return false;
	bool ret = false;
	gc = this;
	for ( goalidx = 0; goalidx < topgoals.size(); goalidx++ ) {
		ExecContext	ec( topgoals.upHorn(goalidx).getGoal(), topgoals.upHorn(goalidx).getVcnt());
		PException excp;
		try {
			ret = ec.execute(excp, true);
		} catch ( exception e ) {
			cerr << e.what() << endl;
		}
		if ( !ret && !excp.message.empty() ) {
			cerr << excp.exception_class << ":" << excp.message << " At " << excp.where <<  endl;
		} else {
			ec.cleanup();
		}
	}
	return ret;
}
