#include "AScript.h"
#include "Expr.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Function
//-----------------------------------------------------------------------------
bool Function::IsCustom() 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; }

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

Function::Function(const Function &func) : _cntRef(1),
	_pSymbol(func._pSymbol),
	_pSymbolDict(func._pSymbolDict), _declOwner(func._declOwner),
	_mathSymbol(func._mathSymbol), _funcType(func._funcType),
	_rsltMode(func._rsltMode), _mapFlag(func._mapFlag), _attrsOpt(func._attrsOpt),
	_dynamicScopeFlag(func._dynamicScopeFlag),
	_allowTooManyArgsFlag(func._allowTooManyArgsFlag)
{
	_blockInfo.occurPattern = func._blockInfo.occurPattern;
	_blockInfo.pSymbol = func._blockInfo.pSymbol;
	_blockInfo.insideScopeFlag = func._blockInfo.insideScopeFlag;
}

Function::Function(const Symbol *pSymbol, FunctionType funcType) : _cntRef(1),
	_pSymbol(pSymbol), _pSymbolDict(NULL), _funcType(funcType),
	_rsltMode(RSLTMODE_Normal), _mapFlag(MAP_Off),
	_dynamicScopeFlag(false), _allowTooManyArgsFlag(false)
{
	_blockInfo.occurPattern = OCCUR_Zero;
	_blockInfo.pSymbol = NULL;
	_blockInfo.insideScopeFlag = false;
}

Function::Function(const TCHAR *name, FunctionType funcType) : _cntRef(1),
	_pSymbol(Symbol::Add(name)), _pSymbolDict(NULL), _funcType(funcType),
	_rsltMode(RSLTMODE_Normal), _mapFlag(MAP_Off),
	_dynamicScopeFlag(false), _allowTooManyArgsFlag(false)
{
	_blockInfo.occurPattern = OCCUR_Zero;
	_blockInfo.pSymbol = NULL;
	_blockInfo.insideScopeFlag = false;
}

Function::~Function()
{
}

Expr *Function::Diff(Environment &env, Signal sig,
						const ExprList &exprArgs, const Symbol *pSymbolVar) const
{
	SetError_DiffUnavailable(sig);
	return NULL;
}

Expr *Function::Optimize(Environment &env, Signal sig, ExprList &exprArgs) const
{
	sig.SetError(ERR_ArithmeticError, _T("optimization is not available"));
	return NULL;
}

bool Function::CustomDeclare(Environment &env, Signal sig,
					const SymbolSet &attrsAcceptable, ContextExpr &contextExpr)
{
	// delcaration of arguments
	foreach_const (ExprList, ppExpr, contextExpr.GetArgs()) {
		const Expr *pExpr = *ppExpr;
		if (pExpr->IsPrefix()) {
			const Expr_Prefix *pExprPrefix =
									dynamic_cast<const Expr_Prefix *>(pExpr);
			const Symbol *pSymbol = pExprPrefix->GetSymbol();
			if (pSymbol->IsIdentical(AScript_Symbol(Char_Modulo))) {
				const Expr *pExprChild = pExprPrefix->GetExprChild();
				if (!pExprChild->IsSymbol()) {
					SetError_InvalidFunctionExpression(sig);
					return false;
				}
				_pSymbolDict =
					dynamic_cast<const Expr_Symbol *>(pExprChild)->GetSymbol();
			} else {
				SetError_InvalidFunctionExpression(sig);
				return false;
			}
		} else {
			Declaration *pDecl = Declaration::Create(env, sig, pExpr);
			if (pDecl == NULL) return false;
			if (_declOwner.IsVariableLength()) {
				sig.SetError(ERR_TypeError,
					_T("any parameters cannot follow after a parameter with variable length"));
				Declaration::Delete(pDecl);
				return false;
			}
			if (pDecl->IsMandatory() &&
						!_declOwner.empty() && _declOwner.back()->IsOptional()) {
				sig.SetError(ERR_TypeError,
					_T("mandatory parameters cannot follow after a parameter with variable length"));
				Declaration::Delete(pDecl);
				return false;
			}
			_declOwner.push_back(pDecl);
		}
	}
	// declaration of attributes
	foreach_const (SymbolSet, ppSymbol, contextExpr.GetAttrs()) {
		const Symbol *pSymbol = *ppSymbol;
		if (pSymbol->IsIdentical(AScript_Symbol(map))) {
			_mapFlag = MAP_On;
		} else if (pSymbol->IsIdentical(AScript_Symbol(nomap))) {
			_mapFlag = MAP_Off;
		} else if (pSymbol->IsIdentical(AScript_Symbol(list))) {
			_rsltMode = RSLTMODE_List;
		} else if (pSymbol->IsIdentical(AScript_Symbol(xlist))) {
			_rsltMode = RSLTMODE_XList;
		} else if (pSymbol->IsIdentical(AScript_Symbol(set))) {
			_rsltMode = RSLTMODE_Set;
		} else if (pSymbol->IsIdentical(AScript_Symbol(xset))) {
			_rsltMode = RSLTMODE_XSet;
		} else if (pSymbol->IsIdentical(AScript_Symbol(void_))) {
			_rsltMode = RSLTMODE_Void;
		} else if (pSymbol->IsIdentical(AScript_Symbol(static_))) {
			_funcType = FUNCTYPE_Class;
		} else if (pSymbol->IsIdentical(AScript_Symbol(dynamic_scope))) {
			_dynamicScopeFlag = true;
		} else if (attrsAcceptable.IsSet(pSymbol)) {
			// nothing to do
		} else {
			sig.SetError(ERR_TypeError,
				_T("unsupported attribute '%s' for function declaration"),
															pSymbol->GetName());
			return false;
		}
	}
	_attrsOpt = contextExpr.GetAttrsOpt();
	// declaration of a block
	if (contextExpr.IsBlockSpecified()) {
		const ExprList &exprList = contextExpr.GetBlock()->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->GetExprChild();
			occurPattern = pExprSuffix->GetOccurPattern();
			if (occurPattern == OCCUR_Invalid) {
				SetError_InvalidFunctionExpression(sig);
				return false;
			}
		}
		if (!pExpr->IsSymbol()) {
			sig.SetError(ERR_TypeError, _T("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());
		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, 
					_T("unsupported attribute '%s' for block declaration"),
															pSymbol->GetName());
				return false;
			}
		}
	}
	return true;
}

