/*
    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.
 */

#include "hawk-prv.h"

#define CHUNKSIZE HAWK_VAL_CHUNK_SIZE

/* this isn't necessary as the implemenation is context(rtx)-centric
 * leave it here for experiment.
#define USE_ATOMIC_REFCNT */

static hawk_val_nil_t hawk_nil = {
	HAWK_SFN(v_refs)   0,
	HAWK_SFN(v_type)   HAWK_VAL_NIL,
	HAWK_SFN(v_static) 1,
	HAWK_SFN(v_nstr)   0,
	HAWK_SFN(v_gc)     0
};
/* zero-length string */
static hawk_val_str_t hawk_zls = {
	HAWK_SFN(v_refs)   0,
	HAWK_SFN(v_type)   HAWK_VAL_STR,
	HAWK_SFN(v_static) 1,
	HAWK_SFN(v_nstr)   0,
	HAWK_SFN(v_gc)     0,
	HAWK_SFN(val)      { HAWK_T(""), 0 }
};
/* zero-length byte string */
static hawk_val_mbs_t hawk_zlbs = {
	HAWK_SFN(v_refs)   0,
	HAWK_SFN(v_type)   HAWK_VAL_MBS,
	HAWK_SFN(v_static) 1,
	HAWK_SFN(v_nstr)   0,
	HAWK_SFN(v_gc)     0,
	HAWK_SFN(val)      { HAWK_BT(""), 0 }
};

hawk_val_t* hawk_val_nil = (hawk_val_t*)&hawk_nil;
hawk_val_t* hawk_val_zls = (hawk_val_t*)&hawk_zls;
hawk_val_t* hawk_val_zlbs = (hawk_val_t*)&hawk_zlbs;

static const hawk_ooch_t* val_type_name[] =
{
	/* synchronize this table with enum hawk_val_type_t in hawk.h */
	HAWK_T("nil"),
	HAWK_T("char"),
	HAWK_T("bchar"),
	HAWK_T("int"),
	HAWK_T("flt"),
	HAWK_T("str"),
	HAWK_T("mbs"),
	HAWK_T("fun"),
	HAWK_T("map"),
	HAWK_T("array"),

	HAWK_T("rex"),
	HAWK_T("ref"),
	HAWK_T("bob")
};

/* --------------------------------------------------------------------- */

#if defined(HAWK_ENABLE_GC)

/*
BEGIN {
  @local a, b, c, nil;
  for (i = 1; i < 10; i++) a[i] = i;
  a[11] = a;
  a[12] = a;
  a = @nil;
  b[1] = a;
  c[1] = 0;
}

BEGIN {
   @local a, b, c, nil;
   j[1] = 20;
   j[2] = 20;
   for (i = 1; i < 10; i++) a[i] = i;
   a[9] = j;
   a[10] = "world";
   a[11] = a;
   a[12] = a;
   a[13] = "hello";
   j[3] = a;
   a[14] = j;
   a = @nil;
   b[1] = a;
   c[1] = 0;
}

BEGIN {
   @local a, b, c, j, nil;
   j[1] = 20;
   j[2] = 20;
   for (i = 1; i < 10; i++) a[i] = i;
   a[9] = j;
   a[10] = "world";
   a[11] = a;
   a[12] = a;
   a[13] = "hello";
   j[3] = a;
   a[14] = j;
   a = @nil;
   b[1] = a;
   c[1] = 0;
}

BEGIN {
   @local a, b, c, nil;
   j[1] = 20;
   j[2] = 20;
   for (i = 1; i < 10; i++) a[i] = i;
   a[9] = j;
   a[10] = "world";
   a[11] = a;
   a[12] = a;
   a[13] = "hello";
   j[3] = a;
   a[14] = j;
   a = @nil;
   j = @nil; hawk::gc();
   b[1] = a;
   c[1] = 0;
}

*/

/*#define DEBUG_GC*/

#define GCH_MOVED HAWK_TYPE_MAX(hawk_uintptr_t)
#define GCH_UNREACHABLE (GCH_MOVED - 1)

static HAWK_INLINE void gc_chain_gch (hawk_gch_t* list, hawk_gch_t* gch)
{
	gch->gc_next = list;
	gch->gc_prev = list->gc_prev;
	gch->gc_prev->gc_next = gch;
	list->gc_prev = gch;
}

static HAWK_INLINE void gc_chain_val (hawk_gch_t* list, hawk_val_t* v)
{
	gc_chain_gch (list, hawk_val_to_gch(v));
}

static HAWK_INLINE void gc_move_all_gchs (hawk_gch_t* src, hawk_gch_t* dst)
{
	/* append src to dst */
	if (src->gc_next != src)
	{
		hawk_gch_t* last;

		last = dst->gc_prev;
		last->gc_next = src->gc_next;
		last->gc_next->gc_prev = last;
		dst->gc_prev = src->gc_prev;
		dst->gc_prev->gc_next = dst;
	}
	src->gc_prev = src;
	src->gc_next = src;
}

static HAWK_INLINE void gc_unchain_gch (hawk_gch_t* gch)
{
	gch->gc_prev->gc_next = gch->gc_next;
	gch->gc_next->gc_prev = gch->gc_prev;
}

static HAWK_INLINE void gc_unchain_val (hawk_val_t* v)
{
	gc_unchain_gch (hawk_val_to_gch(v));
}

static void gc_trace_refs (hawk_gch_t* list)
{
	hawk_gch_t* gch;
	hawk_val_t* v, * iv;
	hawk_map_itr_t itr;
	hawk_map_pair_t* pair;

	/* phase 1 - copy the reference count field from the value header to the gc header */
	gch = list->gc_next;
	while (gch != list)
	{
		gch->gc_refs = hawk_gch_to_val(gch)->v_refs;
		gch = gch->gc_next;
	}

	/* phase 2 - decrement the reference count in the gc header whenever a reference is found */
	gch = list->gc_next;
	while (gch != list)
	{
		v = hawk_gch_to_val(gch);

		if (v->v_type == HAWK_VAL_MAP)
		{
			hawk_map_t* map;

			map = ((hawk_val_map_t*)v)->map;
			hawk_init_map_itr(&itr, 0);
			pair = hawk_map_getfirstpair(map, &itr);
			while (pair)
			{
				iv = (hawk_val_t*)HAWK_MAP_VPTR(pair);
				if (HAWK_VTR_IS_POINTER(iv) && iv->v_gc)
				{
					hawk_val_to_gch(iv)->gc_refs--;
				}
				pair = hawk_map_getnextpair(map, &itr);
			}
		}
		else /* if (v->v_type == HAWK_VAL_ARR) */
		{
			hawk_oow_t size, i;
			hawk_arr_t* arr;

			HAWK_ASSERT(v->v_type == HAWK_VAL_ARR); /* only HAWK_VAL_MAP and HAWK_VAL_ARR */

			arr = ((hawk_val_arr_t*)v)->arr;
			size = HAWK_ARR_SIZE(arr);
			for (i = 0; i < size; i++)
			{
				if (HAWK_ARR_SLOT(arr, i))
				{
					iv = (hawk_val_t*)HAWK_ARR_DPTR(arr, i);
					if (HAWK_VTR_IS_POINTER(iv) && iv->v_gc)
					{
						hawk_val_to_gch(iv)->gc_refs--;
					}
				}
			}
		}

		gch = gch->gc_next;
	}
}

static void gc_dump_refs (hawk_rtx_t* rtx, hawk_gch_t* list)
{
	hawk_gch_t* gch;
	hawk_oow_t count  = 0;

	gch = list->gc_next;
	while (gch != list)
	{
		hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] GCH %p gc_refs %d\n", gch, (int)gch->gc_refs);
		gch = gch->gc_next;
	}
	hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] dumped %ju values\n", count);
}

static void gc_move_reachables (hawk_gch_t* list, hawk_gch_t* reachable_list)
{
	hawk_gch_t* gch, * tmp;
	hawk_val_t* v, * iv;
	hawk_map_itr_t itr;
	hawk_map_pair_t* pair;

	gch = list->gc_next;
	while (gch != list)
	{
		tmp = gch->gc_next;
		if (gch->gc_refs > 0)
		{
			gc_unchain_gch (gch);
			gc_chain_gch (reachable_list, gch);
			gch->gc_refs = GCH_MOVED;
		}
		gch = tmp;
	}

	gch = reachable_list->gc_next;
	while (gch != reachable_list)
	{
		v = hawk_gch_to_val(gch);

		if (v->v_type == HAWK_VAL_MAP)
		{
			hawk_map_t* map;

			/* the key part is a string. don't care. but if a generic value is allowed as a key, this should change... */
			map = ((hawk_val_map_t*)v)->map;

			hawk_init_map_itr (&itr, 0);
			pair = hawk_map_getfirstpair(map, &itr);
			while (pair)
			{
				iv = (hawk_val_t*)HAWK_MAP_VPTR(pair);
				if (HAWK_VTR_IS_POINTER(iv) && iv->v_gc)
				{
					tmp = hawk_val_to_gch(iv);
					if (tmp->gc_refs != GCH_MOVED)
					{
						gc_unchain_gch (tmp);
						gc_chain_gch (reachable_list, tmp);
						tmp->gc_refs = GCH_MOVED;
					}
				}
				pair = hawk_map_getnextpair(map, &itr);
			}
		}
		else /* if (v->v_type == HAWK_VAL_ARR) */
		{
			hawk_oow_t size, i;
			hawk_arr_t* arr;

			HAWK_ASSERT(v->v_type == HAWK_VAL_ARR); /* only HAWK_VAL_MAP and HAWK_VAL_ARR */

			arr = ((hawk_val_arr_t*)v)->arr;
			size = HAWK_ARR_SIZE(arr);
			for (i = 0; i < size; i++)
			{
				if (HAWK_ARR_SLOT(arr, i))
				{
					iv = (hawk_val_t*)HAWK_ARR_DPTR(arr, i);
					if (HAWK_VTR_IS_POINTER(iv) && iv->v_gc)
					{
						tmp = hawk_val_to_gch(iv);
						if (tmp->gc_refs != GCH_MOVED)
						{
							gc_unchain_gch (tmp);
							gc_chain_gch (reachable_list, tmp);
							tmp->gc_refs = GCH_MOVED;
						}
					}
				}
			}
		}

		gch = gch->gc_next;
	}
}

static HAWK_INLINE void gc_free_val (hawk_rtx_t* rtx, hawk_val_t* v)
{
	hawk_rtx_freemem(rtx, hawk_val_to_gch(v));
}

static void gc_free_unreachables (hawk_rtx_t* rtx, hawk_gch_t* list)
{
	hawk_gch_t* gch;

	/* there might be recursive cross references among unreachable values.
	 * simple traversal and sequential disposal causes various issues */

	/* mark all unreabale values */
	gch = list->gc_next;
	while (gch != list)
	{
		gch->gc_refs = GCH_UNREACHABLE;
	#if defined(DEBUG_GC)
		hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] MARKED UNREACHABLE GCH %p\n", gch);
	#endif
		gch = gch->gc_next;
	}

	/* free the values without actually freeing the outer shell */
	gch = list->gc_next;
	while (gch != list)
	{
		hawk_rtx_freeval (rtx, hawk_gch_to_val(gch), HAWK_RTX_FREEVAL_GC_PRESERVE);
		gch = gch->gc_next;
	}

	/* free the outer shell forcibly*/
	while (list->gc_next != list)
	{
		gch = list->gc_next; /* the first entry in the list */

	#if defined(DEBUG_GC)
		hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] FREEING UNREACHABLE GCH %p gc_refs %zu v_refs %zu\n", gch, gch->gc_refs, hawk_gch_to_val(gch)->v_refs);
	#endif
		/* do what hawk_rtx_freeval() would do without HAWK_RTX_FREEVAL_GC_PRESERVE */
		gc_unchain_gch (gch);
		gc_free_val (rtx, hawk_gch_to_val(gch));
	}
}

static HAWK_INLINE void gc_collect_garbage_in_generation (hawk_rtx_t* rtx, int gen)
{
	hawk_oow_t i, newgen;

#if defined(DEBUG_GC)
	hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR,  "[GC] **started - gen %d**\n", gen);
#endif

	newgen = (gen < HAWK_COUNTOF(rtx->gc.g) - 1)? (gen + 1): gen;
	for (i = 0; i < gen; i++)
	{
		gc_move_all_gchs (&rtx->gc.g[i], &rtx->gc.g[gen]);
	}

	if (rtx->gc.g[gen].gc_next != &rtx->gc.g[gen])
	{
		hawk_gch_t reachable;

		gc_trace_refs (&rtx->gc.g[gen]);

		reachable.gc_prev = &reachable;
		reachable.gc_next = &reachable;
		gc_move_reachables (&rtx->gc.g[gen], &reachable);

		/* only unreachables are left in rtx->gc.g[0] */
	#if defined(DEBUG_GC)
	/*gc_dump_refs (rtx, &rtx->gc.g[0]);*/
	#endif
		gc_free_unreachables (rtx, &rtx->gc.g[gen]);
		HAWK_ASSERT(rtx->gc.g[gen].gc_next == &rtx->gc.g[gen]);

		/* move all reachables back to the main list */
		gc_move_all_gchs (&reachable, &rtx->gc.g[newgen]);
	}

	/* [NOTE] pressure is greater than other elements by 1 in size.
	 *        i store the number of collections for gen 0 in pressure[1].
	 *        so i can avoid some comparison when doing this */
	rtx->gc.pressure[gen + 1]++; /* number of collections done for gen */
	rtx->gc.pressure[gen] = 0;   /* reset the number of collections of the previous generation */
	rtx->gc.pressure[0] = 0; /* reset the number of allocations since last gc. this line is redundant if gen is 0. */

#if defined(DEBUG_GC)
	hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR,  "[GC] **ended**\n");
#endif
}

static HAWK_INLINE int gc_collect_garbage_auto (hawk_rtx_t* rtx)
{
	hawk_oow_t i;

	i = HAWK_COUNTOF(rtx->gc.g);
	while (i > 1)
	{
		--i;
		if (rtx->gc.pressure[i] >= rtx->gc.threshold[i])
		{
			gc_collect_garbage_in_generation (rtx, i);
			return i;
		}
	}

	gc_collect_garbage_in_generation (rtx, 0);
	return 0;
}

int hawk_rtx_gc (hawk_rtx_t* rtx, int gen)
{
	if (gen < 0)
	{
		return gc_collect_garbage_auto (rtx);
	}
	else
	{
		if (gen >= HAWK_COUNTOF(rtx->gc.g)) gen = HAWK_COUNTOF(rtx->gc.g) - 1;
		gc_collect_garbage_in_generation (rtx, gen);
		return gen;
	}
}


static HAWK_INLINE hawk_val_t* gc_calloc_val (hawk_rtx_t* rtx, hawk_oow_t size)
{
	hawk_gch_t* gch;
	int gc_gen = 0;

	if (HAWK_UNLIKELY(rtx->gc.pressure[0] >= rtx->gc.threshold[0]))
	{
		/* invoke generational garbage collection */
		gc_gen = gc_collect_garbage_auto(rtx);
	}

	gch = (hawk_gch_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(*gch) + size);
	if (HAWK_UNLIKELY(!gch))
	{
		if (gc_gen < HAWK_COUNTOF(rtx->gc.g) - 1)
		{
			/* perform full gc if full gc has not been triggerred at the beginning of this function */
			hawk_rtx_gc (rtx, HAWK_COUNTOF(rtx->gc.g) - 1);
		}
		gch = (hawk_gch_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(*gch) + size);
		if (HAWK_UNLIKELY(!gch)) return HAWK_NULL;
	}

	rtx->gc.pressure[0]++; /* increment of the number of allocation attempt */
	return hawk_gch_to_val(gch);
}

