package entity import "fmt" import "strings" import "github.com/google/uuid" import "git.tebibyte.media/fspl/fspl/errors" // Type is any type notation. type Type interface { fmt.Stringer // Position returns the position of the type within its source file. Position () errors.Position // Equals reports whether this type is equivalent to another type. Equals (ty Type) bool // Access reports the access permission of the type. Access () Access // Unit reports the unit that the type was defined in. Unit () uuid.UUID // Hash returns a hash of this type that fits within a uint64. Hash () Hash ty () } // TypeNamed refers to a user-defined or built in named type. type TypeNamed struct { // Syntax Pos errors.Position UnitNickname string Name string Type Type // Semantics Acc Access Unt uuid.UUID } func (*TypeNamed) ty(){} func (this *TypeNamed) Position () errors.Position { return this.Pos } func (this *TypeNamed) Access () Access { return this.Acc } func (this *TypeNamed) Unit () uuid.UUID { return this.Unt } func (this *TypeNamed) String () string { if this.UnitNickname == "" { return this.Name } else { return fmt.Sprint(this.UnitNickname, "::", this.Name) } } func (this *TypeNamed) Hash () Hash { return Key { Unit: this.Type.Unit(), Name: this.Name, }.Hash() } func (this *TypeNamed) Equals (ty Type) bool { real, ok := ty.(*TypeNamed) return ok && TypesEqual(real.Type, this.Type) } // TypePointer is a pointer to another type. type TypePointer struct { Pos errors.Position Referenced Type // Semantics Acc Access Unt uuid.UUID } func (*TypePointer) ty(){} func (this *TypePointer) Position () errors.Position { return this.Pos } func (this *TypePointer) Access () Access { return this.Acc } func (this *TypePointer) Unit () uuid.UUID { return this.Unt } func (this *TypePointer) String () string { return fmt.Sprint("*", this.Referenced) } func (this *TypePointer) Hash () Hash { referenced := HashType(this.Referenced) return NewHash(append([]byte("TypePointer:"), referenced[:]...)) } func (this *TypePointer) Equals (ty Type) bool { real, ok := ty.(*TypePointer) return ok && TypesEqual(real.Referenced, this.Referenced) } // TypeSlice is a pointer to several values of a given type stored next to // eachother. Its length is not built into its type and can be changed at // runtime. type TypeSlice struct { Pos errors.Position Element Type // Semantics Acc Access Unt uuid.UUID } func (*TypeSlice) ty(){} func (this *TypeSlice) Position () errors.Position { return this.Pos } func (this *TypeSlice) Access () Access { return this.Acc } func (this *TypeSlice) Unit () uuid.UUID { return this.Unt } func (this *TypeSlice) String () string { return fmt.Sprint("*:", this.Element) } func (this *TypeSlice) Hash () Hash { referenced := HashType(this.Element) return NewHash(append([]byte("TypeSlice:"), referenced[:]...)) } func (this *TypeSlice) Equals (ty Type) bool { real, ok := ty.(*TypeSlice) return ok && TypesEqual(real.Element, this.Element) } // TypeArray is a group of values of a given type stored next to eachother. The // length of an array is fixed and is part of its type. Arrays are passed by // value unless a pointer is used. type TypeArray struct { Pos errors.Position Length int Element Type // Semantics Acc Access Unt uuid.UUID } func (*TypeArray) ty(){} func (this *TypeArray) Position () errors.Position { return this.Pos } func (this *TypeArray) Access () Access { return this.Acc } func (this *TypeArray) Unit () uuid.UUID { return this.Unt } func (this *TypeArray) String () string { return fmt.Sprint(this.Length, ":", this.Element) } func (this *TypeArray) Hash () Hash { referenced := HashType(this.Element) return NewHash(append ( []byte(fmt.Sprintf("TypeArray:%d:", this.Length)), referenced[:]...)) } func (this *TypeArray) Equals (ty Type) bool { real, ok := ty.(*TypeArray) return ok && real.Length == this.Length && TypesEqual(real.Element, this.Element) } // TypeStruct is a composite type that stores keyed values. The positions of the // values within the struct are decided at compile time, based on the order they // are specified in. Structs are passed by value unless a pointer is used. type TypeStruct struct { // Syntax Pos errors.Position Members []*Declaration // Semantics Acc Access Unt uuid.UUID MemberOrder []string MemberMap map[string] *Declaration } func (*TypeStruct) ty(){} func (this *TypeStruct) Position () errors.Position { return this.Pos } func (this *TypeStruct) Access () Access { return this.Acc } func (this *TypeStruct) Unit () uuid.UUID { return this.Unt } func (this *TypeStruct) String () string { out := "(." for _, member := range this.Members { out += fmt.Sprint(" ", member) } return out + ")" } func (this *TypeStruct) Hash () Hash { data := new(strings.Builder) data.WriteString("TypeStruct:") for _, member := range this.Members { memberHash := HashType(member.Type()) data.Write(memberHash[:]) } return NewHash([]byte(data.String())) } func (this *TypeStruct) Equals (ty Type) bool { real, ok := ty.(*TypeStruct) if !ok || len(real.Members) != len(this.Members) { return false } for index, member := range this.Members { if !TypesEqual(member.Type(), real.Members[index].Type()) { return false } } return true } // TypeInterface is a polymorphic pointer that allows any value of any type // through, except it must have at least the methods defined within the // interface. Interfaces are always passed by reference. When assigning a value // to an interface, it will be referenced automatically. When assigning a // pointer to an interface, the pointer's reference will be used instead. type TypeInterface struct { // Syntax Pos errors.Position Behaviors []*Signature // Semantics Acc Access Unt uuid.UUID BehaviorOrder []string BehaviorMap map[string] *Signature } func (*TypeInterface) ty(){} func (this *TypeInterface) Position () errors.Position { return this.Pos } func (this *TypeInterface) Access () Access { return this.Acc } func (this *TypeInterface) Unit () uuid.UUID { return this.Unt } func (this *TypeInterface) String () string { out := "(~" for _, behavior := range this.Behaviors { out += fmt.Sprint(" ", behavior) } return out + ")" } func (this *TypeInterface) Hash () Hash { data := new(strings.Builder) data.WriteString("TypeInterface:") for _, behavior := range this.Behaviors { behaviorHash := HashType(behavior) data.Write(behaviorHash[:]) } return NewHash([]byte(data.String())) } func (this *TypeInterface) Equals (ty Type) bool { real, ok := ty.(*TypeInterface) if !ok || len(real.Behaviors) != len(this.Behaviors) { return false } for index, behavior := range this.Behaviors { if !behavior.Equals(real.Behaviors[index]) { return false } } return true } // TypeUnion is a polymorphic type that can hold any value as long as it is one // of a list of allowed types. It is not a pointer. It holds the hash of the // actual type of the value stored within it, followed by the value. The hash // field is computed using the type's name, and the UUID that it was defined in. // If it is not named, then the hash is computed using the structure of the // type. The value field is always big enough to hold the largest type in the // allowed list. type TypeUnion struct { // Syntax Pos errors.Position Allowed []Type // Semantics Acc Access Unt uuid.UUID } func (*TypeUnion) ty(){} func (this *TypeUnion) Position () errors.Position { return this.Pos } func (this *TypeUnion) Access () Access { return this.Acc } func (this *TypeUnion) Unit () uuid.UUID { return this.Unt } func (this *TypeUnion) String () string { out := "(|" for _, ty := range this.Allowed { out += fmt.Sprint(" ", ty) } return out + ")" } func (this *TypeUnion) Hash () Hash { data := new(strings.Builder) data.WriteString("TypeUnion:") for _, ty := range this.Allowed { typeHash := HashType(ty) data.Write(typeHash[:]) } return NewHash([]byte(data.String())) } func (this *TypeUnion) Equals (ty Type) bool { real, ok := ty.(*TypeUnion) if !ok || len(real.Allowed) != len(this.Allowed) { return false } for index, ty := range this.Allowed { if !ty.Equals(real.Allowed[index]) { return false } } return true } // TypeInt represents any signed or unsigned integer type. type TypeInt struct { Pos errors.Position Width int Signed bool // Semantics Acc Access Unt uuid.UUID } func (*TypeInt) ty(){} func (this *TypeInt) Position () errors.Position { return this.Pos } func (this *TypeInt) Access () Access { return this.Acc } func (this *TypeInt) Unit () uuid.UUID { return this.Unt } func (this *TypeInt) String () string { if this.Signed { return fmt.Sprint("I", this.Width) } else { return fmt.Sprint("U", this.Width) } } func (this *TypeInt) Hash () Hash { return NewHash([]byte(fmt.Sprintf ( "TypeInt:%d:%t", this.Width, this.Signed))) } func (this *TypeInt) Equals (ty Type) bool { real, ok := ty.(*TypeInt) return ok && real.Width == this.Width && real.Signed == this.Signed } // TypeFloat represents any floating point type. type TypeFloat struct { Pos errors.Position Width int // Semantics Acc Access Unt uuid.UUID } func (*TypeFloat) ty(){} func (this *TypeFloat) Position () errors.Position { return this.Pos } func (this *TypeFloat) Access () Access { return this.Acc } func (this *TypeFloat) Unit () uuid.UUID { return this.Unt } func (this *TypeFloat) String () string { return fmt.Sprint("F", this.Width) } func (this *TypeFloat) Hash () Hash { return NewHash([]byte(fmt.Sprintf("TypeFloat:%d", this.Width))) } func (this *TypeFloat) Equals (ty Type) bool { real, ok := ty.(*TypeFloat) return ok && real.Width == this.Width } // TypeWord represents an integer type of unspecified width. The optimal width // is chosen based on the machine word size (32 on 32 bit systems, 64 on 64 bit // systems, etc) type TypeWord struct { Pos errors.Position Signed bool // Semantics Acc Access Unt uuid.UUID } func (*TypeWord) ty(){} func (this *TypeWord) Position () errors.Position { return this.Pos } func (this *TypeWord) Access () Access { return this.Acc } func (this *TypeWord) Unit () uuid.UUID { return this.Unt } func (this *TypeWord) String () string { if this.Signed { return "Int" } else { return "UInt" } } func (this *TypeWord) Hash () Hash { return NewHash([]byte(fmt.Sprintf("TypeWord:%t", this.Signed))) } func (this *TypeWord) Equals (ty Type) bool { real, ok := ty.(*TypeWord) return ok && real.Signed == this.Signed } // TypesEqual checks if two types are equal to eachother, even if one or both // are nil. func TypesEqual (left, right Type) bool { if (left == nil) != (right == nil) { return false } if left == nil { return true } return left.Equals(right) } // FormatType returns a string representing a type. If the type is nil, it // returns "Void". func FormatType (ty Type) string { if ty == nil { return "Void" } else { return ty.String() } } // HashType returns a hash representing a type. If the type is nil, it returns // NewHash([]byte("TypeVoid")) func HashType (ty Type) Hash { if ty == nil { return NewHash([]byte("TypeVoid")) } else { return ty.Hash() } }