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

     1  // Package irgen implements IR generation from source code.
     2  // It assumes that program passed analysis stage and does not enforce any validations.
     3  package irgen
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  
     9  	"github.com/nevalang/neva/internal/compiler"
    10  	src "github.com/nevalang/neva/internal/compiler/sourcecode"
    11  	"github.com/nevalang/neva/internal/compiler/sourcecode/core"
    12  	"github.com/nevalang/neva/internal/runtime/ir"
    13  )
    14  
    15  var ErrNodeUsageNotFound = errors.New("node usage not found")
    16  
    17  type Generator struct{}
    18  
    19  type (
    20  	nodeContext struct {
    21  		path       []string   // Path to current node including current node
    22  		node       src.Node   // Node definition
    23  		portsUsage portsUsage // How parent network uses this node's ports
    24  	}
    25  
    26  	portsUsage struct {
    27  		in  map[relPortAddr]struct{}
    28  		out map[relPortAddr]struct{}
    29  	}
    30  
    31  	relPortAddr struct {
    32  		Port string
    33  		Idx  uint8
    34  	}
    35  )
    36  
    37  func (g Generator) Generate(build src.Build, mainPkgName string) (*ir.Program, *compiler.Error) {
    38  	initialScope := src.Scope{
    39  		Build: build,
    40  		Location: src.Location{
    41  			ModRef:   build.EntryModRef,
    42  			PkgName:  mainPkgName,
    43  			FileName: "", // we don't know at this point and we don't need to
    44  		},
    45  	}
    46  
    47  	result := &ir.Program{
    48  		Ports:       []ir.PortInfo{},
    49  		Connections: []ir.Connection{},
    50  		Funcs:       []ir.FuncCall{},
    51  	}
    52  
    53  	rootNodeCtx := nodeContext{
    54  		path: []string{},
    55  		node: src.Node{
    56  			EntityRef: core.EntityRef{
    57  				Pkg:  "", // ref to local entity
    58  				Name: "Main",
    59  			},
    60  		},
    61  		portsUsage: portsUsage{
    62  			in: map[relPortAddr]struct{}{
    63  				{Port: "start"}: {},
    64  			},
    65  			out: map[relPortAddr]struct{}{
    66  				{Port: "stop"}: {},
    67  			},
    68  		},
    69  	}
    70  
    71  	if err := g.processComponentNode(rootNodeCtx, initialScope, result); err != nil {
    72  		return nil, compiler.Error{
    73  			Location: &initialScope.Location,
    74  		}.Wrap(err)
    75  	}
    76  
    77  	return result, nil
    78  }
    79  
    80  func (g Generator) processComponentNode( //nolint:funlen
    81  	nodeCtx nodeContext,
    82  	scope src.Scope,
    83  	result *ir.Program,
    84  ) *compiler.Error {
    85  	componentEntity, location, err := scope.Entity(nodeCtx.node.EntityRef)
    86  	if err != nil {
    87  		return &compiler.Error{
    88  			Err:      err,
    89  			Location: &scope.Location,
    90  		}
    91  	}
    92  
    93  	component := componentEntity.Component
    94  
    95  	// for inports we only use parent context because all inports are used
    96  	inportAddrs := g.insertAndReturnInports(nodeCtx, result)
    97  	//  for outports we use both parent context and component's interface
    98  	outportAddrs := g.insertAndReturnOutports(nodeCtx, result)
    99  
   100  	runtimeFuncRef, err := getRuntimeFuncRef(component, nodeCtx.node.TypeArgs)
   101  	if err != nil {
   102  		return &compiler.Error{
   103  			Err:      err,
   104  			Location: &location,
   105  			Meta:     &component.Meta,
   106  		}
   107  	}
   108  
   109  	// if component uses #extern, then we only need ports and func call
   110  	// ports are already created, so it's time to create func call
   111  	if runtimeFuncRef != "" {
   112  		// use prev location, not the location where runtime func was found
   113  		runtimeFuncMsg, err := getRuntimeFuncMsg(nodeCtx.node, scope)
   114  		if err != nil {
   115  			return &compiler.Error{
   116  				Err:      err,
   117  				Location: &scope.Location,
   118  			}
   119  		}
   120  
   121  		result.Funcs = append(result.Funcs, ir.FuncCall{
   122  			Ref: runtimeFuncRef,
   123  			IO: ir.FuncIO{
   124  				In:  inportAddrs,
   125  				Out: outportAddrs,
   126  			},
   127  			Msg: runtimeFuncMsg,
   128  		})
   129  
   130  		return nil
   131  	}
   132  
   133  	scope = scope.WithLocation(location) // only use new location if that's not builtin
   134  
   135  	// We use network as a source of true about how subnodes ports instead subnodes interface definitions.
   136  	// We cannot rely on them because there's no information about how many array slots are used (in case of array ports).
   137  	// On the other hand, we believe network has everything we need because program' correctness is verified by analyzer.
   138  	subnodesPortsUsage, err := g.processNetwork(
   139  		component.Net,
   140  		nodeCtx,
   141  		result,
   142  	)
   143  	if err != nil {
   144  		return &compiler.Error{
   145  			Err:      err,
   146  			Location: &scope.Location,
   147  		}
   148  	}
   149  
   150  	for nodeName, node := range component.Nodes {
   151  		nodePortsUsage, ok := subnodesPortsUsage[nodeName]
   152  		if !ok {
   153  			return &compiler.Error{
   154  				Err:      fmt.Errorf("%w: %v", ErrNodeUsageNotFound, nodeName),
   155  				Location: &location,
   156  				Meta:     &component.Meta,
   157  			}
   158  		}
   159  
   160  		subNodeCtx := nodeContext{
   161  			path:       append(nodeCtx.path, nodeName),
   162  			portsUsage: nodePortsUsage,
   163  			node:       node,
   164  		}
   165  
   166  		if injectedNode, ok := nodeCtx.node.Deps[nodeName]; ok {
   167  			subNodeCtx.node = injectedNode
   168  		}
   169  
   170  		if err := g.processComponentNode(subNodeCtx, scope, result); err != nil {
   171  			return &compiler.Error{
   172  				Err:      fmt.Errorf("%w: node '%v'", err, nodeName),
   173  				Location: &location,
   174  				Meta:     &component.Meta,
   175  			}
   176  		}
   177  	}
   178  
   179  	return nil
   180  }
   181  
   182  func New() Generator {
   183  	return Generator{}
   184  }