#endif

/* --------------------------------------------------------------------- */

int hawk_get_val_type (hawk_val_t* val)
{
	return HAWK_GET_VAL_TYPE(val);
}

const hawk_ooch_t* hawk_get_val_type_name (hawk_val_t* val)
{
	return val_type_name[HAWK_GET_VAL_TYPE(val)];
}

hawk_val_t* hawk_get_nil_val (void)
{
	return (hawk_val_t*)&hawk_nil;
}

int hawk_rtx_isnilval (hawk_rtx_t* rtx, const hawk_val_t* val)
{
	return val == (hawk_val_t*)&hawk_nil || (HAWK_VTR_IS_POINTER(val) && val->v_type == HAWK_VAL_NIL);
}

hawk_val_t* hawk_rtx_makenilval (hawk_rtx_t* rtx)
{
	return (hawk_val_t*)&hawk_nil;
}

hawk_val_t* hawk_rtx_makecharval (hawk_rtx_t* rtx, hawk_ooch_t v)
{
	return HAWK_CHAR_TO_VTR((hawk_oochu_t)v);
}

hawk_val_t* hawk_rtx_makebchrval (hawk_rtx_t* rtx, hawk_bch_t v)
{
	return HAWK_BCHR_TO_VTR((hawk_bchu_t)v);
}

hawk_val_t* hawk_rtx_makeintval (hawk_rtx_t* rtx, hawk_int_t v)
{
	hawk_val_int_t* val;

	if (HAWK_IN_INT_RANGE(v)) return HAWK_INT_TO_VTR(v);

	if (!rtx->vmgr.ifree)
	{
		hawk_val_ichunk_t* c;
		/*hawk_val_int_t* x;*/
		hawk_oow_t i;

		/* use hawk_val_ichunk structure to avoid
		 * any alignment issues on platforms requiring
		 * aligned memory access - using the code commented out
		 * will cause a fault on such a platform */
		c = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(hawk_val_ichunk_t));
		if (HAWK_UNLIKELY(!c)) return HAWK_NULL;

		c->next = rtx->vmgr.ichunk;
		/*run->vmgr.ichunk = c;*/
		rtx->vmgr.ichunk = (hawk_val_chunk_t*)c;

		/*x = (hawk_val_int_t*)(c + 1);
		for (i = 0; i < CHUNKSIZE-1; i++)
			x[i].nde = (hawk_nde_int_t*)&x[i+1];
		x[i].nde = HAWK_NULL;

		run->vmgr.ifree = x;
		*/

		for (i = 0; i < CHUNKSIZE-1; i++)
			c->slot[i].nde = (hawk_nde_int_t*)&c->slot[i+1];
		c->slot[i].nde = HAWK_NULL;

		rtx->vmgr.ifree = &c->slot[0];
	}

	val = rtx->vmgr.ifree;
	rtx->vmgr.ifree = (hawk_val_int_t*)val->nde;

	val->v_type = HAWK_VAL_INT;
	val->v_refs = 0;
	val->v_static = 0;
	val->v_nstr = 0;
	val->v_gc = 0;
	val->i_val = v;
	val->nde = HAWK_NULL;

#if defined(DEBUG_VAL)
	hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T("makeintval => %jd [%p] - [%O]\n"), (hawk_intmax_t)v, val, val);
#endif
	return (hawk_val_t*)val;
}

hawk_val_t* hawk_rtx_makefltval (hawk_rtx_t* rtx, hawk_flt_t v)
{
	hawk_val_flt_t* val;

	if (rtx->vmgr.rfree == HAWK_NULL)
	{
		hawk_val_rchunk_t* c;
		hawk_oow_t i;

		c = hawk_rtx_allocmem(rtx, HAWK_SIZEOF(hawk_val_rchunk_t));
		if (!c) return HAWK_NULL;

		c->next = rtx->vmgr.rchunk;
		rtx->vmgr.rchunk = (hawk_val_chunk_t*)c;

		for (i = 0; i < CHUNKSIZE-1; i++)
			c->slot[i].nde = (hawk_nde_flt_t*)&c->slot[i+1];
		c->slot[i].nde = HAWK_NULL;

		rtx->vmgr.rfree = &c->slot[0];
	}

	val = rtx->vmgr.rfree;
	rtx->vmgr.rfree = (hawk_val_flt_t*)val->nde;

	val->v_type = HAWK_VAL_FLT;
	val->v_refs = 0;
	val->v_static = 0;
	val->v_nstr = 0;
	val->v_gc = 0;
	val->val = v;
	val->nde = HAWK_NULL;

#if defined(DEBUG_VAL)
	hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T("makefltval => %Lf [%p] - [%O]\n"), (double)v, val, val);
#endif
	return (hawk_val_t*)val;
}

static HAWK_INLINE hawk_val_t* make_str_val (hawk_rtx_t* rtx, const hawk_ooch_t* str1, hawk_oow_t len1, const hawk_ooch_t* str2, hawk_oow_t len2)
{
	hawk_val_str_t* val = HAWK_NULL;
	hawk_oow_t aligned_len;
#if defined(HAWK_ENABLE_STR_CACHE)
	hawk_oow_t i;
#endif

	if (HAWK_UNLIKELY(len1 <= 0 && len2 <= 0)) return hawk_val_zls;
	aligned_len = HAWK_ALIGN_POW2((len1 + len2 + 1), HAWK_STR_CACHE_BLOCK_UNIT);

#if defined(HAWK_ENABLE_STR_CACHE)
	i = aligned_len / HAWK_STR_CACHE_BLOCK_UNIT;
	if (i < HAWK_COUNTOF(rtx->str_cache_count))
	{
		if (rtx->str_cache_count[i] > 0)
		{
			val = rtx->str_cache[i][--rtx->str_cache_count[i]];
			goto init;
		}
	}
#endif

	val = (hawk_val_str_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(hawk_val_str_t) + (aligned_len * HAWK_SIZEOF(hawk_ooch_t)));
	if (HAWK_UNLIKELY(!val)) return HAWK_NULL;

#if defined(HAWK_ENABLE_STR_CACHE)
init:
#endif
	val->v_type = HAWK_VAL_STR;
	val->v_refs = 0;
	val->v_static = 0;
	val->v_nstr = 0;
	val->v_gc = 0;
	val->val.len = len1 + len2;
	val->val.ptr = (hawk_ooch_t*)(val + 1);
	if (HAWK_LIKELY(str1)) hawk_copy_oochars_to_oocstr_unlimited (&val->val.ptr[0], str1, len1);
	if (str2) hawk_copy_oochars_to_oocstr_unlimited (&val->val.ptr[len1], str2, len2);
	val->val.ptr[val->val.len] = '\0';

#if defined(DEBUG_VAL)
	hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T("make_str_val => %p - [%O]\n"), val, val);
#endif
	return (hawk_val_t*)val;
}

hawk_val_t* hawk_rtx_makestrvalwithuchars (hawk_rtx_t* rtx, const hawk_uch_t* ucs, hawk_oow_t len)
{
#if defined(HAWK_OOCH_IS_UCH)
	return make_str_val(rtx, ucs, len, HAWK_NULL, 0);
#else
	hawk_val_t* v;
	hawk_bch_t* bcs;
	hawk_oow_t bcslen;

	bcs = hawk_rtx_duputobchars(rtx, ucs, len, &bcslen);
	if (HAWK_UNLIKELY(!bcs)) return HAWK_NULL;

	v = make_str_val(rtx, bcs, bcslen, HAWK_NULL, 0);
	hawk_rtx_freemem(rtx, bcs);
	return v;
#endif
}

hawk_val_t* hawk_rtx_makestrvalwithbchars (hawk_rtx_t* rtx, const hawk_bch_t* bcs, hawk_oow_t len)
{
#if defined(HAWK_OOCH_IS_UCH)
	hawk_val_t* v;
	hawk_uch_t* ucs;
	hawk_oow_t ucslen;

	ucs = hawk_rtx_dupbtouchars(rtx, bcs, len, &ucslen, 1);
	if (HAWK_UNLIKELY(!ucs)) return HAWK_NULL;

	v = make_str_val(rtx, ucs, ucslen, HAWK_NULL, 0);
	hawk_rtx_freemem(rtx, ucs);
	return v;
#else
	return make_str_val(rtx, bcs, len, HAWK_NULL, 0);
#endif
}

hawk_val_t* hawk_rtx_makestrvalwithbcstr (hawk_rtx_t* rtx, const hawk_bch_t* bcs)
{
	return hawk_rtx_makestrvalwithbchars(rtx, bcs, hawk_count_bcstr(bcs));
}

hawk_val_t* hawk_rtx_makestrvalwithucstr (hawk_rtx_t* rtx, const hawk_uch_t* ucs)
{
	return hawk_rtx_makestrvalwithuchars(rtx, ucs, hawk_count_ucstr(ucs));
}

hawk_val_t* hawk_rtx_makestrvalwithucs (hawk_rtx_t* rtx, const hawk_ucs_t* ucs)
{
	return hawk_rtx_makestrvalwithuchars(rtx, ucs->ptr, ucs->len);
}

hawk_val_t* hawk_rtx_makestrvalwithbcs (hawk_rtx_t* rtx, const hawk_bcs_t* bcs)
{
	return hawk_rtx_makestrvalwithbchars(rtx, bcs->ptr, bcs->len);
}

hawk_val_t* hawk_rtx_makestrvalwithuchars2 (hawk_rtx_t* rtx, const hawk_uch_t* ucs1, hawk_oow_t len1, const hawk_uch_t* ucs2, hawk_oow_t len2)
{
#if defined(HAWK_OOCH_IS_UCH)
	return make_str_val(rtx, ucs1, len1, ucs2, len2);
#else
	hawk_val_t* v;
	hawk_bch_t* bcs;
	hawk_oow_t bcslen;

	bcs = hawk_rtx_dupu2tobchars(rtx, ucs1, len1, ucs2, len2, &bcslen);
	if (HAWK_UNLIKELY(!bcs)) return HAWK_NULL;

	v = make_str_val(rtx, bcs, bcslen, HAWK_NULL, 0);
	hawk_rtx_freemem(rtx, bcs);
	return v;
#endif
}

hawk_val_t* hawk_rtx_makestrvalwithbchars2 (hawk_rtx_t* rtx, const hawk_bch_t* bcs1, hawk_oow_t len1, const hawk_bch_t* bcs2, hawk_oow_t len2)
{
#if defined(HAWK_OOCH_IS_UCH)
	hawk_val_t* v;
	hawk_uch_t* ucs;
	hawk_oow_t ucslen;

	ucs = hawk_rtx_dupb2touchars(rtx, bcs1, len1, bcs2, len2, &ucslen, 1);
	if (HAWK_UNLIKELY(!ucs)) return HAWK_NULL;

	v = make_str_val(rtx, ucs, ucslen, HAWK_NULL, 0);
	hawk_rtx_freemem(rtx, ucs);
	return v;
#else
	return make_str_val(rtx, bcs1, len1, bcs2, len2);
#endif
}

/* --------------------------------------------------------------------- */

hawk_val_t* hawk_rtx_makenumorstrvalwithuchars (hawk_rtx_t* rtx, const hawk_uch_t* ptr, hawk_oow_t len)
{
	int x;
	hawk_int_t l;
	hawk_flt_t r;

	if (len == 1 && ptr[0] == '.') goto make_str;

	if (HAWK_RTX_IS_NUMSTRDETECT_ON(rtx))
	{
		x = hawk_uchars_to_num(HAWK_OOCHARS_TO_NUM_MAKE_OPTION(1, 1, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0), ptr, len, &l, &r);
		if (x == 0) return hawk_rtx_makeintval(rtx, l);
		else if (x >= 1) return hawk_rtx_makefltval(rtx, r);
	}

make_str:
	return hawk_rtx_makestrvalwithuchars(rtx, ptr, len);
}

hawk_val_t* hawk_rtx_makenumorstrvalwithbchars (hawk_rtx_t* rtx, const hawk_bch_t* ptr, hawk_oow_t len)
{
	int x;
	hawk_int_t l;
	hawk_flt_t r;

	if (len == 1 && ptr[0] == '.') goto make_str;

	if (HAWK_RTX_IS_NUMSTRDETECT_ON(rtx))
	{
		x = hawk_bchars_to_num(HAWK_OOCHARS_TO_NUM_MAKE_OPTION(1, 1, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0), ptr, len, &l, &r);
		if (x == 0) return hawk_rtx_makeintval(rtx, l);
		else if (x >= 1) return hawk_rtx_makefltval(rtx, r);
	}

make_str:
	return hawk_rtx_makestrvalwithbchars(rtx, ptr, len);
}


/* --------------------------------------------------------------------- */

hawk_val_t* hawk_rtx_makenstrvalwithuchars (hawk_rtx_t* rtx, const hawk_uch_t* ptr, hawk_oow_t len)
{
	int x;
	hawk_val_t* v;
	hawk_int_t l;
	hawk_flt_t r;

	x = hawk_uchars_to_num(HAWK_OOCHARS_TO_NUM_MAKE_OPTION(1, 0, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0), ptr, len, &l, &r);
	v = hawk_rtx_makestrvalwithuchars(rtx, ptr, len);
	if (HAWK_UNLIKELY(!v)) return HAWK_NULL;


	if (x >= 0)
	{
		/* set the numeric string flag if a string
		 * can be converted to a number */
		HAWK_ASSERT(x == 0 || x == 1);
		v->v_nstr = x + 1; /* long -> 1, real -> 2 */
	}

	return v;
}

hawk_val_t* hawk_rtx_makenstrvalwithbchars (hawk_rtx_t* rtx, const hawk_bch_t* ptr, hawk_oow_t len)
{
	int x;
	hawk_val_t* v;
	hawk_int_t l;
	hawk_flt_t r;

	x = hawk_bchars_to_num(HAWK_OOCHARS_TO_NUM_MAKE_OPTION(1, 0, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0), ptr, len, &l, &r);
	v = hawk_rtx_makestrvalwithbchars(rtx, ptr, len);
	if (HAWK_UNLIKELY(!v)) return HAWK_NULL;

	if (x >= 0)
	{
		/* set the numeric string flag if a string
		 * can be converted to a number */
		HAWK_ASSERT(x == 0 || x == 1);
		v->v_nstr = x + 1; /* long -> 1, real -> 2 */
	}

	return v;
}

hawk_val_t* hawk_rtx_makenstrvalwithucstr (hawk_rtx_t* rtx, const hawk_uch_t* str)
{
	return hawk_rtx_makenstrvalwithuchars(rtx, str, hawk_count_ucstr(str));
}

hawk_val_t* hawk_rtx_makenstrvalwithbcstr (hawk_rtx_t* rtx, const hawk_bch_t* str)
{
	return hawk_rtx_makenstrvalwithbchars(rtx, str, hawk_count_bcstr(str));
}

hawk_val_t* hawk_rtx_makenstrvalwithucs (hawk_rtx_t* rtx, const hawk_ucs_t* str)
{
	return hawk_rtx_makenstrvalwithuchars(rtx, str->ptr, str->len);
}

hawk_val_t* hawk_rtx_makenstrvalwithbcs (hawk_rtx_t* rtx, const hawk_bcs_t* str)
{
	return hawk_rtx_makenstrvalwithbchars(rtx, str->ptr, str->len);
}

/* --------------------------------------------------------------------- */

