/*
 * $Id$
 *
    Copyright (c) 2006-2020 Chung, Hyung-Hwan. All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:
    1. Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in the
       documentation and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _HAWK_HAWK_HPP_
#define _HAWK_HAWK_HPP_

#include <hawk.h>

#define HAWK_USE_HTB_FOR_FUNCTION_MAP 1
//#define HAWK_VALUE_USE_IN_CLASS_PLACEMENT_NEW 1
//#define HAWK_NO_LOCATION_IN_EXCEPTION 1

// TOOD: redefine these NAMESPACE stuffs or move them somewhere else
#define HAWK_BEGIN_NAMESPACE(x) namespace x {
#define HAWK_END_NAMESPACE(x) }

#if defined(HAWK_USE_HTB_FOR_FUNCTION_MAP)
#	include <hawk-htb.h>
#else
// this part not supported for now
#	include <hawk/HashTable.hpp>
#	include <hawk/Cstr.hpp>
#endif

/// \file
/// Hawk Interpreter

/////////////////////////////////
HAWK_BEGIN_NAMESPACE(HAWK)
/////////////////////////////////

#if (__cplusplus >= 201103L) || (defined(_MSC_VER) && _MSC_VER >= 1900) // C++11 or later
	#define HAWK_LANG_CXX11 1
	#define HAWK_CXX_NOEXCEPT noexcept(true)

	/// The HAWK_CXX_ENABLE_CXX11_MOVE macro enables C++11 move semantics in various classes.
	#define HAWK_CXX_ENABLE_CXX11_MOVE 1

	// The HAWK_CXX_CALL_DESTRUCTOR() macro calls a destructor explicitly.
	#define HAWK_CXX_CALL_DESTRUCTOR(ptr, class_name) ((ptr)->~class_name())

	// The HAWK_CXX_CALL_PLACEMENT_DELETE1() macro calls the global operator delete
	// with 1 extra argument given.
	#define HAWK_CXX_CALL_PLACEMENT_DELETE1(ptr, arg1) (::operator delete((ptr), (arg1)))

#elif (__cplusplus >= 199711L) // C++98
	#undef HAWK_LANG_CXX11
	#define HAWK_CXX_NOEXCEPT throw()

	#define HAWK_CXX_CALL_DESTRUCTOR(ptr, class_name) ((ptr)->~class_name())
	#define HAWK_CXX_CALL_PLACEMENT_DELETE1(ptr, arg1) (::operator delete((ptr), (arg1)))
#else
	#undef HAWK_LANG_CXX11
	#define HAWK_CXX_NOEXCEPT

	#if defined(__BORLANDC__)

		// Explicit destructor call requires a class name depending on the
		// C++ standard/compiler.
		//
		//   Node* x;
		//   x->~Node ();
		//
		// While x->~Node() is ok with modern compilers, some old compilers
		// like BCC55 required the class name in the call as shown below.
		//
		//   x->Node::~Node ();

		#define HAWK_CXX_CALL_DESTRUCTOR(ptr, class_name) ((ptr)->class_name::~class_name())
		#define HAWK_CXX_CALL_PLACEMENT_DELETE1(ptr, arg1) (::operator delete((ptr), (arg1)))


	#elif defined(__WATCOMC__)
		// WATCOM has a problem with this syntax.
		//    Node* x; x->Node::~Node().
		// But it doesn't support operator delete overloading.

		#define HAWK_CXX_CALL_DESTRUCTOR(ptr, class_name) ((ptr)->~class_name())
		#define HAWK_CXX_CALL_PLACEMENT_DELETE1(ptr, arg1) (::hawk_operator_delete((ptr), (arg1)))

		// When  the name of a member template specialization appears after .  or
		// -> in a postfix-expression, or after :: in a qualified-id that explic-
		// itly  depends on a template-argument (_temp.dep_), the member template
		// name must be prefixed by the keyword template.  Otherwise the name  is
		// assumed to name a non-template.  [Example:
		// 		class X {
		// 		public:
		// 			   template<size_t> X* alloc();
		// 		};
		// 		void f(X* p)
		// 		{
		// 			   X* p1 = p->alloc<200>();
		// 					 // ill-formed: < means less than
		// 			   X* p2 = p->template alloc<200>();
		// 					 // fine: < starts explicit qualification
		// 		}
		// --end example]
		//
		// WATCOM doesn't support this qualifier.

		#define HAWK_CXX_NO_OPERATOR_DELETE_OVERLOADING 1
	#else

		#define HAWK_CXX_CALL_DESTRUCTOR(ptr, class_name) ((ptr)->~class_name())
		#define HAWK_CXX_CALL_PLACEMENT_DELETE1(ptr, arg1) (::operator delete((ptr), (arg1)))

	#endif

#endif

#if defined(HAWK_CXX_ENABLE_CXX11_MOVE)

	template<typename T> struct HAWK_CXX_RMREF      { typedef T Type; };
	template<typename T> struct HAWK_CXX_RMREF<T&>  { typedef T Type; };
	template<typename T> struct HAWK_CXX_RMREF<T&&> { typedef T Type; };

	template<typename T> inline
	typename HAWK_CXX_RMREF<T>::Type&& HAWK_CXX_RVREF(T&& v)
	{
		return (typename HAWK_CXX_RMREF<T>::Type&&)v;
	}
#else

	/*
	template<typename T> inline
	T& HAWK_CXX_RVREF(T& v) { return (T&)v; }

	template<typename T> inline
	const T& HAWK_CXX_RVREF(const T& v) { return (const T&)v; }
	*/
	#define HAWK_CXX_RVREF(x) x
#endif

/// The Exception class implements the exception object.
class HAWK_EXPORT Exception
{
public:
	Exception (
		const hawk_ooch_t* name, const hawk_ooch_t* msg,
		const hawk_ooch_t* file, hawk_oow_t line) HAWK_CXX_NOEXCEPT:
		name(name), msg(msg)
#if !defined(HAWK_NO_LOCATION_IN_EXCEPTION)
		, file(file), line(line)
#endif
	{
	}

	const hawk_ooch_t* name;
	const hawk_ooch_t* msg;
#if !defined(HAWK_NO_LOCATION_IN_EXCEPTION)
	const hawk_ooch_t* file;
	hawk_oow_t        line;
#endif
};

#define HAWK_THROW(ex_name) \
	throw ex_name(HAWK_Q(ex_name),HAWK_Q(ex_name), HAWK_T(__FILE__), (hawk_oow_t)__LINE__)
#define HAWK_THROW_WITH_MSG(ex_name,msg) \
	throw ex_name(HAWK_Q(ex_name),msg, HAWK_T(__FILE__), (hawk_oow_t)__LINE__)

#define HAWK_EXCEPTION(ex_name) \
	class ex_name: public HAWK::Exception \
	{ \
	public: \
		ex_name (const hawk_ooch_t* name, const hawk_ooch_t* msg, \
		         const hawk_ooch_t* file, hawk_oow_t line) HAWK_CXX_NOEXCEPT: \
			HAWK::Exception (name, msg, file, line) {} \
	}

#define HAWK_EXCEPTION_NAME(exception_object) ((exception_object).name)
#define HAWK_EXCEPTION_MSG(exception_object)  ((exception_object).msg)

#if !defined(HAWK_NO_LOCATION_IN_EXCEPTION)
#	define HAWK_EXCEPTION_FILE(exception_object) ((exception_object).file)
#	define HAWK_EXCEPTION_LINE(exception_object) ((exception_object).line)
#else
#	define HAWK_EXCEPTION_FILE(exception_object) (HAWK_T(""))
#	define HAWK_EXCEPTION_LINE(exception_object) (0)
#endif

class HAWK_EXPORT Uncopyable
{
public:
	Uncopyable () HAWK_CXX_NOEXCEPT {}
	//virtual ~Uncopyable () {}

private:
	Uncopyable (const Uncopyable&);
	const Uncopyable& operator= (const Uncopyable&);
};

///
/// The Mmgr class defines a memory manager interface that can be inherited
/// by a class in need of a memory manager as defined in the primitive
/// #hawk_mmgr_t type. Using the class over the primitive type enables you to
/// write code in more object-oriented fashion. An inheriting class should
/// implement three pure virtual functions.
///
/// You are free to call allocMem(), reallocMem(), and freeMem() in C++ context
/// where no exception raising is desired. If you want an exception to be
/// raised upon memory allocation errors, you can call allocate(), reallocate(),
/// dispose() instead.
///
///
class HAWK_EXPORT Mmgr: public hawk_mmgr_t
{
public:
	/// defines an alias type to #hawk_mmgr_t
	typedef hawk_mmgr_t mmgr_t;

