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 }