static HAWK_INLINE hawk_val_t* make_mbs_val (hawk_rtx_t* rtx, const hawk_bch_t* mbs1, hawk_oow_t len1, const hawk_bch_t* mbs2, hawk_oow_t len2)
{
	hawk_val_mbs_t* val = HAWK_NULL;
	hawk_oow_t aligned_len;
#if defined(HAWK_ENABLE_MBS_CACHE)
	hawk_oow_t i;
#endif

	if (HAWK_UNLIKELY(len1 <= 0 && len2 <= 0)) return hawk_val_zlbs;
	aligned_len = HAWK_ALIGN_POW2((len1 + len2 + 1), HAWK_MBS_CACHE_BLOCK_UNIT);

#if defined(HAWK_ENABLE_MBS_CACHE)
	i = aligned_len / HAWK_MBS_CACHE_BLOCK_UNIT;
	if (i < HAWK_COUNTOF(rtx->mbs_cache_count))
	{
		if (rtx->mbs_cache_count[i] > 0)
		{
			val = rtx->mbs_cache[i][--rtx->mbs_cache_count[i]];
			goto init;
		}
	}
#endif

	val = (hawk_val_mbs_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(hawk_val_mbs_t) + (aligned_len * HAWK_SIZEOF(hawk_bch_t)));
	if (HAWK_UNLIKELY(!val)) return HAWK_NULL;

#if defined(HAWK_ENABLE_MBS_CACHE)
init:
#endif
	val->v_type = HAWK_VAL_MBS;
	val->v_refs = 0;
	val->v_static = 0;
	val->v_nstr = 0;
	val->v_gc = 0;
	val->val.len = len1 + len2;
	val->val.ptr = (hawk_bch_t*)(val + 1);
	if (HAWK_LIKELY(mbs1)) hawk_copy_bchars_to_bcstr_unlimited (&val->val.ptr[0], mbs1, len1);
	if (mbs2) hawk_copy_bchars_to_bcstr_unlimited (&val->val.ptr[len1], mbs2, len2);
	val->val.ptr[val->val.len] = '\0';

#if defined(DEBUG_VAL)
	hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T("make_mbs_val => %p - [%O]\n"), val, val);
#endif
	return (hawk_val_t*)val;
}

hawk_val_t* hawk_rtx_makembsvalwithbchars (hawk_rtx_t* rtx, const hawk_bch_t* ptr, hawk_oow_t len)
{
	return make_mbs_val(rtx, ptr, len, HAWK_NULL, 0);
}

hawk_val_t* hawk_rtx_makembsvalwithuchars (hawk_rtx_t* rtx, const hawk_uch_t* ucs, hawk_oow_t len)
{
	hawk_val_t* val;
	hawk_bch_t* bcs;
	hawk_oow_t bcslen;

	if (HAWK_UNLIKELY(len <= 0)) return hawk_val_zlbs;

	bcs = hawk_rtx_duputobchars(rtx, ucs, len, &bcslen);
	if (HAWK_UNLIKELY(!bcs)) return HAWK_NULL;

	val = make_mbs_val(rtx, bcs, bcslen, HAWK_NULL, 0);
	hawk_rtx_freemem(rtx, bcs);

	return val;
}

hawk_val_t* hawk_rtx_makembsvalwithbcs (hawk_rtx_t* rtx, const hawk_bcs_t* bcs)
{
	return make_mbs_val(rtx, bcs->ptr, bcs->len, HAWK_NULL, 0);
}

hawk_val_t* hawk_rtx_makembsvalwithucs (hawk_rtx_t* rtx, const hawk_ucs_t* ucs)
{
	return hawk_rtx_makembsvalwithuchars(rtx, ucs->ptr, ucs->len);
}

hawk_val_t* hawk_rtx_makembsvalwithbcstr (hawk_rtx_t* rtx, const hawk_bch_t* bcs)
{
	return make_mbs_val(rtx, bcs, hawk_count_bcstr(bcs), HAWK_NULL, 0);
}

hawk_val_t* hawk_rtx_makembsvalwithucstr (hawk_rtx_t* rtx, const hawk_uch_t* ucs)
{
	return hawk_rtx_makembsvalwithuchars(rtx, ucs, hawk_count_ucstr(ucs));
}
/* --------------------------------------------------------------------- */

hawk_val_t* hawk_rtx_makembsvalwithuchars2 (hawk_rtx_t* rtx, const hawk_uch_t* ucs1, hawk_oow_t len1, const hawk_uch_t* ucs2, hawk_oow_t len2)
{
	hawk_val_t* v;
	hawk_bch_t* bcs;
	hawk_oow_t bcslen;

	bcs = hawk_rtx_dupu2tobchars(rtx, ucs1, len1, ucs2, len2, &bcslen);
	if (HAWK_UNLIKELY(!bcs)) return HAWK_NULL;

	v = make_mbs_val(rtx, bcs, bcslen, HAWK_NULL, 0);
	hawk_rtx_freemem(rtx, bcs);
	return v;
}

hawk_val_t* hawk_rtx_makembsvalwithbchars2 (hawk_rtx_t* rtx, const hawk_bch_t* bcs1, hawk_oow_t len1, const hawk_bch_t* bcs2, hawk_oow_t len2)
{
	return make_mbs_val(rtx, bcs1, len1, bcs2, len2);
}

/* --------------------------------------------------------------------- */

hawk_val_t* hawk_rtx_makenumormbsvalwithuchars (hawk_rtx_t* rtx, const hawk_uch_t* ptr, hawk_oow_t len)
{
	int x;
	hawk_int_t l;
	hawk_flt_t r;

	if (len == 1 && ptr[0] == '.') goto make_str;

	if (HAWK_RTX_IS_NUMSTRDETECT_ON(rtx))
	{
		x = hawk_uchars_to_num(HAWK_OOCHARS_TO_NUM_MAKE_OPTION(1, 1, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0), ptr, len, &l, &r);
		if (x == 0) return hawk_rtx_makeintval(rtx, l);
		else if (x >= 1) return hawk_rtx_makefltval(rtx, r);
	}

make_str:
	return hawk_rtx_makembsvalwithuchars(rtx, ptr, len);
}

hawk_val_t* hawk_rtx_makenumormbsvalwithbchars (hawk_rtx_t* rtx, const hawk_bch_t* ptr, hawk_oow_t len)
{
	int x;
	hawk_int_t l;
	hawk_flt_t r;

	if (len == 1 && ptr[0] == '.') goto make_str;

	if (HAWK_RTX_IS_NUMSTRDETECT_ON(rtx))
	{
		x = hawk_bchars_to_num(HAWK_OOCHARS_TO_NUM_MAKE_OPTION(1, 1, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0), ptr, len, &l, &r);
		if (x == 0) return hawk_rtx_makeintval(rtx, l);
		else if (x >= 1) return hawk_rtx_makefltval(rtx, r);
	}

make_str:
	return hawk_rtx_makembsvalwithbchars(rtx, ptr, len);
}

/* --------------------------------------------------------------------- */

hawk_val_t* hawk_rtx_makerexval (hawk_rtx_t* rtx, const hawk_oocs_t* str, hawk_tre_t* code[2])
{
	hawk_val_rex_t* val;
	hawk_oow_t totsz;

	/* the regular expression value holds:
	 * - header
	 * - a raw string plus with added a terminating '\0'
	 * the total size is just large enough for all these.
	 */
	totsz = HAWK_SIZEOF(*val) + (HAWK_SIZEOF(*str->ptr) * (str->len + 1));
	val = (hawk_val_rex_t*)hawk_rtx_callocmem(rtx, totsz);
	if (HAWK_UNLIKELY(!val)) return HAWK_NULL;

	val->v_type = HAWK_VAL_REX;
	val->v_refs = 0;
	val->v_static = 0;
	val->v_nstr = 0;
	val->v_gc = 0;
	val->str.len = str->len;

	val->str.ptr = (hawk_ooch_t*)(val + 1);
	hawk_copy_oochars_to_oocstr_unlimited (val->str.ptr, str->ptr, str->len);

	val->code[0] = code[0];
	val->code[1] = code[1];

	return (hawk_val_t*)val;
}

/* --------------------------------------------------------------------- */

static void free_arrval (hawk_arr_t* arr, void* dptr, hawk_oow_t dlen)
{
	hawk_rtx_t* rtx = *(hawk_rtx_t**)hawk_arr_getxtn(arr);
	hawk_val_t* v = (hawk_val_t*)dptr;

#if defined(DEBUG_VAL)
	hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T("refdown in arr free - [%O]\n"), v);
#endif

#if defined(HAWK_ENABLE_GC)
	if (HAWK_VTR_IS_POINTER(v) && v->v_gc && hawk_val_to_gch(v)->gc_refs == GCH_UNREACHABLE)
	{
		/* do nothing if the element is unreachable.
		 * this behavior pairs up with gc_free_unreachables() to
		 * achieve safe disposal of a value */
		return;
	}
#endif

	hawk_rtx_refdownval(rtx, v);
}

static void same_arrval (hawk_arr_t* map, void* dptr, hawk_oow_t dlen)
{
	hawk_rtx_t* rtx = *(hawk_rtx_t**)hawk_arr_getxtn(map);
#if defined(DEBUG_VAL)
	hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T("refdown nofree in arr free - [%O]\n"), dptr);
#endif
	hawk_rtx_refdownval_nofree (rtx, dptr);
}


hawk_val_t* hawk_rtx_makearrval (hawk_rtx_t* rtx, hawk_ooi_t init_capa)
{
	static hawk_arr_style_t style =
	{
	/* the key is copied inline into a pair and is freed when the pair
	 * is destroyed. not setting copier for a value means that the pointer
	 * to the data allocated somewhere else is remembered in a pair. but
	 * freeing the actual value is handled by free_arrval and same_arrval */

		HAWK_ARR_COPIER_DEFAULT,
		free_arrval,
		HAWK_ARR_COMPER_DEFAULT,
		same_arrval,
		HAWK_ARR_SIZER_DEFAULT
	};
#if defined(HAWK_ENABLE_GC)
	int retried = 0;
#endif
	hawk_val_arr_t* val;

#if defined(HAWK_ENABLE_GC)
retry:
	val = (hawk_val_arr_t*)gc_calloc_val(rtx, HAWK_SIZEOF(hawk_val_arr_t) + HAWK_SIZEOF(hawk_arr_t) + HAWK_SIZEOF(rtx));
#else
	val = (hawk_val_arr_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(hawk_val_arr_t) + HAWK_SIZEOF(hawk_arr_t) + HAWK_SIZEOF(rtx));
#endif
	if (HAWK_UNLIKELY(!val)) return HAWK_NULL;

	val->v_type = HAWK_VAL_ARR;
	val->v_refs = 0;
	val->v_static = 0;
	val->v_nstr = 0;
	val->v_gc = 0;
	val->arr = (hawk_arr_t*)(val + 1);

	if (init_capa < 0) init_capa = 64; /* TODO: what is the best initial value? */
	if (HAWK_UNLIKELY(hawk_arr_init(val->arr, hawk_rtx_getgem(rtx), init_capa) <= -1))
	{
#if defined(HAWK_ENABLE_GC)
		gc_free_val (rtx, (hawk_val_t*)val);
		if (HAWK_LIKELY(!retried))
		{
			/* this arr involves non-gc allocatinon, which happens outside gc_calloc_val().
			 * reattempt to allocate after full gc like gc_calloc_val() */
			hawk_rtx_gc (rtx, HAWK_COUNTOF(rtx->gc.g) - 1);
			retried = 1;
			goto retry;
		}
#else
		hawk_rtx_freemem(rtx, val);
#endif
		return HAWK_NULL;
	}
	*(hawk_rtx_t**)hawk_arr_getxtn(val->arr) = rtx;
	hawk_arr_setstyle (val->arr, &style);

#if defined(HAWK_ENABLE_GC)
	gc_chain_val (&rtx->gc.g[0], (hawk_val_t*)val);
	val->v_gc = 1; /* only array and map are to be garbaged collected as of now */
	#if defined(DEBUG_GC)
	hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] MADE GCH %p VAL(ARR) %p\n", hawk_val_to_gch(val), val);
	#endif
#endif

	return (hawk_val_t*)val;
}

/* --------------------------------------------------------------------- */

static void free_mapval (hawk_map_t* map, void* dptr, hawk_oow_t dlen)
{
	hawk_rtx_t* rtx = *(hawk_rtx_t**)hawk_map_getxtn(map);
	hawk_val_t* v = (hawk_val_t*)dptr;

#if defined(DEBUG_VAL)
	hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T("refdown in map free - [%O]\n"), v);
#endif

#if defined(HAWK_ENABLE_GC)
	if (HAWK_VTR_IS_POINTER(v) && v->v_gc && hawk_val_to_gch(v)->gc_refs == GCH_UNREACHABLE)
	{
		/* do nothing if the element is unreachable.
		 * this behavior pairs up with gc_free_unreachables() to
		 * achieve safe disposal of a value */
		return;
	}
#endif

	hawk_rtx_refdownval(rtx, v);
}

static void same_mapval (hawk_map_t* map, void* dptr, hawk_oow_t dlen)
{
	hawk_rtx_t* rtx = *(hawk_rtx_t**)hawk_map_getxtn(map);
#if defined(DEBUG_VAL)
	hawk_logfmt (hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T("refdown nofree in map free - [%O]\n"), dptr);
#endif
	hawk_rtx_refdownval_nofree (rtx, dptr);
}

hawk_val_t* hawk_rtx_makemapval (hawk_rtx_t* rtx)
{
	static hawk_map_style_t style =
	{
	/* the key is copied inline into a pair and is freed when the pair
	 * is destroyed. not setting copier for a value means that the pointer
	 * to the data allocated somewhere else is remembered in a pair. but
	 * freeing the actual value is handled by free_mapval and same_mapval */
		{
			HAWK_MAP_COPIER_INLINE,
			HAWK_MAP_COPIER_DEFAULT
		},
		{
			HAWK_MAP_FREEER_DEFAULT,
			free_mapval
		},
		HAWK_MAP_COMPER_DEFAULT,
		same_mapval,
	#if defined(HAWK_MAP_IS_HTB)
		HAWK_MAP_SIZER_DEFAULT,
		HAWK_MAP_HASHER_DEFAULT
	#endif
	};
#if defined(HAWK_ENABLE_GC)
	int retried = 0;
#endif
	hawk_val_map_t* val;

#if defined(HAWK_ENABLE_GC)
retry:
	val = (hawk_val_map_t*)gc_calloc_val(rtx, HAWK_SIZEOF(hawk_val_map_t) + HAWK_SIZEOF(hawk_map_t) + HAWK_SIZEOF(rtx));
#else
	val = (hawk_val_map_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(hawk_val_map_t) + HAWK_SIZEOF(hawk_map_t) + HAWK_SIZEOF(rtx));
#endif
	if (HAWK_UNLIKELY(!val)) return HAWK_NULL;

	val->v_type = HAWK_VAL_MAP;
	val->v_refs = 0;
	val->v_static = 0;
	val->v_nstr = 0;
	val->v_gc = 0;
	val->map = (hawk_map_t*)(val + 1);

	if (HAWK_UNLIKELY(hawk_map_init(val->map, hawk_rtx_getgem(rtx), 256, 70, HAWK_SIZEOF(hawk_ooch_t), 1) <= -1))
	{
#if defined(HAWK_ENABLE_GC)
		gc_free_val (rtx, (hawk_val_t*)val);
		if (HAWK_LIKELY(!retried))
		{
			/* this map involves non-gc allocatinon, which happens outside gc_calloc_val().
			 * reattempt to allocate after full gc like gc_calloc_val() */
			hawk_rtx_gc (rtx, HAWK_COUNTOF(rtx->gc.g) - 1);
			retried = 1;
			goto retry;
		}
#else
		hawk_rtx_freemem(rtx, val);
#endif
		return HAWK_NULL;
	}
	*(hawk_rtx_t**)hawk_map_getxtn(val->map) = rtx;
	hawk_map_setstyle (val->map, &style);

#if defined(HAWK_ENABLE_GC)
	gc_chain_val (&rtx->gc.g[0], (hawk_val_t*)val);
	val->v_gc = 1; /* only array and map are to be garbaged collected as of now */
	#if defined(DEBUG_GC)
	hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] MADE GCH %p VAL(MAP) %p\n", hawk_val_to_gch(val), val);
	#endif
