shithub: mc

ref: ddb327c92ebe3f96c5c816ec61996440551de401
dir: /lib/std/introspect.myr/

View raw version
use "types"
use "die"

pkg std =
	type typedesc = union
		`Tynone

		/* atomic types */
		`Tyvoid
		`Tybool
		`Tychar

		`Tyint8
		`Tyint16
		`Tyint
		`Tyint32
		`Tyint64

		`Tybyte
		`Tyuint8
		`Tyuint16
		`Tyuint
		`Tyuint32
		`Tyuint64
		`Tyflt32
		`Tyflt64
		`Tyvalist

		/* compound types */
		`Typtr byte[:]
		`Tyfunc	typecursor
		`Tyslice byte[:]
		`Tyarray (size, byte[:])

		/* aggregate types */
		`Tytuple	typecursor
		`Tystruct	typecursor
		`Tyunion	typecursor
		/* name info */
		`Tyname (byte[:], byte[:])
	;;

	type typecursor = struct
		nelt	: size
		rem	: byte[:]
		isnamed	: bool
		isiter	: bool
	;;

	type typeinfo = struct
		size	: size
		align	: size
	;;

	generic typeof	: (v : @a -> byte[:])
	const typeenc	: (p : ...# -> typecursor)
	const typeenccursor	: (e : byte[:] -> typecursor)
	const typedesc	: (e : byte[:] -> typedesc)
	const typeinfo	: (e : byte[:] -> typeinfo)

	const tcnext	: (t : typecursor# -> byte[:])
	const tcpeek	: (t : typecursor# -> byte[:])
	const ncpeek	: (t : typecursor# -> (byte[:], byte[:]))
	const ncnext	: (t : typecursor# -> (byte[:], byte[:]))
;;

extern const put	: (fmt : byte[:], args : ... -> size)

const Encnone	: byte = 0
const Encvoid	: byte = 1
const Encbool	: byte = 2
const Encchar	: byte = 3

const Encint8	: byte = 4
const Encint16	: byte = 5
const Encint	: byte = 6
const Encint32	: byte = 7
const Encint64	: byte = 8

const Encbyte	: byte = 9
const Encuint8	: byte = 10
const Encuint16	: byte = 11
const Encuint	: byte = 12
const Encuint32	: byte = 13
const Encuint64	: byte = 14
const Encflt32	: byte = 15
const Encflt64	: byte = 16
const Encvalist	: byte = 17

/* compound types */
const Encptr	: byte = 18
const Encfunc	: byte = 19
const Encslice	: byte = 20
const Encarray	: byte = 21

/* aggregate types */
const Enctuple	: byte = 22
const Encstruct	: byte = 23
const Encunion	: byte = 24
const Encname	: byte = 28
const Encindname	:byte = 28 | 0x80

generic typeof = {v : @a -> byte[:]
	var tc

	tc = typesof(v)
	-> tcnext(&tc)
}

const typeenc = {ap : ...#
	var e

	e = getenc((ap : byte##))
	e = skiptypeinfo(e[1:])
	-> lentypecursor(e)
}

const typeenccursor = {e
	-> [.nelt=1, .rem=e, .isiter=false]
}
	
const typesof : (a : ... -> typecursor) = {a : ...
	-> typeenc(&a)
}

const tcnext = {tc
	var enc

	(_, enc) = ncnext(tc)
	-> enc
}

const tcpeek = {tc
	var enc

	(_, enc) = ncpeek(tc)
	-> enc
}

const ncpeek = {tc
	var name, enc, rem
	var n, sz

	if tc.rem.len == 0 || tc.nelt == 0
		-> ("", "")
	elif !tc.isiter
		-> ("", tc.rem)
	;;

	n = 0
	sz = 0
	name = ""
	rem = tc.rem
	if tc.isnamed
		/* get the name */
		(n, sz) = getipacked(tc.rem)
		name = rem[sz:sz+n]
		rem = rem[sz+n:]
	;;

	/* and the type */
	(n, sz) = getipacked(rem)
	enc = rem[sz:sz+n]
	-> (name, enc)
}

const ncnext = {tc
	var n, sz, name, enc

	if tc.rem.len == 0 || tc.nelt == 0
		-> ("", "")
	elif !tc.isiter
		/* a bit of a trick, if we want to fake arrays */
		tc.nelt--
		-> ("", tc.rem)
	;;

	n = 0
	sz = 0
	name = ""
	if tc.isnamed
		/* get the name */
		(n, sz) = getipacked(tc.rem)
		name = tc.rem[sz:sz+n]
		tc.rem = tc.rem[sz+n:]
	;;

	/* and the type */
	(n, sz) = getipacked(tc.rem)
	enc = tc.rem[sz:sz+n]
	tc.rem = tc.rem[sz+n:]
	-> (name, enc)
}


const getenc = {p : byte##
	var val, sz, x

	(val, sz) = getipacked(p#[:8])
	x = (&sz : byte#)
	-> p#[sz:sz+val]
}

const typedesc = {ti
	var len,sz, p

	match ti[0]
	| Encnone:	-> `Tynone
	| Encvoid:	-> `Tyvoid
	| Encbool:	-> `Tybool
	| Encchar:	-> `Tychar

	| Encint8:	-> `Tyint8
	| Encint16:	-> `Tyint16
	| Encint:	-> `Tyint
	| Encint32:	-> `Tyint32
	| Encint64:	-> `Tyint64

	| Encbyte:	-> `Tybyte
	| Encuint8:	-> `Tyuint8
	| Encuint16:	-> `Tyuint16
	| Encuint:	-> `Tyuint
	| Encuint32:	-> `Tyuint32
	| Encuint64:	-> `Tyuint64
	| Encflt32:	-> `Tyflt32
	| Encflt64:	-> `Tyflt64
	| Encvalist:	-> `Tyvalist

	/* compound types */
	| Encptr:	-> `Typtr getsub(ti[1:])
	| Encfunc:	-> `Tyfunc lentypecursor(ti[1:])
	| Encslice:	-> `Tyslice getsub(ti[1:])
	| Encarray:
		ti = skiptypeinfo(ti[1:])
		(len, sz) = getipacked(ti)
		-> `Tyarray (len, getsub(ti[sz:]))


	/* aggregate types */
	| Enctuple:
		ti = skiptypeinfo(ti[1:])
		-> `Tytuple lentypecursor(ti)
	| Encstruct:
		ti = skiptypeinfo(ti[1:])
		-> `Tystruct lennamecursor(ti)
	| Encunion:
		ti = skiptypeinfo(ti[1:])
		-> `Tyunion lennamecursor(ti)
	| Encname:
		-> `Tyname namedesc(ti[1:])
	| Encindname:
		/* 
		ugly hack: the slice contains a pointer to the
		value, so if we cast it to a byte##, we can
		pull the indirect value out of the pointer.
		*/
		p = (ti[1:] : byte##)
		-> typedesc(getenc(p))
	| _:
		std.die("unknown type encoding")
	;;
}

const typeinfo = {ti
	var p

	match ti[0]
	| Encnone:	-> [.size=0, .align=1]
	| Encvoid:	-> [.size=0, .align=1]
	| Encbool:	-> [.size=0, .align=1]
	| Encchar:	-> [.size=4, .align=4]

	| Encint8:	-> [.size=1, .align=1]
	| Encint16:	-> [.size=2, .align=2]
	| Encint:	-> [.size=4, .align=4]
	| Encint32:	-> [.size=4, .align=4]
	| Encint64:	-> [.size=8, .align=8]

	| Encbyte:	-> [.size=1, .align=1]
	| Encuint8:	-> [.size=1, .align=1]
	| Encuint16:	-> [.size=2, .align=2]
	| Encuint:	-> [.size=4, .align=4]
	| Encuint32:	-> [.size=4, .align=4]
	| Encuint64:	-> [.size=8, .align=8]
	| Encflt32:	-> [.size=4, .align=4]
	| Encflt64:	-> [.size=8, .align=8]
	| Encvalist:	-> [.size=8, .align=8]

	/* compound types */
	| Encptr:	-> [.size=8, .align=8]
	| Encfunc:	-> [.size=8, .align=8]
	| Encslice:	-> [.size=16, .align=8]

	| Encarray: 	-> gettypeinfo(ti[1:])
	| Enctuple:	-> gettypeinfo(ti[1:])
	| Encstruct:	-> gettypeinfo(ti[1:])
	| Encunion:	-> gettypeinfo(ti[1:])
	| Encname:	-> getnameinfo(ti[1:])
	| Encindname:
		p = (ti[1:] : byte##)
		-> typeinfo(getenc(p))
	| _:
		std.die("unknown type encoding")
	;;
}

const gettypeinfo = {e
	var size, align, sz

	(size, sz) = getipacked(e)	/* size */
	e = e[sz:]
	(align, sz) = getipacked(e)	/* align */
	-> [.size = size, .align = align]
}

const skiptypeinfo = {e
	var ignore, sz

	(ignore, sz) = getipacked(e)	/* size */
	e = e[sz:]
	(ignore, sz) = getipacked(e)	/* align */
	-> e[sz:]
}

const getnameinfo = {e
	var n, name, sz, enc

	(n, sz) = getipacked(e)
	name = e[sz:n+sz]
	e = e[n+sz:]
	(n, sz) = getipacked(e)
	enc = e[sz:n+sz]

	-> typeinfo(enc)
}

const namedesc = {e
	var n, sz, name, enc

	(n, sz) = getipacked(e)
	name = e[sz:n+sz]
	e = e[n+sz:]
	(n, sz) = getipacked(e)
	enc = e[sz:n+sz]

	-> (name, enc)
}

const lentypecursor = {e
	var n, sz

	(n, sz) = getipacked(e)
	-> [.nelt=n, .rem=e[sz:], .isnamed=false, .isiter=true]
}

const lennamecursor = {e 
	var n, sz

	(n, sz) = getipacked(e)
	-> [.nelt=n, .rem=e[sz:], .isnamed=true, .isiter=true]
}

const getsub = {e
	var n, sz

	(n, sz) = getipacked(e)
	-> e[sz:sz+n]
}

const getipacked : (p : byte[:] -> (size, size)) = {p : byte[:]
	var mask, val, len, shift, i

	mask = 0x80
	val = 0
	len = 1
	shift = 8 - len
	while p[0] & mask != mask << 1
		len++
		mask >>= 1
		mask |= 0x80
	;;

	shift = 8 - len
	val = (p[0] : size)  & ~(~0 << shift)
	for i = 1; i < len; i++
		val |= (p[i] : size) << (i*8 - len)
	;;
	-> (val, len)
}