529 lines
11 KiB
Go
529 lines
11 KiB
Go
package types
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"aego/script/abi"
|
|
)
|
|
|
|
type Kind uint8
|
|
|
|
const (
|
|
KInvalid Kind = iota
|
|
KVoid
|
|
KBool
|
|
KInt
|
|
KFloat
|
|
KString
|
|
KVec2
|
|
KVec3
|
|
KColor
|
|
KQuat
|
|
KRect
|
|
KTransform2D
|
|
KTransform3D
|
|
KNodeRef
|
|
KAssetRef
|
|
KComponentRef
|
|
KEnum
|
|
KStruct
|
|
KSlice
|
|
KNil
|
|
KUntypedInt
|
|
KUntypedFloat
|
|
KCoroutine
|
|
KConnection
|
|
KScriptRef
|
|
KCurve
|
|
KHandle
|
|
)
|
|
|
|
type Field struct {
|
|
Name string
|
|
Type *Type
|
|
Slot int
|
|
Lane int
|
|
}
|
|
|
|
type Type struct {
|
|
Kind Kind
|
|
Name string
|
|
Local string
|
|
Pkg string
|
|
Elem *Type
|
|
Fields []*Field
|
|
Members []string
|
|
Values []int64
|
|
Asset string
|
|
Component *abi.Component
|
|
Script string
|
|
Handle string
|
|
Await bool
|
|
Host bool
|
|
slots int
|
|
}
|
|
|
|
const (
|
|
slotsUnknown = -1
|
|
slotsBusy = -2
|
|
)
|
|
|
|
func basic(k Kind, name string, slots int) *Type {
|
|
return &Type{Kind: k, Name: name, Local: name, slots: slots}
|
|
}
|
|
|
|
func lanes(k Kind, name string, names ...string) *Type {
|
|
t := basic(k, name, 1)
|
|
for i, n := range names {
|
|
t.Fields = append(t.Fields, &Field{Name: n, Type: Float, Lane: i})
|
|
}
|
|
return t
|
|
}
|
|
|
|
func engine(k Kind, name string, fields ...*Field) *Type {
|
|
t := basic(k, name, 0)
|
|
for _, f := range fields {
|
|
f.Slot, f.Lane = t.slots, -1
|
|
t.slots += f.Type.Slots()
|
|
t.Fields = append(t.Fields, f)
|
|
}
|
|
return t
|
|
}
|
|
|
|
var (
|
|
Invalid = basic(KInvalid, "invalid", 1)
|
|
Void = basic(KVoid, "void", 0)
|
|
Bool = basic(KBool, "bool", 1)
|
|
Int = basic(KInt, "int", 1)
|
|
Float = basic(KFloat, "float", 1)
|
|
String = basic(KString, "string", 1)
|
|
Vec2 = lanes(KVec2, "Vec2", "X", "Y")
|
|
Vec3 = lanes(KVec3, "Vec3", "X", "Y", "Z")
|
|
Color = lanes(KColor, "Color", "R", "G", "B", "A")
|
|
Quat = lanes(KQuat, "Quat", "X", "Y", "Z", "W")
|
|
Rect = lanes(KRect, "Rect", "X", "Y", "W", "H")
|
|
Transform2D = engine(KTransform2D, "Transform2D", &Field{Name: "Position", Type: Vec2}, &Field{Name: "Rotation", Type: Float}, &Field{Name: "Scale", Type: Vec2})
|
|
Transform3D = engine(KTransform3D, "Transform3D", &Field{Name: "Position", Type: Vec3}, &Field{Name: "Rotation", Type: Quat}, &Field{Name: "Scale", Type: Vec3})
|
|
NodeRef = basic(KNodeRef, "NodeRef", 1)
|
|
Nil = basic(KNil, "nil", 1)
|
|
UntypedInt = basic(KUntypedInt, "untyped int", 1)
|
|
UntypedFloat = basic(KUntypedFloat, "untyped float", 1)
|
|
Coroutine = basic(KCoroutine, "Coroutine", 1)
|
|
Connection = basic(KConnection, "Connection", 1)
|
|
Curve = basic(KCurve, "Curve", 1)
|
|
)
|
|
|
|
func shortPkg(pkg string) string {
|
|
return pkg[strings.LastIndexByte(pkg, '/')+1:]
|
|
}
|
|
|
|
func NewStruct(pkg, name string) *Type {
|
|
return &Type{Kind: KStruct, Name: shortPkg(pkg) + "." + name, Local: name, Pkg: pkg, slots: slotsUnknown}
|
|
}
|
|
|
|
func NewEnum(pkg, name string, members []string) *Type {
|
|
t := &Type{Kind: KEnum, Name: shortPkg(pkg) + "." + name, Local: name, Pkg: pkg, Members: members, slots: 1}
|
|
t.Values = make([]int64, len(members))
|
|
for i := range members {
|
|
t.Values[i] = int64(i)
|
|
}
|
|
return t
|
|
}
|
|
|
|
func (t *Type) String() string {
|
|
return t.Name
|
|
}
|
|
|
|
func (t *Type) Qualified() string {
|
|
if t.Pkg == "" {
|
|
return t.Local
|
|
}
|
|
return t.Pkg + "." + t.Local
|
|
}
|
|
|
|
// Slots is the number of Value slots; a struct needs Layout first.
|
|
func (t *Type) Slots() int {
|
|
if t.slots < 0 {
|
|
return 1
|
|
}
|
|
return t.slots
|
|
}
|
|
|
|
// Layout assigns field slots and lanes; false means a recursive struct.
|
|
func (t *Type) Layout() bool {
|
|
switch t.slots {
|
|
case slotsBusy:
|
|
return false
|
|
case slotsUnknown:
|
|
default:
|
|
return true
|
|
}
|
|
t.slots = slotsBusy
|
|
n := 0
|
|
for _, f := range t.Fields {
|
|
if f.Type.Kind == KStruct && !f.Type.Layout() {
|
|
t.slots = 1
|
|
return false
|
|
}
|
|
f.Slot, f.Lane = n, -1
|
|
n += f.Type.Slots()
|
|
}
|
|
t.slots = n
|
|
return true
|
|
}
|
|
|
|
func (t *Type) Field(name string) *Field {
|
|
for _, f := range t.Fields {
|
|
if f.Name == name {
|
|
return f
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *Type) Member(name string) (int64, bool) {
|
|
for i, m := range t.Members {
|
|
if m == name {
|
|
return t.Values[i], true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
func (t *Type) IsUntyped() bool {
|
|
return t.Kind == KUntypedInt || t.Kind == KUntypedFloat
|
|
}
|
|
|
|
func (t *Type) IsNumeric() bool {
|
|
switch t.Kind {
|
|
case KInt, KFloat, KUntypedInt, KUntypedFloat:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t *Type) IsInteger() bool {
|
|
return t.Kind == KInt || t.Kind == KUntypedInt
|
|
}
|
|
|
|
func (t *Type) IsVector() bool {
|
|
switch t.Kind {
|
|
case KVec2, KVec3, KColor, KQuat, KRect:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t *Type) IsRef() bool {
|
|
switch t.Kind {
|
|
case KNodeRef, KAssetRef, KComponentRef, KCoroutine, KConnection, KScriptRef, KHandle:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (t *Type) IsHeap() bool {
|
|
return t.Kind == KString || t.Kind == KSlice
|
|
}
|
|
|
|
func (t *Type) Ordered() bool {
|
|
return t.IsNumeric() || t.Kind == KString
|
|
}
|
|
|
|
func (t *Type) Comparable() bool {
|
|
switch t.Kind {
|
|
case KSlice, KVoid, KInvalid, KCurve:
|
|
return false
|
|
case KQuat, KTransform2D, KTransform3D:
|
|
return false
|
|
case KStruct:
|
|
for _, f := range t.Fields {
|
|
if !f.Type.Comparable() {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (t *Type) HeapSlots(base int, fn func(int)) {
|
|
switch t.Kind {
|
|
case KString, KSlice:
|
|
fn(base)
|
|
case KStruct, KTransform2D, KTransform3D:
|
|
for _, f := range t.Fields {
|
|
f.Type.HeapSlots(base+f.Slot, fn)
|
|
}
|
|
}
|
|
}
|
|
|
|
func Default(t *Type) *Type {
|
|
switch t.Kind {
|
|
case KUntypedInt:
|
|
return Int
|
|
case KUntypedFloat:
|
|
return Float
|
|
}
|
|
return t
|
|
}
|
|
|
|
func Assignable(from, to *Type) bool {
|
|
switch {
|
|
case from == Invalid || to == Invalid:
|
|
// Invalid is already reported; do not cascade.
|
|
return true
|
|
case from == to:
|
|
return true
|
|
case from == Nil:
|
|
return to.IsRef()
|
|
case from == UntypedInt:
|
|
return to == Int || to == Float
|
|
case from == UntypedFloat:
|
|
return to == Float
|
|
}
|
|
return false
|
|
}
|
|
|
|
var assetKinds = map[string]string{"Texture": "texture", "Scene": "scene", "Prefab": "prefab", "Mesh": "mesh"}
|
|
|
|
func AssetKind(name string) (string, bool) {
|
|
k, ok := assetKinds[name]
|
|
return k, ok
|
|
}
|
|
|
|
// Interner deduplicates derived types so Assignable can compare by pointer.
|
|
type Interner struct {
|
|
slices map[*Type]*Type
|
|
assets map[string]*Type
|
|
comps map[*abi.Component]*Type
|
|
enums map[string]*Type
|
|
scripts map[string]*Type
|
|
structs map[string]*Type
|
|
handles map[string]*Type
|
|
}
|
|
|
|
func NewInterner() *Interner {
|
|
return &Interner{
|
|
slices: map[*Type]*Type{},
|
|
assets: map[string]*Type{},
|
|
comps: map[*abi.Component]*Type{},
|
|
enums: map[string]*Type{},
|
|
scripts: map[string]*Type{},
|
|
structs: map[string]*Type{},
|
|
handles: map[string]*Type{},
|
|
}
|
|
}
|
|
|
|
func (u *Interner) Handle(d abi.HandleDef) *Type {
|
|
if t, ok := u.handles[d.Name]; ok {
|
|
return t
|
|
}
|
|
i := strings.LastIndexByte(d.Name, '.')
|
|
mod, local := d.Name[:max(i, 0)], d.Name[i+1:]
|
|
t := &Type{Kind: KHandle, Name: shortPkg(mod) + "." + local, Local: local, Pkg: mod, Handle: d.Name, Await: d.Await, Host: true, slots: 1}
|
|
u.handles[d.Name] = t
|
|
return t
|
|
}
|
|
|
|
func (u *Interner) ScriptRef(full string) *Type {
|
|
if t, ok := u.scripts[full]; ok {
|
|
return t
|
|
}
|
|
short := full[strings.LastIndexByte(full, '.')+1:]
|
|
t := &Type{Kind: KScriptRef, Name: "ScriptRef<" + short + ">", Local: "ScriptRef", Script: full, slots: 1}
|
|
u.scripts[full] = t
|
|
return t
|
|
}
|
|
|
|
func (u *Interner) SliceOf(e *Type) *Type {
|
|
if t, ok := u.slices[e]; ok {
|
|
return t
|
|
}
|
|
t := &Type{Kind: KSlice, Name: "[]" + e.Name, Local: "[]" + e.Name, Elem: e, slots: 1}
|
|
u.slices[e] = t
|
|
return t
|
|
}
|
|
|
|
func (u *Interner) AssetRef(kind string) *Type {
|
|
if t, ok := u.assets[kind]; ok {
|
|
return t
|
|
}
|
|
local := "AssetRef"
|
|
var name string
|
|
switch {
|
|
case strings.HasPrefix(kind, "data:"):
|
|
name = "AssetRef<" + kind[len("data:"):] + ">"
|
|
case strings.HasPrefix(kind, "table:"):
|
|
local = "TableRef"
|
|
name = "TableRef<" + kind[len("table:"):] + ">"
|
|
default:
|
|
display := kind
|
|
for k, v := range assetKinds {
|
|
if v == kind {
|
|
display = k
|
|
}
|
|
}
|
|
name = "AssetRef<" + display + ">"
|
|
}
|
|
t := &Type{Kind: KAssetRef, Name: name, Local: local, Asset: kind, slots: 1}
|
|
u.assets[kind] = t
|
|
return t
|
|
}
|
|
|
|
func (u *Interner) ComponentRef(c *abi.Component) *Type {
|
|
if t, ok := u.comps[c]; ok {
|
|
return t
|
|
}
|
|
t := &Type{Kind: KComponentRef, Name: "ComponentRef<" + c.Short + ">", Local: "ComponentRef", Component: c, slots: 1}
|
|
u.comps[c] = t
|
|
return t
|
|
}
|
|
|
|
func (u *Interner) HostEnum(e *abi.EnumDef) *Type {
|
|
q := e.Qualified()
|
|
if t, ok := u.enums[q]; ok {
|
|
return t
|
|
}
|
|
t := &Type{Kind: KEnum, Name: shortPkg(e.Module) + "." + e.Name, Local: e.Name, Pkg: e.Module, Host: true, slots: 1}
|
|
for _, m := range e.Members {
|
|
t.Members = append(t.Members, m.Name)
|
|
t.Values = append(t.Values, m.Value)
|
|
}
|
|
u.enums[q] = t
|
|
return t
|
|
}
|
|
|
|
func (u *Interner) HostStruct(d *abi.StructDef, s abi.Surface) *Type {
|
|
q := d.Qualified()
|
|
if t, ok := u.structs[q]; ok {
|
|
return t
|
|
}
|
|
t := &Type{Kind: KStruct, Name: shortPkg(d.Module) + "." + d.Name, Local: d.Name, Pkg: d.Module, Host: true, slots: slotsUnknown}
|
|
u.structs[q] = t
|
|
for _, f := range d.Fields {
|
|
t.Fields = append(t.Fields, &Field{Name: f.Name, Type: u.FromABI(f.Type, s), Lane: -1})
|
|
}
|
|
t.Layout()
|
|
return t
|
|
}
|
|
|
|
func (u *Interner) FromABI(r abi.TypeRef, s abi.Surface) *Type {
|
|
switch r.Kind {
|
|
case abi.Void:
|
|
return Void
|
|
case abi.Bool:
|
|
return Bool
|
|
case abi.Int:
|
|
return Int
|
|
case abi.Float:
|
|
return Float
|
|
case abi.String:
|
|
return String
|
|
case abi.Vec2:
|
|
return Vec2
|
|
case abi.Vec3:
|
|
return Vec3
|
|
case abi.Color:
|
|
return Color
|
|
case abi.Quat:
|
|
return Quat
|
|
case abi.Rect:
|
|
return Rect
|
|
case abi.Transform2D:
|
|
return Transform2D
|
|
case abi.Transform3D:
|
|
return Transform3D
|
|
case abi.NodeRef:
|
|
return NodeRef
|
|
case abi.Coroutine:
|
|
return Coroutine
|
|
case abi.Connection:
|
|
return Connection
|
|
case abi.Curve:
|
|
return Curve
|
|
case abi.ScriptRef:
|
|
return u.ScriptRef(r.Name)
|
|
case abi.AssetRef:
|
|
return u.AssetRef(r.Name)
|
|
case abi.Handle:
|
|
if s != nil {
|
|
if d, ok := s.Handle(r.Name); ok {
|
|
return u.Handle(d)
|
|
}
|
|
}
|
|
case abi.ComponentRef:
|
|
if s != nil {
|
|
if l := s.Components(r.Name); len(l) == 1 {
|
|
return u.ComponentRef(l[0])
|
|
}
|
|
}
|
|
case abi.Enum:
|
|
if i := strings.LastIndexByte(r.Name, '.'); s != nil && i > 0 {
|
|
if e, ok := s.Enum(r.Name[:i], r.Name[i+1:]); ok {
|
|
return u.HostEnum(e)
|
|
}
|
|
}
|
|
case abi.Struct:
|
|
if i := strings.LastIndexByte(r.Name, '.'); s != nil && i > 0 {
|
|
if d, ok := s.Struct(r.Name[:i], r.Name[i+1:]); ok {
|
|
return u.HostStruct(d, s)
|
|
}
|
|
}
|
|
case abi.Slice:
|
|
if r.Elem != nil {
|
|
return u.SliceOf(u.FromABI(*r.Elem, s))
|
|
}
|
|
}
|
|
return Invalid
|
|
}
|
|
|
|
func ToABI(t *Type) abi.TypeRef {
|
|
switch t.Kind {
|
|
case KBool:
|
|
return abi.T(abi.Bool)
|
|
case KInt, KUntypedInt:
|
|
return abi.T(abi.Int)
|
|
case KFloat, KUntypedFloat:
|
|
return abi.T(abi.Float)
|
|
case KString:
|
|
return abi.T(abi.String)
|
|
case KVec2:
|
|
return abi.T(abi.Vec2)
|
|
case KVec3:
|
|
return abi.T(abi.Vec3)
|
|
case KColor:
|
|
return abi.T(abi.Color)
|
|
case KQuat:
|
|
return abi.T(abi.Quat)
|
|
case KRect:
|
|
return abi.T(abi.Rect)
|
|
case KTransform2D:
|
|
return abi.T(abi.Transform2D)
|
|
case KTransform3D:
|
|
return abi.T(abi.Transform3D)
|
|
case KNodeRef:
|
|
return abi.T(abi.NodeRef)
|
|
case KAssetRef:
|
|
return abi.AssetOf(t.Asset)
|
|
case KComponentRef:
|
|
return abi.ComponentOf(t.Component.Name)
|
|
case KCoroutine:
|
|
return abi.T(abi.Coroutine)
|
|
case KConnection:
|
|
return abi.T(abi.Connection)
|
|
case KScriptRef:
|
|
return abi.ScriptRefOf(t.Script)
|
|
case KCurve:
|
|
return abi.T(abi.Curve)
|
|
case KHandle:
|
|
return abi.HandleOf(t.Handle)
|
|
case KEnum:
|
|
return abi.EnumOf(t.Qualified())
|
|
case KStruct:
|
|
return abi.StructOf(t.Qualified())
|
|
case KSlice:
|
|
return abi.SliceOf(ToABI(t.Elem))
|
|
}
|
|
return abi.T(abi.Void)
|
|
}
|