#endif

	return (hawk_val_t*)val;
}

hawk_val_t* hawk_rtx_makemapvalwithdata (hawk_rtx_t* rtx, hawk_val_map_data_t data[], hawk_oow_t count)
{
	hawk_val_t* map, * tmp;

	hawk_oow_t i;

	map = hawk_rtx_makemapval(rtx);
	if (HAWK_UNLIKELY(!map)) return HAWK_NULL;

	hawk_rtx_refupval(rtx, map);

	for (i = 0; i < count; i++)
	{
		hawk_val_map_data_t* p;

		p = &data[i];

		switch (p->type)
		{
			case HAWK_VAL_MAP_DATA_INT:
			{
				hawk_int_t iv = 0;

				if (p->type_size > 0 && p->type_size <= HAWK_SIZEOF(iv))
				{
				#if defined(HAWK_ENDIAN_LITTLE)
					HAWK_MEMCPY(&iv, p->vptr, p->type_size);
				#else
					HAWK_MEMCPY((hawk_uint8_t*)&iv + (HAWK_SIZEOF(iv) - p->type_size), p->vptr, p->type_size);
				#endif
				}
				else
				{
					iv = *(hawk_int_t*)p->vptr;
				}
				tmp = hawk_rtx_makeintval(rtx, iv);
				break;
			}

			case HAWK_VAL_MAP_DATA_FLT:
				tmp = hawk_rtx_makefltval(rtx, *(hawk_flt_t*)p->vptr);
				break;

			case HAWK_VAL_MAP_DATA_OOCSTR:
				tmp = hawk_rtx_makestrvalwithoocstr(rtx, (hawk_ooch_t*)p->vptr);
				break;

			case HAWK_VAL_MAP_DATA_BCSTR:
				tmp = hawk_rtx_makestrvalwithbcstr(rtx, (hawk_bch_t*)p->vptr);
				break;

			case HAWK_VAL_MAP_DATA_UCSTR:
				tmp = hawk_rtx_makestrvalwithucstr(rtx, (hawk_uch_t*)p->vptr);
				break;

			case HAWK_VAL_MAP_DATA_OOCS:
				tmp = hawk_rtx_makestrvalwithoocs(rtx, (hawk_oocs_t*)p->vptr);
				break;

			case HAWK_VAL_MAP_DATA_BCS:
				tmp = hawk_rtx_makestrvalwithbcs(rtx, (hawk_bcs_t*)p->vptr);
				break;

			case HAWK_VAL_MAP_DATA_UCS:
				tmp = hawk_rtx_makestrvalwithucs(rtx, (hawk_ucs_t*)p->vptr);
				break;

			default:
				tmp = HAWK_NULL;
				hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINVAL);
				break;
		}

		if (tmp == HAWK_NULL || hawk_rtx_setmapvalfld(rtx, map, p->key.ptr, p->key.len, tmp) == HAWK_NULL)
		{
			if (tmp) hawk_rtx_freeval (rtx, tmp, HAWK_RTX_FREEVAL_CACHE);
			hawk_rtx_refdownval(rtx, map);
			return HAWK_NULL;
		}
	}


	hawk_rtx_refdownval_nofree (rtx, map);
	return map;
}

hawk_val_t* hawk_rtx_setmapvalfld (hawk_rtx_t* rtx, hawk_val_t* map, const hawk_ooch_t* kptr, hawk_oow_t klen, hawk_val_t* v)
{
	HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, map) == HAWK_VAL_MAP);

	if (hawk_map_upsert(((hawk_val_map_t*)map)->map, (hawk_ooch_t*)kptr, klen, v, 0) == HAWK_NULL) return HAWK_NULL;

	/* the value is passed in by an external party. we can't refup()
	 * and refdown() the value if htb_upsert() fails. that way, the value
	 * can be destroyed if it was passed with the reference count of 0.
	 * so we increment the reference count when htb_upsert() is complete */
	hawk_rtx_refupval(rtx, v);

	return v;
}

hawk_val_t* hawk_rtx_getmapvalfld (hawk_rtx_t* rtx, hawk_val_t* map, const hawk_ooch_t* kptr, hawk_oow_t klen)
{
	hawk_map_pair_t* pair;

	HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, map) == HAWK_VAL_MAP);

	pair = hawk_map_search(((hawk_val_map_t*)map)->map, kptr, klen);
	if (!pair)
	{
		/* the given key is not found in the map.
		 * we return NULL here as this function is called by
		 * a user unlike the hawk statement accessing the map key.
		 * so you can easily determine if the key is found by
		 * checking the error number.
		 */
		return HAWK_NULL;
	}

	return HAWK_MAP_VPTR(pair);
}

hawk_val_map_itr_t* hawk_rtx_getfirstmapvalitr (hawk_rtx_t* rtx, hawk_val_t* map, hawk_val_map_itr_t* itr)
{
	HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, map) == HAWK_VAL_MAP);
	hawk_init_map_itr(itr, 0); /* override the caller provided direction to 0 */
	itr->pair = hawk_map_getfirstpair(((hawk_val_map_t*)map)->map, itr);
	return itr->pair? itr: HAWK_NULL;
}

hawk_val_map_itr_t* hawk_rtx_getnextmapvalitr (hawk_rtx_t* rtx, hawk_val_t* map, hawk_val_map_itr_t* itr)
{
	HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, map) == HAWK_VAL_MAP);
	itr->pair = hawk_map_getnextpair(((hawk_val_map_t*)map)->map, itr);
	return itr->pair? itr: HAWK_NULL;
}

int hawk_rtx_scalearrval (hawk_rtx_t* rtx, hawk_val_t* arr, hawk_ooi_t capa)
{
	HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, arr) == HAWK_VAL_ARR);
	if (!hawk_arr_setcapa(((hawk_val_arr_t*)arr)->arr, capa)) return -1;
	return 0;
}

hawk_val_t* hawk_rtx_setarrvalfld (hawk_rtx_t* rtx, hawk_val_t* arr, hawk_ooi_t index, hawk_val_t* v)
{
	HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, arr) == HAWK_VAL_ARR);

	if (hawk_arr_upsert(((hawk_val_arr_t*)arr)->arr, index, v, 0) == HAWK_ARR_NIL) return HAWK_NULL;

	/* the value is passed in by an external party. we can't refup()
	 * and refdown() the value if htb_upsert() fails. that way, the value
	 * can be destroyed if it was passed with the reference count of 0.
	 * so we increment the reference count when htb_upsert() is complete */
	hawk_rtx_refupval(rtx, v);

	return v;
}

hawk_val_t* hawk_rtx_getarrvalfld (hawk_rtx_t* rtx, hawk_val_t* arr, hawk_ooi_t index)
{
	hawk_arr_t* _arr;
	HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, arr) == HAWK_VAL_ARR);
	_arr = ((hawk_val_arr_t*)arr)->arr;
	if (index < 0 || index >= HAWK_ARR_SIZE(_arr) || !HAWK_ARR_SLOT(_arr, index)) return HAWK_NULL;
	return HAWK_ARR_DPTR(_arr, index);
}

hawk_ooi_t hawk_rtx_getarrvaltally (hawk_rtx_t* rtx, hawk_val_t* arr)
{
	hawk_arr_t* _arr;
	HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, arr) == HAWK_VAL_ARR);
	_arr = ((hawk_val_arr_t*)arr)->arr;
	return HAWK_ARR_TALLY(_arr);
}

hawk_val_arr_itr_t* hawk_rtx_getfirstarrvalitr (hawk_rtx_t* rtx, hawk_val_t* arr, hawk_val_arr_itr_t* itr)
{
	hawk_ptl_t* ptl;
	HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, arr) == HAWK_VAL_ARR);
	ptl = hawk_arr_getfirstelem(((hawk_val_arr_t*)arr)->arr, &itr->itr);
	if (!ptl) return HAWK_NULL;
	itr->elem = ptl->ptr;
	return itr;
}

hawk_val_arr_itr_t* hawk_rtx_getnextarrvalitr (hawk_rtx_t* rtx, hawk_val_t* arr, hawk_val_arr_itr_t* itr)
{
	hawk_ptl_t* ptl;
	HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, arr) == HAWK_VAL_ARR);
	ptl = hawk_arr_getnextelem(((hawk_val_arr_t*)arr)->arr, &itr->itr);
	if (!ptl) return HAWK_NULL;
	itr->elem = ptl->ptr;
	return itr;
}

hawk_val_t* hawk_rtx_makerefval (hawk_rtx_t* rtx, int id, hawk_val_t** adr)
{
	hawk_val_ref_t* val;

	if (rtx->rcache_count > 0)
	{
		val = rtx->rcache[--rtx->rcache_count];
	}
	else
	{
		val = (hawk_val_ref_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(*val));
		if (!val) return HAWK_NULL;
	}

	HAWK_RTX_INIT_REF_VAL (val, id, adr, 0);
	return (hawk_val_t*)val;
}

hawk_val_t* hawk_rtx_makefunval (hawk_rtx_t* rtx, const hawk_fun_t* fun)
{
	hawk_val_fun_t* val;

	val = (hawk_val_fun_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(*val));
	if (HAWK_UNLIKELY(!val)) return HAWK_NULL;

	val->v_type = HAWK_VAL_FUN;
	val->v_refs = 0;
	val->v_static = 0;
	val->v_nstr = 0;
	val->v_gc = 0;
	val->fun = (hawk_fun_t*)fun;

	return (hawk_val_t*)val;
}

hawk_val_t* hawk_rtx_makebobval (hawk_rtx_t* rtx, const void* ptr, hawk_oow_t len)
{
	hawk_val_bob_t* val;

	val = (hawk_val_bob_t*)hawk_rtx_callocmem(rtx, HAWK_SIZEOF(hawk_val_bob_t) + len);
	if (HAWK_UNLIKELY(!val)) return HAWK_NULL;

	val->v_type = HAWK_VAL_BOB;
	val->v_refs = 0;
	val->v_static = 0;
	val->v_nstr = 0;
	val->v_gc = 0;
	val->val.len = len;
	val->val.ptr = (hawk_bch_t*)(val + 1);
	HAWK_MEMCPY(val + 1, ptr, len);

#if defined(DEBUG_VAL)
	hawk_logfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T("make_bob_val => %p - [%O]\n"), val, val);
#endif
	return (hawk_val_t*)val;
}

int HAWK_INLINE hawk_rtx_isstaticval (hawk_rtx_t* rtx, const hawk_val_t* val)
{
	return HAWK_VTR_IS_POINTER(val) && HAWK_IS_STATICVAL(val);
}

int hawk_rtx_getvaltype (hawk_rtx_t* rtx, const hawk_val_t* val)
{
	return HAWK_RTX_GETVALTYPE(rtx, val);
}

const hawk_ooch_t* hawk_rtx_getvaltypename(hawk_rtx_t* rtx, const hawk_val_t* val)
{
	return val_type_name[HAWK_RTX_GETVALTYPE(rtx, val)];
}

int hawk_rtx_getintfromval (hawk_rtx_t* rtx, const hawk_val_t* val)
{
	return HAWK_RTX_GETINTFROMVAL(rtx, val);
}

void hawk_rtx_freeval (hawk_rtx_t* rtx, hawk_val_t* val, int flags)
{
	hawk_val_type_t vtype;

	if (HAWK_VTR_IS_POINTER(val))
	{
		if (HAWK_IS_STATICVAL(val)) return;

	#if defined(DEBUG_VAL)
		hawk_logfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T("freeing [cache=%d] - [%O]\n"), cache, val);
	#endif

		vtype = HAWK_RTX_GETVALTYPE (rtx, val);
		switch (vtype)
		{
			case HAWK_VAL_NIL:
			{
				hawk_rtx_freemem(rtx, val);
				break;
			}

			case HAWK_VAL_BCHR:
			case HAWK_VAL_CHAR:
			{
				/* this never happens */
				break;
			}

			case HAWK_VAL_INT:
			{
				((hawk_val_int_t*)val)->nde = (hawk_nde_int_t*)rtx->vmgr.ifree;
				rtx->vmgr.ifree = (hawk_val_int_t*)val;
				break;
			}

			case HAWK_VAL_FLT:
			{
				((hawk_val_flt_t*)val)->nde = (hawk_nde_flt_t*)rtx->vmgr.rfree;
				rtx->vmgr.rfree = (hawk_val_flt_t*)val;
				break;
			}

			case HAWK_VAL_STR:
			{
			#if defined(HAWK_ENABLE_STR_CACHE)
				if (flags & HAWK_RTX_FREEVAL_CACHE)
				{
					hawk_val_str_t* v = (hawk_val_str_t*)val;
					hawk_oow_t aligned_len;
					int i;

					aligned_len = HAWK_ALIGN_POW2((v->val.len + 1), HAWK_STR_CACHE_BLOCK_UNIT);
					i = aligned_len / HAWK_STR_CACHE_BLOCK_UNIT;
					if (i < HAWK_COUNTOF(rtx->str_cache_count) &&
					    rtx->str_cache_count[i] < HAWK_COUNTOF(rtx->str_cache[i]))
					{
						rtx->str_cache[i][rtx->str_cache_count[i]++] = v;
						v->v_nstr = 0;
					}
					else hawk_rtx_freemem(rtx, val);
					break;
				}
			#endif

				hawk_rtx_freemem(rtx, val);
				break;
			}

			case HAWK_VAL_MBS:
			#if defined(HAWK_ENABLE_MBS_CACHE)
				if (flags & HAWK_RTX_FREEVAL_CACHE)
				{
					hawk_val_mbs_t* v = (hawk_val_mbs_t*)val;
					hawk_oow_t aligned_len;
					int i;

					aligned_len = HAWK_ALIGN_POW2((v->val.len + 1), HAWK_MBS_CACHE_BLOCK_UNIT);
					i = aligned_len / HAWK_MBS_CACHE_BLOCK_UNIT;
					if (i < HAWK_COUNTOF(rtx->mbs_cache_count) &&
					    rtx->mbs_cache_count[i] < HAWK_COUNTOF(rtx->mbs_cache[i]))
					{
						rtx->mbs_cache[i][rtx->mbs_cache_count[i]++] = v;
					}
					else hawk_rtx_freemem(rtx, val);
					break;
				}
			#endif

				hawk_rtx_freemem(rtx, val);
				break;

			case HAWK_VAL_REX:
			{
				/* don't free ptr as it is inlined to val
				hawk_rtx_freemem(rtx, ((hawk_val_rex_t*)val)->ptr);
				 */

				/* code is just a pointer to a regular expression stored
				 * in parse tree nodes. so don't free it.
				hawk_freerex (rtx->hawk, ((hawk_val_rex_t*)val)->code[0], ((hawk_val_rex_t*)val)->code[1]);
				 */

				hawk_rtx_freemem(rtx, val);
				break;
			}

			case HAWK_VAL_FUN:
				hawk_rtx_freemem(rtx, val);
				break;

			case HAWK_VAL_MAP:
			#if defined(HAWK_ENABLE_GC)

				#if defined(DEBUG_GC)
				hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] FREEING GCH %p VAL(MAP) %p - flags %d\n", hawk_val_to_gch(val), val, flags);
				#endif

				hawk_map_fini(((hawk_val_map_t*)val)->map);
				if (!(flags & HAWK_RTX_FREEVAL_GC_PRESERVE))
				{
					gc_unchain_val (val);
					gc_free_val (rtx, val);
				}
			#else
				hawk_map_fini(((hawk_val_map_t*)val)->map);
				hawk_rtx_freemem(rtx, val);
			#endif
				break;

			case HAWK_VAL_ARR:
			#if defined(HAWK_ENABLE_GC)

				#if defined(DEBUG_GC)
				hawk_logbfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, "[GC] FREEING GCH %p VAL(ARR) %p - flags %d\n", hawk_val_to_gch(val), val, flags);
				#endif

				hawk_arr_fini(((hawk_val_arr_t*)val)->arr);
				if (!(flags & HAWK_RTX_FREEVAL_GC_PRESERVE))
				{
					gc_unchain_val (val);
					gc_free_val (rtx, val);
				}
			#else
				hawk_arr_fini(((hawk_val_arr_t*)val)->arr);
				hawk_rtx_freemem(rtx, val);
			#endif
				break;

			case HAWK_VAL_REF:
				if ((flags & HAWK_RTX_FREEVAL_CACHE) && rtx->rcache_count < HAWK_COUNTOF(rtx->rcache))
				{
					rtx->rcache[rtx->rcache_count++] = (hawk_val_ref_t*)val;
				}
				else hawk_rtx_freemem(rtx, val);
				break;

			case HAWK_VAL_BOB:
				hawk_rtx_freemem(rtx, val);
				break;
		}
	}
}

