//-----------------------------------------------------------------------------
// AScript basement module
//-----------------------------------------------------------------------------
#include <ascript.h>

AScript_BeginModule(basement)

#define REPEATER_HELP \
"It returns the last evaluated value in the block as its own result,\n" \
"but, if one of :list, :xlist, :set, :xset or :iter is specified,\n" \
"it returns a list or evaluated value or an iterator. The rule is as follows.\n" \
"  :list  returns a list of result values\n" \
"  :xlist returns a list of result values eliminating nil\n" \
"  :set   returns a list of unique values of results\n" \
"  :xset  returns a list of unique values of results eliminating nil\n" \
"  :iter  returns an iterator that executes the block\n" \
"  :xiter returns an iterator that executes the block, skipping nil\n"

//-----------------------------------------------------------------------------
// AScript module functions: basement
//-----------------------------------------------------------------------------
// class(superclass?):[static] {block?}
AScript_DeclareFunctionAlias(class_, "class")
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "superclass", VTYPE_Function, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(static_));
	SetHelp(
	"Returns a function object that constructs an instance with methods and\n"
	"properties specified in the block. If superclass, which is supposed to\n"
	"be a constructor function, is specified, the new class shall inherits\n"
	"methods and properties of the class associated with it.");
}

AScript_ImplementFunction(class_)
{
	const Expr_Block *pExprBlock = args.GetBlock(env, sig);
	if (sig.IsSignalled()) return Value::Null;
	Class *pClassSuper = env.LookupClass(VTYPE_Object);
	const Value &valueSuper = args.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(&env, pClassSuper);
	pClassCustom->SetValueType(pClassSuper->GetValueType());
	Value valueSelf(pClassCustom, false);
	if (pExprBlock != NULL &&
				!pClassCustom->BuildContent(env, sig, valueSelf, pExprBlock)) {
		Class::Delete(pClassCustom);
		return Value::Null;
	}
	ClassPrototype *pFunc = NULL;
	FunctionType funcType = args.IsSet(AScript_Symbol(static_))?
											FUNCTYPE_Class : FUNCTYPE_Function;
	FunctionCustom *pFuncInit = dynamic_cast<FunctionCustom *>(
					pClassCustom->LookupFunction(AScript_Symbol(__init__), false));
	if (pFuncInit == NULL) {
		pFunc = new ClassPrototype(env, AScript_Symbol(_anonymous_),
									new Expr_Block(), pClassCustom, funcType);
		Args argsSub(ExprList::Null);
		if (!pFunc->CustomDeclare(env, sig, SymbolSet::Null, argsSub)) {
			Function::Delete(pFunc);
			return Value::Null;
		}
	} else {
		pFunc = new ClassPrototype(env, AScript_Symbol(_anonymous_),
					pFuncInit->GetExprBody()->IncRef(), pClassCustom, funcType);
		pFunc->CopyDeclare(*pFuncInit);
	}
	pClassCustom->SetConstructor(Function::Reference(pFunc));
	Value result(env, pFunc, Value::Null);
	return result;
}

// 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
AScript_DeclareFunctionAlias(struct_, "struct")
{
	DeclareArg(env, "args", VTYPE_Quote, OCCUR_OnceOrMore);
	DeclareBlock(OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(loose));
	SetHelp(
	"Returns a function object that constructs a structure instance that\n"
	"contains properties specified by args. It can optionally take block\n"
	"which declares some additional methods for constructed instances.\n"
	"If :loose attribute is speicied, the generated constructor function\n"
	"makes an existence check of arguments in a loose way.");
}

AScript_ImplementFunction(struct_)
{
	const Expr_Block *pExprBlock = args.GetBlock(env, sig);
	if (sig.IsSignalled()) return Value::Null;
	Class *pClassSuper = env.LookupClass(VTYPE_Struct);
	ExprList exprListArg;
	foreach_const (ValueList, pValue, args.GetList(0)) {
		exprListArg.push_back(const_cast<Expr *>(pValue->GetExpr()));
	}
	Class_Custom *pClassCustom = new Class_Custom(&env, pClassSuper);
	pClassCustom->SetValueType(pClassSuper->GetValueType());
	Value valueSelf(pClassCustom, false);
	if (pExprBlock != NULL &&
				!pClassCustom->BuildContent(env, sig, valueSelf, pExprBlock)) {
		Class::Delete(pClassCustom);
		return Value::Null;
	}
	StructPrototype *pFunc = new StructPrototype(env, pClassCustom);
	pClassCustom->SetConstructor(Function::Reference(pFunc));
	//Args argsSub(args.GetAttrs(), SymbolSet::Null,
	//								NULL, NULL, Value::Null, exprListArg);
	Args argsSub(exprListArg, Value::Null, NULL, false, NULL, args.GetAttrs());
	if (!pFunc->CustomDeclare(env, sig, _attrsOpt, argsSub)) {
		Function::Delete(pFunc);
		return false;
	}
	if (args.IsSet(AScript_Symbol(loose))) {
		pFunc->GetDeclOwner().SetAsLoose();
	}
	return Value(env, pFunc, Value::Null);
}

// module {block}
AScript_DeclareFunction(module)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Creates a module that contains functions and variables defined in the block\n"
	"and returns it as a module object. This can be used to realize a namespace.");
}

AScript_ImplementFunction(module)
{
	const Expr_Block *pExprBlock = args.GetBlock(env, sig);
	if (sig.IsSignalled()) return Value::Null;
	Module *pModule = new Module(&env, AScript_Symbol(_anonymous_), NULL, NULL);
	pExprBlock->Exec(*pModule, sig);
	return Value(pModule);
}

// import(`module, `alias?):[overwrite] {block?}
AScript_DeclareFunctionAlias(import_, "import")
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "module", VTYPE_Quote);
	DeclareArg(env, "alias", VTYPE_Quote, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(overwrite));
	SetHelp(
	"Imports a module stored in directories specified by a variable sys.path.\n"
	"There are three ways of calling this function like follow:\n"
	"  1. import(foo)\n"
	"  2. import(foo, bar)\n"
	"  3. import(foo) {symbol1, symbol2, symbol3}\n"
	"In the first format, it creates a module object named foo.\n"
	"In the second, it names the module object as bar instead of foo.\n"
	"In the third, it doesn't register the module name into the environment,\n"
	"but it looks up symbols specified in the block and registers them.\n"
	"In thie case, if specified symbols conflict with the existing one,\n"
	"it will cause an error. Attribute :overwrite will disable such an error\n"
	"detection and allow overwriting of symbols. You can specify an asterisk\n"
	"character to include all the registered symbols like follows.\n"
	"  import(foo) {*}");
}

AScript_ImplementFunction(import_)
{
	SymbolSet symbolsToMixIn;
	SymbolSet *pSymbolsToMixIn = NULL;
	if (args.IsBlockSpecified()) {
		const Expr_Block *pExprBlock = args.GetBlock(env, sig);
		if (sig.IsSignalled()) return Value::Null;
		foreach_const (ExprList, ppExpr, pExprBlock->GetExprList()) {
			if (!(*ppExpr)->IsSymbol()) {
				sig.SetError(ERR_SyntaxError,
					"wrong format for an element in import list");
				return Value::Null;
			}
			const Expr_Symbol *pExprSymbol =
							dynamic_cast<const Expr_Symbol *>(*ppExpr);
			symbolsToMixIn.insert(pExprSymbol->GetSymbol());
		}
		pSymbolsToMixIn = &symbolsToMixIn;
	}
	const Symbol *pSymbol = NULL;
	if (!args.IsExpr(1)) {
		// nothing to do
	} else if (!args.GetExpr(1)->IsSymbol()) {
		sig.SetError(ERR_ValueError, "symbol is expected as a module name");
		return Value::Null;
	} else {
		pSymbol =
			dynamic_cast<const Expr_Symbol *>(args.GetExpr(1))->GetSymbol();
	}
	if (!env.ImportModule(sig, args.GetExpr(0), pSymbol,
				pSymbolsToMixIn, args.IsSet(AScript_Symbol(overwrite)))) {
		return Value::Null;
	}
	return Value::Null;
}

// parsestream(stream:stream):map
AScript_DeclareFunction(parsestream)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "stream", VTYPE_Stream);
	SetHelp("Parse a content of a script stream and returns an expr object.");
}

AScript_ImplementFunction(parsestream)
{
	Expr *pExpr = Parser().ParseStream(env, sig, args.GetStream(0));
	if (sig.IsSignalled()) return NULL;
	return Value(env, pExpr);
}

// parse(code:string):map
AScript_DeclareFunction(parse)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "code", VTYPE_String);
	SetHelp("Parse a string returns an expr object.");
}

AScript_ImplementFunction(parse)
{
	const char *str = args.GetString(0);
	Expr *pExpr = Parser().ParseString(env, sig, "<parse function>", str);
	if (sig.IsSignalled()) return Value::Null;
	return Value(env, pExpr);
}

// eval(expr:expr):map
AScript_DeclareFunction(eval)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "expr", VTYPE_Expr);
	SetHelp("Evaluate an expr object.");
}

AScript_ImplementFunction(eval)
{
	Environment envBlock(&env, ENVTYPE_Block);
	return args.GetExpr(0)->Exec(envBlock, sig);
}

// locals(module?:module)
AScript_DeclareFunction(locals)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "module", VTYPE_Module, OCCUR_ZeroOrOnce);
	SetHelp(
	"Returns an environment object that belongs to a specified module.\n"
	"If module is omitted, it returns an environment object of the current scope.");
}

AScript_ImplementFunction(locals)
{
	Value value;
	if (args.IsModule(0)) {
		value.InitAsEnvironment(*args.GetModule(0));
	} else {
		value.InitAsEnvironment(env);
	}
	return value;
}