	HAWK_EXCEPTION (MemoryError);

public:
	///
	/// The Mmgr() function builds a memory manager composed of bridge
	/// functions connecting itself with it.
	///
	Mmgr () HAWK_CXX_NOEXCEPT
	{
		// NOTE:
		//  the #hawk_mmgr_t interface is not affected by raise_exception
		//  because direct calls to the virtual functions that don't raise
		//  exceptions are made via bridge functions below.
		this->alloc = alloc_mem;
		this->realloc = realloc_mem;
		this->free = free_mem;
		this->ctx = this;
	}

	///
	/// The ~Mmgr() function finalizes a memory manager.
	///
	virtual ~Mmgr () HAWK_CXX_NOEXCEPT {}

	///
	/// The allocate() function calls allocMem() for memory
	/// allocation. if it fails, it raise an exception if it's
	/// configured to do so.
	///
	void* allocate (hawk_oow_t n, bool raise_exception = true)
	{
		void* xptr = this->allocMem (n);
		if (!xptr && raise_exception) HAWK_THROW (MemoryError);
		return xptr;
	}

	///
	/// The callocate() function allocates memory like allocate() and
	/// clears the memory before returning.
	///
	void* callocate (hawk_oow_t n, bool raise_exception = true);

	///
	/// The reallocate() function calls reallocMem() for memory
	/// reallocation. if it fails, it raise an exception if it's
	/// configured to do so.
	///
	void* reallocate (void* ptr, hawk_oow_t n, bool raise_exception = true)
	{
		void* xptr = this->reallocMem (ptr, n);
		if (!xptr && raise_exception) HAWK_THROW (MemoryError);
		return xptr;
	}

	///
	/// The dispose() function calls freeMem() for memory disposal.
	///
	void dispose (void* ptr) HAWK_CXX_NOEXCEPT
	{
		this->freeMem (ptr);
	}

//protected:
	///
	/// The allocMem() function allocates a chunk of memory of the
	/// size \a n and return the pointer to the beginning of the chunk.
	/// If it fails to allocate memory, it should return #HAWK_NULL.
	///
	virtual void* allocMem (
		hawk_oow_t n ///< size of memory chunk to allocate in bytes
	) HAWK_CXX_NOEXCEPT = 0;

	///
	/// The reallocMem() function resizes a chunk of memory previously
	/// allocated with the allocMem() function. When resized, the contents
	/// of the surviving part of a memory chunk is preserved. If it fails to
	/// resize memory, it should return HAWK_NULL.
	///
	virtual void* reallocMem (
		void* ptr, ///< pointer to memory chunk to resize
		hawk_oow_t n   ///< new size in bytes
	) HAWK_CXX_NOEXCEPT = 0;

	///
	/// The freeMem() function frees a chunk of memory allocated with
	/// the allocMem() function or resized with the reallocMem() function.
	///
	virtual void freeMem (
		void* ptr ///< pointer to memory chunk to free
	) HAWK_CXX_NOEXCEPT = 0;

protected:
	///
	/// bridge function from the #hawk_mmgr_t type the allocMem() function.
	///
	static void* alloc_mem (mmgr_t* mmgr, hawk_oow_t n) HAWK_CXX_NOEXCEPT;

	///
	/// bridge function from the #hawk_mmgr_t type the reallocMem() function.
	///
	static void* realloc_mem (mmgr_t* mmgr, void* ptr, hawk_oow_t n) HAWK_CXX_NOEXCEPT;

	///
	/// bridge function from the #hawk_mmgr_t type the freeMem() function.
	///
	static void  free_mem (mmgr_t* mmgr, void* ptr) HAWK_CXX_NOEXCEPT;

public:
	static Mmgr* getDFL () HAWK_CXX_NOEXCEPT;
	static void setDFL (Mmgr* mmgr) HAWK_CXX_NOEXCEPT;

protected:
	static Mmgr* dfl_mmgr;
};


class HAWK_EXPORT Mmged
{
public:
	Mmged (Mmgr* mmgr = HAWK_NULL);

	///
	/// The getMmgr() function returns the memory manager associated.
	///
	Mmgr* getMmgr () const { return this->_mmgr; }

protected:
	///
	/// The setMmgr() function changes the memory manager.
	/// Changing memory manager requires extra care to be taken
	/// especially when you have some data allocated with the previous
	/// manager. for this reason, i put this as a protected function.
	///
	void setMmgr(Mmgr* mmgr);

private:
	Mmgr* _mmgr;
};

///
/// The Hawk class implements an AWK interpreter by wrapping around
/// #hhawk_t and #hawk_rtx_t.
///
class HAWK_EXPORT Hawk: public Uncopyable, public Mmged
{
public:

	enum depth_t
	{
		DEPTH_INCLUDE     = HAWK_OPT_DEPTH_INCLUDE,
		DEPTH_BLOCK_PARSE = HAWK_OPT_DEPTH_BLOCK_PARSE,
		DEPTH_BLOCK_RUN   = HAWK_OPT_DEPTH_BLOCK_RUN,
		DEPTH_EXPR_PARSE  = HAWK_OPT_DEPTH_EXPR_PARSE,
		DEPTH_EXPR_RUN    = HAWK_OPT_DEPTH_EXPR_RUN,
		DEPTH_REX_BUILD   = HAWK_OPT_DEPTH_REX_BUILD,
		DEPTH_REX_MATCH   = HAWK_OPT_DEPTH_REX_MATCH
	};

	class Run;
	friend class Run;


protected:
	///
	/// \name Error Handling
	///
	/// \{

	///
	/// The getErrorString() function returns a formatting string
	/// for an error code \a num. You can override this function
	/// to customize an error message. You must include the same numbers
	/// of ${X}'s as the orginal formatting string. Their order may be
	/// different. The example below changes the formatting string for
	/// #HAWK_ENOENT.
	/// \code
	/// const hawk_ooch_t* MyHawk::getErrorString (hawk_errnum_t num) const
	/// {
	///    if (num == HAWK_ENOENT) return HAWK_T("cannot find '${0}'");
	///    return Hawk::getErrorString(num);
	/// }
	/// \endcode
	///
	virtual const hawk_ooch_t* getErrorString (
		hawk_errnum_t num
	) const;

public:
	///
	/// The getErrorNumber() function returns the number of the last
	/// error occurred.
	///
	hawk_errnum_t getErrorNumber () const;

	///
	/// The getErrorLocation() function returns the location of the
	/// last error occurred.
	///
	hawk_loc_t getErrorLocation () const;
	const hawk_ooch_t* getErrorLocationFile () const;
	const hawk_uch_t* getErrorLocationFileU () const;
	const hawk_bch_t* getErrorLocationFileB () const;

	///
	/// The Hawk::getErrorMessage() function returns a message describing
	/// the last error occurred.
	///
	const hawk_ooch_t* getErrorMessage () const;
	const hawk_uch_t* getErrorMessageU () const;
	const hawk_bch_t* getErrorMessageB () const;

	void setError (
		hawk_errnum_t       code, ///< error code
		const hawk_loc_t*   loc   = HAWK_NULL  ///< error location
	);

	void formatError (
		hawk_errnum_t       code,
		const hawk_loc_t*   loc,
		const hawk_bch_t*   fmt,
		...
	);

	void formatError (
		hawk_errnum_t       code,
		const hawk_loc_t*   loc,
		const hawk_uch_t*   fmt,
		...
	);

	///
	/// The clearError() function clears error information
	///
	void clearError ();

//protected: can't make it protected for borland
	void retrieveError ();
	void retrieveError (Run* run);
	/// \}

protected:
	class NoSource;

public:
	///
	/// The Source class is an abstract class to encapsulate
	/// source script I/O. The Hawk::parse function requires a concrete
	/// object instantiated from its child class.
	///
	class HAWK_EXPORT Source
	{
	public:
		///
		/// The Mode type defines opening mode.
		///
		enum Mode
		{
			READ,   ///< open for read
			WRITE   ///< open for write
		};

		///
		/// The Data class encapsulates information passed in and out
		/// for source script I/O.
		///
		class HAWK_EXPORT Data
		{
		public:
			friend class Hawk;

		protected:
			Data (Hawk* hawk, Mode mode, hawk_sio_arg_t* arg):
				hawk(hawk), mode(mode), arg(arg)
			{
			}

		public:
			Mode getMode() const
			{
				return this->mode;
			}

			bool isMaster() const
			{
				return this->arg->prev == HAWK_NULL;
			}

			const hawk_ooch_t* getName() const
			{
				return this->arg->name;
			}

			// since it doesn't copy the contents,
			// it should point to something that outlives this object.
			void setName (const hawk_ooch_t* name)
			{
				this->arg->name = name;
			}

			const hawk_ooch_t* getPath() const
			{
				return this->arg->path;
			}

			void setPath (const hawk_ooch_t* path)
			{
				this->arg->path = (hawk_ooch_t*)path;
			}