void hawk_rtx_refupval(hawk_rtx_t* rtx, hawk_val_t* val)
{
	if (HAWK_VTR_IS_POINTER(val))
	{
		if (HAWK_IS_STATICVAL(val)) return;

	#if defined(DEBUG_VAL)
		hawk_logfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T("ref up [ptr=%p] [count=%d] - [%O]\n"), val, (int)val->v_refs, val);
	#endif

	#if defined(USE_ATOMIC_REFCNT) && __has_builtin(__atomic_fetch_add)
		__atomic_fetch_add(&val->v_refs, 1, __ATOMIC_RELAXED);
	#else
		val->v_refs++;
	#endif
	}
}

void hawk_rtx_refdownval(hawk_rtx_t* rtx, hawk_val_t* val)
{
	if (HAWK_VTR_IS_POINTER(val))
	{
		if (HAWK_IS_STATICVAL(val)) return;

	#if defined(DEBUG_VAL)
		hawk_logfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T("ref down [ptr=%p] [count=%d] - [%O]\n"), val, (int)val->v_refs, val);
	#endif

		/* the reference count of a value should be greater than zero for it to be decremented. check the source code for any bugs */
		HAWK_ASSERT(val->v_refs > 0);

	#if defined(USE_ATOMIC_REFCNT) && __has_builtin(__atomic_fetch_sub)
		if (__atomic_fetch_sub(&val->v_refs, 1, __ATOMIC_RELAXED) == 1)
		{
			hawk_rtx_freeval (rtx, val, HAWK_RTX_FREEVAL_CACHE);
		}
	#else
		val->v_refs--;
		if (val->v_refs <= 0)
		{
			hawk_rtx_freeval (rtx, val, HAWK_RTX_FREEVAL_CACHE);
		}
	#endif
	}
}

void hawk_rtx_refdownval_nofree (hawk_rtx_t* rtx, hawk_val_t* val)
{
	if (HAWK_VTR_IS_POINTER(val))
	{
		if (HAWK_IS_STATICVAL(val)) return;

		/* the reference count of a value should be greater than zero for it to be decremented. check the source code for any bugs */
		HAWK_ASSERT(val->v_refs > 0);

	#if defined(USE_ATOMIC_REFCNT) && __has_builtin(__atomic_fetch_sub)
		__atomic_fetch_sub(&val->v_refs, 1, __ATOMIC_RELAXED);
	#else
		val->v_refs--;
	#endif
	}
}

void hawk_rtx_freevalchunk (hawk_rtx_t* rtx, hawk_val_chunk_t* chunk)
{
	while (chunk != HAWK_NULL)
	{
		hawk_val_chunk_t* next = chunk->next;
		hawk_rtx_freemem(rtx, chunk);
		chunk = next;
	}
}

static int val_ref_to_bool (hawk_rtx_t* rtx, const hawk_val_ref_t* ref)
{
	switch (ref->id)
	{
		case HAWK_VAL_REF_POS:
		{
			hawk_oow_t idx = (hawk_oow_t)ref->adr;
			if (idx == 0)
			{
				return HAWK_OOECS_LEN(&rtx->inrec.line) > 0;
			}
			else if (idx <= rtx->inrec.nflds)
			{
				return rtx->inrec.flds[idx-1].len > 0;
			}
			else
			{
				/* the index is greater than the number of records.
				 * it's an empty string. so false */
				return 0;
			}
		}
		case HAWK_VAL_REF_GBL:
		{
			hawk_oow_t idx = (hawk_oow_t)ref->adr;
			return hawk_rtx_valtobool(rtx, HAWK_RTX_STACK_GBL(rtx, idx));
		}

		default:
		{
			hawk_val_t** xref = (hawk_val_t**)ref->adr;

			/* A reference value is not able to point to another
			 * refernce value for the way values are represented
			 * in HAWK */
			HAWK_ASSERT(HAWK_RTX_GETVALTYPE (rtx, *xref)!= HAWK_VAL_REF);

			/* make a recursive call back to the caller */
			return hawk_rtx_valtobool(rtx, *xref);
		}
	}
}

int hawk_rtx_valtobool (hawk_rtx_t* rtx, const hawk_val_t* val)
{
	hawk_val_type_t vtype;

	if (val == HAWK_NULL) return 0;

	vtype = HAWK_RTX_GETVALTYPE(rtx, val);
	switch (vtype)
	{
		case HAWK_VAL_NIL:
			return 0;

		case HAWK_VAL_BCHR:
		case HAWK_VAL_CHAR:
			/* return always true - treat it like a 1-letter string */
			return 1;

		case HAWK_VAL_INT:
			return HAWK_RTX_GETINTFROMVAL(rtx, val) != 0;
		case HAWK_VAL_FLT:
			return ((hawk_val_flt_t*)val)->val != 0.0;
		case HAWK_VAL_STR:
			return ((hawk_val_str_t*)val)->val.len > 0;
		case HAWK_VAL_MBS:
			return ((hawk_val_mbs_t*)val)->val.len > 0;
		case HAWK_VAL_REX: /* TODO: is this correct? */
			return ((hawk_val_rex_t*)val)->str.len > 0;
		case HAWK_VAL_FUN:
			/* return always true */
			return 1;
		case HAWK_VAL_MAP:
			/* true if the map size is greater than 0. false if not */
			return HAWK_MAP_SIZE(((hawk_val_map_t*)val)->map) > 0;
		case HAWK_VAL_ARR:
			return HAWK_ARR_SIZE(((hawk_val_arr_t*)val)->arr) > 0;
		case HAWK_VAL_REF:
			return val_ref_to_bool(rtx, (hawk_val_ref_t*)val);
		case HAWK_VAL_BOB:
			return ((hawk_val_bob_t*)val)->val.len > 0;
	}

	/* the type of a value should be one of HAWK_VAL_XXX enumerators defined in hawk-prv.h */
	HAWK_ASSERT(!"should never happen - invalid value type");

	return 0;
}

static int str_to_str (hawk_rtx_t* rtx, const hawk_ooch_t* str, hawk_oow_t str_len, hawk_rtx_valtostr_out_t* out)
{
	int type = out->type & ~HAWK_RTX_VALTOSTR_PRINT;

	switch (type)
	{
		case HAWK_RTX_VALTOSTR_CPL:
		{
			out->u.cpl.len = str_len;
			out->u.cpl.ptr = (hawk_ooch_t*)str;
			return 0;
		}

		case HAWK_RTX_VALTOSTR_CPLCPY:
		{
			if (str_len >= out->u.cplcpy.len)
			{
				hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINVAL);
				/*out->u.cplcpy.len = str_len + 1;*/ /* set the required length */
				return -1;
			}

			out->u.cplcpy.len = hawk_copy_oochars_to_oocstr_unlimited(out->u.cplcpy.ptr, str, str_len);
			return 0;
		}

		case HAWK_RTX_VALTOSTR_CPLDUP:
		{
			hawk_ooch_t* tmp;

			tmp = hawk_rtx_dupoochars(rtx, str, str_len);
			if (!tmp) return -1;

			out->u.cpldup.ptr = tmp;
			out->u.cpldup.len = str_len;
			return 0;
		}

		case HAWK_RTX_VALTOSTR_STRP:
		{
			hawk_oow_t n;
			hawk_ooecs_clear (out->u.strp);
			n = hawk_ooecs_ncat(out->u.strp, str, str_len);
			if (n == (hawk_oow_t)-1) return -1;
			return 0;
		}

		case HAWK_RTX_VALTOSTR_STRPCAT:
		{
			hawk_oow_t n;
			n = hawk_ooecs_ncat(out->u.strpcat, str, str_len);
			if (n == (hawk_oow_t)-1) return -1;
			return 0;
		}
	}

	hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINVAL);
	return -1;
}

#if defined(HAWK_OOCH_IS_BCH)
#	define mbs_to_str(rtx,str,str_len,out) str_to_str(rtx,str,str_len,out)
#else
static int mbs_to_str (hawk_rtx_t* rtx, const hawk_bch_t* str, hawk_oow_t str_len, hawk_rtx_valtostr_out_t* out)
{
	int type = out->type & ~HAWK_RTX_VALTOSTR_PRINT;

	switch (type)
	{
		case HAWK_RTX_VALTOSTR_CPL:
			/* conversion is required. i can't simply return it. let CPL
			 * behave like CPLCPY. fall thru */
		case HAWK_RTX_VALTOSTR_CPLCPY:
		{
			hawk_oow_t ucslen;
			if (HAWK_UNLIKELY(out->u.cplcpy.len <= 0))
			{
				hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EBUFFULL);
				return -1;
			}
			/* hawk_rtx_convbtouchars() doesn't null terminate the result. -1 to secure space for '\0' */
			ucslen = out->u.cplcpy.len - 1;
			if (hawk_rtx_convbtouchars(rtx, str, &str_len, out->u.cplcpy.ptr, &ucslen, 1) <= -1) return -1;

			out->u.cplcpy.ptr[ucslen] = HAWK_T('\0');
			out->u.cplcpy.len = ucslen;

			return 0;
		}

		case HAWK_RTX_VALTOSTR_CPLDUP:
		{
			hawk_ooch_t* tmp;
			hawk_oow_t wcslen;

			tmp = hawk_rtx_dupbtouchars(rtx, str, str_len, &wcslen, 1);
			if (!tmp) return -1;

			out->u.cpldup.ptr = tmp;
			out->u.cpldup.len = wcslen;
			return 0;
		}

		case HAWK_RTX_VALTOSTR_STRP:
			hawk_ooecs_clear (out->u.strp);
			if (hawk_uecs_ncatbchars(out->u.strp, str, str_len, hawk_rtx_getcmgr(rtx), 1) == (hawk_oow_t)-1) return -1;
			return 0;

		case HAWK_RTX_VALTOSTR_STRPCAT:
			if (hawk_uecs_ncatbchars(out->u.strpcat, str, str_len, hawk_rtx_getcmgr(rtx), 1) == (hawk_oow_t)-1) return -1;
			return 0;
	}

	hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINVAL);
	return -1;
}
#endif


static int val_int_to_str (hawk_rtx_t* rtx, const hawk_val_int_t* v, hawk_rtx_valtostr_out_t* out)
{
	hawk_ooch_t* tmp;
	hawk_oow_t rlen = 0;
	int type = out->type & ~HAWK_RTX_VALTOSTR_PRINT;
	hawk_int_t orgval = HAWK_RTX_GETINTFROMVAL (rtx, v);
	hawk_uint_t t;

	if (orgval == 0) rlen++;
	else
	{
		/* non-zero values */
		if (orgval < 0)
		{
			t = orgval * -1; rlen++;
		}
		else t = orgval;
		while (t > 0) { rlen++; t /= 10; }
	}

	switch (type)
	{
		case HAWK_RTX_VALTOSTR_CPL:
			/* CPL and CPLCP behave the same for int_t.
			 * i just fall through assuming that cplcpy
			 * and cpl are the same type. the following
			 * assertion at least ensure that they have
			 * the same size. */
			HAWK_ASSERT(HAWK_SIZEOF(out->u.cpl) == HAWK_SIZEOF(out->u.cplcpy));

		case HAWK_RTX_VALTOSTR_CPLCPY:
			if (rlen >= out->u.cplcpy.len)
			{
				hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINVAL);
				/* store the buffer size needed */
				out->u.cplcpy.len = rlen + 1;
				return -1;
			}

			tmp = out->u.cplcpy.ptr;
			tmp[rlen] = HAWK_T('\0');
			out->u.cplcpy.len = rlen;
			break;

		case HAWK_RTX_VALTOSTR_CPLDUP:
			tmp = hawk_rtx_allocmem(rtx, (rlen + 1) * HAWK_SIZEOF(hawk_ooch_t));
			if (!tmp) return -1;

			tmp[rlen] = HAWK_T('\0');
			out->u.cpldup.ptr = tmp;
			out->u.cpldup.len = rlen;
			break;

		case HAWK_RTX_VALTOSTR_STRP:
		{
			hawk_oow_t n;

			hawk_ooecs_clear (out->u.strp);
			HAWK_ASSERT(HAWK_OOECS_LEN(out->u.strp) == 0);

			/* point to the beginning of the buffer */
			tmp = HAWK_OOECS_PTR(out->u.strp);

			/* extend the buffer */
			n = hawk_ooecs_nccat(out->u.strp, HAWK_T(' '), rlen);
			if (n == (hawk_oow_t)-1) return -1;
			break;
		}

		case HAWK_RTX_VALTOSTR_STRPCAT:
		{
			hawk_oow_t n;

			/* point to the insertion point */
			tmp = HAWK_OOECS_PTR(out->u.strpcat) + HAWK_OOECS_LEN(out->u.strpcat);

			/* extend the buffer */
			n = hawk_ooecs_nccat(out->u.strpcat, HAWK_T(' '), rlen);
			if (n == (hawk_oow_t)-1) return -1;
			break;
		}

		default:
		{
			hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINVAL);
			return -1;
		}
	}

	if (orgval == 0) tmp[0] = HAWK_T('0');
	else
	{
		t = (orgval < 0)? (orgval * -1): orgval;

		/* fill in the buffer with digits */
		while (t > 0)
		{
			tmp[--rlen] = (hawk_ooch_t)(t % 10) + HAWK_T('0');
			t /= 10;
		}

		/* insert the negative sign if necessary */
		if (orgval < 0) tmp[--rlen] = HAWK_T('-');
	}

	return 0;
}