// outers()
AScript_DeclareFunction(outers)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	SetHelp("Returns an environment object that accesses to an outer scope.");
}

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

// extern(`syms+)
AScript_DeclareFunctionAlias(extern_, "extern")
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "syms", VTYPE_Quote, OCCUR_OnceOrMore);
	SetHelp("Declares symbols that is supposed to access variables in outer scopes.");
}

AScript_ImplementFunction(extern_)
{
	foreach_const (ValueList, pValue, args.GetList(0)) {
		const Expr *pExpr = pValue->GetExpr();
		if (!pExpr->IsSymbol()) {
			sig.SetError(ERR_ValueError, "symbol must be specified");
			return Value::Null;
		}
		const Symbol *pSymbol = dynamic_cast<const Expr_Symbol *>(pExpr)->GetSymbol();
		if (env.LookupValue(pSymbol, true) == NULL) {
			sig.SetError(ERR_ValueError, "undefined symbol '%s'", pSymbol->GetName());
		}
	}
	return Value::Null;
}

// local(`syms+)
AScript_DeclareFunction(local)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "syms", VTYPE_Quote, OCCUR_OnceOrMore);
	SetHelp("Declares symbols that is supposed to access variables in a local scope.");
}

AScript_ImplementFunction(local)
{
	foreach_const (ValueList, pValue, args.GetList(0)) {
		const Expr *pExpr = pValue->GetExpr();
		if (!pExpr->IsSymbol()) {
			sig.SetError(ERR_ValueError, "symbol must be specified");
			return Value::Null;
		}
		const Symbol *pSymbol = dynamic_cast<const Expr_Symbol *>(pExpr)->GetSymbol();
		if (env.LookupValue(pSymbol, false) == NULL) {
			env.AssignValue(pSymbol, Value::Null, false);
		}
	}
	return Value::Null;
}

// try () {block}
AScript_DeclareFunctionSucceedableAlias(try_, "try")
{
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Specify a try block of a statement of try-except-else.\n"
	"It catches signals that occur in the block and executes a corresponding\n"
	"except() or else() function that follow after it.");
}

AScript_ImplementFunction(try_)
{
	Environment envBlock(&env, ENVTYPE_Block);
	const Expr_Block *pExprBlock = args.GetBlock(envBlock, sig);
	if (sig.IsSignalled()) return Value::Null;
	Value result = pExprBlock->Exec(envBlock, sig);
	sig.SuspendError();
	args.RequestSucceeding(this);
	return result;
}

bool AScript_Function(try_)::IsSucceedable(const ICallable *pCallable) const
{
	return true;
}

// except (errors*:error) {block}
AScript_DeclareFunctionSucceedableAlias(except_, "except")
{
	DeclareArg(env, "errors", VTYPE_Error, OCCUR_ZeroOrMore);
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Specify an except block of a statement of try-except-else.\n"
	"It can take multiple numbers of arguments of error objects to handle.\n"
	"If there's no error objects specified, it handles all the errors that are\n"
	"not handled in the preceding except() function calls.\n"
	"Block parameter format: |error:error|\n"
	"error is an error object that contains information of the handled error.");
}

AScript_ImplementFunction(except_)
{
	bool handleFlag = false;
	if (!sig.IsErrorSuspended()) {
		// nothing to do
	} else if (args.GetList(0).empty()) {
		handleFlag = true;
	} else {
		foreach_const (ValueList, pValue, args.GetList(0)) {
			if (pValue->GetErrorType() == sig.GetErrorType()) {
				handleFlag = true;
				break;
			}
		}
	}
	if (!handleFlag) {
		args.RequestSucceeding(this);
		return Value::Null;
	}
	Object_Error *pObj = new Object_Error(env, sig.GetErrorType());
	pObj->AssignValue(AScript_Symbol(text),
				Value(env, sig.GetErrString(false).c_str()), false);
	pObj->AssignValue(AScript_Symbol(message),
				Value(env, sig.GetErrString(true).c_str()), false);
	pObj->AssignValue(AScript_Symbol(value),
				sig.GetValue(), false);
	Value value(pObj);
	ValueList valListArg(value);
	sig.ClearSignal(); // clear even the suspended state
	const Function *pFuncBlock =
						args.GetBlockFunc(env, sig, GetSymbolForBlock());
	if (sig.IsSignalled()) return Value::Null;
	Environment envBlock(&env, ENVTYPE_Block);
	Args argsSub(valListArg);
	return pFuncBlock->Eval(envBlock, sig, argsSub);
}

bool AScript_Function(except_)::IsSucceedable(const ICallable *pCallable) const
{
	return true;
}

// finally () {block}
AScript_DeclareFunctionAlias(finally_, "finally")
{
	DeclareBlock(OCCUR_Once);
}

AScript_ImplementFunction(finally_)
{
	Environment envBlock(&env, ENVTYPE_Block);
	const Expr_Block *pExprBlock = args.GetBlock(envBlock, sig);
	if (sig.IsSignalled()) return Value::Null;
	return pExprBlock->Exec(envBlock, sig);
}

// if (`cond) {block}
AScript_DeclareFunctionSucceedableAlias(if_, "if")
{
	DeclareArg(env, "cond", VTYPE_Quote);
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Specify an if block of a statement of if-elsif-else.\n"
	"After evaluating an expr object cond, the block shall be executed\n"
	"if it has a value of true.");
}

AScript_ImplementFunction(if_)
{
	Environment envBlock(&env, ENVTYPE_Block);
	Value value = args.GetExpr(0)->Exec(envBlock, sig);
	if (value.GetBoolean()) {
		const Expr_Block *pExprBlock = args.GetBlock(envBlock, sig);
		if (sig.IsSignalled()) return Value::Null;
		return pExprBlock->Exec(envBlock, sig);
	}
	args.RequestSucceeding(this);
	return Value::Null;
}

bool AScript_Function(if_)::IsSucceedable(const ICallable *pCallable) const
{
	return true;
}

// elsif (`cond) {block}
AScript_DeclareFunctionSucceedableAlias(elsif_, "elsif")
{
	DeclareArg(env, "cond", VTYPE_Quote);
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Specify an elsif block of a statement of if-elsif-else.\n"
	"After evaluating an expr object cond, the block shall be executed\n"
	"if it has a value of true.");
}

AScript_ImplementFunction(elsif_)
{
	Environment envBlock(&env, ENVTYPE_Block);
	Value value = args.GetExpr(0)->Exec(envBlock, sig);
	if (value.GetBoolean()) {
		const Expr_Block *pExprBlock = args.GetBlock(envBlock, sig);
		if (sig.IsSignalled()) return Value::Null;
		return pExprBlock->Exec(envBlock, sig);
	}
	args.RequestSucceeding(this);
	return Value::Null;
}

bool AScript_Function(elsif_)::IsSucceedable(const ICallable *pCallable) const
{
	return true;
}

// else () {block}
AScript_DeclareFunctionAlias(else_, "else")
{
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Specify an else block of a statement of if-elsif-else or try-except-else.\n");
}

AScript_ImplementFunction(else_)
{
	// this function works as a terminater of if-else and try-except
	if (sig.IsErrorSuspended()) return Value::Null;
	Environment envBlock(&env, ENVTYPE_Block);
	const Expr_Block *pExprBlock = args.GetBlock(envBlock, sig);
	if (sig.IsSignalled()) return Value::Null;
	return pExprBlock->Exec(envBlock, sig);
}

// switch {block}
AScript_DeclareFunctionAlias(switch_, "switch")
{
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Form a switch block that contains case() and default() function calls.\n"
	"It calls these functions sequentially and exits the execution\n"
	"when one of the conditions is evaluated as true.");
}

AScript_ImplementFunction(switch_)
{
	Environment envBlock(&env, ENVTYPE_Block);
	const Expr_Block *pExprBlock = args.GetBlock(envBlock, sig);
	if (sig.IsSignalled()) return Value::Null;
	pExprBlock->Exec(envBlock, sig);
	if (sig.IsSwitchDone()) {
		Value result = sig.GetValue();
		sig.ClearSignal();
		return result;
	}
	return Value::Null;
}

// case (`cond) {block}
AScript_DeclareFunctionAlias(case_, "case")
{
	DeclareArg(env, "cond", VTYPE_Quote);
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Specify an case block within a switch block.\n"
	"After evaluating an expr object cond, the block shall be executed\n"
	"if it has a value of true.");
}

AScript_ImplementFunction(case_)
{
	Environment envBlock(&env, ENVTYPE_Block);
	Value value = args.GetExpr(0)->Exec(envBlock, sig);
	if (value.GetBoolean()) {
		const Expr_Block *pExprBlock = args.GetBlock(envBlock, sig);
		if (sig.IsSignalled()) return Value::Null;
		Value result = pExprBlock->Exec(envBlock, sig);
		if (sig.IsSignalled()) return Value::Null;
		sig.SetSignal(SIGTYPE_SwitchDone, result);
		return result;
	}
	return Value::Null;
}

// default {block}
AScript_DeclareFunctionAlias(default_, "default")
{
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Specify a default block within a switch block.\n"
	"If all the preceding condition of case block are not evaluated as true,\n"
	"this block shall be executed.\n");
}

AScript_ImplementFunction(default_)
{
	Environment envBlock(&env, ENVTYPE_Block);
	const Expr_Block *pExprBlock = args.GetBlock(envBlock, sig);
	if (sig.IsSignalled()) return Value::Null;
	Value result = pExprBlock->Exec(envBlock, sig);
	if (sig.IsSignalled()) return Value::Null;
	sig.SetSignal(SIGTYPE_SwitchDone, result);
	return result;
}

