//-----------------------------------------------------------------------------
// AScript __builtins__ module
//-----------------------------------------------------------------------------
#include "Module_builtins.h"

AScript_BeginModule(__builtins__)

// declaration of symbols privately used in this module
AScript_DeclarePrivSymbol(open);
AScript_DeclarePrivSymbol(open_l);
AScript_DeclarePrivSymbol(open_r);

// num = int(num):map
class Func_int : public Function {
public:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	Func_int(Environment &env, const TCHAR *name = _T("int"));
};
Func_int::Func_int(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("value"), VTYPE_AnyType);
}

Value Func_int::DoEval(Environment &env, Signal sig, Context &context) const
{
	const Value &value = context.GetValue(0);
	Value result;
	if (value.IsNumber()) {
		result.SetNumber(static_cast<long>(value.GetNumber()));
	} else if (value.IsComplex()) {
		result.SetNumber(static_cast<long>(std::abs(value.GetComplex())));
	} else if (value.IsExprOrSymbol()) {
		result.InitAsExpr(env, new Expr_Function(this, new Expr_Value(value)));
	} else if (value.IsValid()) {
		SetError_InvalidValType(sig, value);
	}
	return result;
}

// func = class(super_class?) {block?}
class Func_class : public Function {
public:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	Func_class(Environment &env, const TCHAR *name = _T("class"));
};
Func_class::Func_class(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	DeclareArg(env, _T("super_class"), VTYPE_Function, false, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(static_));
}

Value Func_class::DoEval(Environment &env, Signal sig, Context &context) const
{
	const ValueList &valListArg = context.GetArgs();
	const Expr_Block *pExprBlock = context.GetBlock();
	Class *pClassSuper = env.GetClass_Object();
	const Value &valueSuper = context.GetValue(0);
	if (valueSuper.IsFunction()) {
		pClassSuper = valueSuper.GetFunction()->GetClassToConstruct();
		if (pClassSuper == NULL) {
			valueSuper.GetFunction()->SetError_NotConstructor(sig);
			return Value::Null;
		}
	}
	Class_Custom *pClassCustom = new Class_Custom(pClassSuper, AScript_Symbol(_anonymous_));
	//pClassCustom->AssignValue(AScript_Symbol(super), valueSuper, false); // *****
	if (pExprBlock != NULL && !pClassCustom->BuildContent(env, sig, pExprBlock)) {
		Class::Delete(pClassCustom);
		return Value::Null;
	}
	FunctionCustom *pFuncInit = dynamic_cast<FunctionCustom *>(
						pClassCustom->LookupFunction(AScript_Symbol(__init__), false));
	Value result;
	FunctionType funcType = context.IsSet(AScript_Symbol(static_))?
											FUNCTYPE_Class : FUNCTYPE_Function;
	if (pFuncInit != NULL) {
		FunctionCustom *pFunc = new FunctionCustom(*pFuncInit, env);
		pFunc->SetSymbol(AScript_Symbol(_anonymous_));
		pFunc->SetClassToConstruct(pClassCustom);
		result.InitAsFunction(env, pFunc);
	} else {
		FunctionCustom *pFunc = new FunctionCustom(env,
			AScript_Symbol(_anonymous_), NULL, pClassCustom, funcType);
		if (!pFunc->CustomDeclare(env, sig, SymbolSet::Null,
											ContextExpr(ExprList::Null))) {
			Function::Delete(pFunc);
			return Value::Null;
		}
		result.InitAsFunction(env, pFunc);
	}
	return result;
}

// func = struct(`args*) {block?}
// if :loose attribute is specified, arguments in the generated function
// will get the following modification.
// - Once attribute will be modified to ZeroOrOnce.
// - OnceOrMore attribute will be modified to ZeroOrMore
class Func_struct : public Function {
public:
	class Constructor : public ConstructorBase {
	public:
		inline Constructor(Class *pClass);
		virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	};
public:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	Func_struct(Environment &env, const TCHAR *name = _T("struct"));
};
Func_struct::Func_struct(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	DeclareArg(env, _T("args"), VTYPE_Quote, false, OCCUR_OnceOrMore);
	DeclareBlock(OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(loose));
}

Value Func_struct::DoEval(Environment &env, Signal sig, Context &context) const
{
	Class *pClassSuper = env.GetClass_Struct();
	ExprList exprArgs;
	foreach_const (ValueList, pValue, context.GetList(0)) {
		exprArgs.push_back(const_cast<Expr *>(pValue->GetExpr()));
	}
	Value result;
	Class_Custom *pClassCustom =
				new Class_Custom(pClassSuper, AScript_Symbol(_anonymous_));
	const Expr_Block *pExprBlock = context.GetBlock();
	if (pExprBlock != NULL && !pClassCustom->BuildContent(env, sig, pExprBlock)) {
		return Value::Null;
	}
	Constructor *pFunc = new Constructor(pClassCustom);
	if (!pFunc->CustomDeclare(env, sig, _attrsOpt,
				ContextExpr(context.GetAttrs(), SymbolSet::Null, NULL, NULL,
														Value::Null, exprArgs))) {
		Function::Delete(pFunc);
		return false;
	}
	//pFunc->AllowTooManyArgs(true);
	if (context.IsSet(AScript_Symbol(loose))) {
		pFunc->GetDeclList().SetAsLoose();
	}
	result.InitAsFunction(env, pFunc);
	return result;
}

Func_struct::Constructor::Constructor(Class *pClass) :
							ConstructorBase(AScript_Symbol(_anonymous_), pClass)
{
}

Value Func_struct::Constructor::DoEval(Environment &env, Signal sig, Context &context) const
{
	Object *pObj = GetClassToConstruct()->
						CreateDescendant(env, sig, GetClassToConstruct());
	ValueList::const_iterator pValue = context.GetArgs().begin();
	DeclarationList::const_iterator ppDecl = GetDeclList().begin();
	for ( ; pValue != context.GetArgs().end() && ppDecl != GetDeclList().end();
														pValue++, ppDecl++) {
		pObj->AssignValue((*ppDecl)->GetSymbol(), *pValue, false);
	}
	return Value(pObj, VTYPE_Object);
}

// val = module {block}
AScript_DeclareFunction(module)
{
	DeclareBlock(OCCUR_Once);
}

AScript_ImplementFunction(module)
{
	Value result;
	Module *pModule = new Module(&env, AScript_Symbol(_anonymous_));
	result.InitAsModule(pModule);
	context.GetBlock()->Exec(*pModule, sig);
	return result;
}

// mixin(module:Module)
AScript_DeclareFunction(mixin)
{
	DeclareArg(env, _T("module"), VTYPE_Module);
}

AScript_ImplementFunction(mixin)
{
	
	return Value::Null;
}

// val = import(`module)
AScript_DeclareFunction(import)
{
	DeclareArg(env, _T("module"), VTYPE_Quote);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(import)
{
	const Expr *pExpr = context.GetExpr(0);
	if (!pExpr->IsSymbol()) {
		sig.SetError(ERR_SyntaxError, _T("wrong format for module name"));
		return Value::Null;
	}
	const Symbol *pSymbolModule = dynamic_cast<const Expr_Symbol *>(pExpr)->GetSymbol();
	SymbolSet symbolSet;
	if (context.IsBlockSpecified()) {
		foreach_const (ExprList, ppExpr, context.GetBlock()->GetExprList()) {
			if (!(*ppExpr)->IsSymbol()) {
				sig.SetError(ERR_SyntaxError,
					_T("wrong format for an element in import list"));
				return Value::Null;
			}
			const Expr_Symbol *pExprSymbol =
							dynamic_cast<const Expr_Symbol *>(*ppExpr);
			symbolSet.insert(pExprSymbol->GetSymbol());
		}
	}
	Module *pModule = env.ImportModule(sig, pSymbolModule, symbolSet);
	if (pModule == NULL) return Value::Null;
	pModule->SetSymbol(pSymbolModule);
	Value result;
	result.InitAsModule(pModule->IncRef());
	env.AssignValue(pSymbolModule, result, false);
	return result;
}

// execfile(filename:string):map
AScript_DeclareFunction(execfile)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("filename"), VTYPE_String);
}

