github.com/nevalang/neva@v0.23.1-0.20240507185603-7696a9bb8dda/internal/compiler/irgen/irgen.go (about) 1 // Package irgen implements IR generation from source code. 2 // It assumes that program passed analysis stage and does not enforce any validations. 3 package irgen 4 5 import ( 6 "errors" 7 "fmt" 8 9 "github.com/nevalang/neva/internal/compiler" 10 src "github.com/nevalang/neva/internal/compiler/sourcecode" 11 "github.com/nevalang/neva/internal/compiler/sourcecode/core" 12 "github.com/nevalang/neva/internal/runtime/ir" 13 ) 14 15 var ErrNodeUsageNotFound = errors.New("node usage not found") 16 17 type Generator struct{} 18 19 type ( 20 nodeContext struct { 21 path []string // Path to current node including current node 22 node src.Node // Node definition 23 portsUsage portsUsage // How parent network uses this node's ports 24 } 25 26 portsUsage struct { 27 in map[relPortAddr]struct{} 28 out map[relPortAddr]struct{} 29 } 30 31 relPortAddr struct { 32 Port string 33 Idx uint8 34 } 35 ) 36 37 func (g Generator) Generate(build src.Build, mainPkgName string) (*ir.Program, *compiler.Error) { 38 initialScope := src.Scope{ 39 Build: build, 40 Location: src.Location{ 41 ModRef: build.EntryModRef, 42 PkgName: mainPkgName, 43 FileName: "", // we don't know at this point and we don't need to 44 }, 45 } 46 47 result := &ir.Program{ 48 Ports: []ir.PortInfo{}, 49 Connections: []ir.Connection{}, 50 Funcs: []ir.FuncCall{}, 51 } 52 53 rootNodeCtx := nodeContext{ 54 path: []string{}, 55 node: src.Node{ 56 EntityRef: core.EntityRef{ 57 Pkg: "", // ref to local entity 58 Name: "Main", 59 }, 60 }, 61 portsUsage: portsUsage{ 62 in: map[relPortAddr]struct{}{ 63 {Port: "start"}: {}, 64 }, 65 out: map[relPortAddr]struct{}{ 66 {Port: "stop"}: {}, 67 }, 68 }, 69 } 70 71 if err := g.processComponentNode(rootNodeCtx, initialScope, result); err != nil { 72 return nil, compiler.Error{ 73 Location: &initialScope.Location, 74 }.Wrap(err) 75 } 76 77 return result, nil 78 } 79 80 func (g Generator) processComponentNode( //nolint:funlen 81 nodeCtx nodeContext, 82 scope src.Scope, 83 result *ir.Program, 84 ) *compiler.Error { 85 componentEntity, location, err := scope.Entity(nodeCtx.node.EntityRef) 86 if err != nil { 87 return &compiler.Error{ 88 Err: err, 89 Location: &scope.Location, 90 } 91 } 92 93 component := componentEntity.Component 94 95 // for inports we only use parent context because all inports are used 96 inportAddrs := g.insertAndReturnInports(nodeCtx, result) 97 // for outports we use both parent context and component's interface 98 outportAddrs := g.insertAndReturnOutports(nodeCtx, result) 99 100 runtimeFuncRef, err := getRuntimeFuncRef(component, nodeCtx.node.TypeArgs) 101 if err != nil { 102 return &compiler.Error{ 103 Err: err, 104 Location: &location, 105 Meta: &component.Meta, 106 } 107 } 108 109 // if component uses #extern, then we only need ports and func call 110 // ports are already created, so it's time to create func call 111 if runtimeFuncRef != "" { 112 // use prev location, not the location where runtime func was found 113 runtimeFuncMsg, err := getRuntimeFuncMsg(nodeCtx.node, scope) 114 if err != nil { 115 return &compiler.Error{ 116 Err: err, 117 Location: &scope.Location, 118 } 119 } 120 121 result.Funcs = append(result.Funcs, ir.FuncCall{ 122 Ref: runtimeFuncRef, 123 IO: ir.FuncIO{ 124 In: inportAddrs, 125 Out: outportAddrs, 126 }, 127 Msg: runtimeFuncMsg, 128 }) 129 130 return nil 131 } 132 133 scope = scope.WithLocation(location) // only use new location if that's not builtin 134 135 // We use network as a source of true about how subnodes ports instead subnodes interface definitions. 136 // We cannot rely on them because there's no information about how many array slots are used (in case of array ports). 137 // On the other hand, we believe network has everything we need because program' correctness is verified by analyzer. 138 subnodesPortsUsage, err := g.processNetwork( 139 component.Net, 140 nodeCtx, 141 result, 142 ) 143 if err != nil { 144 return &compiler.Error{ 145 Err: err, 146 Location: &scope.Location, 147 } 148 } 149 150 for nodeName, node := range component.Nodes { 151 nodePortsUsage, ok := subnodesPortsUsage[nodeName] 152 if !ok { 153 return &compiler.Error{ 154 Err: fmt.Errorf("%w: %v", ErrNodeUsageNotFound, nodeName), 155 Location: &location, 156 Meta: &component.Meta, 157 } 158 } 159 160 subNodeCtx := nodeContext{ 161 path: append(nodeCtx.path, nodeName), 162 portsUsage: nodePortsUsage, 163 node: node, 164 } 165 166 if injectedNode, ok := nodeCtx.node.Deps[nodeName]; ok { 167 subNodeCtx.node = injectedNode 168 } 169 170 if err := g.processComponentNode(subNodeCtx, scope, result); err != nil { 171 return &compiler.Error{ 172 Err: fmt.Errorf("%w: node '%v'", err, nodeName), 173 Location: &location, 174 Meta: &component.Meta, 175 } 176 } 177 } 178 179 return nil 180 } 181 182 func New() Generator { 183 return Generator{} 184 }