static int val_flt_to_str (hawk_rtx_t* rtx, const hawk_val_flt_t* v, hawk_rtx_valtostr_out_t* out)
{
	hawk_ooch_t* tmp;
	hawk_oow_t tmp_len;
	hawk_ooecs_t buf, fbu;
	int buf_inited = 0, fbu_inited = 0;
	int type = out->type & ~HAWK_RTX_VALTOSTR_PRINT;

	if (out->type & HAWK_RTX_VALTOSTR_PRINT)
	{
		tmp = rtx->gbl.ofmt.ptr;
		tmp_len = rtx->gbl.ofmt.len;
	}
	else
	{
		tmp = rtx->gbl.convfmt.ptr;
		tmp_len = rtx->gbl.convfmt.len;
	}

	if (hawk_ooecs_init(&buf, hawk_rtx_getgem(rtx), 256) <= -1) return -1;
	buf_inited = 1;

	if (hawk_ooecs_init(&fbu, hawk_rtx_getgem(rtx), 256) <= -1)
	{
		hawk_ooecs_fini(&buf);
		return -1;
	}
	fbu_inited = 1;

	tmp = hawk_rtx_format(rtx, &buf, &fbu, tmp, tmp_len, (hawk_oow_t)-1, (hawk_nde_t*)v, &tmp_len);
	if (HAWK_UNLIKELY(!tmp)) goto oops;

	switch (type)
	{
		case HAWK_RTX_VALTOSTR_CPL:
			/* CPL and CPLCP behave the same for flt_t.
			 * i just fall through assuming that cplcpy
			 * and cpl are the same type. the following
			 * assertion at least ensure that they have
			 * the same size. */
			HAWK_ASSERT(HAWK_SIZEOF(out->u.cpl) == HAWK_SIZEOF(out->u.cplcpy));

		case HAWK_RTX_VALTOSTR_CPLCPY:
			if (out->u.cplcpy.len <= tmp_len)
			{
				hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINVAL);
				/* store the buffer size required */
				out->u.cplcpy.len = tmp_len + 1;
				goto oops;
			}

			hawk_copy_oochars_to_oocstr_unlimited (out->u.cplcpy.ptr, tmp, tmp_len);
			out->u.cplcpy.len = tmp_len;
			break;

		case HAWK_RTX_VALTOSTR_CPLDUP:
		{
			hawk_ooecs_yield (&buf, HAWK_NULL, 0);
			out->u.cpldup.ptr = tmp;
			out->u.cpldup.len = tmp_len;
			break;
		}

		case HAWK_RTX_VALTOSTR_STRP:
		{
			hawk_oow_t n;
			hawk_ooecs_clear (out->u.strp);
			n = hawk_ooecs_ncat(out->u.strp, tmp, tmp_len);
			if (n == (hawk_oow_t)-1) goto oops;
			break;
		}

		case HAWK_RTX_VALTOSTR_STRPCAT:
		{
			hawk_oow_t n;
			n = hawk_ooecs_ncat(out->u.strpcat, tmp, tmp_len);
			if (n == (hawk_oow_t)-1) goto oops;
			break;
		}

		default:
		{
			hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINVAL);
			goto oops;
		}
	}

	hawk_ooecs_fini(&fbu);
	hawk_ooecs_fini(&buf);
	return 0;

oops:
	if (fbu_inited) hawk_ooecs_fini(&fbu);
	if (buf_inited) hawk_ooecs_fini(&buf);
	return -1;
}

static int val_ref_to_str (hawk_rtx_t* rtx, const hawk_val_ref_t* ref, hawk_rtx_valtostr_out_t* out)
{
	switch (ref->id)
	{
		case HAWK_VAL_REF_POS:
		{
			hawk_oow_t idx;

			/* special case when the reference value is
			 * pointing to the positional */

			idx = (hawk_oow_t)ref->adr;
			if (idx == 0)
			{
				return str_to_str(
					rtx,
					HAWK_OOECS_PTR(&rtx->inrec.line),
					HAWK_OOECS_LEN(&rtx->inrec.line),
					out
				);
			}
			else if (idx <= rtx->inrec.nflds)
			{
				return str_to_str(
					rtx,
					rtx->inrec.flds[idx-1].ptr,
					rtx->inrec.flds[idx-1].len,
					out
				);
			}
			else
			{
				return str_to_str(rtx, HAWK_T(""), 0, out);
			}
		}

		case HAWK_VAL_REF_GBL:
		{
			hawk_oow_t idx = (hawk_oow_t)ref->adr;
			return hawk_rtx_valtostr(rtx, HAWK_RTX_STACK_GBL(rtx, idx), out);
		}

		default:
		{
			hawk_val_t** xref = (hawk_val_t**)ref->adr;

			/* A reference value is not able to point to another
			 * refernce value for the way values are represented
			 * in HAWK */
			HAWK_ASSERT(HAWK_RTX_GETVALTYPE (rtx, *xref) != HAWK_VAL_REF);

			/* make a recursive call back to the caller */
			return hawk_rtx_valtostr(rtx, *xref, out);
		}
	}
}

int hawk_rtx_valtostr (hawk_rtx_t* rtx, const hawk_val_t* v, hawk_rtx_valtostr_out_t* out)
{
	hawk_val_type_t vtype = HAWK_RTX_GETVALTYPE(rtx, v);

	switch (vtype)
	{
		case HAWK_VAL_NIL:
			return str_to_str(rtx, HAWK_T(""), 0, out);

		case HAWK_VAL_BCHR:
		{
			hawk_bch_t tmp = HAWK_RTX_GETBCHRFROMVAL(rtx, v);
		#if defined(HAWK_OOCH_IS_BCH)
			return str_to_str(rtx, &tmp, 1, out);
		#else
			return mbs_to_str(rtx, &tmp, 1, out);
		#endif
		}

		case HAWK_VAL_CHAR:
		{
			hawk_ooch_t tmp = HAWK_RTX_GETCHARFROMVAL(rtx, v);
			return str_to_str(rtx, &tmp, 1, out);
		}

		case HAWK_VAL_INT:
			return val_int_to_str(rtx, (hawk_val_int_t*)v, out);

		case HAWK_VAL_FLT:
			return val_flt_to_str(rtx, (hawk_val_flt_t*)v, out);

		case HAWK_VAL_STR:
		{
			hawk_val_str_t* vs = (hawk_val_str_t*)v;
			return str_to_str(rtx, vs->val.ptr, vs->val.len, out);
		}

		case HAWK_VAL_MBS:
		{
			hawk_val_mbs_t* vs = (hawk_val_mbs_t*)v;
		#if defined(HAWK_OOCH_IS_BCH)
			return str_to_str(rtx, vs->val.ptr, vs->val.len, out);
		#else
			return mbs_to_str(rtx, vs->val.ptr, vs->val.len, out);
		#endif
		}

		case HAWK_VAL_FUN:
			return str_to_str(rtx, ((hawk_val_fun_t*)v)->fun->name.ptr, ((hawk_val_fun_t*)v)->fun->name.len, out);

		case HAWK_VAL_MAP:
			if (rtx->hawk->opt.trait & HAWK_FLEXMAP)
			{
				return str_to_str(rtx, HAWK_T("#MAP"), 4, out);
			}
			goto invalid;

		case HAWK_VAL_ARR:
			if (rtx->hawk->opt.trait & HAWK_FLEXMAP)
			{
				return str_to_str(rtx, HAWK_T("#ARRAY"), 4, out);
			}
			goto invalid;

		case HAWK_VAL_REF:
			return val_ref_to_str(rtx, (hawk_val_ref_t*)v, out);

		case HAWK_VAL_REX:
		default:
		invalid:
		#if defined(DEBUG_VAL)
			hawk_logfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T(">>WRONG VALUE TYPE [%d] in hawk_rtx_valtostr\n"), v->type);
		#endif
			hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EVALTOSTR);
			return -1;
	}
}

hawk_bch_t* hawk_rtx_valtobcstrdupwithcmgr (hawk_rtx_t* rtx, const hawk_val_t* v, hawk_oow_t* len, hawk_cmgr_t* cmgr)
{
	hawk_bch_t* mbs;
	hawk_val_type_t vtype;

	vtype = HAWK_RTX_GETVALTYPE(rtx,v);

	switch (vtype)
	{
		case HAWK_VAL_BCHR:
		{
			hawk_bch_t tmp = HAWK_RTX_GETBCHRFROMVAL(rtx, v);
			mbs = hawk_rtx_dupbchars(rtx, &tmp, 1);
			if (HAWK_UNLIKELY(!mbs)) return HAWK_NULL;
			if (len) *len = 1;
			break;
		}

		case HAWK_VAL_MBS:
			mbs = hawk_rtx_dupbchars(rtx, ((hawk_val_mbs_t*)v)->val.ptr, ((hawk_val_mbs_t*)v)->val.len);
			if (HAWK_UNLIKELY(!mbs)) return HAWK_NULL;
			if (len) *len = ((hawk_val_mbs_t*)v)->val.len;
			break;

		case HAWK_VAL_BOB:
			mbs = hawk_rtx_dupbchars(rtx, (hawk_bch_t*)((hawk_val_bob_t*)v)->val.ptr, ((hawk_val_bob_t*)v)->val.len);
			if (HAWK_UNLIKELY(!mbs)) return HAWK_NULL;
			if (len) *len = ((hawk_val_mbs_t*)v)->val.len;
			break;

		case HAWK_VAL_CHAR:
		case HAWK_VAL_STR:
		{
			const hawk_ooch_t* ptr;
			hawk_oow_t slen;
			hawk_ooch_t tmp;

			if (vtype == HAWK_VAL_CHAR)
			{
				tmp = HAWK_RTX_GETCHARFROMVAL(rtx, v);
				ptr = &tmp;
				slen = 1;
			}
			else
			{
				ptr = ((hawk_val_str_t*)v)->val.ptr;
				slen = ((hawk_val_str_t*)v)->val.len;
			}
		#if defined(HAWK_OOCH_IS_BCH)
			mbs = hawk_rtx_dupbchars(rtx, ptr, slen);
			if (HAWK_UNLIKELY(!mbs)) return HAWK_NULL;
			if (len) *len = slen;
		#else
			{
				hawk_oow_t mbslen, wcslen;
				wcslen = slen;
				mbs = hawk_rtx_duputobcharswithcmgr(rtx, ptr, wcslen, &mbslen, cmgr);
				if (HAWK_UNLIKELY(!mbs)) return HAWK_NULL;
				if (len) *len = mbslen;
			}
		#endif
			break;
		}

		default:
		{
		#if defined(HAWK_OOCH_IS_BCH)
			hawk_rtx_valtostr_out_t out;

			out.type = HAWK_RTX_VALTOSTR_CPLDUP;
			if (hawk_rtx_valtostr(rtx, v, &out) <= -1) return HAWK_NULL;

			mbs = out.u.cpldup.ptr;
			if (len) *len = out.u.cpldup.len;
		#else
			hawk_oow_t mbslen;
			hawk_rtx_valtostr_out_t out;

			out.type = HAWK_RTX_VALTOSTR_CPLDUP;
			if (hawk_rtx_valtostr(rtx, v, &out) <= -1) return HAWK_NULL;
/* TODO IMPLEMENT hawk_rtx_valtobcs()... and use it */

			mbs = hawk_rtx_duputobcharswithcmgr(rtx, out.u.cpldup.ptr, out.u.cpldup.len, &mbslen, cmgr);
			hawk_rtx_freemem(rtx, out.u.cpldup.ptr);
			if (HAWK_UNLIKELY(!mbs)) return HAWK_NULL;
			if (len) *len = mbslen;
		#endif
			break;
		}
	}

	return mbs;
}

hawk_uch_t* hawk_rtx_valtoucstrdupwithcmgr (hawk_rtx_t* rtx, const hawk_val_t* v, hawk_oow_t* len, hawk_cmgr_t* cmgr)
{
	hawk_uch_t* wcs;
	hawk_val_type_t vtype;

	vtype = HAWK_RTX_GETVALTYPE(rtx,v);

	switch (vtype)
	{
		case HAWK_VAL_BCHR:
		{
			hawk_bch_t tmp;
			hawk_oow_t mbslen, wcslen;

			tmp = HAWK_RTX_GETBCHRFROMVAL(rtx, v);
			mbslen = 1;
			wcs = hawk_rtx_dupbtoucharswithcmgr(rtx, &tmp, mbslen, &wcslen, cmgr, 1);
			if (!wcs) return HAWK_NULL;
			if (len) *len = wcslen;
			break;
		}
		case HAWK_VAL_MBS:
		{
			hawk_oow_t mbslen, wcslen;
			mbslen = ((hawk_val_mbs_t*)v)->val.len;
			wcs = hawk_rtx_dupbtoucharswithcmgr(rtx, ((hawk_val_mbs_t*)v)->val.ptr, mbslen, &wcslen, cmgr, 1);
			if (!wcs) return HAWK_NULL;
			if (len) *len = wcslen;
			break;
		}

		case HAWK_VAL_CHAR:
		{
			hawk_ooch_t tmp = HAWK_RTX_GETCHARFROMVAL(rtx, v);
		#if defined(HAWK_OOCH_IS_BCH)
			hawk_oow_t wcslen, mbslen;
			mbslen = 1;
			wcs = hawk_rtx_dupbtoucharswithcmgr(rtx, &tmp, mbslen, &wcslen, cmgr, 1);
		#else
			wcs = hawk_rtx_dupuchars(rtx, &tmp, 1);
		#endif
			if (!wcs) return HAWK_NULL;
		#if defined(HAWK_OOCH_IS_BCH)
			if (len) *len = wcslen;
		#else
			if (len) *len = 1;
		#endif
			break;
		}

		case HAWK_VAL_STR:
		{
		#if defined(HAWK_OOCH_IS_BCH)
			hawk_oow_t wcslen, mbslen;
			mbslen = ((hawk_val_str_t*)v)->val.len;
			wcs = hawk_rtx_dupbtoucharswithcmgr(rtx, ((hawk_val_str_t*)v)->val.ptr, mbslen, &wcslen, cmgr, 1);
		#else
			wcs = hawk_rtx_dupuchars(rtx, ((hawk_val_str_t*)v)->val.ptr, ((hawk_val_str_t*)v)->val.len);
		#endif
			if (!wcs) return HAWK_NULL;
		#if defined(HAWK_OOCH_IS_BCH)
			if (len) *len = wcslen;
		#else
			if (len) *len = ((hawk_val_str_t*)v)->val.len;
		#endif
			break;
		}

		default:
		{
		#if defined(HAWK_OOCH_IS_BCH)
			hawk_oow_t wcslen;
			hawk_rtx_valtostr_out_t out;

			out.type = HAWK_RTX_VALTOSTR_CPLDUP;
			if (hawk_rtx_valtostr(rtx, v, &out) <= -1) return HAWK_NULL;

			wcs = hawk_rtx_dupbtoucharswithcmgr(rtx, out.u.cpldup.ptr, out.u.cpldup.len, &wcslen, cmgr, 1);
			hawk_rtx_freemem(rtx, out.u.cpldup.ptr);
			if (!wcs) return HAWK_NULL;

			if (len) *len = wcslen;
		#else
			hawk_rtx_valtostr_out_t out;

			out.type = HAWK_RTX_VALTOSTR_CPLDUP;
			if (hawk_rtx_valtostr(rtx, v, &out) <= -1) return HAWK_NULL;

			wcs = out.u.cpldup.ptr;
			if (len) *len = out.u.cpldup.len;
		#endif
			break;
		}
	}
	return wcs;
}

hawk_ooch_t* hawk_rtx_getvaloocstrwithcmgr (hawk_rtx_t* rtx, hawk_val_t* v, hawk_oow_t* len, hawk_cmgr_t* cmgr)
{
	hawk_ooch_t c;
	hawk_oow_t l;

	switch (HAWK_RTX_GETVALTYPE(rtx, v))
	{
		case HAWK_VAL_NIL:
			c = '\0';
			l = 0;
			goto ctos;
		case HAWK_VAL_CHAR:
			c = HAWK_RTX_GETCHARFROMVAL(rtx, v);
			l = 1;
		ctos:
			if (rtx->ctos.fi) /* free slot available */
			{
				/* use a ctos slot to avoid duplication */
				hawk_oow_t fi;
				fi = rtx->ctos.fi;
				rtx->ctos.fi = rtx->ctos.b[rtx->ctos.fi].c[0];
				rtx->ctos.b[fi].c[0] = c;
				rtx->ctos.b[fi].c[1] = '\0';
				if (len) *len = l;
				HAWK_ASSERT((void*)&rtx->ctos.b[fi] == (void*)rtx->ctos.b[fi].c);
				return rtx->ctos.b[fi].c;
			}
			goto duplicate;

		case HAWK_VAL_STR:
#if 0
		plain_str:
#endif
			if (len) *len = ((hawk_val_str_t*)v)->val.len;
			return ((hawk_val_str_t*)v)->val.ptr;

#if 0
/* i'm commenting out this part because hawk_rtx_setrefval() changes v->adr
 * and leads hawk_rtx_freevaloocstr() to check a wrong value obejct.
 * if you know that a value is a reference, you can get the referenced value
 * with hawk_rtx_getrefval() and call this function over it */
		case HAWK_VAL_REF:
			v = hawk_rtx_getrefval(rtx, (hawk_val_ref_t*)v);
			if (HAWK_RTX_GETVALTYPE(rtx, v1) == HAWK_VAL_STR) goto plain_str;
			/* fall through */
#endif

		default:
		duplicate:
			return hawk_rtx_valtooocstrdupwithcmgr(rtx, v, len, cmgr);
	}
}