AScript_ImplementFunction(execfile)
{
	File file;
	file.Open(sig, context.GetString(0), _T("rt"));
	if (!sig.IsSignalled()) {
		Parser().Exec(env, sig, file, true);
	}
	return Value::Null;
}

// expr = parse(code:string):map
AScript_DeclareFunction(parse)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("code"), VTYPE_String);
}

AScript_ImplementFunction(parse)
{
	Value result;
	Expr *pExpr = Parser().Parse(env, sig, context.GetString(0));
	if (pExpr == NULL) return Value::Null;
	result.InitAsExpr(env, pExpr);
	return result;
}

// val = eval(expr:Expr):map
AScript_DeclareFunction(eval)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("expr"), VTYPE_Expr);
}

AScript_ImplementFunction(eval)
{
	return context.GetExpr(0)->Exec(env, sig);
}

// val = locals()
AScript_DeclareFunction(locals)
{
	SetMode(RSLTMODE_Normal, MAP_Off);
}

AScript_ImplementFunction(locals)
{
	Value value;
	value.InitAsEnvironment(env);
	return value;
}

// val = outers()
AScript_DeclareFunction(outers)
{
	SetMode(RSLTMODE_Normal, MAP_Off);
}

AScript_ImplementFunction(outers)
{
	Value value;
	Environment envOuter(&env, ENVTYPE_Outer);
	value.InitAsEnvironment(envOuter);
	return value;
}

// try () {block}
class Func_try : public Function {
public:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	virtual bool IsSucceedable(const Object *pObj) const;
	Func_try(Environment &env, const TCHAR *name = _T("try"));
};
Func_try::Func_try(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	DeclareBlock(OCCUR_Once);
}

Value Func_try::DoEval(Environment &env, Signal sig, Context &context) const
{
	Value result = context.GetBlock()->Exec(env, sig);
	sig.SaveError();
	sig.ClearSignal();
	context.RequestSucceeding(this);
	return result;
}

bool Func_try::IsSucceedable(const Object *pObj) const
{
	return true;
}

// except (error*:Error) {block}
class Func_except : public Function {
private:
	SymbolSet _symbolsSucceed;
public:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	virtual bool IsSucceedable(const Object *pObj) const;
	Func_except(Environment &env, const TCHAR *name = _T("except"));
};
Func_except::Func_except(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	DeclareArg(env, _T("error"), VTYPE_Error, false, OCCUR_ZeroOrMore);
	DeclareBlock(OCCUR_Once);
	_symbolsSucceed.Insert(AScript_Symbol(except_));
}

Value Func_except::DoEval(Environment &env, Signal sig, Context &context) const
{
	if (sig.GetSavedErrorType() != ERR_None) {
		bool matchFlag = context.GetList(0).empty();
		foreach_const (ValueList, pValue, context.GetList(0)) {
			if (pValue->GetErrorType() == sig.GetSavedErrorType()) {
				matchFlag = true;
				break;
			}
		}
		if (matchFlag) {
			const Function *pFuncBlock = GetBlockFunction(env, sig, context);
			if (sig.IsSignalled()) return Value::Null;
			Environment envBlock(&env, ENVTYPE_Block);
			return pFuncBlock->Eval(envBlock, sig,
						Context(ValueList(Value(env, sig.GetSavedString()))));
		}
	}
	context.RequestSucceeding(this);
	return Value::Null;
}

bool Func_except::IsSucceedable(const Object *pObj) const
{
	return true;
}

// if (`cond) {block}
class Func_if : public Function {
public:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	virtual bool IsSucceedable(const Object *pObj) const;
	Func_if(Environment &env, const TCHAR *name = _T("if"));
};
Func_if::Func_if(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	DeclareArg(env, _T("cond"), VTYPE_Quote);
	DeclareBlock(OCCUR_Once);
}

Value Func_if::DoEval(Environment &env, Signal sig, Context &context) const
{
	Value value = context.GetExpr(0)->Exec(env, sig);
	if (value.GetBoolean()) {
		return context.GetBlock()->Exec(env, sig);
	}
	context.RequestSucceeding(this);
	return Value::Null;
}

bool Func_if::IsSucceedable(const Object *pObj) const
{
	return true;
}

// elsif (`cond) {block}
class Func_elsif : public Function {
public:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	virtual bool IsSucceedable(const Object *pObj) const;
	Func_elsif(Environment &env, const TCHAR *name = _T("elsif"));
};
Func_elsif::Func_elsif(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	DeclareArg(env, _T("cond"), VTYPE_Quote);
	DeclareBlock(OCCUR_Once);
}

Value Func_elsif::DoEval(Environment &env, Signal sig, Context &context) const
{
	Value value = context.GetExpr(0)->Exec(env, sig);
	if (value.GetBoolean()) {
		return context.GetBlock()->Exec(env, sig);
	}
	context.RequestSucceeding(this);
	return Value::Null;
}

bool Func_elsif::IsSucceedable(const Object *pObj) const
{
	return true;
}

// else () {block}
class Func_else : public Function {
public:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	Func_else(Environment &env, const TCHAR *name = _T("else"));
};
Func_else::Func_else(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	DeclareBlock(OCCUR_Once);
}

Value Func_else::DoEval(Environment &env, Signal sig, Context &context) const
{
	return context.GetBlock()->Exec(env, sig);
}

// repeat (n?:number) {block}, option:list,xlist,set,xset
AScript_DeclareFunction(repeat)
{
	DeclareArg(env, _T("n"), VTYPE_Number, false, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_Once);
}

AScript_ImplementFunction(repeat)
{
	Value result;
	const Value &value = context.GetValue(0);
	const Function *pFuncBlock = GetBlockFunction(env, sig, context);
	if (pFuncBlock == NULL) return Value::Null;
	if (context.IsResList() || context.IsResXList() ||
								context.IsResSet() || context.IsResXSet()) {
		if (!value.IsNumber()) {
			sig.SetError(ERR_AttributeError,
				_T("infinite loop is not allowed with list or xlist"));
			return Value::Null;
		}
		bool removeInvalidFlag = context.IsResXList() || context.IsResXSet();
		bool listFlag = context.IsResList() || context.IsResXList();
		ValueList &valList = result.InitAsList(env);
		int cnt = static_cast<int>(value.GetNumber());
		for (int idx = 0; cnt > 0; cnt--, idx++) {
			Value resultElem = pFuncBlock->Eval(env, sig,
						Context(ValueList(Value(static_cast<Number>(idx)))));
			AScript_BlockSignalHandlerInLoop(sig, resultElem, Value::Null)
			if (removeInvalidFlag && resultElem.IsInvalid()) {
				// nothing to do
			} else if (listFlag || !valList.IsContain(resultElem)) {
				valList.push_back(resultElem);
			}
		}
	} else {
		int cnt, cntStep;
		if (value.IsNumber()) {
			cnt = static_cast<int>(value.GetNumber());
			cntStep = -1;
		} else {
			cnt = 1;
			cntStep = 0;
		}
		for (int idx = 0; cnt > 0; cnt += cntStep, idx++) {
			result = pFuncBlock->Eval(env, sig,
						Context(ValueList(Value(static_cast<Number>(idx)))));
			AScript_BlockSignalHandlerInLoop(sig, result, Value::Null)
		}
	}
	return result;
}

