github.com/nevalang/neva@v0.23.1-0.20240507185603-7696a9bb8dda/internal/compiler/analyzer/component_net.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 ts "github.com/nevalang/neva/internal/compiler/sourcecode/typesystem" 11 ) 12 13 var ( 14 ErrUnusedOutports = errors.New("All component's outports are unused") 15 ErrUnusedOutport = errors.New("Unused outport found") 16 ErrUnusedInports = errors.New("All component inports are unused") 17 ErrUnusedInport = errors.New("Unused inport found") 18 ErrLiteralSenderTypeEmpty = errors.New("Literal network sender must contain message value") 19 ErrComplexLiteralSender = errors.New("Literal network sender must have primitive type") 20 ErrIllegalPortlessConnection = errors.New("Connection to a node, with more than one port, must always has a port name") 21 ErrGuardMixedWithExplicitErrConn = errors.New("If node has error guard '?' it's ':err' outport must not be explicitly used in the network") 22 ) 23 24 // analyzeComponentNetwork must be called after analyzeNodes so we sure nodes are resolved. 25 func (a Analyzer) analyzeComponentNetwork( 26 net []src.Connection, 27 compInterface src.Interface, 28 hasGuard bool, 29 nodes map[string]src.Node, 30 nodesIfaces map[string]foundInterface, 31 scope src.Scope, 32 ) ([]src.Connection, *compiler.Error) { 33 // we create it here because there's recursion down there 34 nodesUsage := make(map[string]nodeNetUsage, len(nodes)) 35 36 resolvedNet, err := a.analyzeConnections(net, compInterface, nodes, nodesIfaces, nodesUsage, scope) 37 if err != nil { 38 return nil, compiler.Error{Location: &scope.Location}.Wrap(err) 39 } 40 41 if err := a.checkNetPortsUsage( 42 compInterface, 43 nodesIfaces, 44 hasGuard, 45 scope, 46 nodesUsage, 47 nodes, 48 ); err != nil { 49 return nil, compiler.Error{Location: &scope.Location}.Wrap(err) 50 } 51 52 return resolvedNet, nil 53 } 54 55 // analyzeConnections does two things: 56 // 1. Analyzes every connection and terminates with non-nil error if any of them is invalid. 57 // 2. Updates nodesUsage (we mutate it in-place instead of returning to avoid merging across recursive calls). 58 func (a Analyzer) analyzeConnections( 59 net []src.Connection, 60 compInterface src.Interface, 61 nodes map[string]src.Node, 62 nodesIfaces map[string]foundInterface, 63 nodesUsage map[string]nodeNetUsage, 64 scope src.Scope, 65 ) ([]src.Connection, *compiler.Error) { 66 resolvedNet := make([]src.Connection, 0, len(net)) 67 68 for _, conn := range net { 69 resolvedConn, err := a.analyzeConnection( 70 conn, 71 compInterface, 72 nodes, 73 nodesIfaces, 74 scope, 75 nodesUsage, 76 ) 77 if err != nil { 78 return nil, err 79 } 80 81 resolvedNet = append(resolvedNet, resolvedConn) 82 } 83 84 return resolvedNet, nil 85 } 86 87 func (a Analyzer) analyzeConnection( 88 conn src.Connection, 89 compInterface src.Interface, 90 nodes map[string]src.Node, 91 nodesIfaces map[string]foundInterface, 92 scope src.Scope, 93 nodesUsage map[string]nodeNetUsage, 94 ) (src.Connection, *compiler.Error) { 95 // first handle array bypass connection, they are simple 96 if conn.ArrayBypass != nil { 97 arrBypassConn := conn.ArrayBypass 98 99 senderType, isArray, err := a.getSenderPortAddrType( 100 arrBypassConn.SenderOutport, 101 scope, 102 compInterface, 103 nodes, 104 nodesIfaces, 105 ) 106 if err != nil { 107 return src.Connection{}, compiler.Error{ 108 Location: &scope.Location, 109 Meta: &conn.Normal.SenderSide.Meta, 110 }.Wrap(err) 111 } 112 if !isArray { 113 return src.Connection{}, &compiler.Error{ 114 Err: errors.New("Non-array outport in array-bypass connection"), 115 Location: &scope.Location, 116 Meta: &arrBypassConn.SenderOutport.Meta, 117 } 118 } 119 120 receiverType, isArray, err := a.getReceiverType( 121 arrBypassConn.ReceiverInport, 122 compInterface, 123 nodes, 124 nodesIfaces, 125 scope, 126 ) 127 if err != nil { 128 return src.Connection{}, compiler.Error{ 129 Location: &scope.Location, 130 Meta: &conn.Meta, 131 }.Wrap(err) 132 } 133 if !isArray { 134 return src.Connection{}, &compiler.Error{ 135 Err: errors.New("Non-array outport in array-bypass connection"), 136 Location: &scope.Location, 137 Meta: &arrBypassConn.SenderOutport.Meta, 138 } 139 } 140 141 if err := a.resolver.IsSubtypeOf( 142 senderType, 143 receiverType, 144 scope, 145 ); err != nil { 146 return src.Connection{}, &compiler.Error{ 147 Err: fmt.Errorf( 148 "Incompatible types: %v -> %v: %w", 149 arrBypassConn.SenderOutport, arrBypassConn.ReceiverInport, err, 150 ), 151 Location: &scope.Location, 152 Meta: &conn.Meta, 153 } 154 } 155 156 nodesNetUsage(nodesUsage).AddOutport( 157 arrBypassConn.SenderOutport.Node, 158 arrBypassConn.SenderOutport.Port, 159 ) 160 nodesNetUsage(nodesUsage).AddInport( 161 arrBypassConn.ReceiverInport.Node, 162 arrBypassConn.ReceiverInport.Port, 163 ) 164 165 return conn, nil 166 } 167 168 // now handle normal connections 169 normConn := conn.Normal 170 171 resolvedSender, resolvedSenderType, isSenderArr, err := a.getSenderSideType( 172 normConn.SenderSide, 173 compInterface, 174 nodes, 175 nodesIfaces, 176 scope, 177 ) 178 if err != nil { 179 return src.Connection{}, compiler.Error{ 180 Location: &scope.Location, 181 Meta: &normConn.SenderSide.Meta, 182 }.Wrap(err) 183 } 184 185 if normConn.SenderSide.PortAddr != nil { 186 // make sure only array outports has indexes 187 if !isSenderArr && normConn.SenderSide.PortAddr.Idx != nil { 188 return src.Connection{}, &compiler.Error{ 189 Err: errors.New("Index for non-array port"), 190 Meta: &normConn.SenderSide.PortAddr.Meta, 191 Location: &scope.Location, 192 } 193 } 194 195 // make sure array outports always has indexes (it's not arr-bypass) 196 if isSenderArr && normConn.SenderSide.PortAddr.Idx == nil { 197 return src.Connection{}, &compiler.Error{ 198 Err: errors.New("Index needed for array outport"), 199 Meta: &normConn.SenderSide.PortAddr.Meta, 200 Location: &scope.Location, 201 } 202 } 203 204 // mark node's outport as used since sender isn't const ref 205 nodesNetUsage(nodesUsage).AddOutport( 206 normConn.SenderSide.PortAddr.Node, 207 normConn.SenderSide.PortAddr.Port, 208 ) 209 } 210 211 if len(normConn.SenderSide.Selectors) > 0 { 212 lastFieldType, err := a.getStructFieldTypeByPath( 213 resolvedSenderType, 214 normConn.SenderSide.Selectors, 215 scope, 216 ) 217 if err != nil { 218 return src.Connection{}, &compiler.Error{ 219 Err: err, 220 Location: &scope.Location, 221 Meta: &conn.Meta, 222 } 223 } 224 resolvedSenderType = lastFieldType 225 } 226 227 if len(normConn.ReceiverSide.DeferredConnections) == 0 && len(normConn.ReceiverSide.Receivers) == 0 { 228 return src.Connection{}, &compiler.Error{ 229 Err: errors.New( 230 "Connection's receiver side cannot be empty, it must either have deferred connection or receivers", 231 ), 232 Location: &scope.Location, 233 Meta: &conn.Meta, 234 } 235 } 236 237 resolvedDefConns := make([]src.Connection, 0, len(normConn.ReceiverSide.DeferredConnections)) 238 if len(normConn.ReceiverSide.DeferredConnections) != 0 { 239 // note that we call analyzeConnections instead of analyzeComponentNetwork 240 // because we only need to analyze connections and update nodesUsage 241 // analyzeComponentNetwork OTOH will also validate nodesUsage by itself 242 var err *compiler.Error 243 resolvedDefConns, err = a.analyzeConnections( // indirect recursion 244 normConn.ReceiverSide.DeferredConnections, 245 compInterface, 246 nodes, 247 nodesIfaces, 248 nodesUsage, 249 scope, 250 ) 251 if err != nil { 252 return src.Connection{}, err 253 } 254 // receiver side can contain both deferred connections and receivers so we don't return yet 255 } 256 257 // TODO handle chain connection 258 259 for _, receiver := range normConn.ReceiverSide.Receivers { 260 inportTypeExpr, isReceiverArr, err := a.getReceiverType( 261 receiver.PortAddr, 262 compInterface, 263 nodes, 264 nodesIfaces, 265 scope, 266 ) 267 if err != nil { 268 return src.Connection{}, compiler.Error{ 269 Location: &scope.Location, 270 Meta: &receiver.Meta, 271 }.Wrap(err) 272 } 273 274 // make sure only array outports has indexes 275 if !isReceiverArr && receiver.PortAddr.Idx != nil { 276 return src.Connection{}, &compiler.Error{ 277 Err: errors.New("Index for non-array port"), 278 Meta: &receiver.PortAddr.Meta, 279 Location: &scope.Location, 280 } 281 } 282 283 // make sure array inports always has indexes (it's not arr-bypass) 284 if isReceiverArr && receiver.PortAddr.Idx == nil { 285 return src.Connection{}, &compiler.Error{ 286 Err: errors.New("Index needed for array inport"), 287 Meta: &receiver.PortAddr.Meta, 288 Location: &scope.Location, 289 } 290 } 291 292 if err := a.resolver.IsSubtypeOf(resolvedSenderType, inportTypeExpr, scope); err != nil { 293 return src.Connection{}, &compiler.Error{ 294 Err: fmt.Errorf( 295 "Incompatible types: %v -> %v: %w", 296 normConn.SenderSide, receiver, err, 297 ), 298 Location: &scope.Location, 299 Meta: &conn.Meta, 300 } 301 } 302 303 nodesNetUsage(nodesUsage).AddInport( 304 receiver.PortAddr.Node, 305 receiver.PortAddr.Port, 306 ) 307 } 308 309 return src.Connection{ 310 Normal: &src.NormalConnection{ 311 SenderSide: resolvedSender, 312 ReceiverSide: src.ConnectionReceiverSide{ 313 DeferredConnections: resolvedDefConns, 314 Receivers: normConn.ReceiverSide.Receivers, 315 }, 316 }, 317 Meta: conn.Meta, 318 }, nil 319 } 320 321 // nodeNetUsage shows which ports was used by the network 322 type nodeNetUsage struct { 323 In, Out map[string]struct{} 324 } 325 326 // checkNetPortsUsage ensures that: 327 // Every component's inport and outport is used; 328 // Every sub-node's inport is used; 329 // For every sub-node's there's at least one used outport. 330 func (Analyzer) checkNetPortsUsage( 331 compInterface src.Interface, 332 nodesIfaces map[string]foundInterface, 333 hasGuard bool, 334 scope src.Scope, 335 nodesUsage map[string]nodeNetUsage, 336 nodes map[string]src.Node, 337 ) *compiler.Error { 338 inportsUsage, ok := nodesUsage["in"] 339 if !ok { 340 return &compiler.Error{ 341 Err: ErrUnusedInports, 342 Location: &scope.Location, 343 Meta: &compInterface.Meta, 344 } 345 } 346 347 for inportName := range compInterface.IO.In { 348 if _, ok := inportsUsage.Out[inportName]; !ok { // note that self inports are outports for the network 349 return &compiler.Error{ 350 Err: fmt.Errorf("%w '%v'", ErrUnusedInport, inportName), 351 Location: &scope.Location, 352 } 353 } 354 } 355 356 outportsUsage, ok := nodesUsage["out"] 357 if !ok { 358 return &compiler.Error{ 359 Err: ErrUnusedOutports, 360 Location: &scope.Location, 361 Meta: &compInterface.Meta, 362 } 363 } 364 365 for outportName := range compInterface.IO.Out { 366 // note that self outports are inports for the network 367 if _, ok := outportsUsage.In[outportName]; ok { 368 continue 369 } 370 371 if outportName == "err" && hasGuard { 372 continue 373 } 374 375 return &compiler.Error{ 376 Err: fmt.Errorf("%w '%v'", ErrUnusedOutport, outportName), 377 Location: &scope.Location, 378 } 379 } 380 381 for nodeName, nodeIface := range nodesIfaces { 382 nodeUsage, ok := nodesUsage[nodeName] 383 if !ok { 384 return &compiler.Error{ 385 Err: fmt.Errorf("%w: %v", ErrUnusedNode, nodeName), 386 Location: &scope.Location, 387 } 388 } 389 390 for inportName := range nodeIface.iface.IO.In { 391 if _, ok := nodeUsage.In[inportName]; !ok { 392 // maybe it's portless connection 393 if _, ok := nodeUsage.In[""]; ok && len(nodeIface.iface.IO.In) == 1 { 394 continue 395 } 396 397 meta := nodeIface.iface.IO.In[inportName].Meta 398 399 return &compiler.Error{ 400 Err: fmt.Errorf( 401 "%w: %v:%v", 402 ErrUnusedNodeInport, 403 nodeName, 404 inportName, 405 ), 406 Location: &scope.Location, 407 Meta: &meta, 408 } 409 } 410 } 411 412 if len(nodeIface.iface.IO.Out) == 0 { // e.g. std/builtin.Del 413 continue 414 } 415 416 atLeastOneOutportIsUsed := false 417 for outportName := range nodeIface.iface.IO.Out { 418 if _, ok := nodeUsage.Out[outportName]; !ok { 419 continue 420 } 421 422 atLeastOneOutportIsUsed = true 423 424 if outportName != "err" { 425 continue 426 } 427 428 meta := nodes[nodeName].Meta 429 430 if nodes[nodeName].ErrGuard { 431 return &compiler.Error{ 432 Err: fmt.Errorf( 433 "%w: %v", 434 ErrGuardMixedWithExplicitErrConn, 435 nodeName, 436 ), 437 Location: &scope.Location, 438 Meta: &meta, 439 } 440 } 441 442 } 443 444 if !atLeastOneOutportIsUsed { 445 // maybe it's portless connection 446 if _, ok := nodeUsage.Out[""]; ok && len(nodeIface.iface.IO.Out) == 1 { 447 continue 448 } 449 450 return &compiler.Error{ 451 Err: fmt.Errorf("%w: %v", ErrUnusedNodeOutports, nodeName), 452 Location: &scope.Location, 453 Meta: &nodeIface.iface.Meta, 454 } 455 } 456 } 457 458 return nil 459 } 460 461 func (a Analyzer) getReceiverType( 462 receiverSide src.PortAddr, 463 iface src.Interface, 464 nodes map[string]src.Node, 465 nodesIfaces map[string]foundInterface, 466 scope src.Scope, 467 ) (ts.Expr, bool, *compiler.Error) { 468 if receiverSide.Node == "in" { 469 return ts.Expr{}, false, &compiler.Error{ 470 Err: ErrWriteSelfIn, 471 Location: &scope.Location, 472 Meta: &receiverSide.Meta, 473 } 474 } 475 476 if receiverSide.Node == "out" { 477 outports := iface.IO.Out 478 479 outport, ok := outports[receiverSide.Port] 480 if !ok { 481 return ts.Expr{}, false, &compiler.Error{ 482 Err: fmt.Errorf("%w: %v", ErrOutportNotFound, receiverSide.Port), 483 Location: &scope.Location, 484 Meta: &receiverSide.Meta, 485 } 486 } 487 488 resolvedOutportType, err := a.resolver.ResolveExprWithFrame( 489 outport.TypeExpr, 490 iface.TypeParams.ToFrame(), 491 scope, 492 ) 493 if err != nil { 494 return ts.Expr{}, false, &compiler.Error{ 495 Err: err, 496 Location: &scope.Location, 497 Meta: &receiverSide.Meta, 498 } 499 } 500 501 return resolvedOutportType, outport.IsArray, nil 502 } 503 504 nodeInportType, isArray, err := a.getNodeInportType(receiverSide, nodes, nodesIfaces, scope) 505 if err != nil { 506 return ts.Expr{}, false, compiler.Error{ 507 Location: &scope.Location, 508 Meta: &receiverSide.Meta, 509 }.Wrap(err) 510 } 511 512 return nodeInportType, isArray, nil 513 } 514 515 func (a Analyzer) getNodeInportType( 516 portAddr src.PortAddr, 517 nodes map[string]src.Node, 518 nodesIfaces map[string]foundInterface, 519 scope src.Scope, 520 ) (ts.Expr, bool, *compiler.Error) { 521 node, ok := nodes[portAddr.Node] 522 if !ok { 523 return ts.Expr{}, false, &compiler.Error{ 524 Err: fmt.Errorf("Node not found '%v'", portAddr.Node), 525 Location: &scope.Location, 526 Meta: &portAddr.Meta, 527 } 528 } 529 530 nodeIface, ok := nodesIfaces[portAddr.Node] 531 if !ok { 532 return ts.Expr{}, false, &compiler.Error{ 533 Err: fmt.Errorf("%w '%v'", ErrNodeNotFound, portAddr.Node), 534 Location: &scope.Location, 535 Meta: &portAddr.Meta, 536 } 537 } 538 539 // TODO optimize: 540 // we can resolve every node's interface just once 541 // before processing the network 542 resolvedInportType, isArray, aerr := a.getResolvedPortType( 543 nodeIface.iface.IO.In, 544 nodeIface.iface.TypeParams.Params, 545 portAddr, 546 node, 547 scope.WithLocation(nodeIface.location), 548 ) 549 if aerr != nil { 550 return ts.Expr{}, false, compiler.Error{ 551 Location: &scope.Location, 552 Meta: &portAddr.Meta, 553 }.Wrap(aerr) 554 } 555 556 return resolvedInportType, isArray, nil 557 } 558 559 // getResolvedPortType returns port's type and isArray bool 560 func (a Analyzer) getResolvedPortType( 561 ports map[string]src.Port, 562 nodeIfaceParams []ts.Param, 563 portAddr src.PortAddr, 564 node src.Node, 565 scope src.Scope, 566 ) (ts.Expr, bool, *compiler.Error) { 567 if portAddr.Port == "" { 568 if len(ports) > 1 { 569 return ts.Expr{}, false, &compiler.Error{ 570 Err: ErrIllegalPortlessConnection, 571 Location: &scope.Location, 572 Meta: &portAddr.Meta, 573 } 574 } 575 576 for name := range ports { 577 portAddr.Port = name 578 break 579 } 580 } 581 582 port, ok := ports[portAddr.Port] 583 if !ok { 584 return ts.Expr{}, false, &compiler.Error{ 585 Err: fmt.Errorf( 586 "Port not found `%v`", 587 portAddr, 588 ), 589 Location: &scope.Location, 590 Meta: &portAddr.Meta, 591 } 592 } 593 594 // we don't resolve node's args assuming they resolved already 595 596 // create frame `nodeParam:resolvedArg` to get resolved port type 597 frame := make(map[string]ts.Def, len(nodeIfaceParams)) 598 for i, param := range nodeIfaceParams { 599 arg := node.TypeArgs[i] 600 frame[param.Name] = ts.Def{ 601 BodyExpr: &arg, 602 Meta: arg.Meta, 603 } 604 } 605 606 resolvedPortType, err := a.resolver.ResolveExprWithFrame( 607 port.TypeExpr, 608 frame, 609 scope, 610 ) 611 if err != nil { 612 return ts.Expr{}, false, &compiler.Error{ 613 Err: err, 614 Location: &scope.Location, 615 Meta: &port.Meta, 616 } 617 } 618 619 return resolvedPortType, port.IsArray, nil 620 } 621 622 func (a Analyzer) getSenderSideType( 623 senderSide src.ConnectionSenderSide, 624 iface src.Interface, 625 nodes map[string]src.Node, 626 nodesIfaces map[string]foundInterface, 627 scope src.Scope, 628 ) (src.ConnectionSenderSide, ts.Expr, bool, *compiler.Error) { 629 if senderSide.PortAddr == nil && senderSide.Const == nil { 630 return src.ConnectionSenderSide{}, ts.Expr{}, false, &compiler.Error{ 631 Err: ErrSenderIsEmpty, 632 Location: &scope.Location, 633 Meta: &senderSide.Meta, 634 } 635 } 636 637 if senderSide.Const != nil { 638 resolvedConst, resolvedExpr, err := a.getResolvedSenderConstType(*senderSide.Const, scope) 639 if err != nil { 640 return src.ConnectionSenderSide{}, ts.Expr{}, false, err 641 } 642 643 return src.ConnectionSenderSide{ 644 Const: &resolvedConst, 645 Selectors: senderSide.Selectors, 646 Meta: senderSide.Meta, 647 }, resolvedExpr, false, nil 648 } 649 650 resolvedExpr, isArr, err := a.getSenderPortAddrType( 651 *senderSide.PortAddr, 652 scope, 653 iface, 654 nodes, 655 nodesIfaces, 656 ) 657 if err != nil { 658 return src.ConnectionSenderSide{}, ts.Expr{}, false, err 659 } 660 661 return src.ConnectionSenderSide{ 662 PortAddr: senderSide.PortAddr, 663 Selectors: senderSide.Selectors, 664 Meta: senderSide.Meta, 665 }, resolvedExpr, isArr, nil 666 } 667 668 // getSenderPortAddrType returns port's type and isArray bool 669 func (a Analyzer) getSenderPortAddrType( 670 senderSidePortAddr src.PortAddr, 671 scope src.Scope, 672 iface src.Interface, 673 nodes map[string]src.Node, 674 nodesIfaces map[string]foundInterface, 675 ) (ts.Expr, bool, *compiler.Error) { 676 if senderSidePortAddr.Node == "out" { 677 return ts.Expr{}, false, &compiler.Error{ 678 Err: ErrReadSelfOut, 679 Location: &scope.Location, 680 Meta: &senderSidePortAddr.Meta, 681 } 682 } 683 684 if senderSidePortAddr.Node == "in" { 685 inports := iface.IO.In 686 687 inport, ok := inports[senderSidePortAddr.Port] 688 if !ok { 689 return ts.Expr{}, false, &compiler.Error{ 690 Err: fmt.Errorf("%w: %v", ErrInportNotFound, senderSidePortAddr.Port), 691 Location: &scope.Location, 692 Meta: &senderSidePortAddr.Meta, 693 } 694 } 695 696 resolvedInportType, err := a.resolver.ResolveExprWithFrame( 697 inport.TypeExpr, 698 iface.TypeParams.ToFrame(), 699 scope, 700 ) 701 if err != nil { 702 return ts.Expr{}, false, &compiler.Error{ 703 Err: err, 704 Location: &scope.Location, 705 Meta: &senderSidePortAddr.Meta, 706 } 707 } 708 709 return resolvedInportType, inport.IsArray, nil 710 } 711 712 return a.getNodeOutportType( 713 senderSidePortAddr, nodes, nodesIfaces, scope, 714 ) 715 } 716 717 func (a Analyzer) getResolvedSenderConstType( 718 constSender src.Const, 719 scope src.Scope, 720 ) (src.Const, ts.Expr, *compiler.Error) { 721 if constSender.Ref != nil { 722 expr, err := a.getResolvedConstTypeByRef(*constSender.Ref, scope) 723 if err != nil { 724 return src.Const{}, ts.Expr{}, compiler.Error{ 725 Location: &scope.Location, 726 Meta: &constSender.Ref.Meta, 727 }.Wrap(err) 728 } 729 return constSender, expr, nil 730 } 731 732 if constSender.Message == nil { 733 return src.Const{}, ts.Expr{}, &compiler.Error{ 734 Err: ErrLiteralSenderTypeEmpty, 735 Location: &scope.Location, 736 Meta: &constSender.Meta, 737 } 738 } 739 740 resolvedExpr, err := a.resolver.ResolveExpr( 741 constSender.Message.TypeExpr, 742 scope, 743 ) 744 if err != nil { 745 return src.Const{}, ts.Expr{}, &compiler.Error{ 746 Err: err, 747 Location: &scope.Location, 748 Meta: &constSender.Message.Meta, 749 } 750 } 751 752 if err := a.validateLiteralSender(resolvedExpr); err != nil { 753 return src.Const{}, ts.Expr{}, &compiler.Error{ 754 Err: err, 755 Location: &scope.Location, 756 Meta: &constSender.Message.Meta, 757 } 758 } 759 760 return src.Const{ 761 Message: &src.Message{ 762 TypeExpr: resolvedExpr, 763 Bool: constSender.Message.Bool, 764 Int: constSender.Message.Int, 765 Float: constSender.Message.Float, 766 Str: constSender.Message.Str, 767 List: constSender.Message.List, 768 MapOrStruct: constSender.Message.MapOrStruct, 769 Enum: constSender.Message.Enum, 770 Meta: constSender.Message.Meta, 771 }, 772 Meta: constSender.Meta, 773 }, resolvedExpr, nil 774 } 775 776 func (a Analyzer) validateLiteralSender(resolvedExpr ts.Expr) error { 777 if resolvedExpr.Inst != nil { 778 switch resolvedExpr.Inst.Ref.String() { 779 case "bool", "int", "float", "string": 780 return nil 781 } 782 return ErrComplexLiteralSender 783 } 784 785 if resolvedExpr.Lit == nil || 786 resolvedExpr.Lit.Enum == nil { 787 return ErrComplexLiteralSender 788 } 789 790 return nil 791 } 792 793 // getNodeOutportType returns port's type and isArray bool 794 func (a Analyzer) getNodeOutportType( 795 portAddr src.PortAddr, 796 nodes map[string]src.Node, 797 nodesIfaces map[string]foundInterface, 798 scope src.Scope, 799 ) (ts.Expr, bool, *compiler.Error) { 800 node, ok := nodes[portAddr.Node] 801 if !ok { 802 return ts.Expr{}, false, &compiler.Error{ 803 Err: fmt.Errorf("%w: %v", ErrNodeNotFound, portAddr.Node), 804 Location: &scope.Location, 805 Meta: &portAddr.Meta, 806 } 807 } 808 809 nodeIface, ok := nodesIfaces[portAddr.Node] 810 if !ok { 811 return ts.Expr{}, false, &compiler.Error{ 812 Err: fmt.Errorf("%w: %v", ErrNodeNotFound, portAddr.Node), 813 Location: &scope.Location, 814 Meta: &portAddr.Meta, 815 } 816 } 817 818 return a.getResolvedPortType( 819 nodeIface.iface.IO.Out, 820 nodeIface.iface.TypeParams.Params, 821 portAddr, 822 node, 823 scope.WithLocation(nodeIface.location), 824 ) 825 } 826 827 func (a Analyzer) getResolvedConstTypeByRef(ref core.EntityRef, scope src.Scope) (ts.Expr, *compiler.Error) { 828 entity, location, err := scope.Entity(ref) 829 if err != nil { 830 return ts.Expr{}, &compiler.Error{ 831 Err: err, 832 Location: &scope.Location, 833 Meta: &ref.Meta, 834 } 835 } 836 837 if entity.Kind != src.ConstEntity { 838 return ts.Expr{}, &compiler.Error{ 839 Err: fmt.Errorf("%w: %v", errors.New("Entity found but is not constant"), entity.Kind), 840 Location: &location, 841 Meta: entity.Meta(), 842 } 843 } 844 845 if entity.Const.Ref != nil { 846 expr, err := a.getResolvedConstTypeByRef(*entity.Const.Ref, scope) 847 if err != nil { 848 return ts.Expr{}, compiler.Error{ 849 Location: &location, 850 Meta: &entity.Const.Meta, 851 }.Wrap(err) 852 } 853 return expr, nil 854 } 855 856 resolvedExpr, err := a.resolver.ResolveExpr(entity.Const.Message.TypeExpr, scope) 857 if err != nil { 858 return ts.Expr{}, &compiler.Error{ 859 Err: err, 860 Location: &scope.Location, 861 Meta: &entity.Const.Message.Meta, 862 } 863 } 864 865 return resolvedExpr, nil 866 } 867 868 func (a Analyzer) getStructFieldTypeByPath( 869 senderType ts.Expr, 870 path []string, 871 scope src.Scope, 872 ) (ts.Expr, error) { 873 if len(path) == 0 { 874 return senderType, nil 875 } 876 877 if senderType.Lit == nil || senderType.Lit.Struct == nil { 878 return ts.Expr{}, fmt.Errorf("Type not struct: %v", senderType.String()) 879 } 880 881 curField := path[0] 882 fieldType, ok := senderType.Lit.Struct[curField] 883 if !ok { 884 return ts.Expr{}, fmt.Errorf("struct field '%v' not found", curField) 885 } 886 887 return a.getStructFieldTypeByPath(fieldType, path[1:], scope) 888 }