bool Function::InitAsBlockFunc(Environment &env, Signal sig,
												const Expr_Block *pExprBlock)
{
	AllowTooManyArgs(true);
	const Expr_BlockParam *pExprBlockParam = pExprBlock->GetParam();
	if (pExprBlockParam == NULL) return true;
	if (!CustomDeclare(env, sig, SymbolSet::Null,
							ContextExpr(pExprBlockParam->GetExprList()))) {
		return false;
	}
	return true;
}

Declaration *Function::DeclareArg(Environment &env, const TCHAR *name,
	ValueType valType, bool listFlag, OccurPattern occurPattern, Expr *pExprDefault)
{
	const Class *pClass = env.LookupClass(valType);
	Declaration *pDecl = new Declaration(Symbol::Add(name),
					valType, pClass, listFlag, occurPattern, pExprDefault);
	ASSUME(env, !_declOwner.IsVariableLength());
	ASSUME(env, !(pDecl->IsMandatory() &&
				!_declOwner.empty() && _declOwner.back()->IsOptional()));
	_declOwner.push_back(pDecl);
	return pDecl;
}

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

void Function::AssignArgs(Environment &env, Signal sig, const ValueList &valList) const
{
	ValueList::const_iterator pValue = valList.begin();
	DeclarationList::const_iterator ppDecl = _declOwner.begin();
	for ( ; pValue != valList.end() && ppDecl != _declOwner.end();
														pValue++, ppDecl++) {
		const Declaration *pDecl = *ppDecl;
		env.AssignValue(pDecl->GetSymbol(), *pValue, false);
	}
}

