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

     1  package analyzer
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     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  	"github.com/nevalang/neva/internal/compiler/sourcecode/typesystem"
    11  )
    12  
    13  //nolint:lll
    14  var (
    15  	ErrAutoPortsArgNonStruct               = errors.New("Type argument for component with struct inports directive must be struct")
    16  	ErrAutoPortsNodeTypeArgsCount          = errors.New("Note that uses component with struct inports directive must pass exactly one type argument")
    17  	ErrAutoPortsTypeParamConstr            = errors.New("Component that uses struct inports directive must have type parameter with struct constraint")
    18  	ErrAutoPortsTypeParamsCount            = errors.New("Component that uses struct inports directive must have type parameter with have exactly one type parameter")
    19  	ErrNormalInportsWithAutoPortsDirective = errors.New("Component that uses struct inports directive must have no defined inports")
    20  	ErrGuardNotAllowedForNode              = errors.New("Guard is not allowed for nodes without 'err' output")
    21  	ErrGuardNotAllowedForComponent         = errors.New("Guard is not allowed for components without 'err' output")
    22  )
    23  
    24  type foundInterface struct {
    25  	iface    src.Interface
    26  	location src.Location
    27  }
    28  
    29  func (a Analyzer) analyzeComponentNodes(
    30  	componentIface src.Interface,
    31  	nodes map[string]src.Node,
    32  	scope src.Scope,
    33  ) (
    34  	map[string]src.Node, // resolved nodes
    35  	map[string]foundInterface, // resolved nodes interfaces with locations
    36  	bool, // one of the nodes has error guard
    37  	*compiler.Error, // err
    38  ) {
    39  	analyzedNodes := make(map[string]src.Node, len(nodes))
    40  	nodesInterfaces := make(map[string]foundInterface, len(nodes))
    41  	hasErrGuard := false
    42  
    43  	for nodeName, node := range nodes {
    44  		if node.ErrGuard {
    45  			hasErrGuard = true
    46  		}
    47  
    48  		analyzedNode, nodeInterface, err := a.analyzeComponentNode(
    49  			componentIface,
    50  			node,
    51  			scope,
    52  		)
    53  		if err != nil {
    54  			return nil, nil, false, compiler.Error{
    55  				Location: &scope.Location,
    56  				Meta:     &node.Meta,
    57  			}.Wrap(err)
    58  		}
    59  
    60  		nodesInterfaces[nodeName] = nodeInterface
    61  		analyzedNodes[nodeName] = analyzedNode
    62  	}
    63  
    64  	return analyzedNodes, nodesInterfaces, hasErrGuard, nil
    65  }
    66  
    67  //nolint:funlen
    68  func (a Analyzer) analyzeComponentNode(
    69  	componentIface src.Interface,
    70  	node src.Node,
    71  	scope src.Scope,
    72  ) (src.Node, foundInterface, *compiler.Error) {
    73  	parentTypeParams := componentIface.TypeParams
    74  
    75  	nodeEntity, location, err := scope.Entity(node.EntityRef)
    76  	if err != nil {
    77  		return src.Node{}, foundInterface{}, &compiler.Error{
    78  			Err:      err,
    79  			Location: &scope.Location,
    80  			Meta:     &node.Meta,
    81  		}
    82  	}
    83  
    84  	if nodeEntity.Kind != src.ComponentEntity &&
    85  		nodeEntity.Kind != src.InterfaceEntity {
    86  		return src.Node{}, foundInterface{}, &compiler.Error{
    87  			Err:      fmt.Errorf("%w: %v", ErrNodeWrongEntity, nodeEntity.Kind),
    88  			Location: &location,
    89  			Meta:     nodeEntity.Meta(),
    90  		}
    91  	}
    92  
    93  	bindDirectiveArgs, usesBindDirective := node.Directives[compiler.BindDirective]
    94  	if usesBindDirective && len(bindDirectiveArgs) != 1 {
    95  		return src.Node{}, foundInterface{}, &compiler.Error{
    96  			Err:      ErrBindDirectiveArgs,
    97  			Location: &location,
    98  			Meta:     nodeEntity.Meta(),
    99  		}
   100  	}
   101  
   102  	nodeIface, aerr := a.getNodeInterface(
   103  		nodeEntity,
   104  		usesBindDirective,
   105  		location,
   106  		node,
   107  		scope,
   108  	)
   109  	if aerr != nil {
   110  		return src.Node{}, foundInterface{}, aerr
   111  	}
   112  
   113  	if node.ErrGuard {
   114  		if _, ok := componentIface.IO.Out["err"]; !ok {
   115  			return src.Node{}, foundInterface{}, &compiler.Error{
   116  				Err:      ErrGuardNotAllowedForNode,
   117  				Location: &scope.Location,
   118  				Meta:     &node.Meta,
   119  			}
   120  		}
   121  		if _, ok := nodeIface.IO.Out["err"]; !ok {
   122  			return src.Node{}, foundInterface{}, &compiler.Error{
   123  				Err:      ErrGuardNotAllowedForComponent,
   124  				Location: &scope.Location,
   125  				Meta:     &node.Meta,
   126  			}
   127  		}
   128  	}
   129  
   130  	// We need to get resolved frame from parent type parameters
   131  	// in order to be able to resolve node's args
   132  	// since they can refer to type parameter of the parent (interface)
   133  	_, resolvedParentParamsFrame, err := a.resolver.ResolveParams(
   134  		parentTypeParams.Params,
   135  		scope,
   136  	)
   137  	if err != nil {
   138  		return src.Node{}, foundInterface{}, &compiler.Error{
   139  			Err:      err,
   140  			Location: &location,
   141  			Meta:     &node.Meta,
   142  		}
   143  	}
   144  
   145  	// Now when we have frame made of parent type parameters constraints
   146  	// we can resolve cases like `subnode SubComponent<T>`
   147  	// where `T` refers to type parameter of the component/interface we're in.
   148  	resolvedNodeArgs, err := a.resolver.ResolveExprsWithFrame(
   149  		node.TypeArgs,
   150  		resolvedParentParamsFrame,
   151  		scope,
   152  	)
   153  	if err != nil {
   154  		return src.Node{}, foundInterface{}, &compiler.Error{
   155  			Err:      err,
   156  			Location: &location,
   157  			Meta:     &node.Meta,
   158  		}
   159  	}
   160  
   161  	// default any
   162  	if len(resolvedNodeArgs) == 0 && len(nodeIface.TypeParams.Params) == 1 {
   163  		resolvedNodeArgs = []typesystem.Expr{
   164  			{
   165  				Inst: &typesystem.InstExpr{
   166  					Ref: core.EntityRef{Name: "any"},
   167  				},
   168  			},
   169  		}
   170  	}
   171  
   172  	// Finally check that every argument is compatible
   173  	// with corresponding parameter of the node's interface.
   174  	if err = a.resolver.CheckArgsCompatibility(
   175  		resolvedNodeArgs,
   176  		nodeIface.TypeParams.Params,
   177  		scope,
   178  	); err != nil {
   179  		return src.Node{}, foundInterface{}, &compiler.Error{
   180  			Err:      err,
   181  			Location: &scope.Location,
   182  			Meta:     &node.Meta,
   183  		}
   184  	}
   185  
   186  	if node.Deps == nil {
   187  		return src.Node{
   188  				Directives: node.Directives,
   189  				EntityRef:  node.EntityRef,
   190  				TypeArgs:   resolvedNodeArgs,
   191  				Meta:       node.Meta,
   192  				ErrGuard:   node.ErrGuard,
   193  			}, foundInterface{
   194  				iface:    nodeIface,
   195  				location: location,
   196  			}, nil
   197  	}
   198  
   199  	resolvedComponentDI := make(map[string]src.Node, len(node.Deps))
   200  	for depName, depNode := range node.Deps {
   201  		resolvedDep, _, err := a.analyzeComponentNode(
   202  			componentIface,
   203  			depNode,
   204  			scope,
   205  		)
   206  		if err != nil {
   207  			return src.Node{}, foundInterface{}, compiler.Error{
   208  				Location: &location,
   209  				Meta:     &depNode.Meta,
   210  			}.Wrap(err)
   211  		}
   212  		resolvedComponentDI[depName] = resolvedDep
   213  	}
   214  
   215  	return src.Node{
   216  			Directives: node.Directives,
   217  			EntityRef:  node.EntityRef,
   218  			TypeArgs:   resolvedNodeArgs,
   219  			Deps:       resolvedComponentDI,
   220  			Meta:       node.Meta,
   221  			ErrGuard:   node.ErrGuard,
   222  		}, foundInterface{
   223  			iface:    nodeIface,
   224  			location: location,
   225  		}, nil
   226  }
   227  
   228  func (a Analyzer) getNodeInterface( //nolint:funlen
   229  	entity src.Entity,
   230  	hasConfigMsg bool,
   231  	location src.Location,
   232  	node src.Node,
   233  	scope src.Scope,
   234  ) (src.Interface, *compiler.Error) {
   235  	if entity.Kind == src.InterfaceEntity {
   236  		if hasConfigMsg {
   237  			return src.Interface{}, &compiler.Error{
   238  				Err:      ErrInterfaceNodeBindDirective,
   239  				Location: &location,
   240  				Meta:     entity.Meta(),
   241  			}
   242  		}
   243  
   244  		if node.Deps != nil {
   245  			return src.Interface{}, &compiler.Error{
   246  				Err:      ErrNonComponentNodeWithDI,
   247  				Location: &location,
   248  				Meta:     entity.Meta(),
   249  			}
   250  		}
   251  
   252  		return entity.Interface, nil
   253  	}
   254  
   255  	externArgs, hasExternDirective := entity.Component.Directives[compiler.ExternDirective]
   256  
   257  	if hasConfigMsg && !hasExternDirective {
   258  		return src.Interface{}, &compiler.Error{
   259  			Err:      ErrNormNodeBind,
   260  			Location: &location,
   261  			Meta:     entity.Meta(),
   262  		}
   263  	}
   264  
   265  	if len(externArgs) > 1 && len(node.TypeArgs) != 1 {
   266  		return src.Interface{}, &compiler.Error{
   267  			Err:      ErrExternOverloadingNodeArgs,
   268  			Location: &location,
   269  			Meta:     entity.Meta(),
   270  		}
   271  	}
   272  
   273  	iface := entity.Component.Interface
   274  
   275  	_, hasAutoPortsDirective := entity.Component.Directives[compiler.AutoportsDirective]
   276  	if !hasAutoPortsDirective {
   277  		return iface, nil
   278  	}
   279  
   280  	// if we here then we have #autoports
   281  
   282  	if len(iface.IO.In) != 0 {
   283  		return src.Interface{}, &compiler.Error{
   284  			Err:      ErrNormalInportsWithAutoPortsDirective,
   285  			Location: &location,
   286  			Meta:     entity.Meta(),
   287  		}
   288  	}
   289  
   290  	if len(iface.TypeParams.Params) != 1 {
   291  		return src.Interface{}, &compiler.Error{
   292  			Err:      ErrAutoPortsTypeParamsCount,
   293  			Location: &location,
   294  			Meta:     entity.Meta(),
   295  		}
   296  	}
   297  
   298  	resolvedTypeParamConstr, err := a.resolver.ResolveExpr(iface.TypeParams.Params[0].Constr, scope)
   299  	if err != nil {
   300  		return src.Interface{}, &compiler.Error{
   301  			Err:      err,
   302  			Location: &location,
   303  			Meta:     entity.Meta(),
   304  		}
   305  	}
   306  
   307  	if resolvedTypeParamConstr.Lit == nil || resolvedTypeParamConstr.Lit.Struct == nil {
   308  		return src.Interface{}, &compiler.Error{
   309  			Err:      ErrAutoPortsTypeParamConstr,
   310  			Location: &location,
   311  			Meta:     entity.Meta(),
   312  		}
   313  	}
   314  
   315  	if len(node.TypeArgs) != 1 {
   316  		return src.Interface{}, &compiler.Error{
   317  			Err:      ErrAutoPortsNodeTypeArgsCount,
   318  			Location: &location,
   319  			Meta:     entity.Meta(),
   320  		}
   321  	}
   322  
   323  	resolvedNodeArg, err := a.resolver.ResolveExpr(node.TypeArgs[0], scope)
   324  	if err != nil {
   325  		return src.Interface{}, &compiler.Error{
   326  			Err:      err,
   327  			Location: &location,
   328  			Meta:     entity.Meta(),
   329  		}
   330  	}
   331  
   332  	if resolvedNodeArg.Lit == nil || resolvedNodeArg.Lit.Struct == nil {
   333  		return src.Interface{}, &compiler.Error{
   334  			Err:      ErrAutoPortsArgNonStruct,
   335  			Location: &location,
   336  			Meta:     entity.Meta(),
   337  		}
   338  	}
   339  
   340  	structFields := resolvedNodeArg.Lit.Struct
   341  	inports := make(map[string]src.Port, len(structFields))
   342  	for fieldName, fieldTypeExpr := range structFields {
   343  		inports[fieldName] = src.Port{
   344  			TypeExpr: fieldTypeExpr,
   345  		}
   346  	}
   347  
   348  	return src.Interface{
   349  		TypeParams: iface.TypeParams,
   350  		IO: src.IO{
   351  			In: inports,
   352  			Out: map[string]src.Port{
   353  				"msg": {
   354  					TypeExpr: resolvedNodeArg,
   355  					IsArray:  false,
   356  					Meta:     iface.IO.Out["v"].Meta,
   357  				},
   358  			},
   359  		},
   360  		Meta: iface.Meta,
   361  	}, nil
   362  }