github.com/nevalang/neva@v0.23.1-0.20240507185603-7696a9bb8dda/internal/compiler/desugarer/struct_selectors.go (about)

     1  package desugarer
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync/atomic"
     7  
     8  	"github.com/nevalang/neva/internal/compiler"
     9  	src "github.com/nevalang/neva/internal/compiler/sourcecode"
    10  	"github.com/nevalang/neva/internal/compiler/sourcecode/core"
    11  	ts "github.com/nevalang/neva/internal/compiler/sourcecode/typesystem"
    12  )
    13  
    14  var (
    15  	ErrEmptySenderSide     = errors.New("Unable to desugar sender side with struct selectors because it's empty.")
    16  	ErrOutportAddrNotFound = errors.New("Outport addr not found")
    17  	ErrTypeNotStruct       = errors.New("Type not struct")
    18  	ErrStructFieldNotFound = errors.New("Struct field not found")
    19  )
    20  
    21  type handleStructSelectorsResult struct {
    22  	connToReplace     src.Connection
    23  	connToInsert      src.Connection
    24  	constToInsertName string
    25  	constToInsert     src.Const
    26  	nodeToInsert      src.Node
    27  	nodeToInsertName  string
    28  }
    29  
    30  var selectorNodeRef = core.EntityRef{
    31  	Pkg:  "builtin",
    32  	Name: "Field",
    33  }
    34  
    35  var virtualSelectorsCount atomic.Uint64
    36  
    37  func (d Desugarer) desugarStructSelectors( //nolint:funlen
    38  	normConn src.NormalConnection,
    39  ) (handleStructSelectorsResult, *compiler.Error) {
    40  	senderSide := normConn.SenderSide
    41  
    42  	constCounter := virtualConstCount.Load()
    43  	virtualConstCount.Store(constCounter + 1)
    44  	constName := fmt.Sprintf("__const__%d", constCounter)
    45  
    46  	counter := virtualSelectorsCount.Load()
    47  	virtualSelectorsCount.Store(counter + 1)
    48  	nodeName := fmt.Sprintf("__field__%d", counter)
    49  
    50  	selectorNode := src.Node{
    51  		Directives: map[src.Directive][]string{
    52  			compiler.BindDirective: {constName},
    53  		},
    54  		EntityRef: selectorNodeRef,
    55  	}
    56  
    57  	// original connection must be replaced with two new connections, this is the first one
    58  	connToReplace := src.Connection{
    59  		Normal: &src.NormalConnection{
    60  			SenderSide: src.ConnectionSenderSide{
    61  				// preserve original sender
    62  				PortAddr: senderSide.PortAddr,
    63  				Const:    senderSide.Const,
    64  				// but remove selectors in desugared version
    65  				Selectors: nil,
    66  			},
    67  			ReceiverSide: src.ConnectionReceiverSide{
    68  				Receivers: []src.ConnectionReceiver{
    69  					{
    70  						PortAddr: src.PortAddr{
    71  							Node: nodeName, // point it to created selector node
    72  							Port: "msg",
    73  						},
    74  					},
    75  				},
    76  				// don't forget sometimes we have both struct selectors and deferred connections
    77  				DeferredConnections: normConn.ReceiverSide.DeferredConnections,
    78  			},
    79  		},
    80  	}
    81  
    82  	// and this is the second
    83  	connToInsert := src.Connection{
    84  		Normal: &src.NormalConnection{
    85  			SenderSide: src.ConnectionSenderSide{
    86  				PortAddr: &src.PortAddr{
    87  					Node: nodeName, // created node received data from original sender and is now sending it further
    88  					Port: "msg",
    89  				},
    90  				Selectors: nil, // no selectors in desugared version
    91  			},
    92  			ReceiverSide: normConn.ReceiverSide, // preserve original receivers
    93  		},
    94  	}
    95  
    96  	constWithCfgMsg := d.createConstWithCfgMsgForSelectorNode(senderSide)
    97  
    98  	return handleStructSelectorsResult{
    99  		connToReplace:     connToReplace,
   100  		connToInsert:      connToInsert,
   101  		constToInsertName: constName,
   102  		constToInsert:     constWithCfgMsg,
   103  		nodeToInsertName:  nodeName,
   104  		nodeToInsert:      selectorNode,
   105  	}, nil
   106  }
   107  
   108  // list<str>
   109  var (
   110  	strTypeExpr = ts.Expr{
   111  		Inst: &ts.InstExpr{
   112  			Ref: core.EntityRef{Pkg: "builtin", Name: "string"},
   113  		},
   114  	}
   115  
   116  	pathConstTypeExpr = ts.Expr{
   117  		Inst: &ts.InstExpr{
   118  			Ref:  core.EntityRef{Pkg: "builtin", Name: "list"},
   119  			Args: []ts.Expr{strTypeExpr},
   120  		},
   121  	}
   122  )
   123  
   124  func (Desugarer) createConstWithCfgMsgForSelectorNode(senderSide src.ConnectionSenderSide) src.Const {
   125  	constToInsert := src.Const{
   126  		Message: &src.Message{
   127  			TypeExpr: pathConstTypeExpr,
   128  			List:     make([]src.Const, 0, len(senderSide.Selectors)),
   129  		},
   130  	}
   131  	for _, selector := range senderSide.Selectors {
   132  		constToInsert.Message.List = append(constToInsert.Message.List, src.Const{
   133  			Message: &src.Message{
   134  				TypeExpr: strTypeExpr,
   135  				Str:      compiler.Pointer(selector),
   136  			},
   137  		})
   138  	}
   139  	return constToInsert
   140  }