Value Function::EvalExpr(Environment &env, Signal sig, ContextExpr &contextExpr) const
{
	if (GetFuncType() == FUNCTYPE_Instance && contextExpr.GetSelfObj() == NULL) {
		sig.SetError(ERR_ValueError,
			_T("object is expected as l-value of field"));
		return Value::Null;
	} else if (GetFuncType() == FUNCTYPE_Class && contextExpr.GetSelfClass() == NULL) {
		sig.SetError(ERR_ValueError,
			_T("class is expected as l-value of field"));
		return Value::Null;
	} else if (_blockInfo.occurPattern == OCCUR_Once &&
											!contextExpr.IsBlockSpecified()) {
		sig.SetError(ERR_ValueError,
			_T("block must be specified for '%s'"), ToString(sig).c_str());
		return Value::Null;
	} else if (_blockInfo.occurPattern == OCCUR_Zero &&
											contextExpr.IsBlockSpecified()) {
		sig.SetError(ERR_ValueError,
			_T("block is unnecessary for '%s'"), ToString(sig).c_str());
		return Value::Null;
	}
	MapFlag mapFlag = _mapFlag;
	ResultMode rsltMode = _rsltMode;
	foreach_const (SymbolSet, ppSymbol, contextExpr.GetAttrs()) {
		const Symbol *pSymbol = *ppSymbol;
		if (pSymbol->IsIdentical(AScript_Symbol(map))) {
			mapFlag = MAP_On;
		} else if (pSymbol->IsIdentical(AScript_Symbol(nomap))) {
			mapFlag = MAP_Off;
		} else if (pSymbol->IsIdentical(AScript_Symbol(void_))) {
			rsltMode = RSLTMODE_Void;
		} else if (pSymbol->IsIdentical(AScript_Symbol(list))) {
			rsltMode = RSLTMODE_List;
		} else if (pSymbol->IsIdentical(AScript_Symbol(xlist))) {
			rsltMode = RSLTMODE_XList;
		} else if (pSymbol->IsIdentical(AScript_Symbol(set))) {
			rsltMode = RSLTMODE_Set;
		} else if (pSymbol->IsIdentical(AScript_Symbol(xset))) {
			rsltMode = RSLTMODE_XSet;
		} else if (_attrsOpt.IsSet(pSymbol)) {
			// nothing to do
		} else {
			sig.SetError(ERR_AttributeError,
				_T("unsupported attribute '%s' for '%s'"),
										pSymbol->GetName(), ToString(sig).c_str());
			return Value::Null;
		}
	}
	const ExprList &exprArgs = contextExpr.GetArgs();
	ValueList valListArg;
	Value valueWithDict;
	valueWithDict.InitAsDict(env);
	if (mapFlag == MAP_On && IsUnary() && exprArgs.size() > 1) {
		if (!PrepareArgsForUnary(env, sig, exprArgs, valListArg, valueWithDict)) {
			return Value::Null;
		}
	} else if (!PrepareArgs(env, sig, exprArgs, valListArg, valueWithDict)) {
		return Value::Null;
	}
	if (IsModulo()) {
		const Value &valueLeft = valListArg[0];
		const Value &valueRight = valListArg[1];
		if (valueLeft.IsFunction()) {
			const Function *pFunc = valueLeft.GetFunction();
			Value result;
			if (!valueRight.IsList()) {
				result = pFunc->Eval(env, sig, Context(ValueList(valueRight)));
			} else if (pFunc->GetMapFlag() == MAP_Off ||
								pFunc->IsApplicable(valueRight.GetList())) {
				result = pFunc->Eval(env, sig, Context(valueRight.GetList()));
			} else if (pFunc->IsUnary()) {
				result = pFunc->EvalMap(env, sig, Context(ValueList(valueRight)));
			} else {
				result = pFunc->EvalMap(env, sig, Context(valueRight.GetList()));
			}
			return result;
		} else if (valueLeft.IsString()) {
			const TCHAR *format = valueLeft.GetString();
			return valueRight.IsList()?
				Value(env, Formatter::Format(sig, format, valueRight.GetList()).c_str()) :
				Value(env, Formatter::Format(sig, format, ValueList(valueRight)).c_str());
		}
	} else if (IsMultiply()) {
		const Value &valueLeft = valListArg[0];
		const Value &valueRight = valListArg[1];
		if (valueLeft.IsFunction() &&
					!valueLeft.GetFunction()->IsUnary() && valueRight.IsList()) {
			return valueLeft.GetFunction()->EvalMulti(env, sig, valueRight.GetList());
		}
	}
	Context context(contextExpr, rsltMode, valListArg, valueWithDict);
	if (mapFlag == MAP_Off || IsApplicable(valListArg)) {
		return Eval(env, sig, context);
	}
	return EvalMap(env, sig, context);
}

Value Function::Eval(Environment &env, Signal sig, Context &context) const
{
	ValueList valListCasted;
	if (!ValidateArgs(env, sig, context.GetArgs(), valListCasted)) return Value::Null;
	Context contextCasted(context, valListCasted, context.GetValueWithDict());
	Value value = DoEval(env, sig, contextCasted);
	if (context.IsResVoid()) return Value::Null;
	return value;
}

Value Function::EvalMulti(Environment &env, Signal sig, const ValueList &valListArg) const
{
	const Function *pFuncSuccRequester = NULL;
	if (valListArg.IsFlat()) {
		return Eval(env, sig, Context(SymbolSet::Null, SymbolSet::Null,
						NULL, &pFuncSuccRequester, Value::Null, valListArg));
	}
	Value result;
	ValueList &valList = result.InitAsList(env);
	foreach_const (ValueList, pValueArg, valListArg) {
		if (pValueArg->IsList()) {
			Value value = EvalMulti(env, sig, pValueArg->GetList());
			if (sig.IsSignalled()) return Value::Null;
			valList.push_back(value);
		} else {
			sig.SetError(ERR_TypeError, _T("invalid structure of arguments"));
			return Value::Null;
		}
	}
	return result;
}

