Eigenstate: myrddin-dev mailing list

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Generating runtime type information.


And, now some sample code that decodes type information from vararg
lists.

Next, it needs to get beaten into a useful API. It requires the most
version of mc, since I fixed a few bugs in the encoded tydesc. I'll write
up some docs about the encoding format soon.

One more thing to note: Right now, I don't write out trait information.
I'm not sure that it's possible to write out meaningful trait information,
since the traits are detached from the type -- that is, anyone can add
a trait to a type at any point.

No idea right now how this should be handled.

On Fri, 27 Mar 2015 14:16:58 -0500
Ryan Gonzalez <rymg19@xxxxxxxxx> wrote:

> I'm kind of confused as to why argtype is 3rd on the stack. But this still
> looks cool.
> 
> One interesting twist would be to allow access to arguments via a trait's
> API, so we'd have something like:
> 
> const fmt : (vals : stringable... )
> 
> assuming stringable is a trait with a `tostr` method or something.
> 
> So then we could use custom structs with put:
> 
> type X = struct
>     blah: int
> ;;
> 
> impl stringable int =
>     tostr = {x
>         -> tostr(x.blah)
>     }
> ;;
> 
> var x: X = [.blah = 1234]
> std.put("our string: %?", x)
> /* puts 1234 */
> 
> 
> On Fri, Mar 27, 2015 at 1:19 AM, Ori Bernstein <ori@xxxxxxxxxxxxxx> wrote:
> 
> > Congratulations everyone! you get to rebuild as we break ABI
> > again!
> >
> > We now generate type descriptions for variadic types, and push
> > a pointer to the variadic arguments on to the stack when we call.
> > The valist code currently ignores this, and does nothing useful
> > with it -- this should be fixed.
> >
> > In other words, if you have a variadic function f:
> >
> >         const f : (a : int, b : byte[:], ... -> void)
> >
> > and you call it like this:
> >
> >         f(1, "asdf", 'c', "blah", false)
> >
> > you will get, on the stack:
> >
> >         [
> >         1       : int,
> >         "asdf"  : byte[:],
> >         argtype : byte#
> >         'c'     : char,
> >         "blah"  : byte[:],
> >         false   : bool
> >         ]
> >
> > The argtype will be a binary encoded description of the types
> > of the tuple (char, byte[:], bool)
> >
> > This means that you can write (admittedly, slightly hairy) code
> > to parse out the types passed in for variadics, and write code
> > that looks like:
> >
> >         const pack : (vals : ... -> byte[:])
> >
> > One goal that I would like to support is making std.fmt()
> > callable as such:
> >
> >         std.fmt("%: list contains %\n", 1, [1,2,3][:])
> >
> >
> > --
> > Ori Bernstein <ori@xxxxxxxxxxxxxx>
> >
> >
> 
> 
> -- 
> Ryan
> [ERROR]: Your autotools build scripts are 200 lines longer than your
> program. Something’s wrong.
> http://kirbyfan64.github.io/


-- 
Ori Bernstein <ori@xxxxxxxxxxxxxx>
use std

const Tybad	: byte = 0
const Tyvoid	: byte = 1

/* start integer types.
 * Keep them ordered between start
 * and end for faster
 * comparisons.*/
const Tybool	: byte = 2
const Tychar	: byte = 3

const Tyint8	: byte = 4
const Tyint16	: byte = 5
const Tyint	: byte = 6
const Tyint32	: byte = 7
const Tyint64	: byte = 8
const Tylong	: byte = 9

const Tybyte	: byte = 10
const Tyuint8	: byte = 11
const Tyuint16	: byte = 12
const Tyuint	: byte = 13
const Tyuint32	: byte = 14
const Tyuint64	: byte = 15
const Tyulong	: byte = 16
/*end integer types*/
const Tyflt32	: byte = 17
const Tyflt64	: byte = 18
/* end primitive types */
const Tyvalist	: byte = 19

/* end atomic types */
const Typtr	: byte = 20
const Tyfunc	: byte = 21

/* these types live on the stack */
const Tyslice	: byte = 22
const Tyarray	: byte = 23
const Tytuple	: byte = 24
const Tystruct	: byte = 25
const Tyunion	: byte = 26
const Tyname	: byte = 30
const Tynameptr	: byte = 30|0x80

const main = {
	var x : union
		`X
		`Y int
	;;
	tydump("asdf", x, false, [1,2,3])
}

const tydump : (args : ... -> void) = {args : ...
	std.put("%s\n", tyname(vatype(&args)))
}

const tyname = {td
	var t, r
		
	(t, r) = tynamerec(td)
	if r.len != 0
		std.fput(1, "warning: did not consume whole type in tynamerec: remaining %i\n", r.len)
	;;
	-> t
}

