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) } } // check if it is even real definition, exists := this.rawTypes[key] if !exists { return nil, errors.Errorf(pos, "no type named %s", key.Name) } // update unit definition.Unt = key.Unit // analyze var err error definition.Type, err = this.analyzeTypeInternal ( definition.Type, definition, false) // assemble constant map definition, err = this.assembleTypedefConstantMap(definition) if err != nil { return nil, err } // analyze constants fillerValue := 0 for index, constant := range definition.Constants { constant, err := this.analyzeConstantDeclaration(constant, definition.Type) if err != nil { return nil, err } if constant.Value == nil { if !isNumeric(definition.Type) { return nil, errors.Errorf ( pos, "cannot fill in constant values for non-numeric type %v", definition.Type) } constant.Value = &entity.LiteralInt { Pos: pos, Value: fillerValue, Ty: definition.Type, } fillerValue ++ } definition.ConstantMap[constant.Name] = constant definition.ConstantOrder[index] = constant.Name definition.Constants[index] = constant } return definition, err } func (this *Tree) analyzeConstantDeclaration ( constant *entity.ConstantDeclaration, into entity.Type, ) ( *entity.ConstantDeclaration, error, ) { constant.Ty = into // analyze the value, if given if constant.Value != nil { // create new scope context for this constant this.pushScopeContext(constant) this.pushScope(constant) defer this.popScopeContext() defer this.popScope() // analyze value value, err := this.analyzeExpression(into, strict, constant.Value) if err != nil { return nil, err } // only allow values that can be used as constants err = this.isConstant(value) if err != nil { return nil, err } } // TODO after analysis, check if constant return constant, nil } 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 } func (this *Tree) assembleTypedefConstantMap ( typedef *entity.Typedef, ) ( *entity.Typedef, error, ) { typedef.ConstantMap = make(map[string] *entity.ConstantDeclaration) typedef.ConstantOrder = make([]string, len(typedef.Constants)) for index, constant := range typedef.Constants { if previous, exists := typedef.ConstantMap[constant.Name]; exists { return nil, errors.Errorf ( constant.Position(), "%s already defined at %v", constant.Name, previous.Position()) } typedef.ConstantMap [constant.Name] = constant typedef.ConstantOrder[index] = constant.Name typedef.Constants [index] = constant } return typedef, nil }