Value Function::EvalMap(Environment &env, Signal sig, Context &context) const
{
	typedef std::vector<ValueList::const_iterator> IteratorList;
	const DeclarationList &declList = GetDeclList();
	IteratorList pValueList, pValueEndList;
	do {
		DeclarationList::const_iterator ppDecl = declList.begin();
		foreach_const (ValueList, pValue, context.GetArgs()) {
			const Declaration *pDecl = *ppDecl;
			if (pDecl->IsApplicable(*pValue)) {
				// nothing to do
			} else {
				ASSUME(env, pValue->IsList());
				const ValueList &valListElem = pValue->GetList();
				if (!valListElem.empty()) {
					// nothing to do
				} else if (context.IsResVoid()) {
					return Value::Null;
				} else {
					Value result;
					result.InitAsList(env); // create an empty list
					return result;
				}
				pValueList.push_back(valListElem.begin());
				pValueEndList.push_back(valListElem.end());
			}
			if (!pDecl->IsVariableLength()) ppDecl++;
		}
	} while (0);
	Value result;
	ResultListComposer resultListComposer(env, context, result);
	for (;;) {
		ValueList valListArg;
		IteratorList::iterator ppValue = pValueList.begin();
		IteratorList::iterator ppValueEnd = pValueEndList.begin();
		DeclarationList::const_iterator ppDecl = declList.begin();
		foreach_const (ValueList, pValue, context.GetArgs()) {
			const Declaration *pDecl = *ppDecl;
			if (pDecl->IsApplicable(*pValue)) {
				valListArg.push_back(*pValue);
			} else if (*ppValue == *ppValueEnd) {
				goto done;
			} else {
				valListArg.push_back(**ppValue);
				ppValue++, ppValueEnd++;
			}
			if (!pDecl->IsVariableLength()) ppDecl++;
		}
		Value valueEach = IsApplicable(valListArg)?
			Eval(env, sig, Context(context, valListArg, context.GetValueWithDict())) :
			EvalMap(env, sig, Context(context, valListArg, context.GetValueWithDict()));
		if (sig.IsSignalled()) return Value::Null;
		resultListComposer.Store(valueEach);
		foreach (IteratorList, ppValue, pValueList) {
			(*ppValue)++;
		}
	}
done:
	return result;
}

String Function::ToString(Signal sig) const
{
	String str(GetName());
	if (GetSymbol()->IsFlowControlSymbol()) {
		str += _T(" ");
	}
	str += _T("(");
	str += _declOwner.ToString(sig);
	if (_pSymbolDict != NULL) {
		if (!_declOwner.empty()) str += _T(", ");
		str += AScript_Symbol(Char_Modulo)->GetName();
		str += _pSymbolDict->GetName();
	}
	str += _T(")");
	if (_funcType == FUNCTYPE_Class) {
		str += _T(":");
		str += AScript_Symbol(static_)->GetName();
	} else if (_funcType == FUNCTYPE_Block) {
		str += _T(":");
		str += AScript_Symbol(block)->GetName();
	}
	if (_mapFlag == MAP_On) {
		str += _T(":");
		str += AScript_Symbol(map)->GetName();
	}
	if (_dynamicScopeFlag) {
		str += _T(":");
		str += AScript_Symbol(dynamic_scope)->GetName();
	}
	if (_rsltMode == RSLTMODE_List) {
		str += _T(":");
		str += AScript_Symbol(list)->GetName();
	} else if (_rsltMode == RSLTMODE_XList) {
		str += _T(":");
		str += AScript_Symbol(xlist)->GetName();
	} else if (_rsltMode == RSLTMODE_Set) {
		str += _T(":");
		str += AScript_Symbol(set)->GetName();
	} else if (_rsltMode == RSLTMODE_XSet) {
		str += _T(":");
		str += AScript_Symbol(xset)->GetName();
	} else if (_rsltMode == RSLTMODE_Void) {
		str += _T(":");
		str += AScript_Symbol(void_)->GetName();
	}
	if (!_attrsOpt.empty()) {
		str += _T(":[");
		foreach_const (SymbolSet, ppSymbol, _attrsOpt) {
			const Symbol *pSymbol = *ppSymbol;
			if (ppSymbol != _attrsOpt.begin()) str += _T(",");
			str += pSymbol->GetName();
		}
		str += _T("]");
	}
	if (_blockInfo.occurPattern != OCCUR_Zero) {
		str += _T(" {");
		str += _blockInfo.pSymbol->GetName();
		str += GetOccurPatternSymbol(_blockInfo.occurPattern)->GetName();
		if (_blockInfo.insideScopeFlag) str += _T(":inside_scope");
		str += _T("}");
	}
	return str;
}

bool Function::PrepareArgsForUnary(Environment &env, Signal sig, const ExprList &exprArgs,
					ValueList &valListArg, Value &valueWithDict) const
{
	Value value;
	ValueList &valList = value.InitAsList(env);
	ValueDict &valDict = valueWithDict.GetDict();
	const Declaration *pDecl = _declOwner.front();
	foreach_const (ExprList, ppExprArg, exprArgs) {
		const Expr *pExprArg = *ppExprArg;
		if (pExprArg->IsDictAssign()) {
			if (_pSymbolDict == NULL) {
				sig.SetError(ERR_ValueError, _T("dictionary assignment is not efficient"));
				return false;
			}
			const Expr_DictAssign *pExprDictAssign =
						dynamic_cast<const Expr_DictAssign *>(pExprArg);
			Value valueKey = pExprDictAssign->GetKey(sig);
			if (sig.IsSignalled()) return false;
			Value value = pExprDictAssign->GetExprRight()->Exec(env, sig);
			if (sig.IsSignalled()) return false;
			valDict[valueKey] = value;
		} else if (pDecl->IsQuote()) {
			Value valueElem;
			valueElem.InitAsExpr(env, pExprArg->IncRef());
			valList.push_back(valueElem);
		} else {
			Value valueElem = pExprArg->Exec(env, sig);
			if (sig.IsSignalled()) return false;
			valList.push_back(valueElem);
		}
	}
	valListArg.push_back(value);
	return true;
}