// repeat (n?:number) {block}
AScript_DeclareFunction(repeat)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Executes the block for n times. If n is omitted, it repeats the block\n"
	"execution forever.\n"
	REPEATER_HELP
	"Block parameter format: |idx:number|");
}

AScript_ImplementFunction(repeat)
{
	Environment envBlock(&env, ENVTYPE_Block);
	const Function *pFuncBlock =
						args.GetBlockFunc(envBlock, sig, GetSymbolForBlock());
	if (pFuncBlock == NULL) return Value::Null;
	bool standaloneFlag = (args.IsRsltIterator() || args.IsRsltXIterator());
	Iterator *pIterator = new Iterator_repeat(envBlock, sig, Function::Reference(pFuncBlock),
			args.IsRsltXIterator(), standaloneFlag,
			args.IsNumber(0)? args.GetInt(0) : -1);
	return DoRepeater(env, sig, args, pIterator);
}

// while (`cond) {block}
AScript_DeclareFunctionAlias(while_, "while")
{
	DeclareArg(env, "cond", VTYPE_Quote);
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Executes the block while the evaluation result of cond is true.\n"
	REPEATER_HELP
	"Block parameter format: |idx:number|");
}

AScript_ImplementFunction(while_)
{
	Environment envBlock(&env, ENVTYPE_Block);
	const Function *pFuncBlock =
						args.GetBlockFunc(envBlock, sig, GetSymbolForBlock());
	if (pFuncBlock == NULL) return Value::Null;
	bool standaloneFlag = (args.IsRsltIterator() || args.IsRsltXIterator());
	Iterator *pIterator = new Iterator_while(envBlock, sig, Function::Reference(pFuncBlock),
			args.IsRsltXIterator(), standaloneFlag, args.GetExpr(0)->IncRef());
	return DoRepeater(env, sig, args, pIterator);
}

// for (`expr+) {block}
AScript_DeclareFunctionAlias(for_, "for")
{
	DeclareArg(env, "expr", VTYPE_Quote, OCCUR_OnceOrMore);
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Executes the block until any of the exprs of \"var in iteratable\" reach at\n"
	"their ends. You can specify one or more such exprs as arguments.\n"
	"Iterators and lists are the most popular iteratables, but even any objects that\n"
	"are cable of generating iterators can be specified as such.\n"
	REPEATER_HELP
	"Block parameter format: |idx:number|");
}

AScript_ImplementFunction(for_)
{
	Environment envBlock(&env, ENVTYPE_Block);
	const Function *pFuncBlock =
						args.GetBlockFunc(envBlock, sig, GetSymbolForBlock());
	if (pFuncBlock == NULL) return Value::Null;
	bool standaloneFlag = (args.IsRsltIterator() || args.IsRsltXIterator());
	Iterator *pIterator = new Iterator_for(envBlock, sig, Function::Reference(pFuncBlock),
				args.IsRsltXIterator(), standaloneFlag, args.GetList(0));
	return DoRepeater(env, sig, args, pIterator);
}

// cross (`expr+) {block}
AScript_DeclareFunction(cross)
{
	DeclareArg(env, "expr", VTYPE_Quote, OCCUR_OnceOrMore);
	DeclareBlock(OCCUR_Once);
	SetHelp(
	"Executes the block until it evaluates all the combinations of results from exprs\n"
	"\"var in iteratable.\" You can specify one or more such exprs as arguments and\n"
	"they are counted up from the one on the right side.\n"
	"Iterators and lists are the most popular iteratables, but even any objects that\n"
	"are cable of generating iterators can be specified as such.\n"
	REPEATER_HELP
	"Block parameter format: |idx:number, i0:number, i1:number, ..|");
}

AScript_ImplementFunction(cross)
{
	Environment envBlock(&env, ENVTYPE_Block);
	const Function *pFuncBlock =
						args.GetBlockFunc(envBlock, sig, GetSymbolForBlock());
	if (pFuncBlock == NULL) return Value::Null;
	bool standaloneFlag = (args.IsRsltIterator() || args.IsRsltXIterator());
	Iterator *pIterator = new Iterator_cross(envBlock, sig, Function::Reference(pFuncBlock),
				args.IsRsltXIterator(), standaloneFlag, args.GetList(0));
	return DoRepeater(env, sig, args, pIterator);
}

// break(value?):symbol_func
AScript_DeclareFunctionAlias(break_, "break")
{
	SetAsSymbolFunc();
	DeclareArg(env, "value", VTYPE_Any, OCCUR_ZeroOrOnce);
	SetHelp(
	"Exits from an inside of a loop that is formed with functions repeat(), while()\n"
	"for() and cross(). If it takes an argument, that value is treated as a result of\n"
	"the loop function. Otherwise, the result is nil and an argument list\n"
	"can be omitted. If the loop function is specified with one of :list, :xlist, :set,\n"
	":xset, :iter and :xiter, break()'s value is NOT included in the result.");
}

AScript_ImplementFunction(break_)
{
	sig.SetSignal(SIGTYPE_Break, args.GetValue(0));
	return Value::Null;
}

// continue(value?):symbol_func
AScript_DeclareFunctionAlias(continue_, "continue")
{
	SetAsSymbolFunc();
	DeclareArg(env, "value", VTYPE_Any, OCCUR_ZeroOrOnce);
	SetHelp(
	"In a loop that is formed with functions repeat(), while(), for() and cross(),\n"
	"skips the following part of it and gets to the top of its process.\n"
	"If it takes an argument, that value is treated as a result of the loop function.\n"
	"Otherwise, the result is nil and an argument list can be omitted.\n"
	"If the loop function is specified with one of :list, :xlist, :set,\n"
	":xset, :iter and :xiter, continue()'s value is included in the result.");
}

AScript_ImplementFunction(continue_)
{
	sig.SetSignal(SIGTYPE_Continue, args.GetValue(0));
	return Value::Null;
}

// return(value?):symbol_func
AScript_DeclareFunctionAlias(return_, "return")
{
	SetAsSymbolFunc();
	DeclareArg(env, "value", VTYPE_Any, OCCUR_ZeroOrOnce);
	SetHelp(
	"Exits from a function skipping the following part of it.\n"
	"If it takes an argument, that value is treated as a result of the function.\n"
	"Otherwise, the result is nil and an argument list can be omitted.");
}

AScript_ImplementFunction(return_)
{
	sig.SetSignal(SIGTYPE_Return, args.GetValue(0));
	return Value::Null;
}

// raise(error:error, msg:string => "error", value?)
AScript_DeclareFunction(raise)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "error", VTYPE_Error);
	DeclareArg(env, "msg", VTYPE_String, OCCUR_Once, false, false, new Expr_String("error"));
	DeclareArg(env, "value", VTYPE_Any, OCCUR_ZeroOrOnce);
	SetHelp(
	"Raises an error signal with a specified error object, a message string and\n"
	"an additional value.");
}

AScript_ImplementFunction(raise)
{
	sig.SetError(args.GetErrorType(0), "%s", args.GetString(1));
	sig.SetValue(args.GetValue(2));
	return Value::Null;
}

// dim(n+:number) {block}
AScript_DeclareFunction(dim)
{
	DeclareArg(env, "n", VTYPE_Number, OCCUR_OnceOrMore);
	DeclareBlock(OCCUR_ZeroOrOnce);
	SetHelp(
	"Creates and returns a multi-dementional list that contains nested lists as\n"
	"specified by the arguments.\n"
	"Block parameter format: |i0:number, i1:number, ..|");
}

bool Func_dim_Sub(Environment &env, Signal sig, const Function *pFuncBlock, ValueList &valListParent,
	IntList &cntList, IntList::iterator pCnt, IntList &idxList, IntList::iterator pIdx)
{
	if (pCnt + 1 == cntList.end()) {
		if (pFuncBlock == NULL) {
			for (*pIdx = 0; *pIdx < *pCnt; (*pIdx)++) {
				valListParent.push_back(Value::Null);
			}
		} else {
			for (*pIdx = 0; *pIdx < *pCnt; (*pIdx)++) {
				ValueList valListArg;
				valListArg.reserve(idxList.size());
				foreach (IntList, pIdxWk, idxList) {
					valListArg.push_back(Value(*pIdxWk));
				}
				Args args(valListArg);
				Value result = pFuncBlock->Eval(env, sig, args);
				if (sig.IsSignalled()) return false;
				valListParent.push_back(result);
			}
		}
	} else {
		for (*pIdx = 0; *pIdx < *pCnt; (*pIdx)++) {
			Value result;
			ValueList &valList = result.InitAsList(env);
			valListParent.push_back(result);
			if (!Func_dim_Sub(env, sig, pFuncBlock, valList,
									cntList, pCnt + 1, idxList, pIdx + 1)) {
				return false;
			}
		}
	}
	return true;
}

AScript_ImplementFunction(dim)
{
	Environment envBlock(&env, ENVTYPE_Block);
	const Function *pFuncBlock =
						args.GetBlockFunc(envBlock, sig, GetSymbolForBlock());
	const ValueList &valListArg = args.GetList(0);
	size_t nArgs = valListArg.size();
	IntList cntList, idxList;
	cntList.reserve(nArgs);
	idxList.reserve(nArgs);
	foreach_const (ValueList, pValArg, valListArg) {
		cntList.push_back(pValArg->GetInt());
		idxList.push_back(0);
	}
	Value result;
	ValueList &valList = result.InitAsList(env);
	if (!Func_dim_Sub(envBlock, sig, pFuncBlock, valList,
						cntList, cntList.begin(), idxList, idxList.begin())) {
		return Value::Null;
	}
	return result;
}

// zip(values+) {block?}
AScript_DeclareFunction(zip)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "values", VTYPE_Any, OCCUR_OnceOrMore);
	DeclareBlock(OCCUR_ZeroOrOnce);
	SetHelp(
	"Creates an iterator generating lists that bind given argument values.\n"
	"When the value is a list or an iterator, each item in it would be zipped.\n"
	ITERATOR_HELP);
}

