github.com/nevalang/neva@v0.23.1-0.20240507185603-7696a9bb8dda/internal/compiler/analyzer/interface.go (about) 1 package analyzer 2 3 import ( 4 "errors" 5 6 "github.com/nevalang/neva/internal/compiler" 7 src "github.com/nevalang/neva/internal/compiler/sourcecode" 8 ts "github.com/nevalang/neva/internal/compiler/sourcecode/typesystem" 9 ) 10 11 var ( 12 ErrInterfaceTypeParams = errors.New("Cannot resolve interface type parameters") 13 ErrEmptyInports = errors.New("Interface must have inports") 14 ErrEmptyOutports = errors.New("Interface must have outports") 15 ErrInvalidInports = errors.New("Inports are invalid") 16 ErrInvalidOutports = errors.New("Outports are invalid") 17 ) 18 19 type analyzeInterfaceParams struct { 20 allowEmptyInports bool 21 allowEmptyOutports bool 22 } 23 24 func (a Analyzer) analyzeInterface( 25 iface src.Interface, 26 scope src.Scope, 27 params analyzeInterfaceParams, 28 ) (src.Interface, *compiler.Error) { 29 resolvedParams, err := a.analyzeTypeParams(iface.TypeParams.Params, scope) 30 if err != nil { 31 return src.Interface{}, compiler.Error{ 32 Err: ErrInterfaceTypeParams, 33 Location: &scope.Location, 34 Meta: &iface.Meta, 35 }.Wrap(err) 36 } 37 38 resolvedIO, err := a.analyzeIO(resolvedParams, iface.IO, scope, params) 39 if err != nil { 40 return src.Interface{}, compiler.Error{ 41 Err: ErrInterfaceTypeParams, 42 Location: &scope.Location, 43 Meta: &iface.Meta, 44 }.Wrap(err) 45 } 46 47 typeParams := src.TypeParams{ 48 Params: resolvedParams, 49 Meta: iface.TypeParams.Meta, 50 } 51 52 return src.Interface{ 53 TypeParams: typeParams, 54 IO: resolvedIO, 55 }, nil 56 } 57 58 func (a Analyzer) analyzeIO( 59 typeParams []ts.Param, 60 io src.IO, 61 scope src.Scope, 62 params analyzeInterfaceParams, 63 ) (src.IO, *compiler.Error) { 64 if !params.allowEmptyInports && len(io.In) == 0 { 65 return src.IO{}, &compiler.Error{ 66 Err: ErrEmptyInports, 67 Location: &scope.Location, 68 } 69 } 70 71 if !params.allowEmptyOutports && len(io.Out) == 0 { 72 return src.IO{}, &compiler.Error{ 73 Err: ErrEmptyOutports, 74 Location: &scope.Location, 75 } 76 } 77 78 resolvedIn, err := a.analyzePorts(typeParams, io.In, scope) 79 if err != nil { 80 return src.IO{}, compiler.Error{ 81 Err: ErrInvalidInports, 82 Location: &scope.Location, 83 }.Wrap(err) 84 } 85 86 resolvedOut, err := a.analyzePorts(typeParams, io.Out, scope) 87 if err != nil { 88 return src.IO{}, compiler.Error{ 89 Err: ErrInvalidOutports, 90 Location: &scope.Location, 91 }.Wrap(err) 92 } 93 94 return src.IO{ 95 In: resolvedIn, 96 Out: resolvedOut, 97 }, nil 98 } 99 100 func (a Analyzer) analyzePorts( 101 params []ts.Param, 102 ports map[string]src.Port, 103 scope src.Scope, 104 ) (map[string]src.Port, *compiler.Error) { 105 resolvedPorts := make(map[string]src.Port, len(ports)) 106 for name, port := range ports { 107 resolvedPort, err := a.analyzePort(params, port, scope) 108 if err != nil { 109 return nil, compiler.Error{ 110 Location: &scope.Location, 111 Meta: &port.Meta, 112 }.Wrap(err) 113 } 114 resolvedPorts[name] = resolvedPort 115 } 116 return resolvedPorts, nil 117 } 118 119 func (a Analyzer) analyzePort(params []ts.Param, port src.Port, scope src.Scope) (src.Port, *compiler.Error) { 120 // TODO https://github.com/nevalang/neva/issues/507 121 resolvedDef, err := a.analyzeTypeDef( 122 ts.Def{ 123 Params: params, 124 BodyExpr: &port.TypeExpr, 125 }, 126 scope, analyzeTypeDefParams{allowEmptyBody: false}, 127 ) 128 if err != nil { 129 return src.Port{}, compiler.Error{ 130 Location: &scope.Location, 131 Meta: &port.Meta, 132 }.Wrap(err) 133 } 134 135 return src.Port{ 136 TypeExpr: *resolvedDef.BodyExpr, 137 IsArray: port.IsArray, 138 }, nil 139 }