bool Function::PrepareArgs(Environment &env, Signal sig, const ExprList &exprArgs,
					ValueList &valListArg, Value &valueWithDict) const
{
	bool variableLengthFlag = false;
	ExprMap exprMap;
	ValueDict &valDict = valueWithDict.GetDict();
	foreach_const (ExprList, ppExprArg, exprArgs) {
		const Expr *pExprArg = *ppExprArg;
		if (!pExprArg->IsDictAssign()) continue;
		const Expr_DictAssign *pExprDictAssign =
						dynamic_cast<const Expr_DictAssign *>(pExprArg);
		Value valueKey = pExprDictAssign->GetKey(sig);
		if (sig.IsSignalled()) return false;
		if (valueKey.IsSymbol()) {
			exprMap[valueKey.GetSymbol()] = pExprDictAssign->GetExprRight();
		} else {
			Value value = pExprDictAssign->GetExprRight()->Exec(env, sig);
			if (sig.IsSignalled()) return false;
			valDict[valueKey] = value;
		}
	}
	DeclarationList::const_iterator ppDecl = _declOwner.begin();
	foreach_const (ExprList, ppExprArg, exprArgs) {
		const Expr *pExprArg = *ppExprArg;
		if (pExprArg->IsDictAssign()) continue;
		if (ppDecl != _declOwner.end()) {
			const Declaration *pDecl = *ppDecl;
			if (exprMap.find(pDecl->GetSymbol()) != exprMap.end()) {
				sig.SetError(ERR_ValueError, _T("argument confliction"));
				return false;
			}
			Value value;
			if (pDecl->IsQuote()) {
				value.InitAsExpr(env, pExprArg->IncRef());
			} else {
				value = pExprArg->Exec(env, sig);
				if (sig.IsSignalled()) return false;
			}
			valListArg.push_back(value);
			if (pDecl->IsVariableLength()) {
				variableLengthFlag = true;
			} else {
				ppDecl++;
			}
		} else if (_allowTooManyArgsFlag) {
			break;
		} else {
			SetError_TooManyArguments(sig);
			return false;
		}
	}
	for ( ; ppDecl != _declOwner.end(); ppDecl++) {
		const Declaration *pDecl = *ppDecl;
		ExprMap::iterator iter = exprMap.find(pDecl->GetSymbol());
		if (iter == exprMap.end()) break;
		exprMap.erase(iter);
		const Expr *pExprArg = iter->second;
		Value value;
		if (pDecl->IsQuote()) {
			value.InitAsExpr(env, pExprArg->IncRef());
		} else {
			value = pExprArg->Exec(env, sig);
			if (sig.IsSignalled()) return false;
		}
		valListArg.push_back(value);
	}
	if (_pSymbolDict == NULL) {
		if (!exprMap.empty()) {
			SetError_InvalidArgumentName(sig, exprMap);
			return false;
		}
	} else {
		foreach (ExprMap, iter, exprMap) {
			const Symbol *pSymbol = iter->first;
			const Expr *pExprArg = iter->second;
			Value value = pExprArg->Exec(env, sig);
			if (sig.IsSignalled()) return false;
			valDict[Value(pSymbol)] = value;
		}
	}
	if (variableLengthFlag) return true;
	for ( ; ppDecl != _declOwner.end(); ppDecl++) {
		const Declaration *pDecl = *ppDecl;
		const Expr *pExprArg = pDecl->GetExprDefault();
		Value value;
		if (pExprArg != NULL) {
			// evaluate for a default value
			if (pDecl->IsQuote()) {
				value.InitAsExpr(env, pExprArg->IncRef());
			} else {
				value = pExprArg->Exec(env, sig);
				if (sig.IsSignalled()) return false;
			}
		} else if (pDecl->GetOccurPattern() == OCCUR_ZeroOrOnce) {
			value = Value::Null;
		} else if (pDecl->GetOccurPattern() == OCCUR_ZeroOrMore) {
			break;
		} else {
			SetError_NotEnoughArguments(sig);
			return false;
		}
		valListArg.push_back(value);
	}
	return true;
}