			const hawk_ooch_t* getPrevName() const
			{
				return this->arg->prev->name;
			}

			const void* getPrevHandle() const
			{
				return this->arg->prev->handle;
			}

			void* getHandle () const
			{
				return this->arg->handle;
			}

			void setHandle (void* handle)
			{
				this->arg->handle = handle;
			}

			operator Hawk* () const
			{
				return this->hawk;
			}

			operator hawk_t* () const
			{
				return this->hawk->getHandle();
			}

		protected:
			Hawk* hawk;
			Mode  mode;
			hawk_sio_arg_t* arg;
		};

		Source () {}
		virtual ~Source () {}

		virtual int open (Data& io) = 0;
		virtual int close (Data& io) = 0;
		virtual hawk_ooi_t read (Data& io, hawk_ooch_t* buf, hawk_oow_t len) = 0;
		virtual hawk_ooi_t write (Data& io, const hawk_ooch_t* buf, hawk_oow_t len) = 0;

		///
		/// The NONE object indicates no source.
		///
		static NoSource NONE;

	private:
		Source (const Source&);
		Source& operator= (const Source&);
	};

protected:
	class HAWK_EXPORT NoSource: public Source
	{
	public:
		int open (Data& io) { return -1; }
		int close (Data& io) { return 0; }
		hawk_ooi_t read (Data& io, hawk_ooch_t* buf, hawk_oow_t len) { return 0; }
		hawk_ooi_t write (Data& io, const hawk_ooch_t* buf, hawk_oow_t len) { return 0; }
	};

public:
	///
	/// The RIOBase class is a base class to represent runtime I/O
	/// operations. The Console, File, Pipe classes implement more specific
	/// I/O operations by inheriting this class.
	///
	class HAWK_EXPORT RIOBase
	{
	protected:
		RIOBase (Run* run, hawk_rio_arg_t* riod): run(run), riod(riod) {}

	public:
		const hawk_ooch_t* getName() const { return this->riod->name; }

		const void* getHandle () const { return this->riod->handle; }
		void setHandle (void* handle) { this->riod->handle = handle; }

		hawk_uint16_t getUflags () const { return this->riod->uflags; }
		void setUflags (hawk_uint16_t uflags) { this->riod->uflags = uflags; }

		operator Hawk* () const { return this->run->hawk; }
		operator hawk_t* () const
		{
			HAWK_ASSERT (hawk_rtx_gethawk(this->run->rtx) == this->run->hawk->getHandle());
			return this->run->hawk->getHandle();
		}
		operator hawk_rio_arg_t* () const { return this->riod; }
		operator Run* () const { return this->run; }
		operator hawk_rtx_t* () const { return this->run->rtx; }
		operator hawk_gem_t* () const { return hawk_rtx_getgem(this->run->rtx); }

	protected:
		Run* run;
		hawk_rio_arg_t* riod;

	private:
		RIOBase (const RIOBase&);
		RIOBase& operator= (const RIOBase&);
	};

	///
	/// The Pipe class encapsulates the pipe operations indicated by
	/// the | and || operators.
	///
	class HAWK_EXPORT Pipe: public RIOBase
	{
	public:
		friend class Hawk;

		/// The Mode type defines the opening mode.
		enum Mode
		{
			/// open for read-only access
			READ = HAWK_RIO_PIPE_READ,
			/// open for write-only access
			WRITE = HAWK_RIO_PIPE_WRITE,
			/// open for read and write
			RW = HAWK_RIO_PIPE_RW
		};

		/// The CloseMode type defines the closing mode for a pipe
		/// opened in the #RW mode.
		enum CloseMode
		{
			/// close both read and write ends
			CLOSE_FULL = HAWK_RIO_CMD_CLOSE_FULL,
			/// close the read end only
			CLOSE_READ = HAWK_RIO_CMD_CLOSE_READ,
			/// close the write end only
			CLOSE_WRITE = HAWK_RIO_CMD_CLOSE_WRITE
		};

		class HAWK_EXPORT Handler
		{
		public:
			virtual ~Handler () {}

			virtual int open (Pipe& io) = 0;
			virtual int close (Pipe& io) = 0;
			virtual hawk_ooi_t read  (Pipe& io, hawk_ooch_t* buf, hawk_oow_t len) = 0;
			virtual hawk_ooi_t readBytes (Pipe& io, hawk_bch_t* buf, hawk_oow_t len) = 0;
			virtual hawk_ooi_t write (Pipe& io, const hawk_ooch_t* buf, hawk_oow_t len) = 0;
			virtual hawk_ooi_t writeBytes (Pipe& io, const hawk_bch_t* buf, hawk_oow_t len) = 0;
			virtual int flush (Pipe& io) = 0;
		};

	protected:
		Pipe (Run* run, hawk_rio_arg_t* riod);

	public:
		/// The getMode() function returns the opening mode requested.
		/// You can inspect the opening mode, typically in the
		/// openPipe() function, to create a pipe with proper
		/// access mode. It is harmless to call this function from
		/// other pipe handling functions.
		Mode getMode () const;

		/// The getCloseMode() function returns the closing mode
		/// requested. The returned value is valid if getMode()
		/// returns #RW.
		CloseMode getCloseMode () const;
	};

	///
	/// The File class encapsulates file operations by inheriting RIOBase.
	///
	class HAWK_EXPORT File: public RIOBase
	{
	public:
		friend class Hawk;

		enum Mode
		{
			READ = HAWK_RIO_FILE_READ,
			WRITE = HAWK_RIO_FILE_WRITE,
			APPEND = HAWK_RIO_FILE_APPEND
		};

		class HAWK_EXPORT Handler
		{
		public:
			virtual ~Handler () {}

			virtual int open (File& io) = 0;
			virtual int close (File& io) = 0;
			virtual hawk_ooi_t read (File& io, hawk_ooch_t* buf, hawk_oow_t len) = 0;
			virtual hawk_ooi_t readBytes (File& io, hawk_bch_t* buf, hawk_oow_t len) = 0;
			virtual hawk_ooi_t write (File& io, const hawk_ooch_t* buf, hawk_oow_t len) = 0;
			virtual hawk_ooi_t writeBytes (File& io, const hawk_bch_t* buf, hawk_oow_t len) = 0;
			virtual int flush (File& io) = 0;
		};

	protected:
		File (Run* run, hawk_rio_arg_t* riod);

	public:
		Mode getMode () const;
	};

	///
	/// The Console class encapsulates the console operations by
	/// inheriting RIOBase.
	///
	class HAWK_EXPORT Console: public RIOBase
	{
	public:
		friend class Hawk;

		/// Console mode enumerators
		enum Mode
		{
			READ = HAWK_RIO_CONSOLE_READ,  ///< open for input
			WRITE = HAWK_RIO_CONSOLE_WRITE ///< open for output
		};

		///
		/// The Handler class is an abstract class that can be
		/// implemented for customized I/O handling.
		class HAWK_EXPORT Handler
		{
		public:
			virtual ~Handler () {}

			/// The open() function is called before the initial
			/// access to the console for input and output.
			/// It must return 0 for success and -1 for failure.
			/// Upon successful opening, it can store information
			/// required using setHandle() and setUflags().
			/// The information set here is available in subsequent
			/// calls to other methods and are accessible with
			/// getHandle() and getUflags().
			virtual int open (Console& io) = 0;

			/// The close() function is called when the console
			/// is not needed any more. It must return 0 for success
			/// and -1 for failure.
			virtual int close (Console& io) = 0;

			/// The read() function is called when the console
			/// is read for input. It must fill the buffer \a buf with
			/// data not more than \a len characters and return the
			/// number of characters filled into the buufer. It can
			/// return 0 to indicate EOF and -1 for failure.
			virtual hawk_ooi_t read (Console& io, hawk_ooch_t* buf, hawk_oow_t len) = 0;

			virtual hawk_ooi_t readBytes (Console& io, hawk_bch_t* buf, hawk_oow_t len) = 0;

			/// The write() function is called when the console
			/// is written for output. It can write upto \a len characters
			/// available in the buffer \a buf and return the number of
			/// characters written. It can return 0 to indicate EOF and -1
			/// for failure.
			virtual hawk_ooi_t write (Console& io, const hawk_ooch_t* buf, hawk_oow_t len) = 0;

			virtual hawk_ooi_t writeBytes (Console& io, const hawk_bch_t* buf, hawk_oow_t len) = 0;

			/// You may choose to buffer the data passed to the write()
			/// function and perform actual writing when flush() is called.
			/// It must return 0 for success and -1 for failure.
			virtual int flush (Console& io) = 0;

			/// The next() function is called when \b nextfile or
			/// \b nextofile is executed. It must return 0 for success
			/// and -1 for failure.
			virtual int next  (Console& io) = 0;
		};