const vatype = {args : ...# -> byte[:]
	var pp

	pp = args castto(byte[:]##)
	-> pp##
}

const tynamerec = {t
	var sub, ret, ty, r, sep
	var len : uint64, addr : uint64
	var i, n, ptr

	match t[0]
	/* basic types */
	| Tybad:	 -> (std.sldup("invalid"), t[1:])
	| Tyvoid:	 -> (std.sldup("void"), t[1:])
	| Tybool:	 -> (std.sldup("bool"), t[1:])
	| Tychar:	 -> (std.sldup("char"), t[1:])
	| Tyint8:	 -> (std.sldup("int8"), t[1:])
	| Tyint16:	 -> (std.sldup("int16"), t[1:])
	| Tyint:	 -> (std.sldup("int"), t[1:])
	| Tyint32:	 -> (std.sldup("int32"), t[1:])
	| Tyint64:	 -> (std.sldup("int64"), t[1:])
	| Tylong:	 -> (std.sldup("long"), t[1:])
	| Tybyte:	 -> (std.sldup("byte"), t[1:])
	| Tyuint8:	 -> (std.sldup("uint8"), t[1:])
	| Tyuint16:	 -> (std.sldup("uint16"), t[1:])
	| Tyuint:	 -> (std.sldup("uint"), t[1:])
	| Tyuint32:	 -> (std.sldup("uint32"), t[1:])
	| Tyuint64:	 -> (std.sldup("uint64"), t[1:])
	| Tyulong:	 -> (std.sldup("ulong"), t[1:])
	| Tyflt32:	 -> (std.sldup("flt32"), t[1:])
	| Tyflt64:	 -> (std.sldup("flt64"), t[1:])
	| Tyvalist:	 -> (std.sldup("..."), t[1:])
	/* compound types */
	| Typtr:
		(sub, r) = tynamerec(t[1:])
		ty = std.bfmt("%s#", sub)
		std.slfree(sub)
		-> (ty, r)
	| Tyfunc:
		n = t[1]
		r = t[2:]
		sep = ""
		(ret, r) = tynamerec(r)
		ty = std.sldup("(")
		for i = 0; i < n; i++
			(sub, r) = tynamerec(r)
			ty = std.fmt("%s%s%s", ty, sep, sub)
			std.slfree(sub)
			sep = ", "
		;;
		sub = std.fmt("%s -> %s)", ty, ret)
		std.slfree(ty)
		std.slfree(ret)
		-> (sub, r)
	| Tyslice:
		(sub, r) = tynamerec(t[1:])
		ty = std.fmt("%s[:]", sub)
		std.slfree(sub)
		-> (ty, r)
	| Tyarray:
		r = t[1:]
		len = std.getle64(r[:8])
		(sub, r) = tynamerec(r[8:])
		ty = std.fmt("%s[%l]", sub, len)
		std.slfree(sub)
		-> (ty, r)
	| Tytuple:
		n = t[1]
		r = t[2:]
		sep = ""
		ty = std.sldup("(")
		for i = 0; i < n; i++
			(sub, r) = tynamerec(r)
			ty = std.fmt("%s%s%s", ty, sep, sub)
			std.slfree(sub)
			sep = ", "
		;;
		sub = std.fmt("%s)", ty)
		std.slfree(ty)
		-> (sub, r)
	| Tystruct:
		-> decodestruct(t[1:])
	| Tyunion:
		-> decodeunion(t[1:])
	| Tyname:
		-> strvec(t[1:])
	| Tynameptr:
		addr = std.getle64(t[1:])
		ptr = addr castto(byte[:]#)
		/*
		r is the remaining on the indirected name.
		we want the remaining on t.
		*/
		(sub, r) = strvec(ptr#[1:])
		-> (sub, t[9:])
	| _:
		std.die("unknown type\n")
	;;
	-> ("", "")
}

const strvec = {t
	var len, ty

	len = std.getle64(t)
	ty = std.sldup(t[8:8+len])
	-> (ty, t[8+len:])
}

const decodestruct = {t
	var memb, membt, ty, new
	var nmemb, i

	nmemb = std.getle64(t)
	t = t[8:]
	ty = std.sldup("struct")
	for i = 0; i < nmemb; i++
		(memb, t) = strvec(t)
		(membt, t) = tynamerec(t)
		new = std.fmt("%s; %s : %s", ty, memb, membt)
		std.slfree(ty)
		std.slfree(memb)
		std.slfree(membt)
		ty = new
	;;
	new = std.fmt("%s;;", ty)
	std.slfree(ty)
	-> (new, t)
}

const decodeunion = {t
	var memb, membt, ty, new
	var nmemb, i

	nmemb = std.getle64(t)
	t = t[8:]
	ty = std.sldup("union")
	for i = 0; i < nmemb; i++
		(memb, t) = strvec(t)
		if t[0] == Tybad
			t = t[1:]
			membt = ""
		else
			(membt, t) = tynamerec(t)
		;;
		new = std.fmt("%s; `%s %s", ty, memb, membt)
		std.slfree(ty)
		std.slfree(memb)
		std.slfree(membt)
		ty = new
	;;
	new = std.fmt("%s;;", ty)
	std.slfree(ty)
	-> (new, t)
}


References:
Generating runtime type information.Ori Bernstein <ori@xxxxxxxxxxxxxx>
Re: Generating runtime type information.Ryan Gonzalez <rymg19@xxxxxxxxx>