bool Function::ValidateArgs(Environment &env, Signal sig,
						const ValueList &valList, ValueList &valListCasted) const
{
	ValueList::const_iterator pValue = valList.begin();
	DeclarationList::const_iterator ppDecl = _declOwner.begin();
	for ( ; ppDecl != _declOwner.end(); ppDecl++) {
		const Declaration *pDecl = *ppDecl;
		OccurPattern occurPattern = pDecl->GetOccurPattern();
		if (occurPattern == OCCUR_ZeroOrMore || occurPattern == OCCUR_OnceOrMore) {
			Value value;
			ValueList &valListElem = value.InitAsList(env);
			valListCasted.push_back(value);
			for ( ; pValue != valList.end(); pValue++) {
				Value value = *pValue;
				if (!ValidateArg(env, sig, pDecl, value, false)) return false;
				valListElem.push_back(value);
			}
			if (occurPattern == OCCUR_OnceOrMore && valListElem.empty()) {
				SetError_NotEnoughArguments(sig);
				return false;
			}
			return true;
		}
		if (pValue == valList.end()) {
			if (occurPattern == OCCUR_ZeroOrOnce) {
				valListCasted.push_back(Value::Null);
			} else {
				SetError_NotEnoughArguments(sig);
				return false;
			}
		} else {
			Value value = *pValue;
			if (!ValidateArg(env, sig, pDecl, value, false)) return false;
			valListCasted.push_back(value);
			pValue++;
		}
	}
	if (pValue == valList.end()) {
		// nothing to do
	} else if (_allowTooManyArgsFlag) {
		// nothing to do
	} else {
		SetError_TooManyArguments(sig);
		return false;
	}
	return true;
}

// value will be casted only when that is valid for declaration.
// it will not be casted if validation fails.
bool Function::ValidateArg(Environment &env, Signal sig, const Declaration *pDecl,
									Value &value, bool listElemFlag) const
{
	if (!listElemFlag && pDecl->GetListFlag()) {
		if (value.IsList()) {
			foreach (ValueList, pValue, value.GetList()) {
				if (!ValidateArg(env, sig, pDecl, *pValue, true)) {
					SetError_ArgumentType(sig, pDecl, *pValue);
					return false;
				}
			}
			return true;
		}
		if (pDecl->IsOptional() && value.IsInvalid()) {
			return true;
		} else {
			SetError_ArgumentMustBeList(sig, pDecl, value);
			return false;
		}
	}
	if (pDecl->GetValueType() == VTYPE_AnyType ||
			pDecl->GetValueType() == value.GetType() ||
			pDecl->IsOptional() && value.IsInvalid()) {
		return true;
	}
	if (pDecl->GetValueType() == VTYPE_List) {
		if (value.IsList()) {
			return true;
		}
	} else if (pDecl->GetValueType() == VTYPE_Object) {
		// type check of different Objects must be in consideration
		if (!value.IsList() && value.IsObject()) {
			return true;
		}
	} else if (pDecl->GetValueType() == VTYPE_Number) {
		bool allowPartFlag = false;
		bool successFlag;
		Number num = value.ToNumber(allowPartFlag, successFlag);
		if (successFlag) {
			value = Value(num);
			return true;
		} else {
			sig.SetError(ERR_ValueError, _T("failed to convert to a number"));
			return false;
		}
	} else if (pDecl->GetValueType() == VTYPE_String) {
		value = Value(env, value.ToString(sig, false).c_str());
		return !sig.IsSignalled();
	} else if (pDecl->GetValueType() == VTYPE_Complex) {
		if (value.IsNumber()) {		// cast number to complex
			return true;
		}
	} else if (pDecl->GetValueType() == VTYPE_Expr) {
		if (value.IsSymbol()) {		// cast Symbol to Expr
			const Symbol *pSymbol = value.GetSymbol();
			value.InitAsExpr(env, new Expr_Symbol(pSymbol));
			return true;
		}
	} else if (pDecl->GetValueType() == VTYPE_Quote) {
		return true;
	} else if (pDecl->GetValueType() == VTYPE_Boolean) {
		if (value.IsList()) {
			return true;	// ?????
		} else {
			value = Value(value.GetBoolean());
			return true;
		}
	}
	SetError_ArgumentType(sig, pDecl, value);
	return false;
}

Expr *Function::DiffHelper(Environment &env, Signal sig, Expr *pExprGen,
								const Expr *pExprChild, const Symbol *pSymbolVar)
{
	if (pExprGen == NULL) return NULL;
	Expr *pExprChildGen = pExprChild->Diff(env, sig, pSymbolVar);
	if (pExprChildGen == NULL) {
		delete pExprGen;
		return NULL;
	}
	return env.GetFunc_Multiply().
				Optimize(env, sig, ExprList(pExprChildGen, pExprGen));
}

Value Function::EvalOverride(Signal sig, Context &context, bool &evaluatedFlag) const
{
	const Value &valueLeft = context.GetValue(0);
	const Value &valueRight = context.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(), context.GetArgs(), evaluatedFlag);
}