AScript_ImplementFunction(zip)
{
	IteratorOwner iterOwner;
	bool iteratorFlag = false;
	foreach_const (ValueList, pValue, args.GetList(0)) {
		if (pValue->IsList() || pValue->IsIterator()) {
			iteratorFlag = true;
			break;
		}
	}
	if (!iteratorFlag) {
		Value result;
		ValueList &valList = result.InitAsList(env);
		foreach_const (ValueList, pValue, args.GetList(0)) {
			valList.push_back(*pValue);
		}
		return result;
	}
	foreach_const (ValueList, pValue, args.GetList(0)) {
		Iterator *pIteratorArg = NULL;
		if (pValue->IsList() || pValue->IsIterator()) {
			pIteratorArg = pValue->CreateIterator(sig);
			if (sig.IsSignalled()) return Value::Null;
		} else {
			pIteratorArg = new Iterator_Constant(*pValue);
		}
		iterOwner.push_back(pIteratorArg);
	}
	Iterator *pIterator = new Iterator_Zip(iterOwner);
	return ReturnIterator(env, sig, args, pIterator);
}

// iterator(value+)
AScript_DeclareFunction(iterator)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "value", VTYPE_Any, OCCUR_OnceOrMore);
}

AScript_ImplementFunction(iterator)
{
	Iterator_Concat *pIterator = new Iterator_Concat();
	foreach_const (ValueList, pValue, args.GetList(0)) {
		Iterator *pIteratorArg = NULL;
		if (pValue->IsList() || pValue->IsIterator()) {
			pIteratorArg = pValue->CreateIterator(sig);
			if (sig.IsSignalled()) return Value::Null;
		} else {
			pIteratorArg = new Iterator_OneShot(*pValue);
		}
		pIterator->Add(pIteratorArg);
	}
	return Value(env, pIterator);
}

// list(iter+:iterator)
AScript_DeclareFunction(list)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "iter", VTYPE_Iterator, OCCUR_OnceOrMore);
}

AScript_ImplementFunction(list)
{
	Value result;
	ValueList &valList = result.InitAsList(env);
	foreach_const (ValueList, pValue, args.GetList(0)) {
		Value value;
		Iterator *pIterator = pValue->GetIterator();
		if (pIterator->IsInfinite()) {
			Iterator::SetError_InfiniteNotAllowed(sig);
			return Value::Null;
		}
		while (pIterator->Next(env, sig, value)) {
			valList.push_back(value);
		}
		if (sig.IsSignalled()) return Value::Null;
	}
	return result;
}

// xlist(iter+:iterator)
AScript_DeclareFunction(xlist)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "iter", VTYPE_Iterator, OCCUR_OnceOrMore);
}

AScript_ImplementFunction(xlist)
{
	Value result;
	ValueList &valList = result.InitAsList(env);
	foreach_const (ValueList, pValue, args.GetList(0)) {
		Value value;
		Iterator *pIterator = pValue->GetIterator();
		if (pIterator->IsInfinite()) {
			Iterator::SetError_InfiniteNotAllowed(sig);
			return Value::Null;
		}
		while (pIterator->Next(env, sig, value)) {
			if (value.IsValid()) valList.push_back(value);
		}
		if (sig.IsSignalled()) return Value::Null;
	}
	return result;
}

// set(iter+:iterator):[and,xor,or]
// xset(iter+:iterator):[and,xor,or]
AScript_DeclareFunctionBegin(set_xset)
	bool _acceptInvalidFlag;
AScript_DeclareFunctionEnd(set_xset)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	_acceptInvalidFlag = (::strcmp(GetName(), "set") == 0);
	DeclareArg(env, "iter", VTYPE_Iterator, OCCUR_OnceOrMore);
	DeclareAttr(AScript_Symbol(or));
	DeclareAttr(AScript_Symbol(and));
	DeclareAttr(AScript_Symbol(xor));
}

AScript_ImplementFunction(set_xset)
{
	Value result;
	ValueList &valList = result.InitAsList(env);
	if (args.IsSet(AScript_Symbol(and))) {			// AND combination
		ValueList valList1, valList2;
		ValueList::const_reverse_iterator pValueArg = args.GetList(0).rbegin();
		Value value;
		for (Iterator *pIterator = pValueArg->GetIterator();
											pIterator->Next(env, sig, value); ) {
			if ((_acceptInvalidFlag || value.IsValid()) &&
											!valList1.IsContain(value)) {
				valList1.push_back(value);
			}
		}
		if (sig.IsSignalled()) return Value::Null;
		pValueArg++;
		ValueList *pValListAnd = &valList1;
		ValueList *pValListWork = &valList2;
		for ( ; pValueArg != args.GetList(0).rend() &&
										!pValListAnd->empty(); pValueArg++) {
			Value value;
			for (Iterator *pIterator = pValueArg->GetIterator();
											pIterator->Next(env, sig, value); ) {
				if (pValListAnd->IsContain(value) && !pValListWork->IsContain(value)) {
					pValListWork->push_back(value);
				}
			}
			ValueList *pValListTmp = pValListAnd;
			pValListAnd = pValListWork, pValListWork = pValListTmp;
			pValListWork->clear();
		}
		foreach_const (ValueList, pValue, *pValListAnd) {
			valList.push_back(*pValue);
		}
	} else if (args.IsSet(AScript_Symbol(xor))) {	// XOR combination
		ValueList valList1, valList2;
		ValueList *pValListAnd = &valList1;
		ValueList valListOr;
		ValueList::const_iterator pValueArg = args.GetList(0).begin();
		Value value;
		for (Iterator *pIterator = pValueArg->GetIterator();
										pIterator->Next(env, sig, value); ) {
			if (!valList1.IsContain(value)) valList1.push_back(value);
			if ((_acceptInvalidFlag || value.IsValid()) &&
											!valListOr.IsContain(value)) {
				valListOr.push_back(value);
			}
		}
		if (sig.IsSignalled()) return Value::Null;
		pValueArg++;
		ValueList *pValListWork = &valList2;
		for ( ; pValueArg != args.GetList(0).end() &&
										!pValListAnd->empty(); pValueArg++) {
			Value value;
			for (Iterator *pIterator = pValueArg->GetIterator();
										pIterator->Next(env, sig, value); ) {
				if (pValListAnd->IsContain(value)) pValListWork->push_back(value);
				if ((_acceptInvalidFlag || value.IsValid()) &&
												!valListOr.IsContain(value)) {
					valListOr.push_back(value);
				}
			}
			if (sig.IsSignalled()) return Value::Null;
			ValueList *pValListTmp = pValListAnd;
			pValListAnd = pValListWork, pValListWork = pValListTmp;
			pValListWork->clear();
		}
		foreach_const (ValueList, pValue, valListOr) {
			if (!pValListAnd->IsContain(*pValue)) {
				valList.push_back(*pValue);
			}
		}
	} else {										// OR combination
		foreach_const (ValueList, pValue, args.GetList(0)) {
			Value value;
			for (Iterator *pIterator = pValue->GetIterator();
												pIterator->Next(env, sig, value); ) {
				if ((_acceptInvalidFlag || value.IsValid()) &&
												!valList.IsContain(value)) {
					valList.push_back(value);
				}
			}
			if (sig.IsSignalled()) return Value::Null;
		}
	}
	return result;
}

// min(values+):map
AScript_DeclareFunction(min)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "values", VTYPE_Any, OCCUR_OnceOrMore);
	SetHelp("Returns the minimum value among the given arguments.");
}

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

// max(values+):map
AScript_DeclareFunction(max)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "values", VTYPE_Any, OCCUR_OnceOrMore);
	SetHelp("Returns the maximum value among the given arguments.");
}

AScript_ImplementFunction(max)
{
	const ValueList &valList = args.GetList(0);
	ValueList::const_iterator pValue = valList.begin();
	Value result = *pValue++;
	for ( ; pValue != valList.end(); pValue++) {
		int cmp = Value::Compare(result, *pValue);
		if (cmp < 0) result = *pValue;
	}
	return result;
}

// choose(index:number, values+):map
AScript_DeclareFunction(choose)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "index", VTYPE_Number);
	DeclareArg(env, "values", VTYPE_Any, OCCUR_OnceOrMore);
}

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

// chooseif(flag, value1, vlaue2):map
AScript_DeclareFunction(chooseif)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "flag", VTYPE_Boolean);
	DeclareArg(env, "value1", VTYPE_Any);
	DeclareArg(env, "value2", VTYPE_Any);
}

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

// tostring(value):map
AScript_DeclareFunction(tostring)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "value", VTYPE_Any);
}

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

// tonumber(value):map:[strict,raise,zero,nil]
AScript_DeclareFunction(tonumber)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "value", VTYPE_Any);
	DeclareAttr(AScript_Symbol(strict));
	DeclareAttr(AScript_Symbol(raise));
	DeclareAttr(AScript_Symbol(zero));
	DeclareAttr(AScript_Symbol(nil));
}

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

// tosymbol(str:string):map
AScript_DeclareFunction(tosymbol)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
}

AScript_ImplementFunction(tosymbol)
{
	return Value(Symbol::Add(args.GetString(0)));
}

// int(value):map
AScript_DeclareFunctionAlias(int_, "int")
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "value", VTYPE_Any);
	SetHelp("Converts any value into an integer number.");
}

