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

     1  package desugarer
     2  
     3  import (
     4  	"fmt"
     5  	"sync/atomic"
     6  
     7  	"github.com/nevalang/neva/internal/compiler"
     8  	src "github.com/nevalang/neva/internal/compiler/sourcecode"
     9  	"github.com/nevalang/neva/internal/compiler/sourcecode/core"
    10  	ts "github.com/nevalang/neva/internal/compiler/sourcecode/typesystem"
    11  )
    12  
    13  var emitterComponentRef = core.EntityRef{
    14  	Pkg:  "builtin",
    15  	Name: "New",
    16  }
    17  
    18  type handleLiteralSenderResult struct {
    19  	constName                  string
    20  	handleConstRefSenderResult // conceptually incorrrect but convenient to reuse
    21  }
    22  
    23  type handleConstRefSenderResult struct {
    24  	connToReplace    src.Connection // connection without const sender
    25  	nodeToInsertName string         // name of emitter node
    26  	nodeToInsert     src.Node       // emitter node
    27  }
    28  
    29  // In the future compiler can operate in concurrently
    30  var (
    31  	virtualEmittersCount atomic.Uint64
    32  	virtualConstCount    atomic.Uint64
    33  )
    34  
    35  func (d Desugarer) handleLiteralSender(
    36  	conn src.Connection,
    37  ) (
    38  	handleLiteralSenderResult,
    39  	*compiler.Error,
    40  ) {
    41  	constCounter := virtualConstCount.Load()
    42  	virtualConstCount.Store(constCounter + 1)
    43  	constName := fmt.Sprintf("__const__%d", constCounter)
    44  
    45  	// we can't call d.handleConstRefSender()
    46  	// because our virtual const isn't in the scope
    47  
    48  	emitterNode := src.Node{
    49  		Directives: map[src.Directive][]string{
    50  			compiler.BindDirective: {constName},
    51  		},
    52  		EntityRef: emitterComponentRef,
    53  		TypeArgs: []ts.Expr{
    54  			conn.
    55  				Normal.SenderSide.
    56  				Const.
    57  				Message.
    58  				TypeExpr,
    59  		},
    60  	}
    61  
    62  	emitterCounter := virtualEmittersCount.Load()
    63  	virtualEmittersCount.Store(emitterCounter + 1)
    64  	emitterNodeName := fmt.Sprintf("__new__%d", emitterCounter)
    65  
    66  	emitterNodeOutportAddr := src.PortAddr{
    67  		Node: emitterNodeName,
    68  		Port: "msg",
    69  	}
    70  
    71  	return handleLiteralSenderResult{
    72  		constName: constName,
    73  		handleConstRefSenderResult: handleConstRefSenderResult{
    74  			connToReplace: src.Connection{
    75  				Normal: &src.NormalConnection{
    76  					SenderSide: src.ConnectionSenderSide{
    77  						PortAddr:  &emitterNodeOutportAddr,
    78  						Selectors: conn.Normal.SenderSide.Selectors,
    79  						Meta:      conn.Normal.SenderSide.Meta,
    80  					},
    81  					ReceiverSide: conn.Normal.ReceiverSide,
    82  				},
    83  				Meta: conn.Meta,
    84  			},
    85  			nodeToInsertName: emitterNodeName,
    86  			nodeToInsert:     emitterNode,
    87  		},
    88  	}, nil
    89  }
    90  
    91  func (d Desugarer) handleConstRefSender(
    92  	conn src.Connection,
    93  	scope src.Scope,
    94  ) (
    95  	handleConstRefSenderResult,
    96  	*compiler.Error,
    97  ) {
    98  	constTypeExpr, err := d.getConstTypeByRef(*conn.Normal.SenderSide.Const.Ref, scope)
    99  	if err != nil {
   100  		return handleConstRefSenderResult{}, compiler.Error{
   101  			Err: fmt.Errorf(
   102  				"Unable to get constant type by reference '%v'",
   103  				*conn.Normal.SenderSide.Const.Ref,
   104  			),
   105  			Location: &scope.Location,
   106  			Meta:     &conn.Normal.SenderSide.Const.Ref.Meta,
   107  		}.Wrap(err)
   108  	}
   109  
   110  	counter := virtualEmittersCount.Load()
   111  	virtualEmittersCount.Store(counter + 1)
   112  	virtualEmitterName := fmt.Sprintf("__new__%d", counter)
   113  
   114  	emitterNode := src.Node{
   115  		Directives: map[src.Directive][]string{
   116  			compiler.BindDirective: {
   117  				conn.Normal.SenderSide.Const.Ref.String(), // don't forget to bind const
   118  			},
   119  		},
   120  		EntityRef: emitterComponentRef,
   121  		TypeArgs:  []ts.Expr{constTypeExpr},
   122  	}
   123  	emitterNodeOutportAddr := src.PortAddr{
   124  		Node: virtualEmitterName,
   125  		Port: "msg",
   126  	}
   127  
   128  	return handleConstRefSenderResult{
   129  		connToReplace: src.Connection{
   130  			Normal: &src.NormalConnection{
   131  				SenderSide: src.ConnectionSenderSide{
   132  					PortAddr:  &emitterNodeOutportAddr,
   133  					Selectors: conn.Normal.SenderSide.Selectors,
   134  					Meta:      conn.Normal.SenderSide.Meta,
   135  				},
   136  				ReceiverSide: conn.Normal.ReceiverSide,
   137  			},
   138  			Meta: conn.Meta,
   139  		},
   140  		nodeToInsertName: virtualEmitterName,
   141  		nodeToInsert:     emitterNode,
   142  	}, nil
   143  }
   144  
   145  // getConstTypeByRef is needed to figure out type parameters for Const node
   146  func (d Desugarer) getConstTypeByRef(ref core.EntityRef, scope src.Scope) (ts.Expr, *compiler.Error) {
   147  	entity, _, err := scope.Entity(ref)
   148  	if err != nil {
   149  		return ts.Expr{}, &compiler.Error{
   150  			Err:      err,
   151  			Location: &scope.Location,
   152  			Meta:     &ref.Meta,
   153  		}
   154  	}
   155  
   156  	if entity.Kind != src.ConstEntity {
   157  		return ts.Expr{}, &compiler.Error{
   158  			Err:      fmt.Errorf("%w: %v", ErrConstSenderEntityKind, entity.Kind),
   159  			Location: &scope.Location,
   160  			Meta:     entity.Meta(),
   161  		}
   162  	}
   163  
   164  	if entity.Const.Ref != nil {
   165  		expr, err := d.getConstTypeByRef(*entity.Const.Ref, scope)
   166  		if err != nil {
   167  			return ts.Expr{}, compiler.Error{
   168  				Location: &scope.Location,
   169  				Meta:     entity.Meta(),
   170  			}.Wrap(err)
   171  		}
   172  		return expr, nil
   173  	}
   174  
   175  	return entity.Const.Message.TypeExpr, nil
   176  }