// while (`cond) {block}, option:list,xlist,set,xset
class Func_while : public Function {
public:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	Func_while(Environment &env, const TCHAR *name = _T("while"));
};
Func_while::Func_while(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	DeclareArg(env, _T("cond"), VTYPE_Quote);
	DeclareBlock(OCCUR_Once);
}

Value Func_while::DoEval(Environment &env, Signal sig, Context &context) const
{
	Value result;
	const Expr *pExpr = context.GetExpr(0);
	const Function *pFuncBlock = GetBlockFunction(env, sig, context);
	if (pFuncBlock == NULL) return Value::Null;
	if (context.IsResList() || context.IsResXList() ||
								context.IsResSet() || context.IsResXSet()) {
		bool removeInvalidFlag = context.IsResXList() || context.IsResXSet();
		bool listFlag = context.IsResList() || context.IsResXList();
		ValueList &valList = result.InitAsList(env);
		for (int idx = 0; pExpr->Exec(env, sig).GetBoolean(); idx++) {
			if (sig.IsSignalled()) return Value::Null;
			Value resultElem = pFuncBlock->Eval(env, sig,
						Context(ValueList(Value(static_cast<Number>(idx)))));
			AScript_BlockSignalHandlerInLoop(sig, resultElem, Value::Null)
			if (removeInvalidFlag && resultElem.IsInvalid()) {
				// nothing to do
			} else if (listFlag || !valList.IsContain(resultElem)) {
				valList.push_back(resultElem);
			}
		}
	} else {
		for (int idx = 0; pExpr->Exec(env, sig).GetBoolean(); idx++) {
			if (sig.IsSignalled()) return Value::Null;
			result = pFuncBlock->Eval(env, sig,
						Context(ValueList(Value(static_cast<Number>(idx)))));
			AScript_BlockSignalHandlerInLoop(sig, result, Value::Null)
		}
	}
	return result;
}

// for (`iter+) {block}, option:list,xlist,set,xset
class Func_for : public Function {
public:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	Func_for(Environment &env, const TCHAR *name = _T("for"));
};
Func_for::Func_for(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	DeclareArg(env, _T("iter"), VTYPE_Quote, false, OCCUR_OnceOrMore);
	DeclareBlock(OCCUR_Once);
}

Value Func_for::DoEval(Environment &env, Signal sig, Context &context) const
{
	ValuePickerList pickerList;
	ValueList valListSave;	// save value to prevent them from being deleted
	Environment envBlock(&env, ENVTYPE_Block);
	const Function *pFuncBlock = GetBlockFunction(envBlock, sig, context);
	if (pFuncBlock == NULL) return Value::Null;
	foreach_const (ValueList, pValue, context.GetList(0)) {
		ASSUME(env, pValue->IsExpr());
		const Symbol *pSymbol;
		const Expr *pExpr = pValue->GetExpr();
		Value value;
		if (pExpr->IsContainCheck()) {
			const Expr_BinaryOp *pExprBin =
								dynamic_cast<const Expr_BinaryOp *>(pExpr);
			if (!pExprBin->GetExprLeft()->IsSymbol()) {
				sig.SetError(ERR_ValueError,
					_T("l-value of assignment in foreach() arguments must be a symbol"));
				return Value::Null;
			}
			const Expr_Symbol *pExprSym =
					dynamic_cast<const Expr_Symbol *>(pExprBin->GetExprLeft());
			pSymbol = pExprSym->GetSymbol();
			value = pExprBin->GetExprRight()->Exec(envBlock, sig);
		} else if (pExpr->IsSymbol()) {
			const Expr_Symbol *pExprSym = dynamic_cast<const Expr_Symbol *>(pExpr);
			pSymbol = pExprSym->GetSymbol();
			value = pExpr->Exec(envBlock, sig);
		} else {
			sig.SetError(ERR_TypeError,
				_T("for() can take iterator or symbol as its argument"));
			return Value::Null;
		}
		valListSave.push_back(value);
		if (sig.IsSignalled()) {
			return Value::Null;
		} else if (value.IsList()) {
			pickerList.push_back(ValuePicker(value.GetList(), pSymbol));
		} else {
			sig.SetError(ERR_TypeError, _T("must be a list"));
			return Value::Null;
		}
	}
	Value result;
	if (context.IsResList() || context.IsResXList() ||
								context.IsResSet() || context.IsResXSet()) {
		bool removeInvalidFlag = context.IsResXList() || context.IsResXSet();
		bool listFlag = context.IsResList() || context.IsResXList();
		ValueList &valList = result.InitAsList(env);
		for (int idx = 0; pickerList.IsValidAll(); pickerList.Advance(), idx++) {
			foreach_const (ValuePickerList, pPicker, pickerList) {
				envBlock.AssignValue(pPicker->GetSymbol(), pPicker->GetValue(), false);
			}
			Value resultElem = pFuncBlock->Eval(envBlock, sig,
						Context(ValueList(Value(static_cast<Number>(idx)))));
			AScript_BlockSignalHandlerInLoop(sig, resultElem, Value::Null)
			if (removeInvalidFlag && resultElem.IsInvalid()) {
				// nothing to do
			} else if (listFlag || !valList.IsContain(resultElem)) {
				valList.push_back(resultElem);
			}
		}
	} else {
		for (int idx = 0; pickerList.IsValidAll(); pickerList.Advance(), idx++) {
			foreach_const (ValuePickerList, pPicker, pickerList) {
				envBlock.AssignValue(pPicker->GetSymbol(), pPicker->GetValue(), false);
			}
			result = pFuncBlock->Eval(envBlock, sig,
						Context(ValueList(Value(static_cast<Number>(idx)))));
			AScript_BlockSignalHandlerInLoop(sig, result, Value::Null)
		}
	}
	return result;
}

// break(value?)
class Func_break : public Function {
public:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	Func_break(Environment &env, const TCHAR *name = _T("break"));
};
Func_break::Func_break(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	DeclareArg(env, _T("value"), VTYPE_AnyType, false, OCCUR_ZeroOrOnce);
}

Value Func_break::DoEval(Environment &env, Signal sig, Context &context) const
{
	sig.SetSignal(SIGTYPE_Break, context.GetValue(0));
	return Value::Null;
}

// continue(value?)
class Func_continue : public Function {
public:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	Func_continue(Environment &env, const TCHAR *name = _T("continue"));
};
Func_continue::Func_continue(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	DeclareArg(env, _T("value"), VTYPE_AnyType, false, OCCUR_ZeroOrOnce);
}

Value Func_continue::DoEval(Environment &env, Signal sig, Context &context) const
{
	sig.SetSignal(SIGTYPE_Continue, context.GetValue(0));
	return Value::Null;
}

// return(value?)
class Func_return : public Function {
public:
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
	Func_return(Environment &env, const TCHAR *name = _T("return"));
};
Func_return::Func_return(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	DeclareArg(env, _T("value"), VTYPE_AnyType, false, OCCUR_ZeroOrOnce);
}

