fspl/entity/type.go

404 lines
11 KiB
Go

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 returns whether this type is equivalent to another type.
Equals (ty Type) bool
// Access returns the access control mode of the type.
Access () Access
// Unit returns 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
AllowedOrder []Hash
AllowedMap map[Hash] Type
}
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()
}
}