github.com/nevalang/neva@v0.23.1-0.20240507185603-7696a9bb8dda/internal/compiler/analyzer/main_component.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 ) 10 11 var ( 12 ErrMainComponentWithTypeParams = errors.New("Main component cannot have type parameters") 13 ErrEntityNotFoundByNodeRef = errors.New("Node references to entity that cannot be found") 14 ErrMainComponentInportsCount = errors.New("Main component must have exactly 1 inport") 15 ErrMainComponentOutportsCount = errors.New("Main component must have exactly 1 outport") 16 ErrMainComponentWithoutEnterInport = errors.New("Main component must have 'enter' inport") 17 ErrMainComponentWithoutExitOutport = errors.New("Main component must have 'exit' outport") 18 ErrMainPortIsArray = errors.New("Main component cannot have array ports") 19 ErrMainComponentPortTypeNotAny = errors.New("Main component's ports must be of type any") 20 ErrMainComponentNodeNotComponent = errors.New("Main component's nodes must only refer to components") 21 ) 22 23 func (a Analyzer) analyzeMainComponent(cmp src.Component, scope src.Scope) *compiler.Error { 24 if len(cmp.Interface.TypeParams.Params) != 0 { 25 return &compiler.Error{ 26 Err: ErrMainComponentWithTypeParams, 27 Meta: &cmp.Interface.Meta, 28 } 29 } 30 31 if err := a.analyzeMainComponentIO(cmp.Interface.IO); err != nil { 32 return compiler.Error{Meta: &cmp.Interface.Meta}.Wrap(err) 33 } 34 35 if err := a.analyzeMainComponentNodes(cmp.Nodes, scope); err != nil { 36 return compiler.Error{Meta: &cmp.Meta}.Wrap(err) 37 } 38 39 return nil 40 } 41 42 func (a Analyzer) analyzeMainComponentIO(io src.IO) *compiler.Error { 43 if len(io.In) != 1 { 44 return &compiler.Error{ 45 Err: fmt.Errorf("%w: got %v", ErrMainComponentInportsCount, len(io.In)), 46 } 47 } 48 if len(io.Out) != 1 { 49 return &compiler.Error{ 50 Err: fmt.Errorf("%w: got %v", ErrMainComponentOutportsCount, len(io.Out)), 51 } 52 } 53 54 enterInport, ok := io.In["start"] 55 if !ok { 56 return &compiler.Error{Err: ErrMainComponentWithoutEnterInport} 57 } 58 if err := a.analyzeMainComponentPort(enterInport); err != nil { 59 return &compiler.Error{ 60 Err: err, 61 Meta: &enterInport.Meta, 62 } 63 } 64 65 exitOutport, ok := io.Out["stop"] 66 if !ok { 67 return &compiler.Error{Err: ErrMainComponentWithoutExitOutport} 68 } 69 if err := a.analyzeMainComponentPort(exitOutport); err != nil { 70 return &compiler.Error{ 71 Err: err, 72 Meta: &exitOutport.Meta, 73 } 74 } 75 76 return nil 77 } 78 79 func (a Analyzer) analyzeMainComponentPort(port src.Port) error { 80 if port.IsArray { 81 return ErrMainPortIsArray 82 } 83 if !(src.Scope{}).IsTopType(port.TypeExpr) { 84 return ErrMainComponentPortTypeNotAny 85 } 86 return nil 87 } 88 89 func (Analyzer) analyzeMainComponentNodes( 90 nodes map[string]src.Node, 91 scope src.Scope, 92 ) *compiler.Error { 93 for nodeName, node := range nodes { 94 nodeEntity, loc, err := scope.Entity(node.EntityRef) 95 if err != nil { 96 return &compiler.Error{ 97 Err: fmt.Errorf( 98 "%w: node '%v', ref '%v', details '%v'", 99 ErrEntityNotFoundByNodeRef, 100 nodeName, 101 node.EntityRef, 102 err, 103 ), 104 Location: &loc, 105 Meta: &node.EntityRef.Meta, 106 } 107 } 108 109 if nodeEntity.Kind != src.ComponentEntity { 110 return &compiler.Error{ 111 Err: fmt.Errorf("%w: %v: %v", ErrMainComponentNodeNotComponent, nodeName, node.EntityRef), 112 Location: &loc, 113 Meta: nodeEntity.Meta(), 114 } 115 } 116 } 117 118 return nil 119 }