// return NULL without error if block is not specified
const Function *Function::GetBlockFunction(Environment &env, Signal sig,
												ContextBase &contextBase) const
{
	const Expr_Block *pExprBlock = contextBase.GetBlock();
	if (pExprBlock == NULL || _blockInfo.pSymbol == NULL) {
		return NULL;
	} else if (contextBase.GetFuncBlock() != NULL) {
		return contextBase.GetFuncBlock();
	}
	FunctionCustom *pFunc = new FunctionCustom(env,
				_blockInfo.pSymbol, pExprBlock->IncRef(), NULL, FUNCTYPE_Block);
	if (!pFunc->InitAsBlockFunc(env, sig, pExprBlock)) {
		Function::Delete(pFunc);
		return NULL;
	}
	contextBase.SetFuncBlock(pFunc);
	return pFunc;
}

bool Function::IsSucceedable(const Object *pObj) const
{
	return false;
}

void Function::SetError_DiffUnavailable(Signal sig) const
{
	sig.SetError(ERR_ArithmeticError, _T("can't generate diff function for %s"), GetName());
}

void Function::SetError_InvalidArgumentName(Signal sig, const ExprMap &exprMap) const
{
	String str(_T("function '"));
	str += GetName();
	str += _T("' doesn't have arguments named ");
	foreach_const (ExprMap, iter, exprMap) {
		if (iter != exprMap.begin()) str += _T(", ");
		str += iter->first->GetName();
	}
	sig.SetError(ERR_ValueError, _T("%s"), str.c_str());
}

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

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

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

void Function::SetError_NotEnoughArguments(Signal sig) const
{
	sig.SetError(ERR_TypeError, _T("not enough arguments for '%s'"), ToString(sig).c_str());
}

void Function::SetError_TooManyArguments(Signal sig) const
{
	sig.SetError(ERR_TypeError, _T("too many arguments for '%s'"), ToString(sig).c_str());
}

void Function::SetError_ArgumentType(Signal sig,
						const Declaration *pDecl, const Value &value) const
{
	sig.SetError(ERR_TypeError, _T("argument '%s' cannot take %s value in '%s'"),
					pDecl->GetSymbol()->GetName(),
					value.GetTypeName(), ToString(sig).c_str());
}

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