AScript_ImplementFunction(int_)
{
	const Value &value = args.GetValue(0);
	Value result;
	if (value.IsNumber()) {
		result.SetNumber(value.GetLong());
	} else if (value.IsComplex()) {
		result.SetNumber(static_cast<long>(std::abs(value.GetComplex())));
	} else if (value.IsString()) {
		bool successFlag;
		Number num = value.ToNumber(true, successFlag);
		if (!successFlag) {
			sig.SetError(ERR_ValueError, "failed to convert to a number");
			return Value::Null;
		}
		result.SetNumber(static_cast<long>(num));
	} else if (value.IsValid()) {
		SetError_InvalidValType(sig, value);
	}
	return result;
}

// ord(str:string):map
AScript_DeclareFunction(ord)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "str", VTYPE_String);
}

AScript_ImplementFunction(ord)
{
	const char *str = args.GetString(0);
	unsigned long num = static_cast<unsigned char>(*str);
	if (IsUTF8First(*str)) {
		str++;
		for ( ; IsUTF8Follower(*str); str++) {
			num = (num << 8) | static_cast<unsigned char>(*str);
		}
	}
	return Value(num);
}

// chr(num:number):map
AScript_DeclareFunction(chr)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "num", VTYPE_Number);
}

AScript_ImplementFunction(chr)
{
	unsigned long num = args.GetULong(0);
	int i = 0;
	char buff[4];
	buff[i++] = static_cast<unsigned char>(num & 0xff);
	num >>= 8;
	for ( ; num != 0; num >>= 8) {
		buff[i++] = static_cast<unsigned char>(num & 0xff);
	}
	String str;
	for ( ; i > 0; i--) {
		str.push_back(buff[i - 1]);
	}
	return Value(env, str.c_str());
}

// hex(num:number, digits?:number):map:[upper]
AScript_DeclareFunction(hex)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "num", VTYPE_Number);
	DeclareArg(env, "digits", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(upper));
}

AScript_ImplementFunction(hex)
{
	int digits = args.IsNumber(1)? args.GetInt(1) : 0;
	bool upperFlag = args.IsSet(AScript_Symbol(upper));
	String str;
	if (digits <= 0) {
		str = Formatter::Format(sig, upperFlag? "%X" : "%x",
						ValueList(args.GetValue(0)));
	} else {
		str = Formatter::Format(sig, upperFlag? "%0*X" : "%0*x",
						ValueList(Value(digits), args.GetValue(0)));
	}
	if (sig.IsSignalled()) return Value::Null;
	return Value(env, str.c_str());
}

// print(value*):map:void
AScript_DeclareFunction(print)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "value", VTYPE_Any, OCCUR_ZeroOrMore);
}

AScript_ImplementFunction(print)
{
	Stream *pConsole = env.GetConsole(false);
	if (pConsole == NULL) return Value::Null;
	foreach_const (ValueList, pValue, args.GetList(0)) {
		pConsole->Print(sig, pValue->ToString(sig, false).c_str());
		if (sig.IsSignalled()) break;
	}
	return Value::Null;
}

// println(value*):map:void
AScript_DeclareFunction(println)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "value", VTYPE_Any, OCCUR_ZeroOrMore);
}

AScript_ImplementFunction(println)
{
	Stream *pConsole = env.GetConsole(false);
	if (pConsole == NULL) return Value::Null;
	foreach_const (ValueList, pValue, args.GetList(0)) {
		pConsole->Print(sig, pValue->ToString(sig, false).c_str());
		if (sig.IsSignalled()) break;
	}
	pConsole->Print(sig, "\n");
	return Value::Null;
}

// printf(format, values*):map:void
AScript_DeclareFunction(printf)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "format", VTYPE_String);
	DeclareArg(env, "values", VTYPE_Any, OCCUR_ZeroOrMore);
}

AScript_ImplementFunction(printf)
{
	Stream *pConsole = env.GetConsole(false);
	if (pConsole == NULL) return Value::Null;
	pConsole->Printf(sig, args.GetString(0), args.GetList(1));
	return Value::Null;
}

// dir(obj?)
AScript_DeclareFunction(dir)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "obj", VTYPE_Any, OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(dir)
{
	Value result;
	ValueList &valList = result.InitAsList(env);
	if (args.IsValid(0)) {
		SymbolSet symbols;
		if (!args.GetValue(0).PropDir(env, sig, symbols)) return Value::Null;
		foreach_const (SymbolSet, ppSymbol, symbols) {
			const Symbol *pSymbol = *ppSymbol;
			valList.push_back(Value(env, pSymbol->GetName()));
		}
	} else {
		foreach_const (ValueMap, iter, env.GetBottomFrame().GetValueMap()) {
			const Symbol *pSymbol = iter->first;
			valList.push_back(Value(env, pSymbol->GetName()));
		}
	}
	return result;
}

#if 0
AScript_ImplementFunction(dir)
{
	const Environment::Frame *pFrame = &env.GetBottomFrame();
	const Value &value = args.GetValue(0);
	if (value.IsModule()) {
		pFrame = &value.GetModule()->GetTopFrame();
	} else if (value.IsClass()) {
		pFrame = &value.GetClass()->GetTopFrame();
	} else if (value.IsFunction() &&
						value.GetFunction()->GetClassToConstruct() != NULL) {
		const Class *pClass = value.GetFunction()->GetClassToConstruct();
		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->IsType(ENVTYPE_Class) && !pFrame->IsType(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, "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;
}
#endif


// help(func:function):map:void
AScript_DeclareFunction(help)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "func", VTYPE_Function);
	SetHelp("Print a help message for the specified function object.");
}

AScript_ImplementFunction(help)
{
	Object_Function *pFuncObj = args.GetFunctionObj(0);
	Stream *pConsole = env.GetConsole(false);
	pConsole->Println(sig, pFuncObj->ToString(sig, true).c_str());
	if (sig.IsSignalled()) return Value::Null;
	if (pFuncObj->GetFunction()->IsHelpExist()) {
		const char *lineTop = "  ";
		bool lineTopFlag = true;
		for (const char *p = pFuncObj->GetFunction()->GetHelp(); *p != '\0'; p++) {
			char ch = *p;
			if (lineTopFlag) {
				pConsole->Print(sig, lineTop);
				if (sig.IsSignalled()) return Value::Null;
				lineTopFlag = false;
			}
			pConsole->PutChar(sig, ch);
			if (sig.IsSignalled()) return Value::Null;
			if (ch == '\n') lineTopFlag = true;
		}
		if (!lineTopFlag) pConsole->PutChar(sig, '\n');
	}
	return Value::Null;
}

// isdefined(`symbol)
AScript_DeclareFunction(isdefined)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "symbol", VTYPE_Quote);
}

AScript_ImplementFunction(isdefined)
{
	bool definedFlag = false;
	const Expr *pExpr = args.GetExpr(0);
	if (pExpr->IsSymbol() || pExpr->IsField()) {
		Value result = pExpr->Exec(env, sig);
		if (sig.IsSignalled() && !sig.IsError()) return Value::Null;
		definedFlag = !sig.IsError() && result.IsDefined();
		sig.ClearSignal();
	} else {
		sig.SetError(ERR_ValueError, "argument must be a symbol");
		return Value::Null;
	}
	return Value(definedFlag);
}

// istype*(value)
class AScript_Function(istype_) : public Function {
private:
	ValueType _valType;
public:
	AScript_Function(istype_)(Environment &env, const char *name, ValueType valType);
	virtual Value DoEval(Environment &env, Signal sig, Args &args) const;
};
AScript_Function(istype_)::AScript_Function(istype_)(
					Environment &env, const char *name, ValueType valType) :
	Function(env, Symbol::Add(name), FUNCTYPE_Function), _valType(valType)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "value", VTYPE_Any);
	char buff[128];
	::sprintf(buff, "Check if the type of the specified value is %s.",
									GetValueTypeSymbol(_valType)->GetName());
	SetHelp(buff);
}

AScript_ImplementFunction(istype_)
{
	ValueType valType = args.GetValue(0).GetType();
	ValueType valTypeCmp = _valType;
	if (valType == VTYPE_Number && valTypeCmp == VTYPE_Complex) return Value(true);
	return Value(valType == valTypeCmp);
}

// istype(value, type:expr):map
AScript_DeclareFunction(istype)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "value", VTYPE_Any);
	DeclareArg(env, "type", VTYPE_Expr);
}

AScript_ImplementFunction(istype)
{
	SymbolList symbolList;
	if (!args.GetExpr(1)->GetChainedSymbolList(symbolList)) {
		sig.SetError(ERR_TypeError, "invalid type name");
		return Value::Null;
	}
	const ValueTypeInfo *pValueTypeInfo = env.LookupValueType(symbolList);
	if (pValueTypeInfo == NULL) {
		sig.SetError(ERR_ValueError, "invalid type name");
		return Value::Null;
	}
	ValueType valType = args.GetValue(0).GetType();
	ValueType valTypeCmp = pValueTypeInfo->GetValueType();
	if (valType == VTYPE_Number && valTypeCmp == VTYPE_Complex) return Value(true);
	return Value(valType == valTypeCmp);
}

// isinstance(value, type:expr):map
AScript_DeclareFunction(isinstance)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "value", VTYPE_Any);
	DeclareArg(env, "type", VTYPE_Expr);
}

AScript_ImplementFunction(isinstance)
{
	SymbolList symbolList;
	if (!args.GetExpr(1)->GetChainedSymbolList(symbolList)) {
		sig.SetError(ERR_TypeError, "invalid type name");
		return Value::Null;
	}
	const ValueTypeInfo *pValueTypeInfo = env.LookupValueType(symbolList);
	if (pValueTypeInfo == NULL) {
		sig.SetError(ERR_ValueError, "invalid type name");
		return Value::Null;
	}
	return args.GetValue(0).IsInstanceOf(pValueTypeInfo->GetValueType());
}

// classref(type:expr):map
AScript_DeclareFunction(classref)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "type", VTYPE_Expr);
}