Value Func_return::DoEval(Environment &env, Signal sig, Context &context) const
{
	sig.SetSignal(SIGTYPE_Return, context.GetValue(0));
	return Value::Null;
}

// raise(error, msg?:string)
AScript_DeclareFunction(raise)
{
	DeclareArg(env, _T("error"), VTYPE_Error);
	DeclareArg(env, _T("msg"), VTYPE_String, false, OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(raise)
{
	sig.SetError(context.GetErrorType(0), _T("%s"),
					context.IsString(1)? context.GetString(1) : _T("error"));
	return Value::Null;
}

// list = zip(value+):map
AScript_DeclareFunction(zip)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("value"), VTYPE_AnyType, false, OCCUR_OnceOrMore);
}

AScript_ImplementFunction(zip)
{
	Value result;
	ValueList &valList = result.InitAsList(env);
	foreach_const (ValueList, pValue, context.GetList(0)) {
		valList.push_back(*pValue);
	}
	return result;
}

// list = concat(list+:List)
AScript_DeclareFunction(concat)
{
	DeclareArg(env, _T("list"), VTYPE_List, false, OCCUR_OnceOrMore);
}

AScript_ImplementFunction(concat)
{
	Value result;
	ValueList &valList = result.InitAsList(env);
	foreach_const (ValueList, pValue, context.GetList(0)) {
		foreach_const (ValueList, pValueSub, pValue->GetList()) {
			valList.push_back(*pValueSub);
		}
	}
	return result;
}

// list = sort(elem[], directive?):[stable]
// list = rank(elem[], directive?):[stable]
AScript_DeclareFunctionBegin(sort_rank)
public:
	typedef std::vector<const Value *> ValuePtrList;
private:
	class Comparator_Ascend {
	public:
		inline bool operator()(const Value *pValue1, const Value *pValue2) const {
			return Value::Compare(*pValue1, *pValue2) < 0;
		}
	};
	class Comparator_Descend {
	public:
		inline bool operator()(const Value *pValue1, const Value *pValue2) const {
			return Value::Compare(*pValue1, *pValue2) > 0;
		}
	};
	class Comparator_Custom {
	private:
		Environment &_env;
		Signal &_sig;
		const Function *_pFunc;
	public:
		inline Comparator_Custom(Environment &env, Signal &sig, const Function *pFunc) :
										_env(env), _sig(sig), _pFunc(pFunc) {}
		bool operator()(const Value *pValue1, const Value *pValue2) const;
	};
private:
	bool _rankFlag;
AScript_DeclareFunctionEnd(sort_rank)
{
	_rankFlag = (::_tcscmp(GetName(), _T("rank")) == 0);
	DeclareArg(env, _T("elem"), VTYPE_AnyType, true);
	DeclareArg(env, _T("directive"), VTYPE_AnyType, false, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(stable));
}

#if 0
int Comparator(const void *p1, const void *p2)
{
	const Value *pValue1 = *static_cast<const Value * const *>(p1);
	const Value *pValue2 = *static_cast<const Value * const *>(p2);
	return Value::Compare(*pValue1, *pValue2);
}
#endif

AScript_ImplementFunction(sort_rank)
{
	enum {
		MODE_Ascend, MODE_Descend, MODE_Custom,
	} mode = MODE_Ascend;
	const Function *pFunc = NULL;
	const ValueList &valList = context.GetList(0);
	if (context.GetValue(1).IsValid()) {
		const Value &valDirective = context.GetValue(1);
		if (valDirective.IsSymbol()) {
			const Symbol *pSymbol = valDirective.GetSymbol();
			if (pSymbol->IsIdentical(AScript_Symbol(ascend))) {
				mode = MODE_Ascend;
			} else if (pSymbol->IsIdentical(AScript_Symbol(descend))) {
				mode = MODE_Descend;
			} else {
				sig.SetError(ERR_ValueError,
					_T("invalid symbol '%s'"), pSymbol->GetName());
				return Value::Null;
			}
		} else if (valDirective.IsFunction()) {
			mode = MODE_Custom;
			pFunc = valDirective.GetFunction();
			if (pFunc->GetDeclList().size() != 2) {
				sig.SetError(ERR_TypeError,
					_T("only a binary function can be specified"));
				return Value::Null;
			}
		} else {
			sig.SetError(ERR_TypeError, _T("invalid argument"));
			return Value::Null;
		}
	}
#if 0
	const Value **valPtrList = new const Value *[valList.size()];
	do {
		const Value **ppValue = valPtrList;
		foreach_const (ValueList, pValue, valList) {
			*ppValue = &*pValue;
			ppValue++;
		}
	} while (0);
	::qsort(valPtrList, valList.size(), sizeof(Value *), Comparator);
	Value result;
	ValueList &valListResult = result.InitAsList(env);
	if (_rankFlag) {
	} else {
		const Value **ppValue = valPtrList;
		for (size_t cnt = valList.size(); cnt > 0; cnt--, ppValue++) {
			valListResult.push_back(**ppValue);
		}
	}
	delete[] ppValPtrList;
#else
	ValuePtrList valPtrList;
	foreach_const (ValueList, pValue, valList) {
		valPtrList.push_back(&(*pValue));
	}
	if (context.IsSet(AScript_Symbol(stable))) {
		if (mode == MODE_Ascend) {
			std::stable_sort(valPtrList.begin(), valPtrList.end(), Comparator_Ascend());
		} else if (mode == MODE_Descend) {
			std::stable_sort(valPtrList.begin(), valPtrList.end(), Comparator_Descend());
		} else { // mode == MODE_Custom
			std::stable_sort(valPtrList.begin(), valPtrList.end(),
											Comparator_Custom(env, sig, pFunc));
		}
	} else {
		if (mode == MODE_Ascend) {
			std::sort(valPtrList.begin(), valPtrList.end(), Comparator_Ascend());
		} else if (mode == MODE_Descend) {
			std::sort(valPtrList.begin(), valPtrList.end(), Comparator_Descend());
		} else { // mode == MODE_Custom
			std::sort(valPtrList.begin(), valPtrList.end(),
											Comparator_Custom(env, sig, pFunc));
		}
	}
	Value result;
	ValueList &valListResult = result.InitAsList(env);
	if (_rankFlag) {
		foreach_const (ValueList, pValue, valList) {
			ValuePtrList::iterator ppValue = valPtrList.begin();
			for ( ; ppValue != valPtrList.end(); ppValue++) {
				if (Value::Compare(*pValue, **ppValue) == 0) break;
			}
			if (ppValue == valPtrList.end()) {
				sig.SetError(ERR_SystemError, _T("fatal error in rank() operation"));
				return Value::Null;
			} else {
				size_t idx = ppValue - valPtrList.begin();
				valListResult.push_back(static_cast<Number>(idx));
			}
		}
	} else {
		foreach_const (ValuePtrList, ppValue, valPtrList) {
			valListResult.push_back(**ppValue);
		}
	}
#endif
	return result;
}

bool AScript_Function(sort_rank)::Comparator_Custom::
				operator()(const Value *pValue1, const Value *pValue2) const
{
	if (_sig.IsSignalled()) return false;
	Value value = _pFunc->Eval(_env, _sig,
							Context(ValueList(*pValue1, *pValue2)));
	return value.GetNumber() < 0;
}

// list = reverse(elem[])
AScript_DeclareFunction(reverse)
{
	SetMode(RSLTMODE_Normal, MAP_Off);
	DeclareArg(env, _T("elem"), VTYPE_AnyType, true);
}

