// value.v — tagged value encoding and helpers for the VuurRaaf VM.
//
// Stack values are 64-bit tagged integers with three tag bits:
//   low bits 000 -> encoded integer (value = raw << 3)
//   low bits 001 -> string handle   (handle = value >> 3, into v.strings)
//   low bits 010 -> struct handle   (handle = value >> 3, into v.structs)
//   low bits 011 -> array handle    (handle = value >> 3, into v.arrays)
//   low bits 100 -> float handle    (handle = value >> 3, into v.floats)
module vm

const tag_mask = u64(7)
const tag_int = u64(0)
const tag_str = u64(1)
const tag_struct = u64(2)
const tag_arr = u64(3)
const tag_float = u64(4)
const tag_closure = u64(5)

// none_val is the sentinel for the `none` literal (and JSON null). Tag 110
// is not a valid encoded integer (those are multiples of 8) nor any handle,
// so it can never collide with a real value. The GC ignores it (its tag
// matches no heap pool).
const none_val = i64(6)

fn (mut v Vm) tag(x i64) u64 {
	return u64(x) & tag_mask
}

fn (mut v Vm) is_int(x i64) bool {
	return u64(x) & tag_mask == tag_int
}

fn (mut v Vm) is_str(x i64) bool {
	return u64(x) & tag_mask == tag_str
}

fn (mut v Vm) is_struct(x i64) bool {
	return u64(x) & tag_mask == tag_struct
}

fn (mut v Vm) is_arr(x i64) bool {
	return u64(x) & tag_mask == tag_arr
}

fn (mut v Vm) is_float(x i64) bool {
	return u64(x) & tag_mask == tag_float
}

fn (mut v Vm) is_closure(x i64) bool {
	return u64(x) & tag_mask == tag_closure
}

fn (mut v Vm) is_none(x i64) bool {
	return x == none_val
}

// is_num reports whether x is an integer (tag 0). Floats are a distinct type.
fn (mut v Vm) is_num(x i64) bool {
	return u64(x) & tag_mask == tag_int
}

fn (mut v Vm) enc_int(x i64) i64 {
	return i64(u64(x) << 3)
}

fn (mut v Vm) dec_int(x i64) i64 {
	return x >> 3
}

fn (mut v Vm) hand(x i64) int {
	return int(x >> 3)
}

fn (mut v Vm) mkstr(idx int) i64 {
	return i64((u64(idx) << 3) | tag_str)
}

fn (mut v Vm) mkarr(idx int) i64 {
	return i64((u64(idx) << 3) | tag_arr)
}

fn (mut v Vm) mkstruct_handle(idx int) i64 {
	return i64((u64(idx) << 3) | tag_struct)
}

fn (mut v Vm) mkfloat(idx int) i64 {
	return i64((u64(idx) << 3) | tag_float)
}

fn (mut v Vm) mkclosure(idx int) i64 {
	return i64((u64(idx) << 3) | tag_closure)
}

// push_float interns a float into the pool and returns its tagged handle.
fn (mut v Vm) push_float(f f64) i64 {
	v.floats << f
	return v.mkfloat(v.floats.len - 1)
}

// fval returns the f64 value of a float handle.
fn (mut v Vm) fval(x i64) f64 {
	return v.floats[v.hand(x)]
}

fn (mut v Vm) truthy(x i64) bool {
	if v.is_none(x) {
		return false
	}
	if v.is_float(x) {
		return v.fval(x) != 0.0
	}
	return x != 0
}

fn (mut v Vm) valid_handle(x i64) bool {
	h := v.hand(x)
	return h >= 0 && h < v.strings.len
}

fn (mut v Vm) valid_arr_handle(x i64) bool {
	h := v.hand(x)
	return h >= 0 && h < v.arrays.len
}

fn (mut v Vm) valid_struct_handle(x i64) bool {
	h := v.hand(x)
	return h >= 0 && h < v.structs.len
}

fn (mut v Vm) valid_float_handle(x i64) bool {
	h := v.hand(x)
	return h >= 0 && h < v.floats.len
}

fn (mut v Vm) valid_closure_handle(x i64) bool {
	h := v.hand(x)
	return h >= 0 && h < v.closures.len
}

// valid_handle_for bounds-checks a handle against the pool matching its tag.
fn (mut v Vm) valid_handle_for(x i64) bool {
	return match v.tag(x) {
		tag_str { v.valid_handle(x) }
		tag_struct { v.valid_struct_handle(x) }
		tag_arr { v.valid_arr_handle(x) }
		tag_float { v.valid_float_handle(x) }
		tag_closure { v.valid_closure_handle(x) }
		else { true }
	}
}