AScript_ImplementFunction(classref)
{
	SymbolList symbolList;
	if (!args.GetExpr(0)->GetChainedSymbolList(symbolList)) {
		sig.SetError(ERR_TypeError, "invalid type name");
		return Value::Null;
	}
	const ValueTypeInfo *pValueTypeInfo = env.LookupValueType(symbolList);
	if (pValueTypeInfo == NULL) {
		sig.SetError(ERR_ValueError, "invalid type name");
		return Value::Null;
	}
	if (pValueTypeInfo->GetClass() == NULL) {
		sig.SetError(ERR_ValueError, "not a class type");
		return Value::Null;
	}
	return Value(pValueTypeInfo->GetClass()->IncRef());
}

// typename(`value)
AScript_DeclareFunctionAlias(typename_, "typename")
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "value", VTYPE_Quote);
}

AScript_ImplementFunction(typename_)
{
	const Expr *pExpr = args.GetExpr(0);
	const char *typeName = "unknown";
	if (pExpr->IsSymbol() || pExpr->IsField()) {
		Value value = pExpr->Exec(env, sig);
		if (sig.IsSignalled() && !sig.IsError()) return Value::Null;
		typeName = sig.IsError()? "undefined" : value.GetTypeName();
		sig.ClearSignal();
	} else {
		Value value = pExpr->Exec(env, sig);
		if (sig.IsSignalled()) return Value::Null;
		typeName = value.GetTypeName();
	}
	return Value(env, typeName);
}

// undef(`value+):[raise]
AScript_DeclareFunctionAlias(undef_, "undef")
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "value", VTYPE_Quote, OCCUR_OnceOrMore);
	DeclareAttr(AScript_Symbol(raise));
}

AScript_ImplementFunction(undef_)
{
	bool raiseFlag = args.IsSet(AScript_Symbol(raise));
	foreach_const (ValueList, pValueArg, args.GetList(0)) {
		const Expr *pExpr = pValueArg->GetExpr();
		Environment *pEnv = &env;
		const Symbol *pSymbol = NULL;
		if (pExpr->IsSymbol()) {
			pSymbol = dynamic_cast<const Expr_Symbol *>(pExpr)->GetSymbol();
		} else {
			SymbolList symbolList;
			if (!pExpr->GetChainedSymbolList(symbolList)) {
				sig.SetError(ERR_ValueError, "invalid symbol name");
				return Value::Null;
			}
			for (SymbolList::iterator ppSymbol = symbolList.begin();
								ppSymbol + 1 != symbolList.end(); ppSymbol++) {
				Value *pValue = pEnv->LookupValue(*ppSymbol, false);
				if (pValue == NULL) {
					if (raiseFlag) {
						sig.SetError(ERR_ValueError, "undefined symbol");
					}
					return Value::Null;
				}
				if (pValue->IsModule()) {
					pEnv = pValue->GetModule();
				} else if (pValue->IsClass()) {
					pEnv = pValue->GetClass();
				} else if (pValue->IsObject()) {
					pEnv = pValue->GetObject();
				} else {
					sig.SetError(ERR_ValueError, "invalid symbol name");
					return Value::Null;
				}
			}
			pSymbol = symbolList.back();
		}
		if (raiseFlag && !pEnv->LookupValue(pSymbol, false)) {
			sig.SetError(ERR_ValueError, "undefined symbol");
			return Value::Null;
		}
		pEnv->RemoveValue(pSymbol);
	}
	return Value::Null;
}

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

AScript_ImplementFunction(range)
{
	Number numBegin = 0.;
	Number numEnd = 0.;
	Number numStep = 1.;
	if (args.GetValue(1).IsInvalid()) {
		numEnd = args.GetValue(0).GetNumber();
		if (numBegin > numEnd) numStep = -1.;
	} else if (args.GetValue(2).IsInvalid()) {
		numBegin = args.GetValue(0).GetNumber();
		numEnd = args.GetValue(1).GetNumber();
		if (numBegin > numEnd) numStep = -1.;
	} else {
		numBegin = args.GetValue(0).GetNumber();
		numEnd = args.GetValue(1).GetNumber();
		numStep = args.GetValue(2).GetNumber();
		if (numStep == 0.) {
			sig.SetError(ERR_ValueError, "step cannot be specified as zero");
			return Value::Null;
		}
	}
	return ReturnIterator(env, sig, args,
						new Iterator_Range(numBegin, numEnd, numStep));
}

// interval(a:number, b:number, samples:number):map:[open,open_l,open_r] {block?}
AScript_DeclareFunction(interval)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "a", VTYPE_Number);
	DeclareArg(env, "b", VTYPE_Number);
	DeclareArg(env, "samples", VTYPE_Number);
	DeclareAttr(AScript_Symbol(open));
	DeclareAttr(AScript_Symbol(open_l));
	DeclareAttr(AScript_Symbol(open_r));
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(interval)
{
	const ValueList &valListArg = args.GetArgs();
	Number numBegin = valListArg[0].GetNumber();
	Number numEnd = valListArg[1].GetNumber();
	int numSamples = valListArg[2].GetInt();
	if (numSamples <= 1) {
		sig.SetError(ERR_ValueError, "samples must be more than one");
		return Value::Null;
	}
	bool openFlag = args.IsSet(AScript_Symbol(open));
	bool openLeftFlag = args.IsSet(AScript_Symbol(open_l));
	bool openRightFlag = args.IsSet(AScript_Symbol(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;
	}
	Iterator *pIterator =
		new Iterator_Interval(numBegin, numEnd, numSamples, numDenom, iFactor);
	return ReturnIterator(env, sig, args, pIterator);
}

// fill(n:number, value?) {block?}
AScript_DeclareFunction(fill)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "n", VTYPE_Number);
	DeclareArg(env, "value", VTYPE_Any, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(fill)
{
	Iterator *pIterator = new Iterator_Fill(args.GetInt(0), args.GetValue(1));
	return ReturnIterator(env, sig, args, pIterator);
}

// rand(range?:number)
AScript_DeclareFunction(rand)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "range", VTYPE_Number, OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(rand)
{
	if (args.IsNumber(0)) {
		unsigned long num = args.GetULong(0);
		Number result = static_cast<unsigned long>(RandomGenerator::Real2() * num);
		return Value(result);
	}
	return Value(RandomGenerator::Real2());
}

// rands(num?:number, range?:number) {block?}
AScript_DeclareFunction(rands)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "num", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareArg(env, "range", VTYPE_Number, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(rands)
{
	Iterator *pIterator = new Iterator_Rand(
				args.IsNumber(0)? args.GetInt(0) : -1,
				args.IsNumber(1)? args.GetInt(1) : 0);
	return ReturnIterator(env, sig, args, pIterator);
}

// object()
AScript_DeclareFunction(object)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementFunction(object)
{
	Object *pObj = new Object(env.LookupClass(VTYPE_Object));
	return Value(pObj);
}

// binary(buff*)
AScript_DeclareFunction(binary)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "buff", VTYPE_Any, OCCUR_ZeroOrMore);
}

AScript_ImplementFunction(binary)
{
	Value result;
	Object_Binary *pObjBinary = result.InitAsBinary(env);
	Binary &binary = pObjBinary->GetBinary();
	foreach_const (ValueList, pValue, args.GetList(0)) {
		if (pValue->IsString()) {
			binary += pValue->GetString();
		} else if (pValue->IsBinary()) {
			binary += pValue->GetBinary();
		} else {
			sig.SetError(ERR_ValueError, "string or binary is expected");
			return Value::Null;
		}
	}
	return result;
}

// pack(format:string, value*):map
AScript_DeclareFunction(pack)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "format", VTYPE_String);
	DeclareArg(env, "value", VTYPE_Any, OCCUR_ZeroOrMore);
}

AScript_ImplementFunction(pack)
{
	Value result;
	Object_Binary *pObjBinary = result.InitAsBinary(env);
	size_t offset = 0;
	pObjBinary->Pack(sig, offset, args.GetString(0), args.GetList(1));
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// @(func?:Function) {block?}
AScript_DeclareFunction(ListInit)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "func", VTYPE_Function, OCCUR_ZeroOrOnce);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(ListInit)
{
	const Expr_Block *pExprBlock = args.GetBlock(env, sig);
	if (sig.IsSignalled()) return Value::Null;
	const Value &valueFunc = args.GetValue(0);
	Value result;
	if (pExprBlock == NULL) {
		result.InitAsList(env);
	} else if (valueFunc.IsFunction()) {
		const Function *pFunc = valueFunc.GetFunction();
		size_t cntArgs = pFunc->GetDeclOwner().size();
		if (cntArgs == 0) {
			sig.SetError(ERR_TypeError, "function '%s' needs no argument", pFunc->GetName());
			return Value::Null;
		}
		Environment envLister(&env, ENVTYPE_Lister);
		Value valueRaw =
				pExprBlock->GetExprList().ExecForList(envLister, sig, false, false);
		if (sig.IsSignalled() || !valueRaw.IsList()) return Value::Null;
		ValueList &valList = result.InitAsList(env);
		foreach_const (ValueList, pValue, valueRaw.GetList()) {
			if (!pValue->IsList()) {
				sig.SetError(ERR_SyntaxError, "invalid format in list initializer");
				return Value::Null;
			}
			Args argsSub(pValue->GetList());
			Value valueElem = pFunc->Eval(env, sig, argsSub);
			valList.push_back(valueElem);
		}
	} else {
		Environment envLister(&env, ENVTYPE_Lister);
		result = pExprBlock->GetExprList().ExecForList(envLister, sig, false, false);
	}
	return result;
}

// func = lambda(`args*) {block}
AScript_DeclareFunction(lambda)
{
	DeclareArg(env, "args", VTYPE_Quote, OCCUR_ZeroOrMore);
	DeclareBlock(OCCUR_Once);
}

