github.com/nevalang/neva@v0.23.1-0.20240507185603-7696a9bb8dda/internal/compiler/analyzer/component_nodes.go (about) 1 package analyzer 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/nevalang/neva/internal/compiler" 8 src "github.com/nevalang/neva/internal/compiler/sourcecode" 9 "github.com/nevalang/neva/internal/compiler/sourcecode/core" 10 "github.com/nevalang/neva/internal/compiler/sourcecode/typesystem" 11 ) 12 13 //nolint:lll 14 var ( 15 ErrAutoPortsArgNonStruct = errors.New("Type argument for component with struct inports directive must be struct") 16 ErrAutoPortsNodeTypeArgsCount = errors.New("Note that uses component with struct inports directive must pass exactly one type argument") 17 ErrAutoPortsTypeParamConstr = errors.New("Component that uses struct inports directive must have type parameter with struct constraint") 18 ErrAutoPortsTypeParamsCount = errors.New("Component that uses struct inports directive must have type parameter with have exactly one type parameter") 19 ErrNormalInportsWithAutoPortsDirective = errors.New("Component that uses struct inports directive must have no defined inports") 20 ErrGuardNotAllowedForNode = errors.New("Guard is not allowed for nodes without 'err' output") 21 ErrGuardNotAllowedForComponent = errors.New("Guard is not allowed for components without 'err' output") 22 ) 23 24 type foundInterface struct { 25 iface src.Interface 26 location src.Location 27 } 28 29 func (a Analyzer) analyzeComponentNodes( 30 componentIface src.Interface, 31 nodes map[string]src.Node, 32 scope src.Scope, 33 ) ( 34 map[string]src.Node, // resolved nodes 35 map[string]foundInterface, // resolved nodes interfaces with locations 36 bool, // one of the nodes has error guard 37 *compiler.Error, // err 38 ) { 39 analyzedNodes := make(map[string]src.Node, len(nodes)) 40 nodesInterfaces := make(map[string]foundInterface, len(nodes)) 41 hasErrGuard := false 42 43 for nodeName, node := range nodes { 44 if node.ErrGuard { 45 hasErrGuard = true 46 } 47 48 analyzedNode, nodeInterface, err := a.analyzeComponentNode( 49 componentIface, 50 node, 51 scope, 52 ) 53 if err != nil { 54 return nil, nil, false, compiler.Error{ 55 Location: &scope.Location, 56 Meta: &node.Meta, 57 }.Wrap(err) 58 } 59 60 nodesInterfaces[nodeName] = nodeInterface 61 analyzedNodes[nodeName] = analyzedNode 62 } 63 64 return analyzedNodes, nodesInterfaces, hasErrGuard, nil 65 } 66 67 //nolint:funlen 68 func (a Analyzer) analyzeComponentNode( 69 componentIface src.Interface, 70 node src.Node, 71 scope src.Scope, 72 ) (src.Node, foundInterface, *compiler.Error) { 73 parentTypeParams := componentIface.TypeParams 74 75 nodeEntity, location, err := scope.Entity(node.EntityRef) 76 if err != nil { 77 return src.Node{}, foundInterface{}, &compiler.Error{ 78 Err: err, 79 Location: &scope.Location, 80 Meta: &node.Meta, 81 } 82 } 83 84 if nodeEntity.Kind != src.ComponentEntity && 85 nodeEntity.Kind != src.InterfaceEntity { 86 return src.Node{}, foundInterface{}, &compiler.Error{ 87 Err: fmt.Errorf("%w: %v", ErrNodeWrongEntity, nodeEntity.Kind), 88 Location: &location, 89 Meta: nodeEntity.Meta(), 90 } 91 } 92 93 bindDirectiveArgs, usesBindDirective := node.Directives[compiler.BindDirective] 94 if usesBindDirective && len(bindDirectiveArgs) != 1 { 95 return src.Node{}, foundInterface{}, &compiler.Error{ 96 Err: ErrBindDirectiveArgs, 97 Location: &location, 98 Meta: nodeEntity.Meta(), 99 } 100 } 101 102 nodeIface, aerr := a.getNodeInterface( 103 nodeEntity, 104 usesBindDirective, 105 location, 106 node, 107 scope, 108 ) 109 if aerr != nil { 110 return src.Node{}, foundInterface{}, aerr 111 } 112 113 if node.ErrGuard { 114 if _, ok := componentIface.IO.Out["err"]; !ok { 115 return src.Node{}, foundInterface{}, &compiler.Error{ 116 Err: ErrGuardNotAllowedForNode, 117 Location: &scope.Location, 118 Meta: &node.Meta, 119 } 120 } 121 if _, ok := nodeIface.IO.Out["err"]; !ok { 122 return src.Node{}, foundInterface{}, &compiler.Error{ 123 Err: ErrGuardNotAllowedForComponent, 124 Location: &scope.Location, 125 Meta: &node.Meta, 126 } 127 } 128 } 129 130 // We need to get resolved frame from parent type parameters 131 // in order to be able to resolve node's args 132 // since they can refer to type parameter of the parent (interface) 133 _, resolvedParentParamsFrame, err := a.resolver.ResolveParams( 134 parentTypeParams.Params, 135 scope, 136 ) 137 if err != nil { 138 return src.Node{}, foundInterface{}, &compiler.Error{ 139 Err: err, 140 Location: &location, 141 Meta: &node.Meta, 142 } 143 } 144 145 // Now when we have frame made of parent type parameters constraints 146 // we can resolve cases like `subnode SubComponent<T>` 147 // where `T` refers to type parameter of the component/interface we're in. 148 resolvedNodeArgs, err := a.resolver.ResolveExprsWithFrame( 149 node.TypeArgs, 150 resolvedParentParamsFrame, 151 scope, 152 ) 153 if err != nil { 154 return src.Node{}, foundInterface{}, &compiler.Error{ 155 Err: err, 156 Location: &location, 157 Meta: &node.Meta, 158 } 159 } 160 161 // default any 162 if len(resolvedNodeArgs) == 0 && len(nodeIface.TypeParams.Params) == 1 { 163 resolvedNodeArgs = []typesystem.Expr{ 164 { 165 Inst: &typesystem.InstExpr{ 166 Ref: core.EntityRef{Name: "any"}, 167 }, 168 }, 169 } 170 } 171 172 // Finally check that every argument is compatible 173 // with corresponding parameter of the node's interface. 174 if err = a.resolver.CheckArgsCompatibility( 175 resolvedNodeArgs, 176 nodeIface.TypeParams.Params, 177 scope, 178 ); err != nil { 179 return src.Node{}, foundInterface{}, &compiler.Error{ 180 Err: err, 181 Location: &scope.Location, 182 Meta: &node.Meta, 183 } 184 } 185 186 if node.Deps == nil { 187 return src.Node{ 188 Directives: node.Directives, 189 EntityRef: node.EntityRef, 190 TypeArgs: resolvedNodeArgs, 191 Meta: node.Meta, 192 ErrGuard: node.ErrGuard, 193 }, foundInterface{ 194 iface: nodeIface, 195 location: location, 196 }, nil 197 } 198 199 resolvedComponentDI := make(map[string]src.Node, len(node.Deps)) 200 for depName, depNode := range node.Deps { 201 resolvedDep, _, err := a.analyzeComponentNode( 202 componentIface, 203 depNode, 204 scope, 205 ) 206 if err != nil { 207 return src.Node{}, foundInterface{}, compiler.Error{ 208 Location: &location, 209 Meta: &depNode.Meta, 210 }.Wrap(err) 211 } 212 resolvedComponentDI[depName] = resolvedDep 213 } 214 215 return src.Node{ 216 Directives: node.Directives, 217 EntityRef: node.EntityRef, 218 TypeArgs: resolvedNodeArgs, 219 Deps: resolvedComponentDI, 220 Meta: node.Meta, 221 ErrGuard: node.ErrGuard, 222 }, foundInterface{ 223 iface: nodeIface, 224 location: location, 225 }, nil 226 } 227 228 func (a Analyzer) getNodeInterface( //nolint:funlen 229 entity src.Entity, 230 hasConfigMsg bool, 231 location src.Location, 232 node src.Node, 233 scope src.Scope, 234 ) (src.Interface, *compiler.Error) { 235 if entity.Kind == src.InterfaceEntity { 236 if hasConfigMsg { 237 return src.Interface{}, &compiler.Error{ 238 Err: ErrInterfaceNodeBindDirective, 239 Location: &location, 240 Meta: entity.Meta(), 241 } 242 } 243 244 if node.Deps != nil { 245 return src.Interface{}, &compiler.Error{ 246 Err: ErrNonComponentNodeWithDI, 247 Location: &location, 248 Meta: entity.Meta(), 249 } 250 } 251 252 return entity.Interface, nil 253 } 254 255 externArgs, hasExternDirective := entity.Component.Directives[compiler.ExternDirective] 256 257 if hasConfigMsg && !hasExternDirective { 258 return src.Interface{}, &compiler.Error{ 259 Err: ErrNormNodeBind, 260 Location: &location, 261 Meta: entity.Meta(), 262 } 263 } 264 265 if len(externArgs) > 1 && len(node.TypeArgs) != 1 { 266 return src.Interface{}, &compiler.Error{ 267 Err: ErrExternOverloadingNodeArgs, 268 Location: &location, 269 Meta: entity.Meta(), 270 } 271 } 272 273 iface := entity.Component.Interface 274 275 _, hasAutoPortsDirective := entity.Component.Directives[compiler.AutoportsDirective] 276 if !hasAutoPortsDirective { 277 return iface, nil 278 } 279 280 // if we here then we have #autoports 281 282 if len(iface.IO.In) != 0 { 283 return src.Interface{}, &compiler.Error{ 284 Err: ErrNormalInportsWithAutoPortsDirective, 285 Location: &location, 286 Meta: entity.Meta(), 287 } 288 } 289 290 if len(iface.TypeParams.Params) != 1 { 291 return src.Interface{}, &compiler.Error{ 292 Err: ErrAutoPortsTypeParamsCount, 293 Location: &location, 294 Meta: entity.Meta(), 295 } 296 } 297 298 resolvedTypeParamConstr, err := a.resolver.ResolveExpr(iface.TypeParams.Params[0].Constr, scope) 299 if err != nil { 300 return src.Interface{}, &compiler.Error{ 301 Err: err, 302 Location: &location, 303 Meta: entity.Meta(), 304 } 305 } 306 307 if resolvedTypeParamConstr.Lit == nil || resolvedTypeParamConstr.Lit.Struct == nil { 308 return src.Interface{}, &compiler.Error{ 309 Err: ErrAutoPortsTypeParamConstr, 310 Location: &location, 311 Meta: entity.Meta(), 312 } 313 } 314 315 if len(node.TypeArgs) != 1 { 316 return src.Interface{}, &compiler.Error{ 317 Err: ErrAutoPortsNodeTypeArgsCount, 318 Location: &location, 319 Meta: entity.Meta(), 320 } 321 } 322 323 resolvedNodeArg, err := a.resolver.ResolveExpr(node.TypeArgs[0], scope) 324 if err != nil { 325 return src.Interface{}, &compiler.Error{ 326 Err: err, 327 Location: &location, 328 Meta: entity.Meta(), 329 } 330 } 331 332 if resolvedNodeArg.Lit == nil || resolvedNodeArg.Lit.Struct == nil { 333 return src.Interface{}, &compiler.Error{ 334 Err: ErrAutoPortsArgNonStruct, 335 Location: &location, 336 Meta: entity.Meta(), 337 } 338 } 339 340 structFields := resolvedNodeArg.Lit.Struct 341 inports := make(map[string]src.Port, len(structFields)) 342 for fieldName, fieldTypeExpr := range structFields { 343 inports[fieldName] = src.Port{ 344 TypeExpr: fieldTypeExpr, 345 } 346 } 347 348 return src.Interface{ 349 TypeParams: iface.TypeParams, 350 IO: src.IO{ 351 In: inports, 352 Out: map[string]src.Port{ 353 "msg": { 354 TypeExpr: resolvedNodeArg, 355 IsArray: false, 356 Meta: iface.IO.Out["v"].Meta, 357 }, 358 }, 359 }, 360 Meta: iface.Meta, 361 }, nil 362 }