AScript_ImplementFunction(reverse)
{
	Value result;
	ValueList &valList = result.InitAsList(env);
	foreach_const_reverse (ValueList, pValue, context.GetList(0)) {
		valList.push_back(*pValue);
	}
	return result;
}

// num = len(elem[])
AScript_DeclareFunction(len)
{
	SetMode(RSLTMODE_Normal, MAP_Off);
	DeclareArg(env, _T("elem"), VTYPE_AnyType, true);
}

AScript_ImplementFunction(len)
{
	return Value(static_cast<Number>(context.GetList(0).size()));
}

// list = list(list?:List)
AScript_DeclareFunction(list)
{
	DeclareArg(env, _T("list"), VTYPE_List, false, OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(list)
{
	if (context.IsInvalid(0)) {
		Value result;
		result.InitAsList(env);	// create an empty list
		return result;
	}
	return context.GetValue(0);
}

// list = xlist(list?:List)
AScript_DeclareFunction(xlist)
{
	DeclareArg(env, _T("list"), VTYPE_List, false, OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(xlist)
{
	Value result;
	ValueList &valList = result.InitAsList(env);
	if (context.IsInvalid(0)) return result;	// return an empty list
	foreach_const (ValueList, pValue, context.GetList(0)) {
		if (pValue->IsValid()) valList.push_back(*pValue);
	}
	return result;
}

// list = set(lists*:List)
// list = xset(lists*:List)
// attrs: and, xor, or
AScript_DeclareFunctionBegin(set_xset)
	bool _acceptInvalidFlag;
AScript_DeclareFunctionEnd(set_xset)
{
	_acceptInvalidFlag = (::_tcscmp(GetName(), _T("set")) == 0);
	DeclareArg(env, _T("lists"), VTYPE_List, false, OCCUR_ZeroOrMore);
	DeclareAttr(AScript_Symbol(or));
	DeclareAttr(AScript_Symbol(and));
	DeclareAttr(AScript_Symbol(xor));
}

AScript_ImplementFunction(set_xset)
{
	Value result;
	ValueList &valList = result.InitAsList(env);
	if (context.IsSet(AScript_Symbol(and))) {			// AND combination
		ValueList valList1, valList2;
		ValueList::const_iterator pValueArg = context.GetList(0).begin();
		foreach_const (ValueList, pValue, pValueArg->GetList()) {
			if ((_acceptInvalidFlag || pValue->IsValid()) &&
											!valList1.IsContain(*pValue)) {
				valList1.push_back(*pValue);
			}
		}
		pValueArg++;
		ValueList *pValListAnd = &valList1;
		ValueList *pValListWork = &valList2;
		for ( ; pValueArg != context.GetList(0).end() &&
										!pValListAnd->empty(); pValueArg++) {
			const ValueList &valListArg = pValueArg->GetList();
			foreach_const (ValueList, pValue, *pValListAnd) {
				if (valListArg.IsContain(*pValue)) pValListWork->push_back(*pValue);
			}
			ValueList *pValListTmp = pValListAnd;
			pValListAnd = pValListWork, pValListWork = pValListTmp;
			pValListWork->clear();
		}
		foreach_const (ValueList, pValue, *pValListAnd) {
			valList.push_back(*pValue);
		}
	} else if (context.IsSet(AScript_Symbol(xor))) {	// XOR combination
		ValueList valListOr;
		foreach_const (ValueList, pValueList, context.GetList(0)) {
			foreach_const (ValueList, pValue, pValueList->GetList()) {
				if ((_acceptInvalidFlag || pValue->IsValid()) &&
												!valListOr.IsContain(*pValue)) {
					valListOr.push_back(*pValue);
				}
			}
		}
		ValueList valList1, valList2;
		ValueList *pValListAnd = &valList1;
		do {
			ValueList::const_iterator pValueArg = context.GetList(0).begin();
			foreach_const (ValueList, pValue, pValueArg->GetList()) {
				if (!valList1.IsContain(*pValue)) valList1.push_back(*pValue);
			}
			pValueArg++;
			ValueList *pValListWork = &valList2;
			for ( ; pValueArg != context.GetList(0).end() &&
											!pValListAnd->empty(); pValueArg++) {
				const ValueList &valListArg = pValueArg->GetList();
				foreach_const (ValueList, pValue, *pValListAnd) {
					if (valListArg.IsContain(*pValue)) pValListWork->push_back(*pValue);
				}
				ValueList *pValListTmp = pValListAnd;
				pValListAnd = pValListWork, pValListWork = pValListTmp;
				pValListWork->clear();
			}
		} while (0);
		foreach_const (ValueList, pValue, valListOr) {
			if (!pValListAnd->IsContain(*pValue)) {
				valList.push_back(*pValue);
			}
		}
	} else {										// OR combination
		foreach_const (ValueList, pValueList, context.GetList(0)) {
			foreach_const (ValueList, pValue, pValueList->GetList()) {
				if ((_acceptInvalidFlag || pValue->IsValid()) &&
												!valList.IsContain(*pValue)) {
					valList.push_back(*pValue);
				}
			}
		}
	}
	return result;
}

// replace(elem[], value, replace)
AScript_DeclareFunction(replace)
{
	DeclareArg(env, _T("elem"), VTYPE_AnyType, true);
	DeclareArg(env, _T("value"), VTYPE_AnyType);
	DeclareArg(env, _T("replace"), VTYPE_AnyType);
}

AScript_ImplementFunction(replace)
{
	const Value &value = context.GetValue(1);
	const Value &replace = context.GetValue(2);
	Value result;
	ValueList &valList = result.InitAsList(env);
	foreach_const (ValueList, pValue, context.GetList(0)) {
		if (Value::Compare(*pValue, value) == 0) {
			valList.push_back(replace);
		} else {
			valList.push_back(*pValue);
		}
	}
	return result;
}

// nil_to(elem[], replace)
AScript_DeclareFunction(nil_to)
{
	DeclareArg(env, _T("elem"), VTYPE_AnyType, true);
	DeclareArg(env, _T("replace"), VTYPE_AnyType);
}

AScript_ImplementFunction(nil_to)
{
	const Value &replace = context.GetValue(1);
	Value result;
	ValueList &valList = result.InitAsList(env);
	foreach_const (ValueList, pValue, context.GetList(0)) {
		if (pValue->IsValid()) {
			valList.push_back(*pValue);
		} else {
			valList.push_back(replace);
		}
	}
	return result;
}

// list = filter(criteria, elem[])
AScript_DeclareFunction(filter)
{
	DeclareArg(env, _T("criteria"), VTYPE_AnyType);
	DeclareArg(env, _T("elem"), VTYPE_AnyType, true);
}

AScript_ImplementFunction(filter)
{
	const Value &criteria = context.GetValue(0);
	if (criteria.IsInvalid()) return Value::Null;
	Value result;
	ValueList &valList = result.InitAsList(env);
	if (criteria.IsFunction()) {
		const Function *pFunc = criteria.GetFunction();
		foreach_const (ValueList, pValue, context.GetList(1)) {
			Value valueFlag = pFunc->Eval(env, sig,
						Context(context, ValueList(*pValue), Value::Null));
			if (sig.IsSignalled()) return Value::Null;
			if (valueFlag.GetBoolean()) {
				valList.push_back(*pValue);
			}
		}
	} else if (criteria.IsList()) {
		const ValueList &valListFlag = criteria.GetList();
		const ValueList &valListSrc = context.GetList(1);
		ValueList::const_iterator pValueFlag = valListFlag.begin();
		ValueList::const_iterator pValueSrc = valListSrc.begin();
		for ( ; pValueFlag != valListFlag.end() && pValueSrc != valListSrc.end();
												pValueFlag++, pValueSrc++) {
			if (pValueFlag->GetBoolean()) {
				valList.push_back(*pValueSrc);
			}
		}
	} else {
		sig.SetError(ERR_TypeError, _T("criteria must be a function or a list of flags"));
		return Value::Null;
	}
	return result;
}

// list = map(func:Function, elem[])
AScript_DeclareFunction(map)
{
	DeclareArg(env, _T("func"), VTYPE_Function);
	DeclareArg(env, _T("elem"), VTYPE_AnyType, true);
}

AScript_ImplementFunction(map)
{
	const Value &value1 = context.GetValue(0);
	const Value &value2 = context.GetValue(1);
	const Function *pFunc = value1.GetFunction();
	Value result;
	ValueList &valList = result.InitAsList(env);
	foreach_const (ValueList, pValue, value2.GetList()) {
		Value value = pFunc->Eval(env, sig,
					Context(context, ValueList(*pValue), Value::Null));
		if (sig.IsSignalled()) return Value::Null;
		valList.push_back(value);
	}
	return result;
}

// list = reduce(func:Function, elem[], value0)
AScript_DeclareFunction(reduce)
{
	DeclareArg(env, _T("func"), VTYPE_Function);
	DeclareArg(env, _T("elem"), VTYPE_AnyType, true);
	DeclareArg(env, _T("value0"), VTYPE_AnyType);
}

AScript_ImplementFunction(reduce)
{
	const Value &value1 = context.GetValue(0);
	const Value &value2 = context.GetValue(1);
	const Function *pFunc = value1.GetFunction();
	Value result = context.GetValue(2);
	foreach_const (ValueList, pValue, value2.GetList()) {
		result = pFunc->Eval(env, sig,
				Context(context, ValueList(result, *pValue), Value::Null));
		if (sig.IsSignalled()) return Value::Null;
	}
	return result;
}

// num = last_index(elem[]):map
AScript_DeclareFunction(last_index)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("list"), VTYPE_AnyType, true);
}

AScript_ImplementFunction(last_index)
{
	return Value(static_cast<Number>(context.GetList(0).size()) - 1);
}

// v = min(elem[]):map / v = max(elem[]):map
AScript_DeclareFunctionBegin(min_max)
	bool _maxFlag;
AScript_DeclareFunctionEnd(min_max)
{
	_maxFlag = (::_tcscmp(GetName(), _T("max")) == 0);
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("elem"), VTYPE_AnyType, true);
	DeclareAttr(AScript_Symbol(index));
	DeclareAttr(AScript_Symbol(last_index));
	DeclareAttr(AScript_Symbol(indices));
}

AScript_ImplementFunction(min_max)
{
	const SymbolSet &attrs = context.GetAttrs();
	const ValueList &valList = context.GetList(0);
	if (valList.empty()) return Value::Null;
	Value result;
	if (attrs.IsSet(AScript_Symbol(index))) {
		size_t idxHit = 0;
		Value valueHit = valList.front();
		size_t idx = 0;
		foreach_const (ValueList, pValue, valList) {
			int cmp = Value::Compare(valueHit, *pValue);
			if (_maxFlag) cmp = -cmp;
			if (cmp > 0) {
				valueHit = *pValue;
				idxHit = idx;
			}
			idx++;
		}
		result.SetNumber(idxHit);
	} else if (attrs.IsSet(AScript_Symbol(last_index))) {
		size_t idxHit = 0;
		Value valueHit = valList.back();
		size_t idx = valList.size() - 1;
		foreach_const_reverse (ValueList, pValue, valList) {
			int cmp = Value::Compare(valueHit, *pValue);
			if (_maxFlag) cmp = -cmp;
			if (cmp > 0) {
				valueHit = *pValue;
				idxHit = idx;
			}
			idx--;
		}
		result.SetNumber(idxHit);
	} else if (attrs.IsSet(AScript_Symbol(indices))) {
		ValueList &resultList = result.InitAsList(env);
		Value valueHit = valList.front();
		size_t idx = 0;
		foreach_const (ValueList, pValue, valList) {
			int cmp = Value::Compare(valueHit, *pValue);
			if (_maxFlag) cmp = -cmp;
			if (cmp > 0) {
				valueHit = *pValue;
				resultList.clear();
				resultList.push_back(Value(static_cast<Number>(idx)));
			} else if (cmp == 0) {
				resultList.push_back(Value(static_cast<Number>(idx)));
			}
			idx++;
		}
	} else {
		result = valList.front();
		foreach_const (ValueList, pValue, valList) {
			int cmp = Value::Compare(result, *pValue);
			if (_maxFlag) cmp = -cmp;
			if (cmp > 0) {
				result = *pValue;
			}
		}
	}
	return result;
}

// v = choosemin(values+):map / v = choosemax(values+):map
AScript_DeclareFunctionBegin(choosemin_max)
	bool _maxFlag;
AScript_DeclareFunctionEnd(choosemin_max)
{
	_maxFlag = (::_tcscmp(GetName(), _T("choosemax")) == 0);
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("values"), VTYPE_AnyType, false, OCCUR_OnceOrMore);
}