		bool getSwitched() const { return this->riod->console_switched; }
		void setSwitched(bool v) { this->riod->console_switched = v; }

	protected:
		Console (Run* run, hawk_rio_arg_t* riod);
		~Console ();

	public:
		/// The getMode() function returns if the console is
		/// opened for reading or writing.
		Mode getMode () const;

		int setFileName (const hawk_ooch_t* name);
		int setFNR (hawk_int_t fnr);

	protected:
		hawk_ooch_t* filename;
	};


	///
	/// The Value class wraps around #hawk_val_t to provide a more
	/// comprehensive interface.
	///
	/// An instance of Value must not outlive the associated run-time
	/// context and its owning hawk instance. If the close() method is
	/// called explicity on the owning hawk instance, you must ensure
	/// that the related values are also destroyed before the call to
	/// close().
	///
	/// The following will end up in a segmentation fault or the like.
	///   HAWK::HawkStd hawk;
	///   hawk.open();
	///   HAWK::HawkStd::SourceString in("BEGIN{}");
	///   hawk.parse(in, HAWK::Hawk::Source::NONE);
	///   HAWK::Hawk::Value v(rtx);
	///   v.setMbs("hello", 5);
	///   hawk.close();
	///
	/// You don't call hawk.close() and rely on the destructor or
	/// enclose 'v' in another nested scope.
	///   HAWK::HawkStd hawk;
	///   hawk.open();
	///   HAWK::HawkStd::SourceString in("BEGIN{}");
	///   hawk.parse(in, HAWK::Hawk::Source::NONE);
	///   {
	///       HAWK::Hawk::Value v(rtx);
	///       v.setMbs("hello", 5);
	///   }
	///   hawk.close();
	///
	class HAWK_EXPORT Value
	{
	public:
		friend class Hawk;

	#if defined(HAWK_VALUE_USE_IN_CLASS_PLACEMENT_NEW)
		// initialization
		void* operator new (hawk_oow_t n, Run* run) throw ();
		void* operator new[] (hawk_oow_t n, Run* run) throw ();

	#if !defined(__BORLANDC__) && !defined(__WATCOMC__)
		// deletion when initialization fails
		void operator delete (void* p, Run* run);
		void operator delete[] (void* p, Run* run);
	#endif

		// normal deletion
		void operator delete (void* p);
		void operator delete[] (void* p);
	#endif

		///
		/// The Index class encapsulates an index of an arrayed value.
		///
		class HAWK_EXPORT Index: protected hawk_oocs_t
		{
		public:
			friend class Value;

			/// The Index() function creates an empty array index.
			Index ()
			{
				this->ptr = (hawk_ooch_t*)Value::getEmptyStr();
				this->len = 0;
			}

			/// The Index() function creates a string array index.
			Index (const hawk_ooch_t* ptr, hawk_oow_t len)
			{
				this->ptr = (hawk_ooch_t*)ptr;
				this->len = len;
			}

			void set (const hawk_oocs_t* x)
			{
				this->ptr = x->ptr;
				this->len = x->len;
			}

			Index& operator= (const hawk_oocs_t* x)
			{
				this->set (x);
				return *this;
			}

			const hawk_ooch_t* pointer () const
			{
				return this->ptr;
			}

			hawk_oow_t length () const
			{
				return this->len;
			}
		};

		///
		/// Represents a numeric index of an arrayed value
		///
		class HAWK_EXPORT IntIndex: public Index
		{
		public:
			IntIndex (hawk_int_t num);

		protected:
			// 2^32: 4294967296
			// 2^64: 18446744073709551616
			// 2^128: 340282366920938463463374607431768211456
			// -(2^32/2): -2147483648
			// -(2^64/2): -9223372036854775808
			// -(2^128/2): -170141183460469231731687303715884105728
		#if HAWK_SIZEOF_LONG_T > 16
		#	error SIZEOF(hawk_int_t) TOO LARGE.
		#	error INCREASE THE BUFFER SIZE TO SUPPORT IT.
		#elif HAWK_SIZEOF_LONG_T == 16
			hawk_ooch_t buf[41];
		#elif HAWK_SIZEOF_LONG_T == 8
			hawk_ooch_t buf[21];
		#else
			hawk_ooch_t buf[12];
		#endif
		};

		///
		/// The IndexIterator class is a helper class to make simple
		/// iteration over array elements.
		///
		class HAWK_EXPORT IndexIterator: public hawk_val_map_itr_t
		{
		public:
			friend class Value;

			///
			/// The END variable is a special variable to
			/// represent the end of iteration.
			///
			static IndexIterator END;

			///
			/// The IndexIterator() function creates an iterator
			/// for an arrayed value.
			///
			IndexIterator ()
			{
				hawk_init_map_itr (this, 0);
			}

			bool operator== (const IndexIterator& ii) const
			{
				// the most common use of this operator is to test it against END
			#if defined(HAWK_MAP_IS_HTB)
				return this->pair == ii.pair && this->buckno == ii.buckno;
			#else
				return this->pair == ii.pair && this->_prev == ii._prev;
			#endif
			}

			bool operator!= (const IndexIterator& ii) const
			{
				return !this->operator==(ii);
			}
		};

		///
		/// The Value() function creates an empty value associated
		/// with no runtime context. To set an actual inner value,
		/// you must specify a context when calling setXXX() functions.
		/// i.e., use setInt(run,10) instead of setInt(10).
		///
		Value ();

		///
		/// The Value() function creates an empty value associated
		/// with a runtime context.
		///
		Value (Run& run);

		///
		/// The Value() function creates an empty value associated
		/// with a runtime context.
		///
		Value (Run* run);

		Value (const Value& v);
		~Value ();

		Value& operator= (const Value& v);

		void clear ();

		operator hawk_val_t* () const { return this->val; }
		operator hawk_int_t () const;
		operator hawk_flt_t () const;
		operator const hawk_ooch_t* () const;
	#if defined(HAWK_OOCH_IS_UCH)
		operator const hawk_bch_t* () const;
	#endif

		hawk_val_t* toVal () const
		{
			return this->operator hawk_val_t* ();
		}

		hawk_int_t toInt () const
		{
			return this->operator hawk_int_t ();
		}

		hawk_flt_t toFlt () const
		{
			return this->operator hawk_flt_t ();
		}

		const hawk_ooch_t* toStr (hawk_oow_t* len) const
		{
			const hawk_ooch_t* p;
			hawk_oow_t l;

			if (this->getStr(&p, &l) <= -1)
			{
				p = this->getEmptyStr();
				l = 0;
			}

			if (len) *len = l;
			return p;
		}

		const hawk_bch_t* toMbs (hawk_oow_t* len) const
		{
			const hawk_bch_t* p;
			hawk_oow_t l;

			if (this->getMbs(&p, &l) <= -1)
			{
				p = this->getEmptyMbs();
				l = 0;
			}

			if (len) *len = l;
			return p;
		}

		const void* toBob (hawk_oow_t* len) const
		{
			const void* p;
			hawk_oow_t l;

			if (this->getBob(&p, &l) <= -1)
			{
				p = this->getEmptyBob();
				l = 0;
			}

			if (len) *len = l;
			return p;
		}

		int getType () const { return hawk_get_val_type(this->val); }
		const hawk_ooch_t* getTypeName () const { return hawk_get_val_type_name(this->val); }

		int getInt (hawk_int_t* v) const;
		int getFlt (hawk_flt_t* v) const;
		int getNum (hawk_int_t* lv, hawk_flt_t* fv) const;
		int getStr (const hawk_ooch_t** str, hawk_oow_t* len) const;
		int getMbs (const hawk_bch_t** str, hawk_oow_t* len) const;
		int getBob (const void** str, hawk_oow_t* len) const;

		int setVal (hawk_val_t* v);
		int setVal (Run* r, hawk_val_t* v);

		int setInt (hawk_int_t v);
		int setInt (Run* r, hawk_int_t v);
		int setFlt (hawk_flt_t v);
		int setFlt (Run* r, hawk_flt_t v);

		int setStr (const hawk_uch_t* str, hawk_oow_t len, bool numeric = false);
		int setStr (Run* r, const hawk_uch_t* str, hawk_oow_t len, bool numeric = false);
		int setStr (const hawk_uch_t* str, bool numeric = false);
		int setStr (Run* r, const hawk_uch_t* str, bool numeric = false);
		int setStr (const hawk_bch_t* str, hawk_oow_t len, bool numeric = false);
		int setStr (Run* r, const hawk_bch_t* str, hawk_oow_t len, bool numeric = false);
		int setStr (const hawk_bch_t* str, bool numeric = false);
		int setStr (Run* r, const hawk_bch_t* str, bool numeric = false);

