#include "ascript/Function.h"
#include "ascript/Expr.h"
#include "ascript/Formatter.h"
#include "ascript/Object.h"
#include "ascript/Object_Custom.h"
#include "ascript/Object_Matrix.h"

namespace AScript {

//-----------------------------------------------------------------------------
// FunctionType
//-----------------------------------------------------------------------------
const char *GetFuncTypeName(FunctionType funcType)
{
	static const struct {
		FunctionType funcType;
		const char *name;
	} tbl[] = {
		{ FUNCTYPE_Function,	"function",		},
		{ FUNCTYPE_Class,		"class",		},
		{ FUNCTYPE_Instance,	"instance",		},
		{ FUNCTYPE_Block,		"block",		},
	};
	for (int i = 0; i < NUMBEROF(tbl); i++) {
		if (tbl[i].funcType == funcType) return tbl[i].name;
	}
	return "unknown";
}

//-----------------------------------------------------------------------------
// Args
//-----------------------------------------------------------------------------
Args::~Args()
{
	Iterator::Delete(_pIteratorSelf);
	Function::Delete(_pFuncBlock);
}

const Expr_Block *Args::GetBlock(Environment &env, Signal sig) const
{
	// check if the block parameter specifies a delegated block information
	// like "g() {|block|}"
	// scope problem remains: 2010.11.02
	const Expr_Block *pExprBlock = _pExprBlock;
	while (pExprBlock != NULL) {
		const Expr_BlockParam *pExprBlockParam = pExprBlock->GetParam();
		if (pExprBlockParam == NULL || !pExprBlock->GetExprList().empty()) {
			break;
		}
		const ExprList &exprList = pExprBlockParam->GetExprList();
		if (exprList.size() != 1 || !exprList.front()->IsSymbol()) {
			break;
		}
		const Expr_Symbol *pExprSymbol =
							dynamic_cast<const Expr_Symbol *>(exprList.front());
		const Value *pValue = env.LookupValue(pExprSymbol->GetSymbol(), true);
		if (pValue == NULL) {
			break;
		} else if (pValue->IsExpr()) {
			const Expr *pExpr = pValue->GetExpr();
			if (!pExpr->IsBlock()) {
				sig.SetError(ERR_ValueError, "invalid value for block delegation");
				return NULL;
			}
			pExprBlock = dynamic_cast<const Expr_Block *>(pExpr);
		} else if (pValue->IsInvalid()) {
			return NULL;
		} else {
			break;
		}
	}
	return pExprBlock;
}

bool Args::ShouldGenerateIterator(const DeclarationList &declList) const
{
	if (IsSelfIterator()) return true;
	ValueList::const_iterator pValue = _valListArg.begin();
	DeclarationList::const_iterator ppDecl = declList.begin();
	for ( ; pValue != _valListArg.end() && ppDecl != declList.end(); pValue++) {
		const Declaration *pDecl = *ppDecl;
		if (pValue->IsIterator() &&
						pDecl->GetValueType() != VTYPE_Iterator) return true;
		if (!pDecl->IsVariableLength()) ppDecl++;
	}
	return false;
}

// return NULL without error if block is not specified
const Function *Args::GetBlockFunc(Environment &env, Signal sig, const Symbol *pSymbol)
{
	const Expr_Block *pExprBlock = GetBlock(env, sig);
	if (pExprBlock == NULL || pSymbol == NULL) return NULL;
	if (_pFuncBlock == NULL) {
		_pFuncBlock = FunctionCustom::CreateBlockFunc(env, sig,
										pSymbol, pExprBlock, FUNCTYPE_Block);
	}
	return _pFuncBlock;
}

//-----------------------------------------------------------------------------
// ICallable
//-----------------------------------------------------------------------------
Value ICallable::Call(Environment &env, Signal sig,
		const Value &valueSelf, Iterator *pIteratorSelf, bool selfListFlag,
		const Expr_Caller *pExprCaller, const ExprList &exprListArg,
		const Function **ppFuncSuccRequester)
{
	if (ppFuncSuccRequester != NULL) {
		const Function *pFuncSuccRequester = *ppFuncSuccRequester;
		*ppFuncSuccRequester = NULL;
		if (pFuncSuccRequester != NULL) {
			if (!pFuncSuccRequester->IsSucceedable(this)) {
				pExprCaller->SetError(sig,
						ERR_SyntaxError, "invalid succeeding process");
				return Value::Null;
			}
		}
	}
	Args args(exprListArg, valueSelf, pIteratorSelf, selfListFlag, ppFuncSuccRequester,
		pExprCaller->GetAttrs(), pExprCaller->GetAttrsOpt(), pExprCaller->GetBlock());
	Value result = DoCall(env, sig, args);
	if (sig.IsSignalled()) {
		sig.AddExprCause(pExprCaller);
		return Value::Null;
	}
	return result;
}

//-----------------------------------------------------------------------------
// Function
//-----------------------------------------------------------------------------
bool Function::IsCustom() const			{ return false; }
bool Function::IsPos() const			{ return false; }
bool Function::IsNeg() const			{ return false; }
bool Function::IsPlus() const			{ return false; }
bool Function::IsMinus() const			{ return false; }
bool Function::IsMultiply() const		{ return false; }
bool Function::IsDivide() const			{ return false; }
bool Function::IsModulo() const			{ return false; }
bool Function::IsPower() const			{ return false; }
bool Function::IsContainCheck() const	{ return false; }
bool Function::IsSequence() const		{ return false; }
bool Function::IsStructPrototype() const{ return false; }

Class *Function::GetClassToConstruct() const { return NULL; }

Function::Function(const Function &func) : _cntRef(1),
	_pSymbol(func._pSymbol),
	_envScope(func._envScope), _declOwner(func._declOwner),
	_elemType(func._elemType), _mathSymbol(func._mathSymbol),
	_funcType(func._funcType),
	_resultMode(func._resultMode), _flags(func._flags),
	_attrsOpt(func._attrsOpt)
{
	_blockInfo.occurPattern = func._blockInfo.occurPattern;
	_blockInfo.pSymbol = func._blockInfo.pSymbol;
	_blockInfo.insideScopeFlag = func._blockInfo.insideScopeFlag;
	_blockInfo.quoteFlag = func._blockInfo.quoteFlag;
}

Function::Function(Environment &envScope, const Symbol *pSymbol, FunctionType funcType) : _cntRef(1),
	_pSymbol(pSymbol), _envScope(envScope), _funcType(funcType),
	_elemType(Parser::ETYPE_Unknown),
	_resultMode(RSLTMODE_Normal), _flags(0)
{
	_blockInfo.occurPattern = OCCUR_Zero;
	_blockInfo.pSymbol = NULL;
	_blockInfo.insideScopeFlag = false;
	_blockInfo.quoteFlag = false;
}

Function::~Function()
{
}

bool Function::CustomDeclare(Environment &env, Signal sig,
								const SymbolSet &attrsAcceptable, Args &args)
{
	// delcaration of arguments
	if (!_declOwner.Declare(env, sig, args.GetExprListArg())) return false;
	// declaration of attributes
	foreach_const (SymbolSet, ppSymbol, args.GetAttrs()) {
		const Symbol *pSymbol = *ppSymbol;
		if (pSymbol->IsIdentical(AScript_Symbol(map))) {
			_flags |= FLAG_Map;
		} else if (pSymbol->IsIdentical(AScript_Symbol(nomap))) {
			_flags &= ~FLAG_Map;
		} else if (pSymbol->IsIdentical(AScript_Symbol(flat))) {
			_flags |= FLAG_Flat;
		} else if (pSymbol->IsIdentical(AScript_Symbol(noflat))) {
			_flags &= ~FLAG_Flat;
		} else if (pSymbol->IsIdentical(AScript_Symbol(list))) {
			_resultMode = RSLTMODE_List;
		} else if (pSymbol->IsIdentical(AScript_Symbol(xlist))) {
			_resultMode = RSLTMODE_XList;
		} else if (pSymbol->IsIdentical(AScript_Symbol(set))) {
			_resultMode = RSLTMODE_Set;
		} else if (pSymbol->IsIdentical(AScript_Symbol(xset))) {
			_resultMode = RSLTMODE_XSet;
		} else if (pSymbol->IsIdentical(AScript_Symbol(iter))) {
			_resultMode = RSLTMODE_Iterator;
		} else if (pSymbol->IsIdentical(AScript_Symbol(xiter))) {
			_resultMode = RSLTMODE_XIterator;
		} else if (pSymbol->IsIdentical(AScript_Symbol(void_))) {
			_resultMode = RSLTMODE_Void;
		} else if (pSymbol->IsIdentical(AScript_Symbol(reduce))) {
			_resultMode = RSLTMODE_Reduce;
		} else if (pSymbol->IsIdentical(AScript_Symbol(xreduce))) {
			_resultMode = RSLTMODE_XReduce;
		} else if (pSymbol->IsIdentical(AScript_Symbol(static_))) {
			// just ignore it
		} else if (pSymbol->IsIdentical(AScript_Symbol(dynamic_scope))) {
			_flags |= FLAG_DynamicScope;
		} else if (pSymbol->IsIdentical(AScript_Symbol(symbol_func))) {
			_flags |= FLAG_SymbolFunc;
		} else if (attrsAcceptable.IsSet(pSymbol)) {
			// nothing to do
		} else {
			sig.SetError(ERR_TypeError,
				"unsupported attribute '%s' for function declaration",
															pSymbol->GetName());
			return false;
		}
	}
	_attrsOpt = args.GetAttrsOpt();
	// declaration of a block
	if (!args.IsBlockSpecified()) return true;
	const Expr_Block *pExprBlock = args.GetBlock(env, sig);
	if (sig.IsSignalled()) return false;
	const ExprList &exprList = pExprBlock->GetExprList();
	if (exprList.size() != 1) {
		SetError_InvalidFunctionExpression(sig);
		return false;
	}
	const Expr *pExpr = exprList.front();
	OccurPattern occurPattern = OCCUR_Once;
	if (pExpr->IsSuffix()) {
		const Expr_Suffix *pExprSuffix = dynamic_cast<const Expr_Suffix *>(pExpr);
		pExpr = pExprSuffix->GetChild();
		occurPattern = pExprSuffix->GetOccurPattern();
		if (occurPattern == OCCUR_Invalid) {
			SetError_InvalidFunctionExpression(sig);
			return false;
		}
	}
	bool quoteFlag = false;
	if (pExpr->IsQuote()) {
		const Expr_Quote *pExprQuote = dynamic_cast<const Expr_Quote *>(pExpr);
		quoteFlag = true;
		pExpr = pExprQuote->GetChild();
	}
	if (!pExpr->IsSymbol()) {
		sig.SetError(ERR_TypeError,
				"content of block in a function declaration must be a symbol");
		return false;
	}
	const Expr_Symbol *pExprSym = dynamic_cast<const Expr_Symbol *>(pExpr);
	DeclareBlock(occurPattern, pExprSym->GetSymbol(), false, quoteFlag);
	foreach_const (SymbolSet, ppSymbol, pExprSym->GetAttrs()) {
		const Symbol *pSymbol = *ppSymbol;
		if (pSymbol->IsIdentical(AScript_Symbol(inside_scope))) {
			_blockInfo.insideScopeFlag = true;
		} else {
			sig.SetError(ERR_TypeError, 
				"unsupported attribute '%s' for block declaration",
														pSymbol->GetName());
			return false;
		}
	}
	return true;
}

void Function::CopyDeclare(const Function &func)
{
	_declOwner	= func._declOwner;
	_resultMode	= func._resultMode;	// :list, :xlist, :set, :xset, :iter, :xiter, :void
	_flags		= func._flags;		// :map, :nomap, :flat, :noflat
	_attrsOpt	= func._attrsOpt;
	_blockInfo.occurPattern		= func._blockInfo.occurPattern;
	_blockInfo.insideScopeFlag	= func._blockInfo.insideScopeFlag;
	_blockInfo.quoteFlag		= func._blockInfo.quoteFlag;
	_blockInfo.pSymbol			= func._blockInfo.pSymbol;
}

Declaration *Function::DeclareArg(Environment &env, const Symbol *pSymbol,
						ValueType valType, OccurPattern occurPattern,
						bool listFlag, bool nomapFlag, Expr *pExprDefault)
{
	Declaration *pDecl = new Declaration(pSymbol,
					valType, occurPattern, listFlag, nomapFlag, pExprDefault);
	ASSUME(env, !_declOwner.IsVariableLength());
	ASSUME(env, !(!(pDecl->IsOptional() || occurPattern == OCCUR_ZeroOrMore) &&
				!_declOwner.empty() && _declOwner.back()->IsOptional()));
	_declOwner.push_back(pDecl);
	return pDecl;
}

void Function::DeclareBlock(OccurPattern occurPattern,
					const Symbol *pSymbol, bool insideScopeFlag, bool quoteFlag)
{
	_blockInfo.occurPattern = occurPattern;
	_blockInfo.pSymbol =
			(occurPattern == OCCUR_Zero)? NULL :
			(pSymbol == NULL)? AScript_Symbol(block) : pSymbol;
	_blockInfo.insideScopeFlag = insideScopeFlag;
	_blockInfo.quoteFlag = quoteFlag;
}

void Function::SetHelp(const char *help)
{
	_help = help;
}

Value Function::EvalExpr(Environment &env, Signal sig, Args &args) const
{
	if (GetType() == FUNCTYPE_Instance && args.GetSelfObj() == NULL) {
		sig.SetError(ERR_ValueError,
			"object is expected as l-value of field");
		return Value::Null;
	} else if (GetType() == FUNCTYPE_Class && args.GetSelfClass() == NULL) {
		sig.SetError(ERR_ValueError,
			"class is expected as l-value of field");
		return Value::Null;
	}
	if (args.IsBlockSpecified()) {
		if (_blockInfo.occurPattern == OCCUR_Zero) {
			sig.SetError(ERR_ValueError,
				"block is unnecessary for '%s'", ToString().c_str());
			return Value::Null;
		}
	} else {
		if (_blockInfo.occurPattern == OCCUR_Once) {
			sig.SetError(ERR_ValueError,
				"block must be specified for '%s'", ToString().c_str());
			return Value::Null;
		}
	}
	bool mapFlag = GetMapFlag();
	unsigned long flags = _flags;
	ResultMode resultMode = _resultMode;
	foreach_const (SymbolSet, ppSymbol, args.GetAttrs()) {
		const Symbol *pSymbol = *ppSymbol;
		if (pSymbol->IsIdentical(AScript_Symbol(map))) {
			mapFlag = true;
		} else if (pSymbol->IsIdentical(AScript_Symbol(nomap))) {
			mapFlag = false;
		} else if (pSymbol->IsIdentical(AScript_Symbol(flat))) {
			flags |= FLAG_Flat;
		} else if (pSymbol->IsIdentical(AScript_Symbol(noflat))) {
			flags &= ~FLAG_Flat;
		} else if (pSymbol->IsIdentical(AScript_Symbol(list))) {
			resultMode = RSLTMODE_List;
		} else if (pSymbol->IsIdentical(AScript_Symbol(xlist))) {
			resultMode = RSLTMODE_XList;
		} else if (pSymbol->IsIdentical(AScript_Symbol(set))) {
			resultMode = RSLTMODE_Set;
		} else if (pSymbol->IsIdentical(AScript_Symbol(xset))) {
			resultMode = RSLTMODE_XSet;
		} else if (pSymbol->IsIdentical(AScript_Symbol(iter))) {
			resultMode = RSLTMODE_Iterator;
		} else if (pSymbol->IsIdentical(AScript_Symbol(xiter))) {
			resultMode = RSLTMODE_XIterator;
		} else if (pSymbol->IsIdentical(AScript_Symbol(void_))) {
			resultMode = RSLTMODE_Void;
		} else if (pSymbol->IsIdentical(AScript_Symbol(reduce))) {
			resultMode = RSLTMODE_Reduce;
		} else if (pSymbol->IsIdentical(AScript_Symbol(xreduce))) {
			resultMode = RSLTMODE_XReduce;
		} else if (_attrsOpt.IsSet(pSymbol)) {
			// nothing to do
		} else {
			sig.SetError(ERR_AttributeError,
				"unsupported attribute '%s' for '%s'",
										pSymbol->GetName(), ToString().c_str());
			return Value::Null;
		}
	}
	const ExprList &exprListArg = args.GetExprListArg();
	ValueList valListArg;
	Value valueWithDict;
	valueWithDict.InitAsDict(env);
	if (mapFlag && IsUnary() && (exprListArg.size() > 1 ||
						!exprListArg.empty() && exprListArg.front()->IsSuffix())) {
		if (!_declOwner.PrepareArgsForUnary(env, sig, exprListArg, valListArg, valueWithDict)) {
			return Value::Null;
		}
	} else if (!_declOwner.PrepareArgs(env, sig, exprListArg, valListArg, valueWithDict)) {
		return Value::Null;
	}
	Args argsSub(args, valListArg, valueWithDict, resultMode, flags);
	if (!mapFlag) {
		return Eval(env, sig, argsSub);
	} else if (_declOwner.ShouldImplicitMap(argsSub)) {
		return EvalMap(env, sig, argsSub);
	} else {
		return Eval(env, sig, argsSub);
	}
}

Value Function::Eval(Environment &env, Signal sig, Args &args) const
{
	bool exprFlag = false;
	ValueList valListCasted;
	if (!_declOwner.ValidateAndCast(env, sig, args.GetArgs(), valListCasted)) {
		return Value::Null;
	}
	Args argsCasted(args, valListCasted, args.GetValueWithDict(),
									args.GetResultMode(), args.GetFlatFlag());
	Value value = DoEval(env, sig, argsCasted);
	if (args.IsRsltVoid()) return Value::Null;
	return value;
}

Value Function::EvalMap(Environment &env, Signal sig, Args &args) const
{
	if (args.IsRsltIterator() || args.IsRsltXIterator()) {
		// nothing to do
	} else if (!args.IsRsltNormal() || !args.ShouldGenerateIterator(_declOwner)) {
		return EvalMapRecursive(env, sig, NULL, args);
	}
	bool skipInvalidFlag = args.IsRsltXIterator();
	Iterator_ImplicitMap *pIterator = new Iterator_ImplicitMap(env, sig,
			Function::Reference(this),
			args.GetSelf(), Iterator::Reference(args.GetIteratorSelf()),
			args.GetArgs(), skipInvalidFlag);
	if (sig.IsSignalled()) {
		Iterator::Delete(pIterator);
		return Value::Null;
	}
	return Value(env, pIterator);
}

Value Function::EvalMapRecursive(Environment &env, Signal sig,
				ResultComposer *pResultComposer, Args &args) const
{
	IteratorOwner iterOwner;
	if (!iterOwner.PrepareForMap(sig, GetDeclOwner(), args.GetArgs())) {
		return Value::Null;
	}
	Value result;
	std::auto_ptr<ResultComposer> pResultComposerOwner;
	if (pResultComposer == NULL) {
		pResultComposer = new ResultComposer(env, args, result);
		pResultComposerOwner.reset(pResultComposer);
	}
	bool doneSelfFlag = false;
	Iterator *pIteratorSelf = args.GetIteratorSelf();
	for (size_t n = 0; ; n++) {
		ValueList valListArg;
		if (doneSelfFlag || !iterOwner.Next(env, sig, valListArg)) {
			if (sig.IsSignalled()) return Value::Null;
			if (n == 0 && !args.IsRsltVoid() && pResultComposerOwner.get() != NULL) {
				result.InitAsList(env);
			}
			break;
		}
		Args argsEach(args, valListArg, args.GetValueWithDict(),
									args.GetResultMode(), args.GetFlatFlag());
		if (!_declOwner.ShouldImplicitMap(valListArg)) {
			Value valueEach = Eval(env, sig, argsEach);
			if (sig.IsSignalled()) return Value::Null;
			pResultComposer->Store(valueEach);
		} else if (args.IsRsltFlat()) {
			EvalMapRecursive(env, sig, pResultComposer, argsEach);
			if (sig.IsSignalled()) return Value::Null;
		} else {
			Value valueEach = EvalMapRecursive(env, sig, NULL, argsEach);
			if (sig.IsSignalled()) return Value::Null;
			pResultComposer->Store(valueEach);
		}
		if (pIteratorSelf != NULL) {
			Value valueSelf;
			doneSelfFlag = !pIteratorSelf->Next(env, sig, valueSelf);
			if (sig.IsSignalled()) return Value::Null;
			args.SetSelf(valueSelf);
		}
	}
	return result;
}

Value Function::EvalOverride(Signal sig, Args &args, bool &evaluatedFlag) const
{
	const Value &valueLeft = args.GetValue(0);
	const Value &valueRight = args.GetValue(1);
	Object *pObj = NULL;
	if (valueLeft.IsObject()) {
		Value valueObj = valueLeft;
		pObj = valueObj.GetObject();
	} else if (valueRight.IsObject()) {
		Value valueObj = valueRight;
		pObj = valueObj.GetObject();
	} else {
		evaluatedFlag = false;
		return Value::Null;
	}
	evaluatedFlag = true;
	return pObj->EvalMethod(sig, GetSymbol(), args.GetArgs(), evaluatedFlag);
}

Environment *Function::PrepareEnvironment(Environment &env, Signal sig, Args &args) const
{
	EnvType envType =
		(_funcType == FUNCTYPE_Class)?		ENVTYPE_Method :
		(_funcType == FUNCTYPE_Instance)?	ENVTYPE_Method :
		(_funcType == FUNCTYPE_Block)?		ENVTYPE_Block :
		ENVTYPE_Local;
	Environment *pEnvOuter = GetDynamicScopeFlag()?
							&env : const_cast<Environment *>(&_envScope);
	Environment *pEnvLocal = new Environment(pEnvOuter, envType);
	const ValueList &valListArg = args.GetArgs();
	ValueList::const_iterator pValue = valListArg.begin();
	DeclarationList::const_iterator ppDecl = _declOwner.begin();
	for ( ; pValue != valListArg.end() && ppDecl != _declOwner.end();
														pValue++, ppDecl++) {
		pEnvLocal->AssignValue((*ppDecl)->GetSymbol(), *pValue, false);
	}
	if (_declOwner.GetSymbolDict() != NULL) {
		Value valueWithDict = args.GetValueWithDict();
		if (valueWithDict.IsInvalid()) {
			valueWithDict.InitAsDict(env);
		}
		pEnvLocal->AssignValue(_declOwner.GetSymbolDict(), valueWithDict, false);
	}
	if (_blockInfo.pSymbol == NULL) return pEnvLocal;
	const Expr_Block *pExprBlock = args.GetBlock(env, sig);
	if (sig.IsSignalled()) {
		delete pEnvLocal;
		return NULL;
	}
	if (pExprBlock == NULL) {
		// set nil value to the variable with a symbol specified by
		// _blockInfo.pSymbol
		pEnvLocal->AssignValue(_blockInfo.pSymbol, Value::Null, false);
	} else if (_blockInfo.quoteFlag) {
		Value value;
		value.InitAsExpr(env, pExprBlock->IncRef());
		pEnvLocal->AssignValue(_blockInfo.pSymbol, value, false);
	} else {
		Environment *pEnv = _blockInfo.insideScopeFlag? pEnvLocal : &env;
		FunctionCustom *pFuncBlock = FunctionCustom::CreateBlockFunc(*pEnv, sig,
							_blockInfo.pSymbol, pExprBlock, FUNCTYPE_Block);
		if (pFuncBlock == NULL) {
			delete pEnvLocal;
			return NULL;
		}
		pEnvLocal->AssignFunction(pFuncBlock);
	}
	return pEnvLocal;
}

bool Function::IsSucceedable(const ICallable *pCallable) const
{
	return false;
}

Value Function::ReturnIterator(Environment &env, Signal sig,
								Args &args, Iterator *pIterator) const
{
	if (pIterator == NULL) return Value::Null;
	if (sig.IsSignalled()) {
		Iterator::Delete(pIterator);
		return Value::Null;
	}
	Value result;
	if (args.IsBlockSpecified()) {
		Environment envBlock(&env, ENVTYPE_Block);
		const Function *pFuncBlock =
						args.GetBlockFunc(envBlock, sig, GetSymbolForBlock());
		if (pFuncBlock == NULL) return Value::Null;
		// :iter and :xiter must be effective here
		result = pIterator->Eval(envBlock, sig, args, pFuncBlock);
		Iterator::Delete(pIterator);
		if (sig.IsSignalled()) return Value::Null;
	} else if (args.IsRsltList() || args.IsRsltXList() ||
								args.IsRsltSet() || args.IsRsltXSet()) {
		result = pIterator->Eval(env, sig, args);
		Iterator::Delete(pIterator);
		if (sig.IsSignalled()) return Value::Null;
	} else {
		pIterator->SetSkipInvalidFlag(args.IsRsltXIterator());
		result = Value(env, pIterator);
	}
	return result;
}

Value Function::ReturnValue(Environment &env, Signal sig,
				Args &args, const Value &result, bool blockResultFlag) const
{
	if (sig.IsSignalled()) return Value::Null;
	if (args.IsBlockSpecified()) {
		Environment envBlock(&env, ENVTYPE_Block);
		const Function *pFuncBlock =
						args.GetBlockFunc(envBlock, sig, GetSymbolForBlock());
		if (pFuncBlock == NULL) return Value::Null;
		ValueList valListArg(result);
		Args argsSub(valListArg);
		Value value = pFuncBlock->Eval(env, sig, argsSub);
		if (blockResultFlag) return value;
	}
	return result;
}

Value Function::DoRepeater(Environment &env, Signal sig,
								Args &args, Iterator *pIterator) const
{
	if (sig.IsSignalled()) {
		Iterator::Delete(pIterator);
		return Value::Null;
	}
	Value result;
	if (args.IsRsltIterator() || args.IsRsltXIterator()) {
		result = Value(env, pIterator);
	} else if (args.IsRsltList() || args.IsRsltXList() ||
									args.IsRsltSet() || args.IsRsltXSet()) {
		result = pIterator->Eval(env, sig, args);
		Iterator::Delete(pIterator);
		if (sig.IsSignalled()) return Value::Null;
	} else {
		while (pIterator->Next(env, sig, result)) ;
		Iterator::Delete(pIterator);
		if (sig.IsSignalled()) return Value::Null;
	}
	return result;
}

Expr *Function::DiffUnary(Environment &env, Signal sig,
							const Expr *pExprArg, const Symbol *pSymbol) const
{
	SetError_MathDiffError(sig);
	return NULL;
}

Expr *Function::DiffBinary(Environment &env, Signal sig,
		const Expr *pExprArg1, const Expr *pExprArg2, const Symbol *pSymbol) const
{
	SetError_MathDiffError(sig);
	return NULL;
}

Expr *Function::OptimizeUnary(Environment &env, Signal sig, Expr *pExprOpt) const
{
	SetError_MathOptimizeError(sig);
	return NULL;
}

Expr *Function::OptimizeBinary(Environment &env, Signal sig,
										Expr *pExprOpt1, Expr *pExprOpt2) const
{
	SetError_MathOptimizeError(sig);
	return NULL;
}

String Function::ToString() const
{
	String str(GetName());
	if (GetSymbol()->IsFlowControlSymbol()) {
		str += " ";
	}
	str += "(";
	str += _declOwner.ToString();
	str += ")";
	if (_funcType == FUNCTYPE_Class) {
		str += ":";
		str += AScript_Symbol(static_)->GetName();
	} else if (_funcType == FUNCTYPE_Block) {
		str += ":";
		str += AScript_Symbol(block)->GetName();
	}
	if (GetMapFlag()) {
		str += ":";
		str += AScript_Symbol(map)->GetName();
	}
	if (GetFlatFlag()) {
		str += ":";
		str += AScript_Symbol(flat)->GetName();
	}
	if (GetDynamicScopeFlag()) {
		str += ":";
		str += AScript_Symbol(dynamic_scope)->GetName();
	}
	if (GetSymbolFuncFlag()) {
		str += ":";
		str += AScript_Symbol(symbol_func)->GetName();
	}
	if (_resultMode == RSLTMODE_List) {
		str += ":";
		str += AScript_Symbol(list)->GetName();
	} else if (_resultMode == RSLTMODE_XList) {
		str += ":";
		str += AScript_Symbol(xlist)->GetName();
	} else if (_resultMode == RSLTMODE_Set) {
		str += ":";
		str += AScript_Symbol(set)->GetName();
	} else if (_resultMode == RSLTMODE_XSet) {
		str += ":";
		str += AScript_Symbol(xset)->GetName();
	} else if (_resultMode == RSLTMODE_Void) {
		str += ":";
		str += AScript_Symbol(void_)->GetName();
	} else if (_resultMode == RSLTMODE_Reduce) {
		str += ":";
		str += AScript_Symbol(reduce)->GetName();
	} else if (_resultMode == RSLTMODE_XReduce) {
		str += ":";
		str += AScript_Symbol(xreduce)->GetName();
	} else if (_resultMode == RSLTMODE_Iterator) {
		str += ":";
		str += AScript_Symbol(iter)->GetName();
	} else if (_resultMode == RSLTMODE_XIterator) {
		str += ":";
		str += AScript_Symbol(xiter)->GetName();
	}
	if (!_attrsOpt.empty()) {
		str += ":[";
		foreach_const (SymbolSet, ppSymbol, _attrsOpt) {
			const Symbol *pSymbol = *ppSymbol;
			if (ppSymbol != _attrsOpt.begin()) str += ",";
			str += pSymbol->GetName();
		}
		str += "]";
	}
	if (_blockInfo.pSymbol != NULL) {
		str += " {";
		if (_blockInfo.quoteFlag) str += "`";
		str += _blockInfo.pSymbol->GetName();
		str += GetOccurPatternSymbol(_blockInfo.occurPattern)->GetName();
		if (_blockInfo.insideScopeFlag) str += ":inside_scope";
		str += "}";
	}
	return str;
}

void Function::SetError_UnsupportedAttr(Signal sig, const SymbolSet &attrs) const
{
	String str;
	str += "function '";
	str += GetName();
	str += "' doesn't support attribute ";
	foreach_const (SymbolSet, ppSymbol, attrs) {
		if (ppSymbol != attrs.begin()) str += ", ";
		str += "'";
		str += (*ppSymbol)->GetName();
		str += "'";
	}
	sig.SetError(ERR_AttributeError, "%s", str.c_str());
}

void Function::SetError_DivideByZero(Signal sig) const
{
	sig.SetError(ERR_ZeroDivisionError, "divide by zero");
}

void Function::SetError_NotConstructor(Signal sig) const
{
	sig.SetError(ERR_ValueError, "'%s' is not a constructor", GetName());
}

void Function::SetError_ArgumentTypeByIndex(Signal sig,
						size_t idxArg, const Value &value) const
{
	if (idxArg < _declOwner.size()) {
		const Declaration *pDecl = _declOwner[idxArg];
		pDecl->SetError_ArgumentType(sig, value);
	} else {
		sig.SetError(ERR_TypeError, "argument error");
	}
}

void Function::SetError_InvalidValue(Signal sig, const Value &value) const
{
	sig.SetError(ERR_TypeError, "can't evaluate %s(%s)",
				GetName(), value.ToString(sig).c_str());
}

void Function::SetError_InvalidValue(Signal sig, const Value &value1, const Value &value2) const
{
	sig.SetError(ERR_TypeError, "can't evaluate %s(%s, %s)",
				GetName(), value1.ToString(sig).c_str(), value2.GetTypeName());
}

void Function::SetError_InvalidValType(Signal sig, const Value &value) const
{
	sig.SetError(ERR_TypeError, "can't evaluate %s(%s)",
				GetName(), value.GetTypeName());
}

void Function::SetError_InvalidValType(Signal sig, const Value &value1, const Value &value2) const
{
	sig.SetError(ERR_TypeError, "can't evaluate %s(%s, %s)",
				GetName(), value1.GetTypeName(), value2.GetTypeName());
}

void Function::SetError_InvalidValTypeM(Signal sig, const Value &value1, const Value &value2) const
{
	sig.SetError(ERR_TypeError, "can't evaluate (%s %s %s)",
				value1.GetTypeName(), GetMathSymbol(), value2.GetTypeName());
}

void Function::SetError_InvalidFunctionExpression(Signal sig) const
{
	sig.SetError(ERR_SyntaxError, "invalid function expression");
}

void Function::SetError_MathDiffError(Signal sig) const
{
	sig.SetError(ERR_ValueError, "failed to generate a differential function");
}

void Function::SetError_MathOptimizeError(Signal sig) const
{
	sig.SetError(ERR_ValueError, "mathematical optimization is not supported");
}

//-----------------------------------------------------------------------------
// Function::ResultComposer
// this function's behaviour is affected by the following attributes.
//   :void, :reduce, :xreduce, :list, :xlist, :set, :xet, :flat
//-----------------------------------------------------------------------------
Function::ResultComposer::ResultComposer(Environment &env, Args &args, Value &result) :
	_env(env), _args(args), _result(result), _pValList(NULL), _cnt(0),
	_excludeNilFlag(args.IsRsltXList() || args.IsRsltXSet()),
	_setFlag(args.IsRsltSet() || args.IsRsltXSet())
{
	if (_args.IsRsltList() || _args.IsRsltXList() ||
							_args.IsRsltSet() || _args.IsRsltXSet()) {
		_pValList = &_result.InitAsList(_env);
	}
}

void Function::ResultComposer::Store(const Value &value)
{
	if (_args.IsRsltVoid()) {
		// nothing to do
	} else if (_args.IsRsltReduce()) {
		_result = value;
	} else if (_args.IsRsltXReduce()) {
		if (value.IsValid()) _result = value;
	} else if (_args.IsRsltFlat() && value.IsList()) {
		const ValueList &valList = value.GetList();
		foreach_const (ValueList, pValue, value.GetList()) {
			Store(*pValue);
		}
	} else {
		if (_args.IsRsltList()) {
			_pValList->push_back(value);
		} else if (value.IsValid()) {
			if (_pValList == NULL) {
				_pValList = &_result.InitAsList(_env, _cnt, Value::Null);
			}
			if (!_setFlag || !_pValList->IsContain(value)) {
				_pValList->push_back(value);
			}
		} else if (_excludeNilFlag) {
			// nothing to do
		} else if (_pValList != NULL) {
			if (!_setFlag || !_pValList->IsContain(value)) {
				_pValList->push_back(value);
			}
		}
		_cnt++;
	}
}

//-----------------------------------------------------------------------------
// FunctionCustom
//-----------------------------------------------------------------------------
bool FunctionCustom::IsCustom() const { return true; }

FunctionCustom::FunctionCustom(Environment &envScope,
				const Symbol *pSymbol, Expr *pExprBody, FunctionType funcType) :
		Function(envScope, pSymbol, funcType), _pExprBody(pExprBody)
{
}

FunctionCustom::~FunctionCustom()
{
	Expr::Delete(_pExprBody);
}

Value FunctionCustom::DoEval(Environment &env, Signal sig, Args &args) const
{
	std::auto_ptr<Environment> pEnvLocal(PrepareEnvironment(env, sig, args));
	if (pEnvLocal.get() == NULL) return Value::Null;
	if (_funcType != FUNCTYPE_Block) {
		pEnvLocal->AssignValue(AScript_Symbol(self), args.GetSelf(), false);
	}
	EnvType envType = pEnvLocal->GetEnvType();
	Value result = GetExprBody()->Exec(*pEnvLocal, sig);
	if (envType == ENVTYPE_Block) {
		// nothing to do. simply pass the signal to the outside.
	} else if (!sig.IsSignalled()) {
		// nothing to do
	} else if (sig.IsBreak()) {
		sig.ClearSignal();
	} else if (sig.IsContinue()) {
		sig.ClearSignal();
	} else if (sig.IsReturn()) {
		result = sig.GetValue();
		sig.ClearSignal();
	}
	return result;
}

Expr *FunctionCustom::DiffUnary(Environment &env, Signal sig,
							const Expr *pExprArg, const Symbol *pSymbol) const
{
	SetError_MathDiffError(sig);
	return NULL;
}

FunctionCustom *FunctionCustom::CreateBlockFunc(Environment &env, Signal sig,
	const Symbol *pSymbol, const Expr_Block *pExprBlock, FunctionType funcType)
{
	const Expr_BlockParam *pExprBlockParam = pExprBlock->GetParam();
	FunctionCustom *pFunc = new FunctionCustom(env,
								pSymbol, pExprBlock->IncRef(), funcType);
	pFunc->_declOwner.AllowTooManyArgs(true);
	Args args(pExprBlockParam->GetExprList());
	if (pExprBlockParam != NULL &&
			!pFunc->CustomDeclare(env, sig, SymbolSet::Null, args)) {
		Function::Delete(pFunc);
		return NULL;
	}
	return pFunc;
}

//-----------------------------------------------------------------------------
// ClassPrototype
//-----------------------------------------------------------------------------
ClassPrototype::ClassPrototype(Environment &envScope, const Symbol *pSymbol,
			Expr *pExprBody, Class_Custom *pClassCustom, FunctionType funcType) :
		Function(envScope, pSymbol, funcType), _envScope(envScope),
		_pExprBody(pExprBody), _pClassCustom(pClassCustom)
{
}

ClassPrototype::~ClassPrototype()
{
	Expr::Delete(_pExprBody);
	Class::Delete(_pClassCustom);
}

Value ClassPrototype::DoEval(Environment &env, Signal sig, Args &args) const
{
	std::auto_ptr<Environment> pEnvLocal(PrepareEnvironment(env, sig, args));
	if (pEnvLocal.get() == NULL) return Value::Null;
	EnvType envType = pEnvLocal->GetEnvType();
	Value valueSelf(args.GetSelf());
	if (!valueSelf.IsObject()) {
		Object *pObj = _pClassCustom->CreateDescendant(*pEnvLocal, sig, _pClassCustom);
		valueSelf.InitAsObject(pObj);
	}
	Class *pClassSuper = _pClassCustom->GetClassSuper();
	Function *pConstructorSuper =
				(pClassSuper == NULL)? NULL : pClassSuper->GetConstructor();
	if (pConstructorSuper != NULL) {
		const Expr *pExpr = GetExprBody();
		const ExprList *pExprList = &ExprList::Null;
		if (pExpr->IsBlock()) {
			const Expr_Block *pExprBlock = dynamic_cast<const Expr_Block *>(pExpr);
			const Expr_BlockParam *pExprParam = pExprBlock->GetParam();
			if (pExprParam != NULL) {
				pExprList = &pExprParam->GetExprList();
			}
		}
		Environment envSuper(pEnvLocal.get(), ENVTYPE_Method);
		Args argsSub(*pExprList, valueSelf);
		pConstructorSuper->EvalExpr(envSuper, sig, argsSub);
		if (sig.IsSignalled()) return Value::Null;
	}
	pEnvLocal->AssignValue(AScript_Symbol(self), valueSelf, false);
	GetExprBody()->Exec(*pEnvLocal, sig);
	if (sig.IsSignalled()) return Value::Null;
	return valueSelf;
}

Class *ClassPrototype::GetClassToConstruct() const
{
	return _pClassCustom;
}

//-----------------------------------------------------------------------------
// StructPrototype
//-----------------------------------------------------------------------------
bool StructPrototype::IsStructPrototype() const { return true; }

StructPrototype::StructPrototype(Environment &env, Class_Custom *pClassCustom) :
			Function(env, AScript_Symbol(_anonymous_), FUNCTYPE_Function),
			_pClassCustom(pClassCustom)
{
}

StructPrototype::~StructPrototype()
{
	Class::Delete(_pClassCustom);
}

Value StructPrototype::DoEval(Environment &env, Signal sig, Args &args) const
{
	Object *pObjSelf = NULL;
	Value valueSelf(args.GetSelf());
	if (valueSelf.IsObject()) {
		pObjSelf = valueSelf.GetObject();
	} else {
		pObjSelf = _pClassCustom->CreateDescendant(env, sig, _pClassCustom);
		valueSelf.InitAsObject(pObjSelf);
	}
	ValueList::const_iterator pValue = args.GetArgs().begin();
	DeclarationList::const_iterator ppDecl = GetDeclOwner().begin();
	for ( ; pValue != args.GetArgs().end() && ppDecl != GetDeclOwner().end();
														pValue++, ppDecl++) {
		const Declaration *pDecl = *ppDecl;
		pObjSelf->AssignValue(pDecl->GetSymbol(), *pValue, false);
	}
	return valueSelf;
}

Class *StructPrototype::GetClassToConstruct() const
{
	return _pClassCustom;
}

}
