fspl/analyzer/type.go

306 lines
7.9 KiB
Go

package analyzer
import "fmt"
import "git.tebibyte.media/fspl/fspl/errors"
import "git.tebibyte.media/fspl/fspl/entity"
func (this *Tree) analyzeTypedef (
pos errors.Position,
key entity.Key,
acceptIncomplete bool,
) (
*entity.Typedef,
error,
) {
// return a builtin if it exists (search types with zero uuid)
if definition, exists := this.Types[entity.Key { Name: key.Name }]; exists {
return definition, nil
}
// return if exists already
if definition, exists := this.Types[key]; exists {
return definition, nil
}
// check if incomplete
if definition, incomplete := this. incompleteTypes[key]; incomplete {
if acceptIncomplete {
return definition, nil
} else {
return nil, errors.Errorf (
pos, "type %s cannot be used in this context",
key.Name)
}
}
// analyze if it still needs to be analyzed
if definition, exists := this.rawTypes[key]; exists {
// update unit
definition.Unt = key.Unit
// analyze
var err error
definition.Type, err = this.analyzeTypeInternal (
definition.Type, definition, false)
return definition, err
}
// if we couldn't get the type, error
return nil, errors.Errorf(pos, "no type named %s", key.Name)
}
func (this *Tree) analyzeType (
ty entity.Type,
acceptIncomplete bool,
) (
entity.Type,
error,
) {
return this.analyzeTypeInternal (ty, nil, acceptIncomplete)
}
func (this *Tree) analyzeTypeInternal (
ty entity.Type,
root *entity.Typedef,
acceptIncomplete bool,
) (
entity.Type,
error,
) {
// edge cases
if ty == nil { return nil, nil }
var err error
// if we are analyzing a typedef, we will need to update incomplete type
// information as we go in order for recursive type definitions to work
rootKey := func () entity.Key {
return entity.Key {
Unit: this.unit,
Name: root.Name,
}
}
updateIncompleteInfo := func () {
if root != nil { this.incompleteTypes[rootKey()] = root }
}
updatePseudoCompleteInfo := func () {
if root != nil { this.Types[rootKey()] = root }
}
if root != nil { defer delete(this.incompleteTypes, rootKey()) }
// ---------
// determine the access permission for the type
access := entity.AccessPublic; if root != nil {
access = root.Acc
}
switch ty := ty.(type) {
// named type
case *entity.TypeNamed:
ty.Unt = this.unit
ty.Acc = access
updateIncompleteInfo()
// resolve nickname of the unit where the typedef is
if primitive, isPrimitive := primitiveTypes[ty.Name]; isPrimitive {
return primitive, nil
}
unit, err := this.resolveNickname(ty.Position(), ty.UnitNickname)
if err != nil { return nil, err }
// analyze the typedef
def, err := this.analyzeTypedef(ty.Position(), entity.Key {
Unit: unit,
Name: ty.Name,
}, acceptIncomplete)
if err != nil { return nil, err }
ty.Type = def.Type
// check access permissions
err = this.typePrivate(ty.Position(), ty)
if err != nil { return nil, err }
return ty, nil
// pointer type
case *entity.TypePointer:
ty.Unt = this.unit
ty.Acc = access
updateIncompleteInfo()
ty.Referenced, err = this.analyzeType(ty.Referenced, true)
return ty, err
// slice type
case *entity.TypeSlice:
ty.Unt = this.unit
ty.Acc = access
updateIncompleteInfo()
ty.Element, err = this.analyzeType(ty.Element, false)
return ty, err
// array type
case *entity.TypeArray:
ty.Unt = this.unit
ty.Acc = access
updateIncompleteInfo()
if ty.Length < 1 {
return ty, errors.Errorf (
ty.Position(), "array length must be > 0")
}
ty.Element, err = this.analyzeType(ty.Element, false)
return ty, err
// struct type
case *entity.TypeStruct:
ty.Unt = this.unit
ty.Acc = access
ty, err = this.assembleStructMap(ty)
updateIncompleteInfo()
if err != nil { return nil, err }
for name, member := range ty.MemberMap {
ty.MemberMap[name].Ty,
err = this.analyzeType(member.Ty, false)
if err != nil { return nil, err }
}
return ty, nil
// interface type
case *entity.TypeInterface:
ty.Unt = this.unit
ty.Acc = access
ty, err = this.assembleInterfaceMap(ty)
updatePseudoCompleteInfo()
if err != nil { return nil, err }
for name, behavior := range ty.BehaviorMap {
ty.BehaviorMap[name], err = this.analyzeBehavior(behavior)
if err != nil { return nil, err }
}
return ty, nil
// union type
case *entity.TypeUnion:
ty.Unt = this.unit
ty.Acc = access
updateIncompleteInfo()
return this.analyzeTypeUnion(ty)
// integer type
case *entity.TypeInt:
ty.Unt = this.unit
ty.Acc = access
updateIncompleteInfo()
if ty.Width < 1 {
return ty, errors.Errorf (
ty.Position(), "integer width must be > 0")
}
return ty, nil
// floating point and word types
case *entity.TypeFloat:
ty.Unt = this.unit
ty.Acc = access
return ty, nil
case *entity.TypeWord:
ty.Unt = this.unit
ty.Acc = access
return ty, nil
default: panic(fmt.Sprint("BUG: analyzer doesnt know about type ", ty))
}
}
// typePrivate checks if the given type is private.
func (this *Tree) typePrivate (pos errors.Position, ty entity.Type) error {
if ty == nil { return nil }
original := ty
if named, ok := ty.(*entity.TypeNamed); ok {
ty = named.Type
}
if ty.Unit() != this.unit && ty.Access() == entity.AccessPrivate {
return errors.Errorf(pos, "type %v is private", entity.FormatType(original))
}
return nil
}
// typeOpaque checks if the given type is opaque or private.
func (this *Tree) typeOpaque (pos errors.Position, ty entity.Type) error {
if ty == nil { return nil }
err := this.typePrivate(pos, ty)
if err != nil { return err }
original := ty
if named, ok := ty.(*entity.TypeNamed); ok {
ty = named.Type
}
if ty.Unit() != this.unit && ty.Access() == entity.AccessOpaque {
return errors.Errorf(pos, "type %v is opaque", entity.FormatType(original))
}
return nil
}
func (this *Tree) assembleStructMap (ty *entity.TypeStruct) (*entity.TypeStruct, error) {
ty.MemberMap = make(map[string] *entity.Declaration)
ty.MemberOrder = make([]string, len(ty.Members))
for index, member := range ty.Members {
if previous, exists := ty.MemberMap[member.Name]; exists {
return ty, errors.Errorf (
member.Position(), "%s already listed in struct at %v",
member.Name, previous.Position())
}
ty.MemberMap [member.Name] = member
ty.MemberOrder[index] = member.Name
ty.Members [index] = member
}
return ty, nil
}
func (this *Tree) assembleInterfaceMap (ty *entity.TypeInterface) (*entity.TypeInterface, error) {
ty.BehaviorMap = make(map[string] *entity.Signature)
ty.BehaviorOrder = make([]string, len(ty.Behaviors))
for index, method := range ty.Behaviors {
if previous, exists := ty.BehaviorMap[method.Name]; exists {
return ty, errors.Errorf (
method.Position(), "%s already listed in interface at %v",
method.Name, previous.Position())
}
ty.BehaviorMap [method.Name] = method
ty.BehaviorOrder[index] = method.Name
ty.Behaviors [index] = method
}
return ty, nil
}
func (this *Tree) analyzeTypeUnion (ty *entity.TypeUnion) (*entity.TypeUnion, error) {
ty.AllowedMap = make(map[entity.Hash] entity.Type)
ty.AllowedOrder = make([]entity.Hash, len(ty.Allowed))
for index, allowed := range ty.Allowed {
allowed, err := this.analyzeType(allowed, true)
if err != nil { return nil, err }
hash := allowed.Hash()
if previous, exists := ty.AllowedMap[hash]; exists {
return ty, errors.Errorf (
allowed.Position(), "%v already listed in union at %v",
allowed, previous.Position())
}
ty.AllowedMap [hash] = allowed
ty.AllowedOrder[index] = hash
ty.Allowed [index] = allowed
}
return ty, nil
}
func (this *Tree) analyzeBehavior (behavior *entity.Signature) (*entity.Signature, error) {
behavior, err := this.assembleSignatureMap(behavior)
behavior.Unt = this.unit
if err != nil { return behavior, nil }
for name, argument := range behavior.ArgumentMap {
behavior.ArgumentMap[name].Ty, err = this.analyzeType(argument.Ty, false)
if err != nil { return behavior, err }
}
behavior.Return, err = this.analyzeType(behavior.Return, false)
return behavior, err
}