void hawk_rtx_freevaloocstr(hawk_rtx_t* rtx, hawk_val_t* v, hawk_ooch_t* str)
{
	switch (HAWK_RTX_GETVALTYPE(rtx, v))
	{
		case HAWK_VAL_NIL:
		case HAWK_VAL_CHAR:
		{
			hawk_ctos_b_t* b = (hawk_ctos_b_t*)str;
			if (b >= &rtx->ctos.b[0] && b < &rtx->ctos.b[HAWK_COUNTOF(rtx->ctos.b)])
			{
				hawk_oow_t fi;
				fi = b - &rtx->ctos.b[0];
				rtx->ctos.b[fi].c[0] = rtx->ctos.fi;
				rtx->ctos.fi = fi;
				break;
			}

			goto freemem;
		}

		case HAWK_VAL_STR:
#if 0
		plain_str:
#endif
			if (str != ((hawk_val_str_t*)v)->val.ptr) hawk_rtx_freemem(rtx, str);
			break;

#if 0
		case HAWK_VAL_REF:
			v = hawk_rtx_getrefval(rtx, (hawk_val_ref_t*)v);
			if (v && HAWK_RTX_GETVALTYPE(rtx, v) == HAWK_VAL_STR) goto plain_str;
			/* fall through */
#endif

		default:
		freemem:
			hawk_rtx_freemem(rtx, str);
			break;
	}
}

hawk_bch_t* hawk_rtx_getvalbcstrwithcmgr (hawk_rtx_t* rtx, hawk_val_t* v, hawk_oow_t* len, hawk_cmgr_t* cmgr)
{
	hawk_bch_t c;
	hawk_oow_t l;

	switch (HAWK_RTX_GETVALTYPE(rtx, v))
	{
		case HAWK_VAL_NIL:
			c = '\0';
			l = 0;
			goto bctos;
		case HAWK_VAL_BCHR:
			c = HAWK_RTX_GETBCHRFROMVAL(rtx, v);
			l = 1;
		bctos:
			if (rtx->bctos.fi) /* free slot available */
			{
				/* use a bctos slot to avoid duplication */
				hawk_oow_t fi;
				fi = rtx->bctos.fi;
				rtx->bctos.fi = rtx->bctos.b[rtx->bctos.fi].c[0];
				rtx->bctos.b[fi].c[0] = c;
				rtx->bctos.b[fi].c[1] = '\0';
				if (len) *len = l;
				HAWK_ASSERT((void*)&rtx->bctos.b[fi] == (void*)rtx->bctos.b[fi].c);
				return (hawk_bch_t*)rtx->bctos.b[fi].c; /* type-cast hawk_bchu_t* to hawk_bch_t* */
			}
			goto duplicate;

		case HAWK_VAL_MBS:
#if 0
		plain_mbs:
#endif
			if (len) *len = ((hawk_val_mbs_t*)v)->val.len;
			return ((hawk_val_mbs_t*)v)->val.ptr;

#if 0
/* i'm commenting out this part because hawk_rtx_setrefval() changes v->adr
 * and leads hawk_rtx_freevalbcstr() to check a wrong value obejct.
 * if you know that a value is a reference, you can get the referenced value
 * with hawk_rtx_getrefval() and call this function over it */
		case HAWK_VAL_REF:
			v1 = hawk_rtx_getrefval(rtx, (hawk_val_ref_t*)v);
			if (v1 && HAWK_RTX_GETVALTYPE(rtx, v1) == HAWK_VAL_MBS) goto plain_mbs;
			/* fall through */
#endif

		case HAWK_VAL_BOB: /* BOB must be duplicated */
		default:
		duplicate:
			return hawk_rtx_valtobcstrdupwithcmgr(rtx, v, len, cmgr);
	}
}

void hawk_rtx_freevalbcstr (hawk_rtx_t* rtx, hawk_val_t* v, hawk_bch_t* str)
{
	switch (HAWK_RTX_GETVALTYPE(rtx, v))
	{
		case HAWK_VAL_NIL:
		case HAWK_VAL_BCHR:
		{
			hawk_bctos_b_t* b = (hawk_bctos_b_t*)str;
			if (b >= &rtx->bctos.b[0] && b < &rtx->bctos.b[HAWK_COUNTOF(rtx->bctos.b)])
			{
				hawk_oow_t fi;
				fi = b - &rtx->bctos.b[0];
				rtx->bctos.b[fi].c[0] = rtx->bctos.fi;
				rtx->bctos.fi = fi;
				break;
			}

			goto freemem;
		}

		case HAWK_VAL_MBS:
#if 0
		plain_mbs:
#endif
			if (str != ((hawk_val_mbs_t*)v)->val.ptr) hawk_rtx_freemem(rtx, str);
			break;

#if 0
		case HAWK_VAL_REF:
			v = hawk_rtx_getrefval(rtx, (hawk_val_ref_t*)v);
			if (v && HAWK_RTX_GETVALTYPE(rtx, v) == HAWK_VAL_MBS) goto plain_mbs;
			/* fall through */
#endif

		case HAWK_VAL_BOB: /* BOB to MBS is always duplication */
		default:
		freemem:
			hawk_rtx_freemem(rtx, str);
			break;
	}
}

static int val_ref_to_num (hawk_rtx_t* rtx, const hawk_val_ref_t* ref, hawk_int_t* l, hawk_flt_t* r)
{
	switch (ref->id)
	{
		case HAWK_VAL_REF_POS:
		{
			hawk_oow_t idx;

			idx = (hawk_oow_t)ref->adr;
			if (idx == 0)
			{
				return hawk_oochars_to_num(
					HAWK_OOCHARS_TO_NUM_MAKE_OPTION(0, 0, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0),
					HAWK_OOECS_PTR(&rtx->inrec.line),
					HAWK_OOECS_LEN(&rtx->inrec.line),
					l, r
				);
			}
			else if (idx <= rtx->inrec.nflds)
			{
				return hawk_oochars_to_num(
					HAWK_OOCHARS_TO_NUM_MAKE_OPTION(0, 0, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0),
					rtx->inrec.flds[idx-1].ptr,
					rtx->inrec.flds[idx-1].len,
					l, r
				);
			}
			else
			{
				return hawk_oochars_to_num(HAWK_OOCHARS_TO_NUM_MAKE_OPTION(0, 0, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0), HAWK_T(""), 0, l, r);
			}
		}

		case HAWK_VAL_REF_GBL:
		{
			hawk_oow_t idx = (hawk_oow_t)ref->adr;
			return hawk_rtx_valtonum(rtx, HAWK_RTX_STACK_GBL(rtx, idx), l, r);
		}

		default:
		{
			hawk_val_t** xref = (hawk_val_t**)ref->adr;

			/* A reference value is not able to point to another
			 * refernce value for the way values are represented
			 * in HAWK */
			HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, *xref) != HAWK_VAL_REF);

			/* make a recursive call back to the caller */
			return hawk_rtx_valtonum(rtx, *xref, l, r);
		}
	}
}


int hawk_rtx_valtonum (hawk_rtx_t* rtx, const hawk_val_t* v, hawk_int_t* l, hawk_flt_t* r)
{
	hawk_val_type_t vtype = HAWK_RTX_GETVALTYPE(rtx, v);

	switch (vtype)
	{
		case HAWK_VAL_NIL:
			*l = 0;
			return 0;

		case HAWK_VAL_BCHR:
		{
			hawk_bch_t tmp = HAWK_RTX_GETBCHRFROMVAL(rtx, v);
			return hawk_bchars_to_num(
				HAWK_OOCHARS_TO_NUM_MAKE_OPTION(0, 0, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0),
				&tmp, 1, l, r
			);
		}

		case HAWK_VAL_CHAR:
		{
			/* treat it as if it is a 1-letter string */
			hawk_ooch_t tmp = HAWK_RTX_GETCHARFROMVAL(rtx, v);
			return hawk_oochars_to_num(
				HAWK_OOCHARS_TO_NUM_MAKE_OPTION(0, 0, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0),
				&tmp, 1, l, r
			);
		}

		case HAWK_VAL_INT:
			*l = HAWK_RTX_GETINTFROMVAL(rtx, v);
			return 0; /* long */

		case HAWK_VAL_FLT:
			*r = ((hawk_val_flt_t*)v)->val;
			return 1; /* real */

		case HAWK_VAL_STR:
			return hawk_oochars_to_num(
				HAWK_OOCHARS_TO_NUM_MAKE_OPTION(0, 0, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0),
				((hawk_val_str_t*)v)->val.ptr,
				((hawk_val_str_t*)v)->val.len,
				l, r
			);

		case HAWK_VAL_MBS:
			return hawk_bchars_to_num(
				HAWK_OOCHARS_TO_NUM_MAKE_OPTION(0, 0, HAWK_RTX_IS_STRIPSTRSPC_ON(rtx), 0),
				((hawk_val_mbs_t*)v)->val.ptr,
				((hawk_val_mbs_t*)v)->val.len,
				l, r
			);

		case HAWK_VAL_FUN:
			/* unable to convert a function to a number */
			goto invalid;

		case HAWK_VAL_MAP:
			if (rtx->hawk->opt.trait & HAWK_FLEXMAP)
			{
				*l = HAWK_MAP_SIZE(((hawk_val_map_t*)v)->map);
				return 0; /* long */
			}
			goto invalid;

		case HAWK_VAL_ARR:
			if (rtx->hawk->opt.trait & HAWK_FLEXMAP)
			{
				*l = HAWK_ARR_SIZE(((hawk_val_arr_t*)v)->arr);
				return 0; /* long */
			}
			goto invalid;

		case HAWK_VAL_REF:
			return val_ref_to_num(rtx, (hawk_val_ref_t*)v, l, r);

		case HAWK_VAL_REX:
		case HAWK_VAL_BOB: /* never allow reference */
		default:
		invalid:
		#if defined(DEBUG_VAL)
			hawk_logfmt(hawk, HAWK_T(">>WRONG VALUE TYPE [%d] in hawk_rtx_valtonum()\n"), v->type);
		#endif
			hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EVALTONUM);
			return -1; /* error */
	}
}

int hawk_rtx_valtoint (hawk_rtx_t* rtx, const hawk_val_t* v, hawk_int_t* l)
{
	int n;
	hawk_flt_t r;

	n = hawk_rtx_valtonum(rtx, v, l, &r);
	if (n == 1)
	{
		*l = (hawk_int_t)r;
		n = 0;
	}

	return n;
}

int hawk_rtx_valtoflt (hawk_rtx_t* rtx, const hawk_val_t* v, hawk_flt_t* r)
{
	int n;
	hawk_int_t l;

	n = hawk_rtx_valtonum(rtx, v, &l, r);
	if (n == 0) *r = (hawk_flt_t)l;
	else if (n == 1) n = 0;

	return n;
}


hawk_fun_t* hawk_rtx_valtofun (hawk_rtx_t* rtx, hawk_val_t* v)
{
	hawk_fun_t* fun;
	hawk_val_type_t vtype;

	vtype = HAWK_RTX_GETVALTYPE(rtx, v);

	switch (vtype)
	{
		case HAWK_VAL_FUN:
			fun = ((hawk_val_fun_t*)v)->fun;
			break;

		case HAWK_VAL_BCHR:
		case HAWK_VAL_MBS:
		case HAWK_VAL_BOB:
		{
			hawk_bcs_t x;
			x.ptr = hawk_rtx_getvalbcstr(rtx, v, &x.len);
			if (HAWK_UNLIKELY(!x.ptr)) return HAWK_NULL;
			if (hawk_count_bcstr(x.ptr) != x.len)
			{
				hawk_rtx_freevalbcstr (rtx, v, x.ptr);
				goto error_inval;
			}
			fun = hawk_rtx_findfunwithbcstr(rtx, x.ptr);
			hawk_rtx_freevalbcstr (rtx, v, x.ptr);
			if (!fun) return HAWK_NULL;
			break;
		}

		case HAWK_VAL_CHAR:
		case HAWK_VAL_STR:
		{
			hawk_oocs_t x;
			x.ptr = hawk_rtx_getvaloocstr(rtx, v, &x.len);
			if (HAWK_UNLIKELY(!x.ptr)) return HAWK_NULL;
			if (hawk_count_oocstr(x.ptr) != x.len)
			{
				hawk_rtx_freevaloocstr(rtx, v, x.ptr);
				goto error_inval;
			}
			fun = hawk_rtx_findfunwithoocstr(rtx, x.ptr);
			hawk_rtx_freevaloocstr(rtx, v, x.ptr);
			if (!fun) return HAWK_NULL;
			break;
		}

		default:
		error_inval:
			hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINVAL);
			return HAWK_NULL;
	}

	return fun;
}

hawk_fnc_t* hawk_rtx_valtofnc (hawk_rtx_t* rtx, hawk_val_t* v, hawk_fnc_t* rfnc)
{
	/* this function looks for intrinsic functions as well as module functions.
	 * it combines the functionality of the following two functions.
	 *   hawk_findfncwithoocs() - finds an intrisic function
	 *   hawk_querymodulewithname() - finds a function defined in a module
	 */

	hawk_t* hawk = hawk_rtx_gethawk(rtx);
	hawk_val_type_t vtype;

	vtype = HAWK_RTX_GETVALTYPE(rtx, v);

	switch (vtype)
	{
		case HAWK_VAL_BCHR:
		case HAWK_VAL_MBS:
		case HAWK_VAL_BOB:
		case HAWK_VAL_CHAR:
		case HAWK_VAL_STR:
		{
			hawk_oocs_t x;
			hawk_fnc_t* fnc;

			x.ptr = hawk_rtx_getvaloocstr(rtx, v, &x.len);
			if (HAWK_UNLIKELY(!x.ptr)) return HAWK_NULL;
			if (hawk_count_oocstr(x.ptr) != x.len)
			{
				hawk_rtx_freevaloocstr(rtx, v, x.ptr);
				goto error_inval;
			}

			fnc = hawk_findfncwithoocs(hawk, &x);
			if (fnc)
			{
				hawk_rtx_freevaloocstr(rtx, v, x.ptr);
				*rfnc = *fnc;
			}
			else
			{
				hawk_mod_t* mod;
				hawk_mod_sym_t sym;

				mod = hawk_querymodulewithname(hawk, x.ptr, &sym);
				if (!mod)
				{
					hawk_rtx_freevaloocstr(rtx, v, x.ptr);
					return HAWK_NULL;
				}

				if (sym.type != HAWK_MOD_FNC || (hawk->opt.trait & sym.u.fnc_.trait) != sym.u.fnc_.trait)
				{
					hawk_rtx_seterrfmt(rtx, HAWK_NULL, HAWK_ENOENT, HAWK_T("'%.*js' not a function or unsupported"), x.len, x.ptr);
					hawk_rtx_freevaloocstr(rtx, v, x.ptr);
					return HAWK_NULL;
				}

				hawk_rtx_freevaloocstr(rtx, v, x.ptr);

				HAWK_MEMSET(rfnc, 0, HAWK_SIZEOF(*rfnc));
				rfnc->name.ptr = sym.name;
				rfnc->name.len = hawk_count_oocstr(sym.name);
				rfnc->spec = sym.u.fnc_;
				/* TODO: FIX THIS - set the owner name */
				/*rfnc->owner = name of the module?*/
				rfnc->mod = mod;
			}
			break;
		}

		default:
		error_inval:
			hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINVAL);
			return HAWK_NULL;
	}

	return rfnc;
}