		int setMbs (const hawk_bch_t* str, hawk_oow_t len);
		int setMbs (Run* r, const hawk_bch_t* str, hawk_oow_t len);
		int setMbs (const hawk_bch_t* str);
		int setMbs (Run* r, const hawk_bch_t* str);

		int setBob (const void* str, hawk_oow_t len);
		int setBob (Run* r, const void* str, hawk_oow_t len);

		int setArrayedVal (hawk_ooi_t idx, hawk_val_t* v);
		int setArrayedVal (Run* r, hawk_ooi_t idx, hawk_val_t* v);

		int scaleArrayed (hawk_ooi_t size);
		int scaleArrayed (Run* r, hawk_ooi_t size);

		hawk_ooi_t getArrayedSize () const; // same value as hawk::size() for an array
		hawk_ooi_t getArrayedLength () const; // same value as hawk::length() for an array
		hawk_ooi_t getArrayedCapa () const;

		bool isArrayed () const;
		int getArrayed (
			hawk_ooi_t    idx, ///< array index
			Value*        val  ///< value holder
		) const;

		int setIndexedVal (const Index& idx, hawk_val_t* v);
		int setIndexedVal (Run* r, const Index& idx, hawk_val_t* v);
		int setIndexedInt (const Index& idx, hawk_int_t v);
		int setIndexedInt (Run* r, const Index& idx, hawk_int_t v);
		int setIndexedFlt (const Index& idx, hawk_flt_t v);
		int setIndexedFlt (Run* r, const Index&  idx, hawk_flt_t v);

		int setIndexedStr (const Index& idx, const hawk_uch_t* str, hawk_oow_t len, bool numeric = false);
		int setIndexedStr (Run* r, const Index& idx, const hawk_uch_t* str, hawk_oow_t len, bool numeric = false);
		int setIndexedStr (const Index& idx, const hawk_uch_t* str, bool  numeric = false);
		int setIndexedStr (Run* r, const Index&  idx, const hawk_uch_t* str, bool  numeric = false);

		int setIndexedStr (const Index& idx, const hawk_bch_t* str, hawk_oow_t len, bool numeric = false);
		int setIndexedStr (Run* r, const Index& idx, const hawk_bch_t* str, hawk_oow_t len, bool numeric = false);
		int setIndexedStr (const Index& idx, const hawk_bch_t* str, bool  numeric = false);
		int setIndexedStr (Run* r, const Index&  idx, const hawk_bch_t* str, bool  numeric = false);

		int setIndexedMbs (const Index& idx, const hawk_bch_t* str, hawk_oow_t len);
		int setIndexedMbs (Run* r, const Index& idx, const hawk_bch_t* str, hawk_oow_t len);
		int setIndexedMbs (const Index& idx, const hawk_bch_t* str);
		int setIndexedMbs (Run* r, const Index&  idx, const hawk_bch_t* str);

		hawk_ooi_t getIndexedSize () const; // same value as hawk::size() for a map
		hawk_ooi_t getIndexedLength () const; // same value as hawk::length() for a map
		hawk_ooi_t getIndexedCapa () const;

		///
		/// The isIndexed() function determines if a value is a map.
		/// \return true if indexed, false if not.
		///
		bool isIndexed () const;

		///
		/// The getIndexed() function gets a value at the given
		/// index \a idx and sets it to \a val.
		/// \return 0 on success, -1 on failure
		///
		int getIndexed (
			const Index&  idx, ///< map index
			Value*        val  ///< value holder
		) const;

		///
		/// The getFirstIndex() function stores the first index of
		/// an arrayed value into \a idx.
		/// \return IndexIterator::END if the arrayed value is empty,
		///         iterator that can be passed to getNextIndex() if not
		///
		IndexIterator getFirstIndex (
			Index* idx ///< index holder
		) const;

		///
		/// The getNextIndex() function stores into \a idx the next
		/// index of an array value from the position indicated by
		/// \a iter.
		/// \return IndexIterator::END if the arrayed value is empty,
		///         iterator that can be passed to getNextIndex() if not
		///
		IndexIterator getNextIndex (
			Index* idx,                 ///< index holder
			const IndexIterator& curitr ///< current position
		) const;

	protected:
		Run* run;
		hawk_val_t* val;

		mutable struct
		{
			hawk_oocs_t str;
			hawk_bcs_t mbs;
		} cached;

	public:
		static const hawk_ooch_t* getEmptyStr();
		static const hawk_bch_t* getEmptyMbs();
		static const void* getEmptyBob();
	};

public:
	///
	/// The Run class wraps around #hawk_rtx_t to represent the
	/// runtime context.
	///
	class HAWK_EXPORT Run
	{
	protected:
		friend class Hawk;
		friend class Value;
		friend class RIOBase;
		friend class Console;

		Run (Hawk* hawk);
		Run (Hawk* hawk, hawk_rtx_t* run);
		~Run ();

	public:
		operator Hawk* () const
		{
			return this->hawk;
		}

		operator hawk_rtx_t* () const
		{
			return this->rtx;
		}

		operator hawk_gem_t* () const
		{
			return this->rtx? hawk_rtx_getgem(this->rtx): HAWK_NULL;
		}

		void halt () const;
		bool isHalt () const;

		hawk_errnum_t getErrorNumber () const;
		hawk_loc_t getErrorLocation () const;
		const hawk_ooch_t* getErrorMessage () const;
		const hawk_uch_t* getErrorMessageU () const;
		const hawk_bch_t* getErrorMessageB () const;

		///
		/// The setError() function sets error information.
		///
		void setError (
			hawk_errnum_t      code, ///< error code
			const hawk_loc_t*  loc   = HAWK_NULL  ///< error location
		);

		void formatError (
			hawk_errnum_t       code,
			const hawk_loc_t*   loc,
			const hawk_bch_t*   fmt,
			...
		);

		void formatError (
			hawk_errnum_t       code,
			const hawk_loc_t*   loc,
			const hawk_uch_t*   fmt,
			...
		);

		///
		/// The setGlobal() function sets the value of a global
		/// variable identified by \a id
		/// to \a v.
		/// \return 0 on success, -1 on failure
		///
		int setGlobal (int id, hawk_int_t v);

		///
		/// The setGlobal() function sets the value of a global
		/// variable identified by \a id
		/// to \a v.
		/// \return 0 on success, -1 on failure
		///
		int setGlobal (int id, hawk_flt_t v);

		///
		/// The setGlobal() function sets the value of a global
		/// variable identified by \a id
		/// to a string as long as \a len characters pointed to by
		/// \a ptr.
		/// \return 0 on success, -1 on failure
		///
		int setGlobal (int id, const hawk_uch_t* ptr, hawk_oow_t len, bool mbs = false);
		int setGlobal (int id, const hawk_bch_t* ptr, hawk_oow_t len, bool mbs = false);
		int setGlobal (int id, const hawk_uch_t* ptr, bool mbs = false);
		int setGlobal (int id, const hawk_bch_t* ptr, bool mbs = false);

		///
		/// The setGlobal() function sets a global variable
		/// identified by \a id to a value \a v.
		/// \return 0 on success, -1 on failure
		///
		int setGlobal (int id, const Value& v);

		///
		/// The getGlobal() function gets the value of a global
		/// variable identified by \a id and stores it in \a v.
		/// \return 0 on success, -1 on failure
		///
		int getGlobal (int id, Value& v) const;

	protected:
		Hawk* hawk;
		hawk_rtx_t* rtx;
	};

	///
	/// Returns the primitive handle
	///
	operator hawk_t* () const
	{
		return this->hawk;
	}

	operator hawk_gem_t* () const
	{
		return this->hawk? hawk_getgem(this->hawk): HAWK_NULL;
	}

	///
	/// \name Basic Functions
	/// \{
	///

	/// The Hawk() function creates an interpreter without fully
	/// initializing it. You must call open() for full initialization
	/// before calling other functions.
	Hawk (Mmgr* mmgr = HAWK_NULL);

	/// The ~Hawk() function destroys an interpreter. Make sure to have
	/// called close() for finalization before the destructor is executed.
	virtual ~Hawk ();

	hawk_cmgr_t* getCmgr () const;
	void setCmgr (hawk_cmgr_t* cmgr);

	///
	/// The open() function initializes an interpreter.
	/// You must call this function before doing anything meaningful.
	/// \return 0 on success, -1 on failure
	///
	int open ();

	///
	/// The close() function closes the interpreter.
	///
	void close ();

	///
	/// The uponClosing() function is called back after Hawk::close()
	/// has cleared most of the internal data but before destroying
	/// the underlying hawk object. This maps to the close callback
	/// of #hawk_ecb_t.
	///
	virtual void uponClosing ();

	///
	/// The uponClearing() function is called back when Hawk::close()
	/// begins clearing internal data. This maps to the clear callback
	/// of #hawk_ecb_t.
	///
	virtual void uponClearing ();

