404 lines
11 KiB
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()
|
|
}
|
|
}
|