AScript_ImplementFunction(choosemin_max)
{
	const ValueList &valList = context.GetList(0);
	ValueList::const_iterator pValue = valList.begin();
	Value result = *pValue++;
	for ( ; pValue != valList.end(); pValue++) {
		int cmp = Value::Compare(result, *pValue);
		if (_maxFlag) cmp = -cmp;
		if (cmp > 0) result = *pValue;
	}
	return result;
}

// v = choose(index:number, values+):map
AScript_DeclareFunction(choose)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("index"), VTYPE_Number);
	DeclareArg(env, _T("values"), VTYPE_AnyType, false, OCCUR_OnceOrMore);
}

AScript_ImplementFunction(choose)
{
	size_t index = static_cast<size_t>(context.GetNumber(0));
	const ValueList &valList = context.GetList(1);
	if (index >= valList.size()) {
		sig.SetError(ERR_IndexError, _T("index is out of range"));
		return Value::Null;
	}
	return valList[index];
}

// v = chooseif(flag, value1, vlaue2):map
AScript_DeclareFunction(chooseif)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("flag"), VTYPE_Boolean);
	DeclareArg(env, _T("value1"), VTYPE_AnyType);
	DeclareArg(env, _T("value2"), VTYPE_AnyType);
}

AScript_ImplementFunction(chooseif)
{
	return context.GetBoolean(0)? context.GetValue(1) : context.GetValue(2);
}

// num = sum(elem[]):map
AScript_DeclareFunction(sum)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("elem"), VTYPE_AnyType, true);
}

AScript_ImplementFunction(sum)
{
	const ValueList &valList = context.GetList(0);
	if (valList.empty()) return Value::Null;
	ValueList::const_iterator pValue = valList.begin();
	Value result = *pValue++;
	for ( ; pValue != valList.end(); pValue++) {
		result = env.GetFunc_Plus().Eval(env, sig,
								Context(ValueList(result, *pValue)));
	}
	return result;
}

// num = average(elem[]):map
AScript_DeclareFunction(average)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("elem"), VTYPE_AnyType, true);
}