	///
	/// The parse() function parses the source code read from the input
	/// stream \a in and writes the parse tree to the output stream \a out.
	/// To disable deparsing, you may set \a out to Hawk::Source::NONE.
	/// However, it is not allowed to specify Hawk::Source::NONE for \a in.
	///
	/// \return Run object on success, #HAWK_NULL on failure
	///
	Hawk::Run* parse (
		Source& in,  ///< script to parse
		Source& out  ///< deparsing target
	);

	///
	/// The getRunContext() funciton returns the execution context
	/// returned by the parse() function. The returned context
	/// is valid if parse() has been called. You may call this
	/// function to get the context if you forgot to store it
	/// in a call to parse().
	///
	const Hawk::Run* getRunContext () const
	{
		return &this->runctx;
	}

	///
	/// The getRunContext() funciton returns the execution context
	/// returned by the parse() function. The returned context
	/// is valid if parse() has been called. You may call this
	/// function to get the context if you forgot to store it
	/// in a call to parse().
	///
	Hawk::Run* getRunContext ()
	{
		return &this->runctx;
	}

	///
	/// The resetRunContext() function closes an existing
	/// execution context and creates a new execution context.
	/// You may want to call this function if you want to
	/// reset it without calling the parse() function again
	/// after the first call to it.
	Hawk::Run* resetRunContext ();

	///
	/// The loop() function executes the BEGIN block, pattern-action blocks,
	/// and the END block. The return value is stored into \a ret.
	/// \return 0 on succes, -1 on failure
	///
	int loop (
		Value* ret  ///< return value holder
	);

	///
	/// The call() function invokes a function named \a name.
	///
	int call (
		const hawk_bch_t*  name,  ///< function name
		Value*             ret,   ///< return value holder
		const Value*       args,  ///< argument array
		hawk_oow_t         nargs  ///< number of arguments
	);

	///
	/// The call() function invokes a function named \a name.
	///
	int call (
		const hawk_uch_t*  name,  ///< function name
		Value*             ret,   ///< return value holder
		const Value*       args,  ///< argument array
		hawk_oow_t         nargs  ///< number of arguments
	);

	///
	/// The exec() function is the same as loop() if no @pragma startup
	/// is specified. It is the same as call() if it is specifed
	///
	int exec (
		Value*             ret,   ///< return value holder
		const Value*       args,  ///< argument array
		hawk_oow_t         nargs  ///< number of arguments
	);

	///
	/// The halt() function makes request to abort execution
	///
	void halt ();
	/// \}

	///
	/// \name Configuration
	/// \{
	///

	///
	/// The getTrait() function gets the current options.
	/// \return current traits
	///
	int getTrait () const;

	///
	/// The setTrait() function changes the current traits.
	///
	void setTrait (
		int trait
	);

	///
	/// The setMaxDepth() function sets the maximum processing depth
	/// for operations identified by \a ids.
	///
	void setMaxDepth (
		depth_t id,  ///< depth identifier
		hawk_oow_t depth ///< new depth
	);

	///
	/// The getMaxDepth() function gets the maximum depth for an operation
	/// type identified by \a id.
	///
	hawk_oow_t getMaxDepth (
		depth_t id   ///< depth identifier
	) const;

	int setIncludeDirs (const hawk_uch_t* dirs);
	int setIncludeDirs (const hawk_bch_t* dirs);

	///
	/// The addArgument() function adds an ARGV string as long as \a len
	/// characters pointed to
	/// by \a arg. loop() and call() make a string added available
	/// to a script through ARGV.
	/// \return 0 on success, -1 on failure
	///
	int addArgument (
		const hawk_uch_t* arg,  ///< string pointer
		hawk_oow_t        len   ///< string length
	);

	int addArgument (
		const hawk_bch_t* arg,  ///< string pointer
		hawk_oow_t        len   ///< string length
	);

	///
	/// The addArgument() function adds a null-terminated string \a arg.
	/// loop() and call() make a string added available to a script
	/// through ARGV.
	/// \return 0 on success, -1 on failure
	///
	int addArgument (
		const hawk_uch_t* arg ///< string pointer
	);

	int addArgument (
		const hawk_bch_t* arg ///< string pointer
	);

	///
	/// The clearArguments() function deletes all ARGV strings.
	///
	void clearArguments ();

	hawk_oow_t getArgumentCount () const
	{
		return this->runarg.len;
	}

	const hawk_ooch_t* getArgument (hawk_oow_t index, hawk_oow_t* len = HAWK_NULL) const
	{
		if (len) *len = this->runarg.ptr[index].len;
		return this->runarg.ptr[index].ptr;
	}

	///
	/// The addGlobal() function registers an intrinsic global variable.
	/// \return integer >= 0 on success, -1 on failure.
	///
	int addGlobal (
		const hawk_bch_t* name ///< variable name
	);

	int addGlobal (
		const hawk_uch_t* name ///< variable name
	);

	///
	/// The deleteGlobal() function unregisters an intrinsic global
	/// variable by name.
	/// \return 0 on success, -1 on failure.
	///
	int deleteGlobal (
		const hawk_bch_t* name ///< variable name
	);

	int deleteGlobal (
		const hawk_uch_t* name ///< variable name
	);

	///
	/// The findGlobal() function returns the numeric ID of an intrinsic
	//  global variable.
	/// \return integer >= 0 on success, -1 on failure.
	///
	int findGlobal (
		const hawk_bch_t* name, ///> variable name
		bool inc_builtins = false
	);
	int findGlobal (
		const hawk_uch_t* name, ///> variable name
		bool inc_builtins = false
	);

	///
	/// The setGlobal() function sets the value of a global variable
	/// identified by \a id. The \a id is either a value returned by
	/// addGlobal() or one of the #hawk_gbl_id_t enumerators. It is not allowed
	/// to call this function prior to parse().
	/// \return 0 on success, -1 on failure
	///
	int setGlobal (
		int          id,  ///< numeric identifier
		const Value& v    ///< value
	);

	///
	/// The getGlobal() function gets the value of a global variable
	/// identified by \a id. The \a id is either a value returned by
	/// addGlobal() or one of the #hawk_gbl_id_t enumerators. It is not allowed
	/// to call this function before parse().
	/// \return 0 on success, -1 on failure
	///
	int getGlobal (
		int    id, ///< numeric identifier
		Value& v   ///< value store
	);

	///
	/// The FunctionHandler type defines a intrinsic function handler.
	///
	typedef int (Hawk::*FunctionHandler) (
		Run&              run,
		Value&            ret,
		Value*            args,
		hawk_oow_t        nargs,
		const hawk_fnc_info_t* fi
	);

	///
	/// The addFunction() function adds a new user-defined intrinsic
	/// function.
	///
	int addFunction (
		const hawk_bch_t* name,      ///< function name
		hawk_oow_t minArgs,          ///< minimum numbers of arguments
		hawk_oow_t maxArgs,          ///< maximum numbers of arguments
		const hawk_bch_t* argSpec,   ///< argument specification
		FunctionHandler handler,     ///< function handler
		int validOpts = 0            ///< valid if these options are set
	);

	int addFunction (
		const hawk_uch_t* name,      ///< function name
		hawk_oow_t minArgs,          ///< minimum numbers of arguments
		hawk_oow_t maxArgs,          ///< maximum numbers of arguments
		const hawk_uch_t* argSpec,   ///< argument specification
		FunctionHandler handler,     ///< function handler
		int validOpts = 0            ///< valid if these options are set
	);

	///
	/// The deleteFunction() function deletes a user-defined intrinsic
	/// function by name.
	///
	int deleteFunction (
		const hawk_ooch_t* name ///< function name
	);
	/// \}

	Pipe::Handler* getPipeHandler ()
	{
		return this->pipe_handler;
	}

	const Pipe::Handler* getPipeHandler () const
	{
		return this->pipe_handler;
	}

	///
	/// The setPipeHandler() function registers an external pipe
	/// handler object. An external pipe handler can be implemented
	/// outside this class without overriding various pipe functions.
	/// Note that an external pipe handler must outlive an outer
	/// hawk object.
	///
	void setPipeHandler (Pipe::Handler* handler)
	{
		this->pipe_handler = handler;
	}

	File::Handler* getFileHandler ()
	{
		return this->file_handler;
	}

	const File::Handler* getFileHandler () const
	{
		return this->file_handler;
	}

	///
	/// The setFileHandler() function registers an external file
	/// handler object. An external file handler can be implemented
	/// outside this class without overriding various file functions.
	/// Note that an external file handler must outlive an outer
	/// hawk object.
	///
	void setFileHandler (File::Handler* handler)
	{
		this->file_handler = handler;
	}

