279 lines
7.1 KiB
Go
279 lines
7.1 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.Unit = 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 ty, err }
|
|
for name, member := range ty.MemberMap {
|
|
ty.MemberMap[name].Ty,
|
|
err = this.analyzeType(member.Ty, false)
|
|
if err != nil { return ty, 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 ty, err }
|
|
for name, behavior := range ty.BehaviorMap {
|
|
ty.BehaviorMap[name], err = this.analyzeBehavior(behavior)
|
|
if err != nil { return ty, err }
|
|
}
|
|
return ty, nil
|
|
|
|
// 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
|
|
}
|
|
|
|
// typeRestricted checks if the given type is restricted or private.
|
|
func (this *Tree) typeRestricted (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.AccessRestricted {
|
|
return errors.Errorf(pos, "type %v is restricted", 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) 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
|
|
}
|