Skip to content

Commit

Permalink
wip(analyzer): adding code for setting overload index for nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
emil14 committed Jan 16, 2025
1 parent 80598bc commit 06026c4
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 43 deletions.
14 changes: 14 additions & 0 deletions .vscode/bookmarks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"files": [
{
"path": "internal/compiler/analyzer/nodes.go",
"bookmarks": [
{
"line": 363,
"column": 33,
"label": ""
}
]
}
]
}
17 changes: 12 additions & 5 deletions internal/compiler/analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,11 @@ func (a Analyzer) analyzeEntity(entity src.Entity, scope src.Scope) (src.Entity,

switch entity.Kind {
case src.TypeEntity:
resolvedTypeDef, err := a.analyzeTypeDef(entity.Type, scope, analyzeTypeDefParams{allowEmptyBody: isStd})
resolvedTypeDef, err := a.analyzeType(
entity.Type,
scope,
analyzeTypeDefParams{allowEmptyBody: isStd},
)
if err != nil {
meta := entity.Type.Meta
return src.Entity{}, compiler.Error{
Expand All @@ -202,10 +206,13 @@ func (a Analyzer) analyzeEntity(entity src.Entity, scope src.Scope) (src.Entity,
}
resolvedEntity.Const = resolvedConst
case src.InterfaceEntity:
resolvedInterface, err := a.analyzeInterface(entity.Interface, scope, analyzeInterfaceParams{
allowEmptyInports: false,
allowEmptyOutports: false,
})
resolvedInterface, err := a.analyzeInterface(
entity.Interface,
scope,
analyzeInterfaceParams{
allowEmptyInports: false,
allowEmptyOutports: false,
})
if err != nil {
meta := entity.Interface.Meta
return src.Entity{}, compiler.Error{
Expand Down
1 change: 1 addition & 0 deletions internal/compiler/analyzer/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func (a Analyzer) analyzeComponent(
component.Interface,
component.Nodes,
scope,
component.Net,
)
if err != nil {
return src.Component{}, compiler.Error{
Expand Down
2 changes: 1 addition & 1 deletion internal/compiler/analyzer/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (a Analyzer) analyzePorts(

func (a Analyzer) analyzePort(params []ts.Param, port src.Port, scope src.Scope) (src.Port, *compiler.Error) {
// TODO https://github.com/nevalang/neva/issues/507
resolvedDef, err := a.analyzeTypeDef(
resolvedDef, err := a.analyzeType(
ts.Def{
Params: params,
BodyExpr: &port.TypeExpr,
Expand Down
98 changes: 62 additions & 36 deletions internal/compiler/analyzer/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func (a Analyzer) analyzeNodes(
flowIface src.Interface,
nodes map[string]src.Node,
scope src.Scope,
net []src.Connection,
) (
map[string]src.Node, // resolved nodes
map[string]foundInterface, // resolved nodes interfaces with locations
Expand All @@ -37,6 +38,7 @@ func (a Analyzer) analyzeNodes(
flowIface,
node,
scope,
net,
)
if err != nil {
return nil, nil, false, compiler.Error{
Expand All @@ -52,11 +54,12 @@ func (a Analyzer) analyzeNodes(
}

func (a Analyzer) analyzeNode(
compIface src.Interface,
iface src.Interface,
node src.Node,
scope src.Scope,
net []src.Connection,
) (src.Node, foundInterface, *compiler.Error) {
parentTypeParams := compIface.TypeParams
parentTypeParams := iface.TypeParams

nodeEntity, location, err := scope.Entity(node.EntityRef)
if err != nil {
Expand Down Expand Up @@ -125,25 +128,29 @@ func (a Analyzer) analyzeNode(
}
}

var nodeIface src.Interface
var (
nodeIface src.Interface
overloadIndex *int // only for component nodes
)
if nodeEntity.Kind == src.InterfaceEntity {
nodeIface = nodeEntity.Interface
} else {
var err *compiler.Error
nodeIface, err = a.getComponentNodeInterface(
nodeIface, overloadIndex, err = a.getComponentNodeInterface(
nodeEntity,
hasBind,
node,
scope,
resolvedNodeArgs,
net,
)
if err != nil {
return src.Node{}, foundInterface{}, err
}
}

if node.ErrGuard {
if _, ok := compIface.IO.Out["err"]; !ok {
if _, ok := iface.IO.Out["err"]; !ok {
return src.Node{}, foundInterface{}, &compiler.Error{
Message: "Error-guard operator '?' can only be used in components with ':err' outport to propagate errors",
Meta: &node.Meta,
Expand Down Expand Up @@ -183,27 +190,25 @@ func (a Analyzer) analyzeNode(

if node.DIArgs == nil {
return src.Node{
Directives: node.Directives,
EntityRef: node.EntityRef,
TypeArgs: resolvedNodeArgs,
Meta: node.Meta,
ErrGuard: node.ErrGuard,
Directives: node.Directives,
EntityRef: node.EntityRef,
TypeArgs: resolvedNodeArgs,
Meta: node.Meta,
OverloadIndex: overloadIndex,
ErrGuard: node.ErrGuard,
}, foundInterface{
iface: nodeIface,
location: location,
}, nil
}

// TODO probably here
// implement interface->component subtyping
// in a way where FP possible

resolvedFlowDI := make(map[string]src.Node, len(node.DIArgs))
for depName, depNode := range node.DIArgs {
resolvedDep, _, err := a.analyzeNode(
compIface,
iface,
depNode,
scope,
net, // TODO make sure DI works with overloading (example: Reduce{Add})
)
if err != nil {
return src.Node{}, foundInterface{}, compiler.Error{
Expand All @@ -214,12 +219,13 @@ func (a Analyzer) analyzeNode(
}

return src.Node{
Directives: node.Directives,
EntityRef: node.EntityRef,
TypeArgs: resolvedNodeArgs,
DIArgs: resolvedFlowDI,
Meta: node.Meta,
ErrGuard: node.ErrGuard,
Directives: node.Directives,
EntityRef: node.EntityRef,
TypeArgs: resolvedNodeArgs,
DIArgs: resolvedFlowDI,
ErrGuard: node.ErrGuard,
OverloadIndex: overloadIndex,
Meta: node.Meta,
}, foundInterface{
iface: nodeIface,
location: location,
Expand All @@ -228,25 +234,36 @@ func (a Analyzer) analyzeNode(

// getComponentNodeInterface returns interface of the component node.
// It also performs some validation.
// Overloading at the level of sourcecode is implemented here.
func (a Analyzer) getComponentNodeInterface(
entity src.Entity,
hasBind bool,
node src.Node,
scope src.Scope,
resolvedNodeArgs []typesystem.Expr,
) (src.Interface, *compiler.Error) {
// TODO: node.OverloadIndex needs to be set here based on how node is used in the network

var version src.Component
net []src.Connection,
) (src.Interface, *int, *compiler.Error) {
var (
overloadIndex *int
version src.Component
)
if len(entity.Component) == 1 {
version = entity.Component[0]
} else {
version = entity.Component[*node.OverloadIndex]
v, err := a.getNodeOverloadIndex(node, net, entity.Component, scope)
if err != nil {
return src.Interface{}, nil, &compiler.Error{
Message: "Node can't use #bind if it isn't instantiated with the component that use #extern",
Meta: &node.Meta,
}
}
version = entity.Component[v]
overloadIndex = &v
}

_, hasExtern := version.Directives[compiler.ExternDirective]
if hasBind && !hasExtern {
return src.Interface{}, &compiler.Error{
return src.Interface{}, nil, &compiler.Error{
Message: "Node can't use #bind if it isn't instantiated with the component that use #extern",
Meta: entity.Meta(),
}
Expand All @@ -256,57 +273,57 @@ func (a Analyzer) getComponentNodeInterface(

_, hasAutoPortsDirective := version.Directives[compiler.AutoportsDirective]
if !hasAutoPortsDirective {
return iface, nil
return iface, overloadIndex, nil
}

// if we here then we have #autoports (only for structs)

if len(iface.IO.In) != 0 {
return src.Interface{}, &compiler.Error{
return src.Interface{}, nil, &compiler.Error{
Message: "Component that uses struct inports directive must have no defined inports",
Meta: entity.Meta(),
}
}

if len(iface.TypeParams.Params) != 1 {
return src.Interface{}, &compiler.Error{
return src.Interface{}, nil, &compiler.Error{
Message: "Exactly one type parameter expected",
Meta: entity.Meta(),
}
}

resolvedTypeParamConstr, err := a.resolver.ResolveExpr(iface.TypeParams.Params[0].Constr, scope)
if err != nil {
return src.Interface{}, &compiler.Error{
return src.Interface{}, nil, &compiler.Error{
Message: err.Error(),
Meta: entity.Meta(),
}
}

if resolvedTypeParamConstr.Lit == nil || resolvedTypeParamConstr.Lit.Struct == nil {
return src.Interface{}, &compiler.Error{
return src.Interface{}, nil, &compiler.Error{
Message: "Struct type expected",
Meta: entity.Meta(),
}
}

if len(resolvedNodeArgs) != 1 {
return src.Interface{}, &compiler.Error{
return src.Interface{}, nil, &compiler.Error{
Message: "Exactly one type argument expected",
Meta: entity.Meta(),
}
}

resolvedNodeArg, err := a.resolver.ResolveExpr(resolvedNodeArgs[0], scope)
if err != nil {
return src.Interface{}, &compiler.Error{
return src.Interface{}, nil, &compiler.Error{
Message: err.Error(),
Meta: entity.Meta(),
}
}

if resolvedNodeArg.Lit == nil || resolvedNodeArg.Lit.Struct == nil {
return src.Interface{}, &compiler.Error{
return src.Interface{}, nil, &compiler.Error{
Message: "Struct argument expected",
Meta: entity.Meta(),
}
Expand Down Expand Up @@ -335,5 +352,14 @@ func (a Analyzer) getComponentNodeInterface(
},
},
Meta: iface.Meta,
}, nil
}, overloadIndex, nil
}

func (a Analyzer) getNodeOverloadIndex(
node src.Node,
net []src.Connection,
versions []src.Component,
scope src.Scope,
) (int, error) {
panic("not implemented") // TODO
}
2 changes: 1 addition & 1 deletion internal/compiler/analyzer/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type analyzeTypeDefParams struct {
allowEmptyBody bool
}

func (a Analyzer) analyzeTypeDef(def ts.Def, scope src.Scope, params analyzeTypeDefParams) (ts.Def, *compiler.Error) {
func (a Analyzer) analyzeType(def ts.Def, scope src.Scope, params analyzeTypeDefParams) (ts.Def, *compiler.Error) {
if !params.allowEmptyBody && def.BodyExpr == nil {
meta := def.Meta
return ts.Def{}, &compiler.Error{
Expand Down

0 comments on commit 06026c4

Please sign in to comment.