AScript_ImplementFunction(lambda)
{
	const Expr_Block *pExprBlock = args.GetBlock(env, sig);
	if (sig.IsSignalled()) return Value::Null;
	ExprList exprListArg;
	foreach_const (ValueList, pValue, args.GetList(0)) {
		exprListArg.push_back(const_cast<Expr *>(pValue->GetExpr()));
	}
	if (exprListArg.empty()) {
		pExprBlock->GatherSimpleLambdaArgs(exprListArg);
	}
	FunctionCustom *pFunc = new FunctionCustom(env, AScript_Symbol(_anonymous_),
									pExprBlock->IncRef(), FUNCTYPE_Function);
	//Args argsSub(args.GetAttrs(), SymbolSet::Null,
	//								NULL, NULL, Value::Null, exprListArg);
	Args argsSub(exprListArg, Value::Null, NULL, false, NULL, args.GetAttrs());
	if (!pFunc->CustomDeclare(env, sig, SymbolSet::Null, argsSub)) {
		Function::Delete(pFunc);
		return Value::Null;
	}
	return Value(env, pFunc, Value::Null);
}

// matrix(nrows:number, ncols:number, value?)
AScript_DeclareFunction(matrix)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "nrows", VTYPE_Number);
	DeclareArg(env, "ncols", VTYPE_Number);
	DeclareArg(env, "value", VTYPE_Any, OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(matrix)
{
	Value result;
	Value valueZero(0.);
	result.InitAsMatrix(env, args.GetInt(0), args.GetInt(1),
					args.IsValid(2)? args.GetValue(2) : valueZero);
	return result;
}

// @@ {block}
AScript_DeclareFunction(MatrixInit)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareBlock(OCCUR_Once);
}

AScript_ImplementFunction(MatrixInit)
{
	const Expr_Block *pExprBlock = args.GetBlock(env, sig);
	if (sig.IsSignalled()) return Value::Null;
	Environment envLister(&env, ENVTYPE_Lister);
	Value valueInit = pExprBlock->GetExprList().ExecForList(envLister, sig, false, false);
	if (sig.IsSignalled()) return Value::Null;
	Value result;
	result.InitAsMatrix(env, sig, valueInit.GetList());
	if (sig.IsSignalled()) return Value::Null;
	return result;
}

// dict(elem[]?) {block?}, %{block}
AScript_DeclareFunction(dict)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "elem", VTYPE_Any, OCCUR_ZeroOrOnce, true);
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(dict)
{
	Object_Dict *pObj = new Object_Dict(env);
	ValueDict &valDict = pObj->GetDict();
	if (args.GetValue(0).IsList() && !valDict.Store(sig, args.GetList(0), false)) {
		return Value::Null;
	}
	if (args.IsBlockSpecified()) {
		const Expr_Block *pExprBlock = args.GetBlock(env, sig);
		if (sig.IsSignalled()) return Value::Null;
		Environment envLister(&env, ENVTYPE_Lister);
		Value valueList =
				pExprBlock->GetExprList().ExecForList(envLister, sig, false, false);
		if (sig.IsSignalled() || !valueList.IsList()) return Value::Null;
		if (!valDict.Store(sig, valueList.GetList(), false)) return Value::Null;
	}
	return Value(pObj);
}

// open(name:string, mode:string => "r", encoding:string => "utf-8"):map {block?}
AScript_DeclareFunction(open)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "name", VTYPE_String);
	DeclareArg(env, "mode", VTYPE_String, OCCUR_Once, false, false,
												new Expr_String("r"));
	DeclareArg(env, "encoding", VTYPE_String, OCCUR_Once, false, false,
												new Expr_String("utf-8"));
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(open)
{
	unsigned long attr = Stream::ATTR_None;
	do {
		const char *p = args.GetString(1);
		if (*p == 'r') {
			attr |= Stream::ATTR_Readable;
		} else if (*p == 'w') {
			attr |= Stream::ATTR_Writable;
		} else if (*p == 'a') {
			attr |= Stream::ATTR_Writable | Stream::ATTR_Append;
		} else {
			sig.SetError(ERR_IOError, "invalid open mode");
			return NULL;
		}
		p++;
		for ( ; *p != '\0'; p++) {
			char ch = *p;
			if (ch == '+') {
				attr |= Stream::ATTR_Readable | Stream::ATTR_Writable;
			} else {
				sig.SetError(ERR_IOError, "invalid open mode");
				return NULL;
			}
		}
	} while (0);
	Stream *pStream = Directory::OpenStream(env, sig,
							args.GetString(0), attr, args.GetString(2));
	if (sig.IsSignalled()) return Value::Null;
	Value result(new Object_Stream(env, pStream));
	if (args.IsBlockSpecified()) {
		const Function *pFuncBlock =
						args.GetBlockFunc(env, sig, GetSymbolForBlock());
		if (pFuncBlock == NULL) return Value::Null;
		ValueList valListArg(result);
		Args argsSub(valListArg);
		pFuncBlock->Eval(env, sig, argsSub);
		result = Value::Null;	// object is destroyed here
	}
	return result;
}

// copy(src:stream, dst:stream):map:void
AScript_DeclareFunction(copy)
{
	SetMode(RSLTMODE_Void, FLAG_Map);
	DeclareArg(env, "src", VTYPE_Stream);
	DeclareArg(env, "dst", VTYPE_Stream);
}

AScript_ImplementFunction(copy)
{
	Stream &streamSrc = args.GetStream(0);
	Stream &streamDst = args.GetStream(1);
	streamSrc.ReadToStream(sig, streamDst);
	return Value::Null;
}

// readlines(stream:stream):[chop] {block?}
AScript_DeclareFunction(readlines)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "stream", VTYPE_Stream, OCCUR_ZeroOrOnce);
	DeclareAttr(AScript_Symbol(chop));
	DeclareBlock(OCCUR_ZeroOrOnce);
}

AScript_ImplementFunction(readlines)
{
	Object_Stream *pObjStream = args.GetStreamObj(0);
	if (!pObjStream->GetStream().CheckReadable(sig)) return Value::Null;
	bool includeEOLFlag = !args.IsSet(AScript_Symbol(chop));
	Iterator *pIterator = new Object_Stream::IteratorLine(
				Object_Stream::Reference(pObjStream), -1, includeEOLFlag);
	return ReturnIterator(env, sig, args, pIterator);
}

// semaphore()
AScript_DeclareFunction(semaphore)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
}

AScript_ImplementFunction(semaphore)
{
	Object *pObj = new Object_Semaphore(env);
	return Value(pObj);
}

// image(args+):map
AScript_DeclareFunction(image)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "args", VTYPE_Any, OCCUR_OnceOrMore);
	SetHelp(
	"Returns an image object with specified characteristics. There are three patterns\n"
	"to call the function as follows.\n"
	"- image(stream:stream, format?:symbol, imgtype?:string)\n"
	"- image(format:symbol)\n"
	"- image(format:symbol, width:number, height:number, color?:color)\n"
	"In the first pattern, it creates an empty image with a specified format.\n"
	"The second reads image data from the stream and expand it in the buffer.\n"
	"The last allocates an image buffer of specified size and fills it with the color.\n"
	"Parameter format specifies the internal format. Available formats are `rgb and `rgba.");
}

AScript_ImplementFunction(image)
{
	if (sig.IsSignalled()) return Value::Null;
	ValueList valList = args.GetList(0);
	Object_Image *pObj = NULL;
	if (valList[0].IsSymbol()) {
		Declaration(AScript_Symbol(format), VTYPE_Symbol).
									ValidateAndCast(env, sig, valList[0]);
		if (sig.IsSignalled()) return Value::Null;
		Object_Image::Format format =
					Object_Image::SymbolToFormat(sig, valList[0].GetSymbol());
		if (sig.IsSignalled()) return Value::Null;
		pObj = new Object_Image(env, format);
		if (valList.size() >= 2) {
			Declaration(AScript_Symbol(width), VTYPE_Number).
										ValidateAndCast(env, sig, valList[1]);
			if (sig.IsSignalled()) return Value::Null;
			size_t width = valList[1].GetSizeT();
			size_t height = width;
			if (valList.size() >= 3) {
				Declaration(AScript_Symbol(height), VTYPE_Number).
											ValidateAndCast(env, sig, valList[2]);
				if (sig.IsSignalled()) return Value::Null;
				height = valList[2].GetSizeT();
			}
			if (!pObj->AllocBuffer(sig, width, height, 0x00)) {
				delete pObj;
				return Value::Null;
			}
			if (valList.size() >= 4) {
				Declaration(AScript_Symbol(color), VTYPE_Color).
										ValidateAndCast(env, sig, valList[3]);
				if (sig.IsSignalled()) return Value::Null;
				pObj->Fill(valList[3].GetColorObj());
			}
		}
	} else {
		Declaration(AScript_Symbol(stream), VTYPE_Stream).
									ValidateAndCast(env, sig, valList[0]);
		if (sig.IsSignalled()) return Value::Null;
		Stream &stream = valList[0].GetStream();
		Object_Image::Format format = Object_Image::FORMAT_RGBA;
		if (valList.size() >= 2) {
			Declaration(AScript_Symbol(format), VTYPE_Symbol).
									ValidateAndCast(env, sig, valList[1]);
			if (sig.IsSignalled()) return Value::Null;
			format = Object_Image::SymbolToFormat(sig, valList[1].GetSymbol());
			if (sig.IsSignalled()) return Value::Null;
		}
		pObj = new Object_Image(env, format);
		const char *imgType = NULL;
		if (valList.size() >= 3) {
			Declaration(AScript_Symbol(imgtype), VTYPE_String).
									ValidateAndCast(env, sig, valList[2]);
			if (sig.IsSignalled()) return Value::Null;
			imgType = valList[2].GetString();
		}
		if (!pObj->Read(sig, stream, imgType)) return Value::Null;
	}
	return Value(pObj);
}

