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

     1  package analyzer
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/nevalang/neva/internal/compiler"
     7  	src "github.com/nevalang/neva/internal/compiler/sourcecode"
     8  	ts "github.com/nevalang/neva/internal/compiler/sourcecode/typesystem"
     9  )
    10  
    11  var (
    12  	ErrInterfaceTypeParams = errors.New("Cannot resolve interface type parameters")
    13  	ErrEmptyInports        = errors.New("Interface must have inports")
    14  	ErrEmptyOutports       = errors.New("Interface must have outports")
    15  	ErrInvalidInports      = errors.New("Inports are invalid")
    16  	ErrInvalidOutports     = errors.New("Outports are invalid")
    17  )
    18  
    19  type analyzeInterfaceParams struct {
    20  	allowEmptyInports  bool
    21  	allowEmptyOutports bool
    22  }
    23  
    24  func (a Analyzer) analyzeInterface(
    25  	iface src.Interface,
    26  	scope src.Scope,
    27  	params analyzeInterfaceParams,
    28  ) (src.Interface, *compiler.Error) {
    29  	resolvedParams, err := a.analyzeTypeParams(iface.TypeParams.Params, scope)
    30  	if err != nil {
    31  		return src.Interface{}, compiler.Error{
    32  			Err:      ErrInterfaceTypeParams,
    33  			Location: &scope.Location,
    34  			Meta:     &iface.Meta,
    35  		}.Wrap(err)
    36  	}
    37  
    38  	resolvedIO, err := a.analyzeIO(resolvedParams, iface.IO, scope, params)
    39  	if err != nil {
    40  		return src.Interface{}, compiler.Error{
    41  			Err:      ErrInterfaceTypeParams,
    42  			Location: &scope.Location,
    43  			Meta:     &iface.Meta,
    44  		}.Wrap(err)
    45  	}
    46  
    47  	typeParams := src.TypeParams{
    48  		Params: resolvedParams,
    49  		Meta:   iface.TypeParams.Meta,
    50  	}
    51  
    52  	return src.Interface{
    53  		TypeParams: typeParams,
    54  		IO:         resolvedIO,
    55  	}, nil
    56  }
    57  
    58  func (a Analyzer) analyzeIO(
    59  	typeParams []ts.Param,
    60  	io src.IO,
    61  	scope src.Scope,
    62  	params analyzeInterfaceParams,
    63  ) (src.IO, *compiler.Error) {
    64  	if !params.allowEmptyInports && len(io.In) == 0 {
    65  		return src.IO{}, &compiler.Error{
    66  			Err:      ErrEmptyInports,
    67  			Location: &scope.Location,
    68  		}
    69  	}
    70  
    71  	if !params.allowEmptyOutports && len(io.Out) == 0 {
    72  		return src.IO{}, &compiler.Error{
    73  			Err:      ErrEmptyOutports,
    74  			Location: &scope.Location,
    75  		}
    76  	}
    77  
    78  	resolvedIn, err := a.analyzePorts(typeParams, io.In, scope)
    79  	if err != nil {
    80  		return src.IO{}, compiler.Error{
    81  			Err:      ErrInvalidInports,
    82  			Location: &scope.Location,
    83  		}.Wrap(err)
    84  	}
    85  
    86  	resolvedOut, err := a.analyzePorts(typeParams, io.Out, scope)
    87  	if err != nil {
    88  		return src.IO{}, compiler.Error{
    89  			Err:      ErrInvalidOutports,
    90  			Location: &scope.Location,
    91  		}.Wrap(err)
    92  	}
    93  
    94  	return src.IO{
    95  		In:  resolvedIn,
    96  		Out: resolvedOut,
    97  	}, nil
    98  }
    99  
   100  func (a Analyzer) analyzePorts(
   101  	params []ts.Param,
   102  	ports map[string]src.Port,
   103  	scope src.Scope,
   104  ) (map[string]src.Port, *compiler.Error) {
   105  	resolvedPorts := make(map[string]src.Port, len(ports))
   106  	for name, port := range ports {
   107  		resolvedPort, err := a.analyzePort(params, port, scope)
   108  		if err != nil {
   109  			return nil, compiler.Error{
   110  				Location: &scope.Location,
   111  				Meta:     &port.Meta,
   112  			}.Wrap(err)
   113  		}
   114  		resolvedPorts[name] = resolvedPort
   115  	}
   116  	return resolvedPorts, nil
   117  }
   118  
   119  func (a Analyzer) analyzePort(params []ts.Param, port src.Port, scope src.Scope) (src.Port, *compiler.Error) {
   120  	// TODO https://github.com/nevalang/neva/issues/507
   121  	resolvedDef, err := a.analyzeTypeDef(
   122  		ts.Def{
   123  			Params:   params,
   124  			BodyExpr: &port.TypeExpr,
   125  		},
   126  		scope, analyzeTypeDefParams{allowEmptyBody: false},
   127  	)
   128  	if err != nil {
   129  		return src.Port{}, compiler.Error{
   130  			Location: &scope.Location,
   131  			Meta:     &port.Meta,
   132  		}.Wrap(err)
   133  	}
   134  
   135  	return src.Port{
   136  		TypeExpr: *resolvedDef.BodyExpr,
   137  		IsArray:  port.IsArray,
   138  	}, nil
   139  }