	Console::Handler* getConsoleHandler ()
	{
		return this->console_handler;
	}

	const Console::Handler* getConsoleHandler () const
	{
		return this->console_handler;
	}

	///
	/// The setConsoleHandler() function registers an external console
	/// handler object. An external file handler can be implemented
	/// outside this class without overriding various console functions.
	/// Note that an external console handler must outlive an outer
	/// hawk object.
	///
	void setConsoleHandler (Console::Handler* handler)
	{
		this->console_handler = handler;
	}

protected:
	///
	/// \name Pipe I/O handlers
	/// Pipe operations are achieved through the following functions
	/// if no external pipe handler is set with setPipeHandler().
	/// \{

	/// The openPipe() function is a pure virtual function that must be
	/// overridden by a child class to open a pipe. It must return 1
	/// on success, 0 on end of a pipe, and -1 on failure.
	virtual int     openPipe  (Pipe& io);

	/// The closePipe() function is a pure virtual function that must be
	/// overridden by a child class to close a pipe. It must return 0
	/// on success and -1 on failure.
	virtual int     closePipe (Pipe& io);

	virtual hawk_ooi_t readPipe (Pipe& io, hawk_ooch_t* buf, hawk_oow_t len);
	virtual hawk_ooi_t readPipeBytes (Pipe& io, hawk_bch_t* buf, hawk_oow_t len);
	virtual hawk_ooi_t writePipe (Pipe& io, const hawk_ooch_t* buf, hawk_oow_t len);
	virtual hawk_ooi_t writePipeBytes (Pipe& io, const hawk_bch_t* buf, hawk_oow_t len);
	virtual int flushPipe (Pipe& io);
	/// \}

	///
	/// \name File I/O handlers
	/// File operations are achieved through the following functions
	/// if no external file handler is set with setFileHandler().
	/// \{
	///
	virtual int openFile (File& io);
	virtual int closeFile (File& io);
	virtual hawk_ooi_t readFile (File& io, hawk_ooch_t* buf, hawk_oow_t len);
	virtual hawk_ooi_t readFileBytes (File& io, hawk_bch_t* buf, hawk_oow_t len);
	virtual hawk_ooi_t writeFile (File& io, const hawk_ooch_t* buf, hawk_oow_t len);
	virtual hawk_ooi_t writeFileBytes (File& io, const hawk_bch_t* buf, hawk_oow_t len);
	virtual int flushFile (File& io);
	/// \}

	///
	/// \name Console I/O handlers
	/// Console operations are achieved through the following functions.
	/// if no external console handler is set with setConsoleHandler().
	/// \{
	///
	virtual int openConsole (Console& io);
	virtual int closeConsole (Console& io);
	virtual hawk_ooi_t readConsole (Console& io, hawk_ooch_t* buf, hawk_oow_t len);
	virtual hawk_ooi_t readConsoleBytes (Console& io, hawk_bch_t* buf, hawk_oow_t len);
	virtual hawk_ooi_t writeConsole (Console& io, const hawk_ooch_t* buf, hawk_oow_t len);
	virtual hawk_ooi_t writeConsoleBytes (Console& io, const hawk_bch_t* buf, hawk_oow_t len);
	virtual int flushConsole (Console& io);
	virtual int nextConsole (Console& io);
	/// \}

	// primitive handlers
	virtual hawk_flt_t pow (hawk_flt_t x, hawk_flt_t y) = 0;
	virtual hawk_flt_t mod (hawk_flt_t x, hawk_flt_t y) = 0;

	virtual void* modopen (const hawk_mod_spec_t* spec) = 0;
	virtual void  modclose (void* handle) = 0;
	virtual void* modgetsym (void* handle, const hawk_ooch_t* name) = 0;

	// static glue members for various handlers
	static hawk_ooi_t readSource (
		hawk_t* hawk, hawk_sio_cmd_t cmd, hawk_sio_arg_t* arg,
		hawk_ooch_t* data, hawk_oow_t count);
	static hawk_ooi_t writeSource (
		hawk_t* hawk, hawk_sio_cmd_t cmd, hawk_sio_arg_t* arg,
		hawk_ooch_t* data, hawk_oow_t count);

	static hawk_ooi_t pipeHandler (
		hawk_rtx_t* rtx, hawk_rio_cmd_t cmd, hawk_rio_arg_t* riod,
		void* data, hawk_oow_t count);
	static hawk_ooi_t fileHandler (
		hawk_rtx_t* rtx, hawk_rio_cmd_t cmd, hawk_rio_arg_t* riod,
		void* data, hawk_oow_t count);
	static hawk_ooi_t consoleHandler (
		hawk_rtx_t* rtx, hawk_rio_cmd_t cmd, hawk_rio_arg_t* riod,
		void* data, hawk_oow_t count);

	static int functionHandler (hawk_rtx_t* rtx, const hawk_fnc_info_t* fi);


	static hawk_flt_t pow (hawk_t* hawk, hawk_flt_t x, hawk_flt_t y);
	static hawk_flt_t mod (hawk_t* hawk, hawk_flt_t x, hawk_flt_t y);

	static void* modopen (hawk_t* hawk, const hawk_mod_spec_t* spec);
	static void  modclose (hawk_t* hawk, void* handle);
	static void* modgetsym (hawk_t* hawk, void* handle, const hawk_ooch_t* name);

public:
	// use these with care
	hawk_t* getHandle() const { return this->hawk; }

protected:
	hawk_t* hawk;

	hawk_errstr_t dflerrstr;
	hawk_errinf_t errinf;
#if defined(HAWK_OOCH_IS_BCH)
	mutable hawk_uch_t xerrmsg[HAWK_ERRMSG_CAPA];
	mutable hawk_uch_t xerrlocfile[128];
#else
	mutable hawk_bch_t xerrmsg[HAWK_ERRMSG_CAPA * 2];
	mutable hawk_bch_t xerrlocfile[256];
#endif

#if defined(HAWK_USE_HTB_FOR_FUNCTION_MAP)
	hawk_htb_t* functionMap;
#else

	class FunctionMap: public HashTable<Cstr,FunctionHandler>
	{
	public:
		FunctionMap (Hawk* hawk): hawk(hawk) {}

	protected:
		Hawk* hawk;
	};

	FunctionMap functionMap;
#endif

	Source* source_reader;
	Source* source_writer;

	Pipe::Handler* pipe_handler;
	File::Handler* file_handler;
	Console::Handler* console_handler;

	struct xstrs_t
	{
		xstrs_t (): ptr (HAWK_NULL), len (0), capa (0) {}

		int add (hawk_t* hawk, const hawk_uch_t* arg, hawk_oow_t len);
		int add (hawk_t* hawk, const hawk_bch_t* arg, hawk_oow_t len);

		void clear (hawk_t* hawk);

		hawk_oocs_t* ptr;
		hawk_oow_t   len;
		hawk_oow_t   capa;
	};

	xstrs_t runarg;

private:
	hawk_cmgr_t* _cmgr;
	Run runctx;

	int init_runctx ();
	void fini_runctx ();
	int dispatch_function (Run* run, const hawk_fnc_info_t* fi);

	static const hawk_ooch_t* xerrstr (hawk_t* a, hawk_errnum_t num);
};



// ----------------------------------------------------------------------------------------

class HAWK_EXPORT MmgrStd: public Mmgr
{
public:
	MmgrStd () HAWK_CXX_NOEXCEPT: Mmgr () {}

	void* allocMem (hawk_oow_t n) HAWK_CXX_NOEXCEPT;
	void* reallocMem (void* ptr, hawk_oow_t n) HAWK_CXX_NOEXCEPT;
	void freeMem (void* ptr) HAWK_CXX_NOEXCEPT;

#if 0
	/// The getInstance() function returns the stock instance of the MmgrStd class.
	static MmgrStd* getInstance () HAWK_CXX_NOEXCEPT;
#endif
};


// ----------------------------------------------------------------------------------------

///
/// The HawkStd class provides an easier-to-use interface by overriding
/// primitive methods, and implementing the file handler, the pipe handler,
/// and common intrinsic functions.
///
class HAWK_EXPORT HawkStd: public Hawk
{
public:
	///
	/// The SourceFile class implements script I/O from and to a file.
	///
	class HAWK_EXPORT SourceFile: public Source
	{
	public:
		SourceFile (const hawk_uch_t* name, hawk_cmgr_t* cmgr = HAWK_NULL): _type(NAME_UCH), _name(name), cmgr(cmgr)
		{
			dir.ptr = HAWK_NULL; dir.len = 0;
		}

		SourceFile (const hawk_bch_t* name, hawk_cmgr_t* cmgr = HAWK_NULL): _type(NAME_BCH), _name(name), cmgr(cmgr)
		{
			dir.ptr = HAWK_NULL; dir.len = 0;
		}