AScript_ImplementFunction(average)
{
	const ValueList &valList = context.GetList(0);
	if (valList.empty()) return Value::Null;
	ValueList::const_iterator pValue = valList.begin();
	Value result = *pValue++;
	for ( ; pValue != valList.end(); pValue++) {
		result = env.GetFunc_Plus().Eval(env, sig,
								Context(ValueList(result, *pValue)));
	}
	Number cnt = static_cast<Number>(valList.size());
	return env.GetFunc_Divide().Eval(env, sig,
								Context(ValueList(result, Value(cnt))));
}

// flag = and(elem[]:boolean):map
AScript_DeclareFunction(and)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("elem"), VTYPE_Boolean, true);
}

AScript_ImplementFunction(and)
{
	const ValueList &valList = context.GetList(0);
	if (valList.empty()) return Value::Null;
	foreach_const (ValueList, pValue, valList) {
		if (!pValue->GetBoolean()) return Value(false);
	}
	return Value(true);
}

// flag = or(elem[]:boolean):map
AScript_DeclareFunction(or)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("elem"), VTYPE_Boolean, true);
}

AScript_ImplementFunction(or)
{
	const ValueList &valList = context.GetList(0);
	if (valList.empty()) return Value::Null;
	foreach_const (ValueList, pValue, valList) {
		if (pValue->GetBoolean()) return Value(true);
	}
	return Value(false);
}

// text = to_string(value):map
AScript_DeclareFunction(to_string)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("value"), VTYPE_AnyType);
}

AScript_ImplementFunction(to_string)
{
	return Value(env, context.GetValue(0).ToString(sig, false).c_str());
}

// num = to_number(value):map:[strict,raise,zero,nil]
AScript_DeclareFunction(to_number)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("value"), VTYPE_AnyType);
	DeclareAttr(AScript_Symbol(strict));
	DeclareAttr(AScript_Symbol(raise));
	DeclareAttr(AScript_Symbol(zero));
	DeclareAttr(AScript_Symbol(nil));
}

AScript_ImplementFunction(to_number)
{
	bool allowPartFlag = !context.IsSet(AScript_Symbol(strict));
	bool successFlag;
	Number num = context.GetValue(0).ToNumber(allowPartFlag, successFlag);
	if (successFlag) {
		return Value(num);
	} else if (context.IsSet(AScript_Symbol(raise))) {
		sig.SetError(ERR_ValueError, _T("failed to convert to a number"));
		return Value::Null;
	} else if (context.IsSet(AScript_Symbol(zero))) {
		return Value(0.);
	} else { // context.IsSet(AScript_PrivSymbol(nil)
		return Value::Null;
	}
}

// symbol = to_symbol(str:string):map
AScript_DeclareFunction(to_symbol)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("str"), VTYPE_String);
}

AScript_ImplementFunction(to_symbol)
{
	return Value(Symbol::Add(context.GetString(0)));
}

// num = rand(num?)
AScript_DeclareFunction(rand)
{
	DeclareArg(env, _T("num"), VTYPE_Number, false, OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(rand)
{
	if (context.IsInvalid(0)) {
		return Value(::genrand_real2());
	}
	unsigned long num = static_cast<unsigned long>(context.GetNumber(0));
	Number result = static_cast<unsigned long>(::genrand_real2() * num);
	return Value(result);
}

// print(value*):map:void
AScript_DeclareFunction(print)
{
	SetMode(RSLTMODE_Void, MAP_On);
	DeclareArg(env, _T("value"), VTYPE_AnyType, false, OCCUR_ZeroOrMore);
}

AScript_ImplementFunction(print)
{
	foreach_const (ValueList, pValue, context.GetList(0)) {
		env.PutValue(sig, *pValue);
	}
	return Value::Null;
}

// println(value*):map:void
AScript_DeclareFunction(println)
{
	SetMode(RSLTMODE_Void, MAP_On);
	DeclareArg(env, _T("value"), VTYPE_AnyType, false, OCCUR_ZeroOrMore);
}

AScript_ImplementFunction(println)
{
	foreach_const (ValueList, pValue, context.GetList(0)) {
		env.PutValue(sig, *pValue);
	}
	env.PutChar(_T('\n'));
	return Value::Null;
}

// printf(format, values*):map:void
AScript_DeclareFunction(printf)
{
	SetMode(RSLTMODE_Void, MAP_On);
	DeclareArg(env, _T("format"), VTYPE_String);
	DeclareArg(env, _T("values"), VTYPE_AnyType, false, OCCUR_ZeroOrMore);
}

AScript_ImplementFunction(printf)
{
	Value value(env, Formatter::Format(sig,
							context.GetString(0), context.GetList(1)).c_str());
	env.PutValue(sig, value);
	return Value::Null;
}

// str = sprintf(format, values*):map
AScript_DeclareFunction(sprintf)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("format"), VTYPE_String);
	DeclareArg(env, _T("values"), VTYPE_AnyType, false, OCCUR_ZeroOrMore);
}

AScript_ImplementFunction(sprintf)
{
	Value value(env, Formatter::Format(sig,
							context.GetString(0), context.GetList(1)).c_str());
	return value;
}

// dir(obj?)
AScript_DeclareFunction(dir)
{
	DeclareArg(env, _T("obj"), VTYPE_AnyType, false, OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(dir)
{
	const Environment::Frame *pFrame = &env.GetBottomFrame();
	const Value &value = context.GetValue(0);
	if (value.IsModule()) {
		pFrame = &value.GetModule()->GetTopFrame();
	} else if (value.IsFunction()) {
		const Function *pFunc = value.GetFunction();
		const Class *pClass = pFunc->GetClassToConstruct();
		if (pClass == NULL) {
			pFunc->SetError_NotConstructor(sig);
			return Value::Null;
		}
		pFrame = &pClass->GetTopFrame();
	} else if (value.IsObject()) {
		const Object *pObj = value.GetObject();
		Value result;
		ValueList &valList = result.InitAsList(env);
		foreach_const (Environment::FrameList, ppFrame, pObj->GetFrameList()) {
			const Environment::Frame *pFrame = *ppFrame;
			if (pFrame->GetEnvType() != ENVTYPE_Class &&
									pFrame->GetEnvType() != ENVTYPE_Instance) {
				break;
			}
			foreach_const (ValueMap, iter, pFrame->GetValueMap()) {
				valList.push_back(Value(env, iter->first->GetName()));
			}
		}
		return result;
	} else if (value.IsValid()) {
		sig.SetError(ERR_TypeError, _T("invalid argument"));
		return Value::Null;
	}
	Value result;
	ValueList &valList = result.InitAsList(env);
	foreach_const (ValueMap, iter, pFrame->GetValueMap()) {
		valList.push_back(Value(env, iter->first->GetName()));
	}
	return result;
}

// typename():map
class Func_typename : public Function {
public:
	Func_typename(Environment &env, const TCHAR *name = _T("typename"));
	virtual Value DoEval(Environment &env, Signal sig, Context &context) const;
};
Func_typename::Func_typename(Environment &env, const TCHAR *name) : Function(name, FUNCTYPE_Function)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("value"), VTYPE_AnyType);
}

Value Func_typename::DoEval(Environment &env, Signal sig, Context &context) const
{
	return Value(env, context.GetValue(0).GetTypeName());
}

