github.com/nevalang/neva@v0.23.1-0.20240507185603-7696a9bb8dda/internal/compiler/desugarer/network.go (about) 1 package desugarer 2 3 import ( 4 "errors" 5 "maps" 6 "slices" 7 8 "github.com/nevalang/neva/internal/compiler" 9 src "github.com/nevalang/neva/internal/compiler/sourcecode" 10 ) 11 12 type handleNetResult struct { 13 desugaredConnections []src.Connection // desugared network 14 virtualConstants map[string]src.Const // constants that needs to be inserted in to make desugared network work 15 virtualNodes map[string]src.Node // nodes that needs to be inserted in to make desugared network work 16 usedNodePorts nodePortsMap // to find unused to create virtual del connections 17 } 18 19 func (d Desugarer) handleNetwork( 20 net []src.Connection, 21 nodes map[string]src.Node, 22 scope src.Scope, 23 ) (handleNetResult, *compiler.Error) { 24 desugaredConns := make([]src.Connection, 0, len(net)) 25 nodesToInsert := map[string]src.Node{} 26 constsToInsert := map[string]src.Const{} 27 usedNodePorts := newNodePortsMap() 28 29 for _, conn := range net { 30 result, err := d.desugarConn( 31 conn, 32 usedNodePorts, 33 scope, 34 nodes, 35 nodesToInsert, 36 constsToInsert, 37 ) 38 if err != nil { 39 return handleNetResult{}, err 40 } 41 42 desugaredConns = append(desugaredConns, result.connToReplace) 43 desugaredConns = append(desugaredConns, result.connsToInsert...) 44 } 45 46 return handleNetResult{ 47 desugaredConnections: desugaredConns, 48 usedNodePorts: usedNodePorts, 49 virtualConstants: constsToInsert, 50 virtualNodes: nodesToInsert, 51 }, nil 52 } 53 54 type desugarConnResult struct { 55 connToReplace src.Connection 56 connsToInsert []src.Connection 57 } 58 59 // desugarConn modifies given nodesToInsert, constsToInsert and usedNodePorts 60 // it also returns connection to replace the original one and other connections 61 // that were generated while desugared the original one. 62 func (d Desugarer) desugarConn( 63 conn src.Connection, 64 usedNodePorts nodePortsMap, 65 scope src.Scope, 66 nodes map[string]src.Node, 67 nodesToInsert map[string]src.Node, 68 constsToInsert map[string]src.Const, 69 ) (desugarConnResult, *compiler.Error) { 70 // array bypass connection - nothing to desugar, just mark as used and return as-is 71 if conn.ArrayBypass != nil { 72 usedNodePorts.set( 73 conn.ArrayBypass.SenderOutport.Node, 74 conn.ArrayBypass.SenderOutport.Port, 75 ) 76 usedNodePorts.set( 77 conn.ArrayBypass.ReceiverInport.Node, 78 conn.ArrayBypass.ReceiverInport.Port, 79 ) 80 return desugarConnResult{ 81 connToReplace: conn, 82 }, nil 83 } 84 85 // normal connection with port address sender 86 if conn.Normal.SenderSide.PortAddr != nil { 87 // if port is unknown, find first and use it instead 88 if conn.Normal.SenderSide.PortAddr.Port == "" { 89 found, err := getFirstOutPortName(scope, nodes, *conn.Normal.SenderSide.PortAddr) 90 if err != nil { 91 return desugarConnResult{}, &compiler.Error{Err: err} 92 } 93 94 conn = src.Connection{ 95 Normal: &src.NormalConnection{ 96 SenderSide: src.ConnectionSenderSide{ 97 PortAddr: &src.PortAddr{ 98 Port: found, 99 Node: conn.Normal.SenderSide.PortAddr.Node, 100 Idx: conn.Normal.SenderSide.PortAddr.Idx, 101 Meta: conn.Normal.SenderSide.PortAddr.Meta, 102 }, 103 Selectors: conn.Normal.SenderSide.Selectors, 104 Meta: conn.Normal.SenderSide.Meta, 105 }, 106 ReceiverSide: conn.Normal.ReceiverSide, 107 }, 108 Meta: conn.Meta, 109 } 110 } 111 112 // mark as used 113 usedNodePorts.set( 114 conn.Normal.SenderSide.PortAddr.Node, 115 conn.Normal.SenderSide.PortAddr.Port, 116 ) 117 } 118 119 connsToInsert := []src.Connection{} 120 121 // if conn has selectors, desugar it, then replace it and insert generated ones 122 if len(conn.Normal.SenderSide.Selectors) != 0 { 123 result, err := d.desugarStructSelectors(*conn.Normal) 124 if err != nil { 125 return desugarConnResult{}, compiler.Error{ 126 Err: errors.New("Cannot desugar struct selectors"), 127 Location: &scope.Location, 128 Meta: &conn.Meta, 129 }.Wrap(err) 130 } 131 132 nodesToInsert[result.nodeToInsertName] = result.nodeToInsert 133 constsToInsert[result.constToInsertName] = result.constToInsert 134 135 // generated connection might need desugaring itself 136 connToInsertDesugarRes, err := d.desugarConn( 137 result.connToInsert, 138 usedNodePorts, 139 scope, 140 nodes, 141 nodesToInsert, 142 constsToInsert, 143 ) 144 if err != nil { 145 return desugarConnResult{}, err 146 } 147 148 connsToInsert = append(connsToInsert, connToInsertDesugarRes.connToReplace) 149 connsToInsert = append(connsToInsert, connToInsertDesugarRes.connsToInsert...) 150 151 // connection that replaces original one might need desugaring itself 152 replacedConnDesugarRes, err := d.desugarConn( 153 result.connToReplace, 154 usedNodePorts, 155 scope, 156 nodes, 157 nodesToInsert, 158 constsToInsert, 159 ) 160 if err != nil { 161 return desugarConnResult{}, err 162 } 163 164 connsToInsert = append(connsToInsert, replacedConnDesugarRes.connsToInsert...) 165 166 conn = replacedConnDesugarRes.connToReplace 167 } 168 169 // if sender is const or literal, replace it with desugared and insert const/node for emitter 170 if conn.Normal.SenderSide.Const != nil { 171 if conn.Normal.SenderSide.Const.Ref != nil { 172 result, err := d.handleConstRefSender(conn, scope) 173 if err != nil { 174 return desugarConnResult{}, err 175 } 176 nodesToInsert[result.nodeToInsertName] = result.nodeToInsert 177 conn = result.connToReplace 178 } else if conn.Normal.SenderSide.Const.Message != nil { 179 result, err := d.handleLiteralSender(conn) 180 if err != nil { 181 return desugarConnResult{}, err 182 } 183 constsToInsert[result.constName] = *conn.Normal.SenderSide.Const 184 nodesToInsert[result.nodeToInsertName] = result.nodeToInsert 185 conn = result.connToReplace 186 } 187 } 188 189 // if there's no deferred connections, then desugar empty port receivers and that's it 190 if len(conn.Normal.ReceiverSide.DeferredConnections) == 0 { 191 desugaredReceivers := slices.Clone(conn.Normal.ReceiverSide.Receivers) 192 193 for i, receiver := range conn.Normal.ReceiverSide.Receivers { 194 if receiver.PortAddr.Port != "" { 195 continue 196 } 197 198 found, err := getFirstInportName(scope, nodes, receiver.PortAddr) 199 if err != nil { 200 return desugarConnResult{}, &compiler.Error{Err: err} 201 } 202 203 desugaredReceivers[i] = src.ConnectionReceiver{ 204 PortAddr: src.PortAddr{ 205 Port: found, 206 Node: receiver.PortAddr.Node, 207 Idx: receiver.PortAddr.Idx, 208 Meta: receiver.PortAddr.Meta, 209 }, 210 Meta: receiver.Meta, 211 } 212 } 213 214 return desugarConnResult{ 215 connToReplace: src.Connection{ 216 Normal: &src.NormalConnection{ 217 SenderSide: conn.Normal.SenderSide, 218 ReceiverSide: src.ConnectionReceiverSide{ 219 Receivers: desugaredReceivers, 220 }, 221 }, 222 Meta: conn.Meta, 223 }, 224 connsToInsert: connsToInsert, 225 }, nil 226 } 227 228 // if there's desugared connections, desugar them, 229 // insert what's needed and replace original connection 230 deferredConnsResult, err := d.handleDeferredConnections( 231 *conn.Normal, 232 nodes, 233 scope, 234 ) 235 if err != nil { 236 return desugarConnResult{}, err 237 } 238 239 usedNodePorts.merge(deferredConnsResult.nodesPortsUsed) 240 maps.Copy(constsToInsert, deferredConnsResult.constsToInsert) 241 maps.Copy(nodesToInsert, deferredConnsResult.nodesToInsert) 242 243 return desugarConnResult{ 244 connToReplace: deferredConnsResult.connToReplace, 245 connsToInsert: deferredConnsResult.connsToInsert, 246 }, nil 247 } 248 249 func getNodeIOByPortAddr( 250 scope src.Scope, 251 nodes map[string]src.Node, 252 portAddr *src.PortAddr, 253 ) (src.IO, *compiler.Error) { 254 node, ok := nodes[portAddr.Node] 255 if !ok { 256 panic(portAddr.Node) 257 } 258 259 entity, _, err := scope.Entity(node.EntityRef) 260 if err != nil { 261 return src.IO{}, &compiler.Error{ 262 Err: err, 263 Location: &scope.Location, 264 Meta: &portAddr.Meta, 265 } 266 } 267 268 var iface src.Interface 269 if entity.Kind == src.InterfaceEntity { 270 iface = entity.Interface 271 } else { 272 iface = entity.Component.Interface 273 } 274 275 return iface.IO, nil 276 } 277 278 func getFirstInportName(scope src.Scope, nodes map[string]src.Node, portAddr src.PortAddr) (string, error) { 279 io, err := getNodeIOByPortAddr(scope, nodes, &portAddr) 280 if err != nil { 281 return "", err 282 } 283 for inport := range io.In { 284 return inport, nil 285 } 286 return "", errors.New("first inport not found") 287 } 288 289 func getFirstOutPortName(scope src.Scope, nodes map[string]src.Node, portAddr src.PortAddr) (string, error) { 290 io, err := getNodeIOByPortAddr(scope, nodes, &portAddr) 291 if err != nil { 292 return "", err 293 } 294 for outport := range io.Out { 295 return outport, nil 296 } 297 return "", errors.New("first outport not found") 298 }