/* ========================================================================== */

static HAWK_INLINE hawk_uint_t hash (hawk_uint8_t* ptr, hawk_oow_t len)
{
	hawk_uint_t h;
	HAWK_HASH_BYTES (h, ptr, len);
	return h;
}

hawk_int_t hawk_rtx_hashval (hawk_rtx_t* rtx, hawk_val_t* v)
{
	hawk_val_type_t vtype = HAWK_RTX_GETVALTYPE(rtx, v);
	hawk_int_t hv;

	switch (vtype)
	{
		case HAWK_VAL_NIL:
			hv = 0;
			break;

		case HAWK_VAL_BCHR:
		{
			hawk_bch_t tmp = HAWK_RTX_GETBCHRFROMVAL(rtx, v);
			hv = (hawk_int_t)hash((hawk_uint8_t*)&tmp, HAWK_SIZEOF(tmp));
			break;
		}

		case HAWK_VAL_CHAR:
		{
			hawk_ooch_t tmp = HAWK_RTX_GETCHARFROMVAL(rtx, v);
			hv = (hawk_int_t)hash((hawk_uint8_t*)&tmp, HAWK_SIZEOF(tmp));
			break;
		}

		case HAWK_VAL_INT:
		{
			hawk_int_t tmp = HAWK_RTX_GETINTFROMVAL(rtx, v);
			/*hv = ((hawk_val_int_t*)v)->val;*/
			hv = (hawk_int_t)hash((hawk_uint8_t*)&tmp, HAWK_SIZEOF(tmp));
			break;
		}

		case HAWK_VAL_FLT:
		{
			hawk_val_flt_t* dv = (hawk_val_flt_t*)v;
			hv = (hawk_int_t)hash((hawk_uint8_t*)&dv->val, HAWK_SIZEOF(dv->val));
			break;
		}

		case HAWK_VAL_STR:
		{
			hawk_val_str_t* dv = (hawk_val_str_t*)v;
			hv = (hawk_int_t)hash((hawk_uint8_t*)dv->val.ptr, dv->val.len * HAWK_SIZEOF(*dv->val.ptr));
			break;
		}

		case HAWK_VAL_MBS:
		{
			hawk_val_mbs_t* dv = (hawk_val_mbs_t*)v;
			hv = (hawk_int_t)hash((hawk_uint8_t*)dv->val.ptr, dv->val.len * HAWK_SIZEOF(*dv->val.ptr));
			break;
		}

		case HAWK_VAL_BOB:
		{
			hawk_val_bob_t* dv = (hawk_val_bob_t*)v;
			hv = (hawk_int_t)hash((hawk_uint8_t*)dv->val.ptr, dv->val.len * HAWK_SIZEOF(*dv->val.ptr));
			break;
		}

		default:

#if defined(DEBUG_VAL)
			hawk_logfmt(hawk_rtx_gethawk(rtx), HAWK_LOG_STDERR, HAWK_T(">>WRONG VALUE TYPE [%d] in hawk_rtx_hashval()\n"), v->type);
#endif
			hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EHASHVAL);
			return -1;
	}

	/* turn off the sign bit */
	return hv  & ~(((hawk_uint_t)1) << ((HAWK_SIZEOF(hawk_uint_t) * 8) - 1));
}

hawk_val_type_t hawk_rtx_getrefvaltype (hawk_rtx_t* rtx, hawk_val_ref_t* ref)
{
	/* return the type of the value that the reference points to */
	switch (ref->id)
	{
		case HAWK_VAL_REF_POS:
		{
			hawk_oow_t idx;

			idx = (hawk_oow_t)ref->adr;
			if (idx == 0)
			{
				return HAWK_RTX_GETVALTYPE(rtx, rtx->inrec.d0);
			}
			else if (idx <= rtx->inrec.nflds)
			{
				return HAWK_RTX_GETVALTYPE(rtx, rtx->inrec.flds[idx-1].val);
			}
			else
			{
				return HAWK_RTX_GETVALTYPE(rtx, hawk_val_nil);
			}
		}

		case HAWK_VAL_REF_GBL:
		{
			hawk_oow_t idx;
			hawk_val_t* v;
			idx = (hawk_oow_t)ref->adr;
			v = HAWK_RTX_STACK_GBL(rtx, idx);
			return HAWK_RTX_GETVALTYPE(rtx, v);
		}

		default:
		{
			hawk_val_t** xref = (hawk_val_t**)ref->adr;
			hawk_val_t* v;

			/* A reference value is not able to point to another
			 * refernce value for the way values are represented
			 * in HAWK */
			v = *xref;
			HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, v) != HAWK_VAL_REF);
			return HAWK_RTX_GETVALTYPE(rtx, v);
		}
	}
}

hawk_val_t* hawk_rtx_getrefval (hawk_rtx_t* rtx, hawk_val_ref_t* ref)
{
	switch (ref->id)
	{
		case HAWK_VAL_REF_POS:
		{
			hawk_oow_t idx;

			idx = (hawk_oow_t)ref->adr;
			if (idx == 0)
			{
				return rtx->inrec.d0;
			}
			else if (idx <= rtx->inrec.nflds)
			{
				return rtx->inrec.flds[idx-1].val;
			}
			else
			{
				return hawk_val_nil;
			}
		}

		case HAWK_VAL_REF_GBL:
		{
			hawk_oow_t idx;
			idx = (hawk_oow_t)ref->adr;
			return HAWK_RTX_STACK_GBL(rtx, idx);
		}

		default:
		{
			hawk_val_t** xref = (hawk_val_t**)ref->adr;
			/* A reference value is not able to point to another
			 * refernce value for the way values are represented
			 * in HAWK */
			HAWK_ASSERT(HAWK_RTX_GETVALTYPE(rtx, *xref) != HAWK_VAL_REF);
			return *xref;
		}
	}
}

int hawk_rtx_setrefval (hawk_rtx_t* rtx, hawk_val_ref_t* ref, hawk_val_t* val)
{
	hawk_val_type_t vtype = HAWK_RTX_GETVALTYPE(rtx, val);

	if (vtype == HAWK_VAL_REX || vtype == HAWK_VAL_REF)
	{
		/* though it is possible that an intrinsic function handler
		 * can accept a regular expression withtout evaluation when 'x'
		 * is specified for the parameter, this function doesn't allow
		 * regular expression to be set to a reference variable to
		 * avoid potential chaos. the nature of performing '/rex/ ~ $0'
		 * for a regular expression without the match operator
		 * makes it difficult to be implemented. */
		hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_EINVAL);
		return -1;
	}

	switch (ref->id)
	{
		case HAWK_VAL_REF_POS:
		{
			switch (vtype)
			{
				case HAWK_VAL_MAP:
				case HAWK_VAL_ARR:
					/* a map is assigned to a positional. this is disallowed. */
					hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_ENONSCATOPOS);
					return -1;

				default:
				{
					hawk_oocs_t str;
					int x;

					hawk_rtx_refupval(rtx, val);
					str.ptr = hawk_rtx_getvaloocstr(rtx, val, &str.len);
					if (HAWK_UNLIKELY(!str.ptr))
					{
						hawk_rtx_refdownval(rtx, val);
						return -1;
					}
					x = hawk_rtx_setrec(rtx, (hawk_oow_t)ref->adr, &str, 0);
					hawk_rtx_freevaloocstr(rtx, val, str.ptr);
					hawk_rtx_refdownval(rtx, val);
					return x;
				}
			}
		}

		case HAWK_VAL_REF_GBL:
			/* ref->adr is the index to the global variables, not a real pointer address for HAWK_VAL_REF_GBL */
			return hawk_rtx_setgbl(rtx, (int)ref->adr, val);

		case HAWK_VAL_REF_NAMEDIDX:
		case HAWK_VAL_REF_GBLIDX:
		case HAWK_VAL_REF_LCLIDX:
		case HAWK_VAL_REF_ARGIDX:
		#if !defined(HAWK_ENABLE_GC)
			if (vtype == HAWK_VAL_MAP || vtype == HAWK_VAL_ARR)
			{
				/* an indexed variable cannot be assigned a map.
				 * in other cases, it falls down to the default case. */
				hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_ENONSCATOIDX);
				return -1;
			}
		#endif
			/* fall through */

		default:
		{
			hawk_val_t** rref;
			hawk_val_type_t rref_vtype;

			rref = (hawk_val_t**)ref->adr; /* old value pointer */
			rref_vtype = HAWK_RTX_GETVALTYPE(rtx, *rref); /* old value type */
			if (vtype == HAWK_VAL_MAP || vtype == HAWK_VAL_ARR)
			{
				/* new value: map, old value: nil or map => ok */
				if (rref_vtype != HAWK_VAL_NIL && rref_vtype != vtype)
				{
					if (!(rtx->hawk->opt.trait & HAWK_FLEXMAP))
					{
						/* cannot change a scalar value to a map */
						hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_ESCALARTONONSCA);
						return -1;
					}
				}
			}
			else
			{
				/* new value: scalar, old value: nil or scalar => ok */
				if (rref_vtype == HAWK_VAL_MAP || rref_vtype == HAWK_VAL_ARR)
				{
					if (!(rtx->hawk->opt.trait & HAWK_FLEXMAP))
					{
						hawk_rtx_seterrnum(rtx, HAWK_NULL, HAWK_ENONSCATOSCALAR);
						return -1;
					}
				}
			}

			if (*rref != val)
			{
				/* if the new value is not the same as the old value */
				hawk_rtx_refdownval(rtx, *rref);
				*rref = val;
				hawk_rtx_refupval(rtx, *rref);
			}
			return 0;
		}
	}
}

#if 0

#define hawk_errputstrf hawk_errputstrf

static hawk_map_walk_t print_pair (hawk_map_t* map, hawk_map_pair_t* pair, void* arg)
{
	hawk_rtx_t* rtx = (hawk_rtx_t*)arg;

	HAWK_ASSERT(rtx == *(hawk_rtx_t**)hawk_map_getxtn(map));

	hawk_errputstrf(HAWK_T(" %.*s=>"), HAWK_MAP_KLEN(pair), HAWK_MAP_KPTR(pair));
	hawk_dprintval((hawk_rtx_t*)arg, HAWK_MAP_VPTR(pair));
	hawk_errputstrf(HAWK_T(" "));

	return HAWK_MAP_WALK_FORWARD;
}


static hawk_arr_walk_t print_pair (hawk_arr_t* arr, hawk_oow_t index, void* arg)
{
	hawk_rtx_t* rtx = (hawk_rtx_t*)arg;

	HAWK_ASSERT(rtx == *(hawk_rtx_t**)hawk_arr_getxtn(arr));

	hawk_errputstrf(HAWK_T(" %lu=>"), (unsigned long int)index);
	hawk_dprintval((hawk_rtx_t*)arg, (HAWK_ARR_SLOT(arr,index)? HAWK_ARR_DPTR(arr, index): hawk_val_nil));
	hawk_errputstrf(HAWK_T(" "));

	return HAWK_ARR_WALK_FORWARD;
}


void hawk_dprintval(hawk_rtx_t* run, hawk_val_t* val)
{
	/* TODO: better value printing ... */

	switch (val->type)
	{
		case HAWK_VAL_NIL:
			hawk_errputstrf(HAWK_T("@nil"));
			break;

		case HAWK_VAL_NIL:
		{
			hawk_ooch_t tmp = HAWK_RTX_GETCHARFROMVAL(rtx, val);
			if (tmp == '\'')
				hawk_errputstrf(HAWK_T("'\\%c'"), tmp);
			else if (tmp == '\0')
				hawk_errputstrf(HAWK_T("'\\0'"));
			else if (hawk_is_ooch_print(tmp))
				hawk_errputstrf(HAWK_T("'%jc'"), tmp);
		#if defined(HAWK_OOCH_IS_UCH)
			else if (tmp <= 0xFFFF)
				hawk_errputstrf(HAWK_T("'\\u%04x'"), tmp);
			else
				hawk_errputstrf(HAWK_T("'\\U%08x'"), tmp);
		#else
			else
				hawk_errputstrf(HAWK_T("'\\x%02x'"), tmp);
		#endif
			break;
		}

		case HAWK_VAL_BCHR:
			hawk_errputstrf(HAWK_T("%hc"), HAWK_GETBCHRFROMVAL(val));
			break;

		case HAWK_VAL_CHAR:
			hawk_errputstrf(HAWK_T("%jc"), HAWK_GETCHARFROMVAL(val));
			break;

		case HAWK_VAL_INT:
			hawk_errputstrf(HAWK_T("%jd"), (hawk_intmax_t)HAWK_GETINTFROMVAL(val));
			break;

		case HAWK_VAL_FLT:
		#if defined(HAWK_USE_FLTMAX)
			/*hawk_errputstrf(HAWK_T("%jf"), ((hawk_val_flt_t*)val)->val);*/
			hawk_errputstrf(HAWK_T("%jjf"), &((hawk_val_flt_t*)val)->val);
		#else
			hawk_errputstrf(HAWK_T("%zf"), ((hawk_val_flt_t*)val)->val);
		#endif
			break;

		case HAWK_VAL_STR:
			hawk_errputstrf(HAWK_T("%.*s"), ((hawk_val_str_t*)val)->len, ((hawk_val_str_t*)val)->ptr);
			break;

		case HAWK_VAL_MBS:
			hawk_errputstrf(HAWK_T("%.*hs"), ((hawk_val_mbs_t*)val)->len, ((hawk_val_mbs_t*)val)->ptr);
			break;

		case HAWK_VAL_REX:
			hawk_errputstrf(HAWK_T("/%s/"), ((hawk_val_rex_t*)val)->ptr);
			break;

		case HAWK_VAL_FUN:
			hawk_errputstrf(HAWK_T("%.*s"), ((hawk_val_fun_t*)val)->fun->name.len, ((hawk_val_fun_t*)val)->fun->name.ptr);
			break;

		case HAWK_VAL_MAP:
			hawk_errputstrf(HAWK_T("MAP["));
			hawk_map_walk(((hawk_val_map_t*)val)->map, print_pair, run);
			hawk_errputstrf(HAWK_T("]"));
			break;

		case HAWK_VAL_ARR:
			hawk_errputstrf(HAWK_T("ARR["));
			hawk_arr_walk(((hawk_val_arr_t*)val)->arr, print_elem, run);
			hawk_errputstrf(HAWK_T("]"));
			break;

		case HAWK_VAL_REF:
			hawk_errputstrf(HAWK_T("REF[id=%d,val="), ((hawk_val_ref_t*)val)->id);
			hawk_dprintval(run, *((hawk_val_ref_t*)val)->adr);
			hawk_errputstrf(HAWK_T("]"));
			break;

		case HAWK_VAL_BOB:
			hawk_errputstrf(HAWK_T("%.*k"), ((hawk_val_bob_t*)val)->len, ((hawk_val_bob_t*)val)->ptr);
			break;

		default:
			hawk_errputstrf(HAWK_T("**** INTERNAL ERROR - INVALID VALUE TYPE ****\n"));
	}
}

#endif
