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  }