ref: 92847c0604e5f4fa790b1783595e8a503ad271be
dir: /lib/std/introspect.myr/
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) }