// list = range(num:number, num_end?:number, step?:number):map {block?}
AScript_DeclareFunction(range)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("num"),		VTYPE_Number);
	DeclareArg(env, _T("num_end"),	VTYPE_Number, false, OCCUR_ZeroOrOnce);
	DeclareArg(env, _T("step"),		VTYPE_Number, false, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(range)
{
	Number numBegin = 0.;
	Number numEnd = 0.;
	Number numStep = 1.;
	if (context.GetValue(1).IsInvalid()) {
		numEnd = context.GetValue(0).GetNumber();
		if (numBegin > numEnd) numStep = -1.;
	} else if (context.GetValue(2).IsInvalid()) {
		numBegin = context.GetValue(0).GetNumber();
		numEnd = context.GetValue(1).GetNumber();
		if (numBegin > numEnd) numStep = -1.;
	} else {
		numBegin = context.GetValue(0).GetNumber();
		numEnd = context.GetValue(1).GetNumber();
		numStep = context.GetValue(2).GetNumber();
		if (numStep == 0.) {
			sig.SetError(ERR_ValueError, _T("step cannot be specified as zero"));
			return Value::Null;
		}
	}
	// pFuncBlock is set to NULL without error if block is not specified.
	const Function *pFuncBlock = GetBlockFunction(env, sig, context);
	if (sig.IsSignalled()) return Value::Null;
	Environment envBlock(&env, ENVTYPE_Block);
	Value result;
	ResultListComposer resultListComposer(env, context, result);
	if (numStep > 0) {
		int idx = 0;
		for (Number num = numBegin; num < numEnd; num += numStep, idx++) {
			Value value(num);
			if (pFuncBlock != NULL) {
				value = pFuncBlock->Eval(envBlock, sig,
						Context(ValueList(value, Value(static_cast<Number>(idx)))));
				AScript_BlockSignalHandlerInLoop(sig, value, Value::Null)
			}
			resultListComposer.Store(value);
		}
	} else if (numStep < 0) {
		int idx = 0;
		for (Number num = numBegin; num > numEnd; num += numStep, idx++) {
			Value value(num);
			if (pFuncBlock != NULL) {
				value = pFuncBlock->Eval(envBlock, sig,
						Context(ValueList(value, Value(static_cast<Number>(idx)))));
				AScript_BlockSignalHandlerInLoop(sig, value, Value::Null)
			}
			resultListComposer.Store(value);
		}
	}
	return result;
}

// list = interval(a:number, b:number, samples:number):map
AScript_DeclareFunction(interval)
{
	SetMode(RSLTMODE_Normal, MAP_On);
	DeclareArg(env, _T("a"), VTYPE_Number);
	DeclareArg(env, _T("b"), VTYPE_Number);
	DeclareArg(env, _T("samples"), VTYPE_Number);
	DeclareAttr(AScript_PrivSymbol(open));
	DeclareAttr(AScript_PrivSymbol(open_l));
	DeclareAttr(AScript_PrivSymbol(open_r));
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(interval)
{
	const ValueList &valListArg = context.GetArgs();
	Number numBegin = valListArg[0].GetNumber();
	Number numEnd = valListArg[1].GetNumber();
	int numSamples = static_cast<int>(valListArg[2].GetNumber());
	if (numSamples <= 1) {
		sig.SetError(ERR_ValueError, _T("samples must be more than one"));
		return Value::Null;
	}
	// pFuncBlock is set to NULL without error if block is not specified.
	const Function *pFuncBlock = GetBlockFunction(env, sig, context);
	if (sig.IsSignalled()) return Value::Null;
	Environment envBlock(&env, ENVTYPE_Block);
	Value result;
	ResultListComposer resultListComposer(env, context, result);
	bool openFlag = context.IsSet(AScript_PrivSymbol(open));
	bool openLeftFlag = context.IsSet(AScript_PrivSymbol(open_l));
	bool openRightFlag = context.IsSet(AScript_PrivSymbol(open_r));
	int iFactor = 0;
	Number numDenom = numSamples - 1;
	if (openFlag || (openLeftFlag && openRightFlag)) {
		numDenom = numSamples + 1;
		iFactor = 1;
	} else if (openLeftFlag) {
		numDenom = numSamples;
		iFactor = 1;
	} else if (openRightFlag) {
		numDenom = numSamples;
		iFactor = 0;
	}
	for (int idx = 0; idx < numSamples; idx++, iFactor++) {
		Value value((numEnd - numBegin) * iFactor / numDenom + numBegin);
		if (pFuncBlock != NULL) {
			value = pFuncBlock->Eval(envBlock, sig,
					Context(ValueList(value, Value(static_cast<Number>(idx)))));
			AScript_BlockSignalHandlerInLoop(sig, value, Value::Null)
		}
		resultListComposer.Store(value);
	}
	return result;
}

// Module entry
AScript_ModuleEntry()
{
	AScript_RealizePrivSymbol(open);
	AScript_RealizePrivSymbol(open_l);
	AScript_RealizePrivSymbol(open_r);
	env.AssignFunction(new Func_class(env));
	env.AssignFunction(new Func_struct(env));
	env.AssignFunction(new Func_int(env));
	env.AssignFunction(new Func_try(env));
	env.AssignFunction(new Func_except(env));
	env.AssignFunction(new Func_if(env));
	env.AssignFunction(new Func_elsif(env));
	env.AssignFunction(new Func_else(env));
	AScript_AssignFunction(repeat);
	env.AssignFunction(new Func_while(env));
	env.AssignFunction(new Func_for(env));
	env.AssignFunction(new Func_break(env));
	env.AssignFunction(new Func_continue(env));
	env.AssignFunction(new Func_return(env));
	AScript_AssignFunction(raise);
	AScript_AssignFunction(len);
	AScript_AssignFunction(list);
	AScript_AssignFunction(xlist);
	AScript_AssignFunctionEx(set_xset, _T("set"));
	AScript_AssignFunctionEx(set_xset, _T("xset"));
	AScript_AssignFunction(replace);
	AScript_AssignFunction(nil_to);
	AScript_AssignFunction(filter);
	AScript_AssignFunction(map);
	AScript_AssignFunction(reduce);
	AScript_AssignFunction(concat);
	AScript_AssignFunction(zip);
	AScript_AssignFunction(last_index);
	AScript_AssignFunctionEx(sort_rank, _T("sort"));
	AScript_AssignFunctionEx(sort_rank, _T("rank"));
	AScript_AssignFunction(reverse);
	AScript_AssignFunctionEx(min_max, _T("min"));
	AScript_AssignFunctionEx(min_max, _T("max"));
	AScript_AssignFunctionEx(choosemin_max, _T("choosemin"));
	AScript_AssignFunctionEx(choosemin_max, _T("choosemax"));
	AScript_AssignFunction(choose);
	AScript_AssignFunction(chooseif);
	AScript_AssignFunction(sum);
	AScript_AssignFunction(average);
	AScript_AssignFunction(and);
	AScript_AssignFunction(or);
	AScript_AssignFunction(module);
	AScript_AssignFunction(mixin);
	env.AssignFunction(new Func_import(env));
	AScript_AssignFunction(execfile);
	AScript_AssignFunction(parse);
	AScript_AssignFunction(eval);
	AScript_AssignFunction(locals);
	AScript_AssignFunction(outers);
	AScript_AssignFunction(to_string);
	AScript_AssignFunction(to_number);
	AScript_AssignFunction(to_symbol);
	AScript_AssignFunction(rand);
	AScript_AssignFunction(print);
	AScript_AssignFunction(println);
	AScript_AssignFunction(printf);
	AScript_AssignFunction(sprintf);
	AScript_AssignFunction(dir);
	env.AssignFunction(new Func_typename(env));
	AScript_AssignFunction(range);
	AScript_AssignFunction(interval);
}

AScript_EndModule(__builtins__)

AScript_DLLModuleEntry(__builtins__)