		~SourceFile () {};

		int open (Data& io);
		int close (Data& io);
		hawk_ooi_t read (Data& io, hawk_ooch_t* buf, hawk_oow_t len);
		hawk_ooi_t write (Data& io, const hawk_ooch_t* buf, hawk_oow_t len);

	protected:
		enum
		{
			NAME_UCH,
			NAME_BCH
		} _type;
		const void* _name;
		hawk_oocs_t dir;
		hawk_cmgr_t* cmgr;
	};

	///
	/// The SourceString class implements script input from a string.
	/// Deparsing is not supported.
	///
	class HAWK_EXPORT SourceString: public Source
	{
	public:
		SourceString (const hawk_uch_t* str, hawk_cmgr_t* cmgr = HAWK_NULL): _type(STR_UCH), _str(str), _hawk(HAWK_NULL), str(HAWK_NULL), ptr(HAWK_NULL), cmgr(cmgr) {}
		SourceString (const hawk_bch_t* str, hawk_cmgr_t* cmgr = HAWK_NULL): _type(STR_BCH), _str(str), _hawk(HAWK_NULL), str(HAWK_NULL), ptr(HAWK_NULL), cmgr(cmgr) {}
		~SourceString ();

		int open (Data& io);
		int close (Data& io);
		hawk_ooi_t read (Data& io, hawk_ooch_t* buf, hawk_oow_t len);
		hawk_ooi_t write (Data& io, const hawk_ooch_t* buf, hawk_oow_t len);

	protected:
		enum
		{
			STR_UCH,
			STR_BCH
		} _type;
		const void* _str;
		hawk_t* _hawk;

		hawk_ooch_t* str;
		const hawk_ooch_t* ptr;
		hawk_cmgr_t* cmgr; // for reading included files
	};

	HawkStd (Mmgr* mmgr = HAWK_NULL): Hawk(mmgr), cmgrtab_inited(false), stdmod_up(false), console_cmgr(HAWK_NULL)
	{
	}

	~HawkStd();

	int open ();
	void close ();

	void uponClosing ();

	Run* parse (Source& in, Source& out);

	/// The setConsoleCmgr() function sets the encoding type of
	/// the console streams. They include both the input and the output
	/// streams. It provides no way to specify a different encoding
	/// type for the input and the output stream.
	void setConsoleCmgr (const hawk_cmgr_t* cmgr);

	/// The getConsoleCmgr() function returns the current encoding
	/// type set for the console streams.
	const hawk_cmgr_t* getConsoleCmgr () const;

	/// The addConsoleOutput() function adds a file to form an
	/// output console stream.
	int addConsoleOutput (const hawk_uch_t* arg, hawk_oow_t len);
	int addConsoleOutput (const hawk_uch_t* arg);
	int addConsoleOutput (const hawk_bch_t* arg, hawk_oow_t len);
	int addConsoleOutput (const hawk_bch_t* arg);

	void clearConsoleOutputs ();

protected:
	#define HAWK_STD_ENV_CHAR_IS_BCH
	typedef hawk_bch_t env_char_t;

	int make_additional_globals (Run* run);
	int build_argcv (Run* run);
	int build_environ (Run* run, env_char_t* envarr[]);

	// intrinsic functions
	hawk_cmgr_t* getiocmgr (const hawk_ooch_t* ioname);

	int setioattr (Run& run, Value& ret, Value* args, hawk_oow_t nargs, const hawk_ooch_t* name, hawk_oow_t len);
	int getioattr (Run& run, Value& ret, Value* args, hawk_oow_t nargs, const hawk_ooch_t* name, hawk_oow_t len);

	// pipe io handlers
	int openPipe (Pipe& io);
	int closePipe (Pipe& io);
	hawk_ooi_t readPipe (Pipe& io, hawk_ooch_t* buf, hawk_oow_t len);
	hawk_ooi_t readPipeBytes (Pipe& io, hawk_bch_t* buf, hawk_oow_t len);
	hawk_ooi_t writePipe (Pipe& io, const hawk_ooch_t* buf, hawk_oow_t len);
	hawk_ooi_t writePipeBytes (Pipe& io, const hawk_bch_t* buf, hawk_oow_t len);
	int flushPipe (Pipe& io);

	// file io handlers
	int openFile (File& io);
	int closeFile (File& io);
	hawk_ooi_t readFile (File& io, hawk_ooch_t* buf, hawk_oow_t len);
	hawk_ooi_t readFileBytes (File& io, hawk_bch_t* buf, hawk_oow_t len);
	hawk_ooi_t writeFile (File& io, const hawk_ooch_t* buf, hawk_oow_t len);
	hawk_ooi_t writeFileBytes (File& io, const hawk_bch_t* buf, hawk_oow_t len);
	int flushFile (File& io);

	// console io handlers
	int openConsole (Console& io);
	int closeConsole (Console& io);
	hawk_ooi_t readConsole (Console& io, hawk_ooch_t* buf, hawk_oow_t len);
	hawk_ooi_t readConsoleBytes (Console& io, hawk_bch_t* buf, hawk_oow_t len);
	hawk_ooi_t writeConsole (Console& io, const hawk_ooch_t* buf, hawk_oow_t len);
	hawk_ooi_t writeConsoleBytes (Console& io, const hawk_bch_t* buf, hawk_oow_t len);
	int flushConsole (Console& io);
	int nextConsole (Console& io);

	// primitive handlers
	void* allocMem (hawk_oow_t n);
	void* reallocMem (void* ptr, hawk_oow_t n);
	void  freeMem (void* ptr);

	hawk_flt_t pow (hawk_flt_t x, hawk_flt_t y);
	hawk_flt_t mod (hawk_flt_t x, hawk_flt_t y);

	void* modopen (const hawk_mod_spec_t* spec);
	void  modclose (void* handle);
	void* modgetsym (void* handle, const hawk_ooch_t* name);

protected:
	hawk_htb_t cmgrtab;
	bool cmgrtab_inited;
	bool stdmod_up;

	hawk_cmgr_t* console_cmgr;

	// global variables
	int gbl_argc;
	int gbl_argv;
	int gbl_environ;

	// standard input console - reuse runarg
	hawk_oow_t runarg_index;
	hawk_oow_t runarg_count;

	// standard output console
	xstrs_t ofile;
	hawk_oow_t ofile_index;
	hawk_oow_t ofile_count;

public:
	struct ioattr_t
	{
		hawk_cmgr_t* cmgr;
		hawk_ooch_t cmgr_name[64]; // i assume that the cmgr name never exceeds this length.
		hawk_ntime_t tmout[4];

		ioattr_t (): cmgr (HAWK_NULL)
		{
			this->cmgr_name[0] = HAWK_T('\0');
			for (hawk_oow_t i = 0; i < HAWK_COUNTOF(this->tmout); i++)
			{
				this->tmout[i].sec = -999;
				this->tmout[i].nsec = 0;
			}
		}
	};

	static ioattr_t default_ioattr;

protected:
	ioattr_t* get_ioattr (const hawk_ooch_t* ptr, hawk_oow_t len);
	ioattr_t* find_or_make_ioattr (const hawk_ooch_t* ptr, hawk_oow_t len);


private:
	int open_console_in (Console& io);
	int open_console_out (Console& io);

	int open_pio (Pipe& io);
	int open_nwio (Pipe& io, int flags, void* nwad);
};

/////////////////////////////////
HAWK_END_NAMESPACE(HAWK)
/////////////////////////////////


HAWK_EXPORT void* operator new (hawk_oow_t size, HAWK::Mmgr* mmgr);

#if defined(HAWK_CXX_NO_OPERATOR_DELETE_OVERLOADING)
HAWK_EXPORT void hawk_operator_delete (void* ptr, HAWK::Mmgr* mmgr);
#else
HAWK_EXPORT void operator delete (void* ptr, HAWK::Mmgr* mmgr);
#endif

HAWK_EXPORT void* operator new (hawk_oow_t size, HAWK::Mmgr* mmgr, void* existing_ptr);

#if 0
// i found no way to delete an array allocated with
// the placement new operator. if the array element is an instance
// of a class, the pointer returned by the new operator call
// may not be the actual pointer allocated. some compilers
// seem to put some information about the array at the
// beginning of the allocated memory and return the pointer
// off a few bytes from the beginning.
void* operator new[] (hawk_oow_t size, HAWK::Mmgr* mmgr);
void operator delete[] (void* ptr, HAWK::Mmgr* mmgr);
#endif

#define HAWK_CXX_DELETE_WITH_MMGR(ptr, class_name, mmgr) \
	do { \
		HAWK_CXX_CALL_DESTRUCTOR (ptr, class_name); \
		HAWK_CXX_CALL_PLACEMENT_DELETE1(ptr, mmgr); \
	} while(0);

#endif