void Function::SetError_InvalidValue(Signal sig, const Value &value) const
{
	sig.SetError(ERR_TypeError, _T("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, _T("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, _T("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, _T("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, _T("can't evaluate (%s %s %s)"),
				value1.GetTypeName(), GetMathSymbol(), value2.GetTypeName());
}

void Function::SetError_ArgumentMustBeList(Signal sig,
						const Declaration *pDecl, const Value &value) const
{
	sig.SetError(ERR_TypeError, _T("argument '%s' can only take a list in '%s'"),
					pDecl->GetSymbol()->GetName(), ToString(sig).c_str());
}

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

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

FunctionCustom::FunctionCustom(const FunctionCustom &func, Environment &envScope) :
		Function(func), _envScope(envScope),
		_pExprBody(func._pExprBody->IncRef()),
		_pClassToConstruct((func._pClassToConstruct == NULL)?
			NULL : dynamic_cast<Class_Custom *>(func._pClassToConstruct->IncRef()))
{
}

FunctionCustom::FunctionCustom(Environment &envScope, const Symbol *pSymbol,
			Expr *pExprBody, Class_Custom *pClassToConstruct, FunctionType funcType) :
		Function(pSymbol, funcType), _envScope(envScope),
		_pExprBody((pExprBody == NULL)? new Expr_Block() : pExprBody),
		_pClassToConstruct(pClassToConstruct)
{
}

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

Value FunctionCustom::DoEval(Environment &env, Signal sig, Context &context) const
{
	const ValueList &valListArg = context.GetArgs();
	const Expr_Block *pExprBlock = context.GetBlock();
	EnvType envType =
		(_funcType == FUNCTYPE_Class)?		ENVTYPE_Method :
		(_funcType == FUNCTYPE_Instance)?	ENVTYPE_Method :
		(_funcType == FUNCTYPE_Block)?		ENVTYPE_Block :
		ENVTYPE_Local;
	Environment *pEnvOuter =
				_dynamicScopeFlag? &env : const_cast<Environment *>(&_envScope);
	Environment envLocal(pEnvOuter, envType);
	AssignArgs(envLocal, sig, valListArg);
	if (_pSymbolDict != NULL) {
		Value valueWithDict = context.GetValueWithDict();
		if (valueWithDict.IsInvalid()) {
			valueWithDict.InitAsDict(env);
		}
		envLocal.AssignValue(_pSymbolDict, valueWithDict, false);
	}
	if (_funcType != FUNCTYPE_Block) {
		envLocal.AssignValue(AScript_Symbol(self), context.GetSelf(), false);
	}
	if (sig.IsSignalled()) return Value::Null;
	if (_blockInfo.occurPattern == OCCUR_Zero) {
		// nothing to do
	} else if (pExprBlock == NULL) {
		// set nil value to the variable with a symbol specified by
		// _blockInfo.pSymbol
		envLocal.AssignValue(_blockInfo.pSymbol, Value::Null, false);
	} else {
		Environment *pEnv = _blockInfo.insideScopeFlag? &envLocal : &env;
		FunctionCustom *pFuncBlock = new FunctionCustom(*pEnv,
				_blockInfo.pSymbol, pExprBlock->IncRef(), NULL, FUNCTYPE_Block);
		if (!pFuncBlock->InitAsBlockFunc(*pEnv, sig, pExprBlock)) {
			Function::Delete(pFuncBlock);
			return Value::Null;
		}
		envLocal.AssignFunction(pFuncBlock);
	}
	if (_pClassToConstruct != NULL) {
		//Value valueSelf;
		Object *pObj = _pClassToConstruct->
						CreateDescendant(envLocal, sig, _pClassToConstruct);
		Value valueSelf(pObj, VTYPE_Object);
		envLocal.AssignValue(AScript_Symbol(self), valueSelf, false);
		GetExprBody()->Exec(envLocal, sig);
		if (sig.IsSignalled()) return Value::Null;
		return valueSelf;
	}
	Value result = GetExprBody()->Exec(envLocal, sig);
	if (envType == ENVTYPE_Block) {
		// nothing to do. simply pass the signal to the outside.
	} else if (sig.IsError()) {
		// 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;
}

void FunctionCustom::SetClassToConstruct(Class_Custom *pClassToConstruct)
{
	Class::Delete(_pClassToConstruct);
	_pClassToConstruct = pClassToConstruct;
}

Class *FunctionCustom::GetClassToConstruct() const
{
	return _pClassToConstruct;
}

Expr *FunctionCustom::Diff(Environment &env, Signal sig,
						const ExprList &exprArgs, const Symbol *pSymbolVar) const
{
	return GetExprBody()->Diff(env, sig, pSymbolVar);
}

//-----------------------------------------------------------------------------
// ConstructorBase
//-----------------------------------------------------------------------------
Class *ConstructorBase::GetClassToConstruct() const
{
	return _pClassToConstruct;
}

//-----------------------------------------------------------------------------
// ContextBase
//-----------------------------------------------------------------------------
ContextBase::ContextBase(const SymbolSet &attrs, const SymbolSet &attrsOpt,
		const Expr_Block *pExprBlock, const Function **ppFuncSuccRequester, const Value &valueSelf, ResultMode rsltMode) :
		_attrs(attrs), _attrsOpt(attrsOpt), _pExprBlock(pExprBlock), _pFuncBlock(NULL),
		_ppFuncSuccRequester(ppFuncSuccRequester), _valueSelf(valueSelf), _rsltMode(rsltMode)
{
}

ContextBase::ContextBase(ContextBase &context) :
		_attrs(context._attrs), _attrsOpt(context._attrsOpt),
		_pExprBlock(context._pExprBlock),
		_pFuncBlock((context._pFuncBlock == NULL)?
									NULL : context._pFuncBlock->IncRef()),
		_ppFuncSuccRequester(context._ppFuncSuccRequester),
		_valueSelf(context._valueSelf), _rsltMode(context._rsltMode)
{
}

ContextBase::ContextBase(ContextBase &context, ResultMode rsltMode) :
		_attrs(context._attrs), _attrsOpt(context._attrsOpt),
		_pExprBlock(context._pExprBlock),
		_pFuncBlock((context._pFuncBlock == NULL)?
									NULL : context._pFuncBlock->IncRef()),
		_ppFuncSuccRequester(context._ppFuncSuccRequester),
		_valueSelf(context._valueSelf), _rsltMode(rsltMode)
{
}

ContextBase::~ContextBase()
{
	Function::Delete(_pFuncBlock);
}

//-----------------------------------------------------------------------------
// Function::ResultListComposer
// this function's behaviour is affected by the following attributes.
//   :void, :list, :xlist, :set, :xet
//-----------------------------------------------------------------------------
Function::ResultListComposer::ResultListComposer(Environment &env,
										ContextBase &contextBase, Value &result) :
	_env(env), _contextBase(contextBase), _result(result), _pValList(NULL), _cnt(0),
	_excludeNilFlag(contextBase.IsResXList() || contextBase.IsResXSet()),
	_setFlag(contextBase.IsResSet() || contextBase.IsResXSet())
{
	if (_contextBase.IsResList() || _contextBase.IsResXList() ||
							_contextBase.IsResSet() || _contextBase.IsResXSet()) {
		_pValList = &_result.InitAsList(_env);
	}
}

void Function::ResultListComposer::Store(const Value &value)
{
	if (_contextBase.IsResVoid()) {
		// nothing to do
	} else if (_contextBase.IsResList()) {
		_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++;
}

}