// color(elem?, green:number => 0, blue:number => 0, alpha:number => 255)
AScript_DeclareFunction(color)
{
	SetMode(RSLTMODE_Normal, FLAG_Map);
	DeclareArg(env, "elem", VTYPE_Any, OCCUR_ZeroOrOnce);
	DeclareArg(env, "green", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "blue", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(0));
	DeclareArg(env, "alpha", VTYPE_Number, OCCUR_Once, false, false, new Expr_Value(255));
	SetHelp(
	"Gets either of a color symbol or a list of elements of red, green, blue and alpha,\n"
	"and returns an color object.");
}

AScript_ImplementFunction(color)
{
	if (args.IsString(0)) {
		Object_Color *pObj = Object_Color::CreateNamedColor(env,
					sig, args.GetString(0), args.GetUChar(3));
		if (sig.IsSignalled()) return Value::Null;
		return Value(pObj);
	} else if (args.IsSymbol(0)) {
		Object_Color *pObj = Object_Color::CreateNamedColor(env,
					sig, args.GetSymbol(0)->GetName(), args.GetUChar(3));
		if (sig.IsSignalled()) return Value::Null;
		return Value(pObj);
	} else if (args.IsNumber(0)) {
		unsigned char red = args.GetUChar(0);
		unsigned char green = args.GetUChar(1);
		unsigned char blue = args.GetUChar(2);
		unsigned char alpha = args.GetUChar(3);
		return Value(new Object_Color(env, red, green, blue, alpha));
	}
	Declaration::SetError_InvalidArgument(sig);
	return Value::Null;
}

// palette(type:symbol)
AScript_DeclareFunction(palette)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "type", VTYPE_Symbol);
	SetHelp(
	"Creates a palette of the specified type. type is one of: basic, win256, websafe,\n"
	"blank4, blank8, blank16, blank32, blank64, blank128 and blank256.");
}

AScript_ImplementFunction(palette)
{
	Object_Palette *pObjPalette = new Object_Palette(env);
	if (!pObjPalette->Prepare(sig, args.GetSymbol(0))) {
		Object::Delete(pObjPalette);
		return Value::Null;
	}
	return Value(pObjPalette);
}

// audio(format:symbol, len:number, channels:number => 1)
AScript_DeclareFunction(audio)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "format", VTYPE_Symbol);
	DeclareArg(env, "len", VTYPE_Number);
	DeclareArg(env, "channels", VTYPE_Number,
							OCCUR_Once, false, false, new Expr_Value(1));
}

AScript_ImplementFunction(audio)
{
	Object_Audio::Format format = Object_Audio::SymbolToFormat(sig, args.GetSymbol(0));
	if (sig.IsSignalled()) return Value::Null;
	size_t nChannels = args.GetSizeT(2);
	Object_Audio *pObj = new Object_Audio(env, format, nChannels);
	if (!pObj->AllocBuffer(sig, args.GetSizeT(1))) {
		delete pObj;
		return Value::Null;
	}
	return Value(pObj);
}

// codec(encoding:string, process_eol:boolean => false)
AScript_DeclareFunction(codec)
{
	SetMode(RSLTMODE_Normal, FLAG_None);
	DeclareArg(env, "encoding", VTYPE_String);
	DeclareArg(env, "process_eol", VTYPE_Boolean,
							OCCUR_Once, false, false, new Expr_Value(false));
}

AScript_ImplementFunction(codec)
{
	Object_Codec *pObj = new Object_Codec(env);
	if (!pObj->InstallCodec(sig, args.GetString(0), args.GetBoolean(1))) {
		return Value::Null;
	}
	return Value(pObj);
}

// Module entry
AScript_ModuleEntry()
{
	// value assignment
	AScript_AssignValue(__name__,	Value(env, "__main__"));
	AScript_AssignValue(nil,		Value::Null);
	AScript_AssignValueEx("true",	Value(true));
	AScript_AssignValueEx("false",	Value(false));
	AScript_AssignValueEx("@rem",	Value::Null); // dummy for MS-DOS batch
	AScript_AssignValueEx("*",		Value(env, new Iterator_SequenceInf(0)));
	do {
		Object_Environment *pObj = new Object_Environment(env);
		AScript_AssignValue(root, Value(pObj));
	} while (0);
	// function assignment
	AScript_AssignFunction(class_);
	AScript_AssignFunction(struct_);
	AScript_AssignFunction(try_);
	AScript_AssignFunction(except_);
	AScript_AssignFunction(finally_);
	AScript_AssignFunction(if_);
	AScript_AssignFunction(elsif_);
	AScript_AssignFunction(else_);
	AScript_AssignFunction(switch_);
	AScript_AssignFunction(case_);
	AScript_AssignFunction(default_);
	AScript_AssignFunction(repeat);
	AScript_AssignFunction(while_);
	AScript_AssignFunction(for_);
	AScript_AssignFunction(cross);
	AScript_AssignFunction(break_);
	AScript_AssignFunction(continue_);
	AScript_AssignFunction(return_);
	AScript_AssignFunction(raise);
	AScript_AssignFunction(iterator);
	AScript_AssignFunction(list);
	AScript_AssignFunction(xlist);
	AScript_AssignFunctionEx(set_xset, "set");
	AScript_AssignFunctionEx(set_xset, "xset");
	AScript_AssignFunction(dim);
	AScript_AssignFunction(zip);
	AScript_AssignFunction(min);
	AScript_AssignFunction(max);
	AScript_AssignFunction(choose);
	AScript_AssignFunction(chooseif);
	AScript_AssignFunction(module);
	AScript_AssignFunction(import_);
	AScript_AssignFunction(parsestream);
	AScript_AssignFunction(parse);
	AScript_AssignFunction(eval);
	AScript_AssignFunction(locals);
	AScript_AssignFunction(outers);
	AScript_AssignFunction(extern_);
	AScript_AssignFunction(local);
	AScript_AssignFunction(tostring);
	AScript_AssignFunction(tonumber);
	AScript_AssignFunction(tosymbol);
	AScript_AssignFunction(int_);
	AScript_AssignFunction(ord);
	AScript_AssignFunction(chr);
	AScript_AssignFunction(hex);
	AScript_AssignFunction(print);
	AScript_AssignFunction(println);
	AScript_AssignFunction(printf);
	AScript_AssignFunction(dir);
	AScript_AssignFunction(help);
	AScript_AssignFunction(isdefined);
	AScript_AssignFunction(typename_);
	AScript_AssignFunction(undef_);
	AScript_AssignFunctionExx(istype_, "issymbol",		VTYPE_Symbol);
	AScript_AssignFunctionExx(istype_, "isboolean",		VTYPE_Boolean);
	AScript_AssignFunctionExx(istype_, "isnumber",		VTYPE_Number);
	AScript_AssignFunctionExx(istype_, "iscomplex",		VTYPE_Complex);
	AScript_AssignFunctionExx(istype_, "isfunction",	VTYPE_Function);
	AScript_AssignFunctionExx(istype_, "isstring",		VTYPE_String);
	AScript_AssignFunctionExx(istype_, "isbinary",		VTYPE_Binary);
	AScript_AssignFunctionExx(istype_, "islist",		VTYPE_List);
	AScript_AssignFunctionExx(istype_, "ismatrix",		VTYPE_Matrix);
	AScript_AssignFunctionExx(istype_, "isdict",		VTYPE_Dict);
	AScript_AssignFunctionExx(istype_, "isdatetime",	VTYPE_DateTime);
	AScript_AssignFunctionExx(istype_, "istimedelta",	VTYPE_TimeDelta);
	AScript_AssignFunctionExx(istype_, "isiterator",	VTYPE_Iterator);
	AScript_AssignFunctionExx(istype_, "isexpr",		VTYPE_Expr);
	AScript_AssignFunctionExx(istype_, "isenvironment",	VTYPE_Environment);
	AScript_AssignFunctionExx(istype_, "iserror",		VTYPE_Error);
	AScript_AssignFunctionExx(istype_, "isuri",			VTYPE_URI);
	AScript_AssignFunctionExx(istype_, "issemaphore",	VTYPE_Semaphore);
	AScript_AssignFunctionExx(istype_, "ismodule",		VTYPE_Module);
	AScript_AssignFunctionExx(istype_, "isclass",		VTYPE_Class);
	AScript_AssignFunction(istype);
	AScript_AssignFunction(isinstance);
	AScript_AssignFunction(classref);
	AScript_AssignFunction(range);
	AScript_AssignFunction(interval);
	AScript_AssignFunction(fill);
	AScript_AssignFunction(rand);
	AScript_AssignFunction(rands);
	AScript_AssignFunction(object);
	AScript_AssignFunction(binary);
	AScript_AssignFunction(pack);
	AScript_AssignFunctionEx(ListInit, "@");
	AScript_AssignFunction(lambda);
	AScript_AssignFunctionEx(lambda, "&");
	AScript_AssignFunction(matrix);
	AScript_AssignFunctionEx(MatrixInit, "@@");
	AScript_AssignFunction(dict);
	AScript_AssignFunctionEx(dict, "%");
	AScript_AssignFunction(open);
	AScript_AssignFunction(copy);
	AScript_AssignFunction(readlines);
	AScript_AssignFunction(semaphore);
	AScript_AssignFunction(image);
	AScript_AssignFunction(color);
	AScript_AssignFunction(palette);
	AScript_AssignFunction(audio);
	AScript_AssignFunction(codec);
}

AScript_ModuleTerminate()
{
}

AScript_EndModule(basement, basement)

AScript_RegisterModule(basement)
