From f45e882ce16251301af1f5b0d203ccdfc6bf6190 Mon Sep 17 00:00:00 2001 From: Emil Valeev Date: Sat, 11 Jan 2025 22:26:57 +0500 Subject: [PATCH] fix(desugarer:tests): network/union (add missing directive to __new__ node) --- internal/compiler/desugarer/network.go | 16 + internal/compiler/desugarer/network_test.go | 195 +++++++++ internal/compiler/desugarer/union.go | 422 ++++++++++---------- 3 files changed, 422 insertions(+), 211 deletions(-) diff --git a/internal/compiler/desugarer/network.go b/internal/compiler/desugarer/network.go index 936a348d..fc78b104 100644 --- a/internal/compiler/desugarer/network.go +++ b/internal/compiler/desugarer/network.go @@ -791,6 +791,22 @@ func (d *Desugarer) desugarSingleSender( }, nil } + if sender.Union != nil { + result, err := d.desugarUnionSender( + *sender.Union, + normConn, + nodesToInsert, + constsToInsert, + ) + if err != nil { + return desugarSenderResult{}, fmt.Errorf("desugar union sender: %w", err) + } + return desugarSenderResult{ + replace: result.replace, + insert: result.insert, + }, nil + } + if sender.Ternary != nil { result, err := d.desugarTernarySender( iface, diff --git a/internal/compiler/desugarer/network_test.go b/internal/compiler/desugarer/network_test.go index b9fc3429..4376c898 100644 --- a/internal/compiler/desugarer/network_test.go +++ b/internal/compiler/desugarer/network_test.go @@ -953,6 +953,201 @@ func TestDesugarNetwork(t *testing.T) { }, }, }, + { + name: "union_sender_tag_only", + net: []src.Connection{ + { + Normal: &src.NormalConnection{ + Senders: []src.ConnectionSender{ + { + Union: &src.UnionSender{ + EntityRef: core.EntityRef{Name: "Input"}, + Tag: "Int", + }, + }, + }, + Receivers: []src.ConnectionReceiver{ + {PortAddr: &src.PortAddr{Node: "foo", Port: "bar"}}, + }, + }, + }, + }, + nodes: map[string]src.Node{ + "foo": {EntityRef: core.EntityRef{Name: "Foo"}}, + }, + expectedResult: handleNetworkResult{ + desugaredConnections: []src.Connection{ + { + Normal: &src.NormalConnection{ + Senders: []src.ConnectionSender{{ + PortAddr: &src.PortAddr{ + Node: "__new__1", + Port: "res", + }, + }}, + Receivers: []src.ConnectionReceiver{ + {PortAddr: &src.PortAddr{Node: "foo", Port: "bar"}}, + }, + }, + }, + { + Normal: &src.NormalConnection{ + Senders: []src.ConnectionSender{{ + Const: &src.Const{ + Value: src.ConstValue{ + Message: &src.MsgLiteral{ + Union: &src.UnionLiteral{ + EntityRef: core.EntityRef{Name: "Input"}, + Tag: "Int", + }, + }, + }, + }, + }}, + Receivers: []src.ConnectionReceiver{{ + PortAddr: &src.PortAddr{ + Node: "__new__1", + Port: "data", + }, + }}, + }, + }, + }, + nodesToInsert: map[string]src.Node{ + "__new__1": { + EntityRef: core.EntityRef{ + Pkg: "builtin", + Name: "New", + }, + Directives: map[src.Directive][]string{ + compiler.BindDirective: {"__union_const__1"}, + }, + }, + }, + constsToInsert: map[string]src.Const{ + "__union_const__1": { + Value: src.ConstValue{ + Message: &src.MsgLiteral{ + Union: &src.UnionLiteral{ + EntityRef: core.EntityRef{Name: "Input"}, + Tag: "Int", + }, + }, + }, + }, + }, + }, + }, + { + name: "union_sender_with_value", + net: []src.Connection{ + { + Normal: &src.NormalConnection{ + Senders: []src.ConnectionSender{ + { + Union: &src.UnionSender{ + EntityRef: core.EntityRef{Name: "Input"}, + Tag: "Int", + Data: &src.ConnectionSender{ + Const: &src.Const{ + TypeExpr: ts.Expr{ + Inst: &ts.InstExpr{ + Ref: core.EntityRef{Name: "int"}, + }, + }, + Value: src.ConstValue{ + Message: &src.MsgLiteral{Int: compiler.Pointer(42)}, + }, + }, + }, + }, + }, + }, + Receivers: []src.ConnectionReceiver{ + {PortAddr: &src.PortAddr{Node: "foo", Port: "bar"}}, + }, + }, + }, + }, + nodes: map[string]src.Node{ + "foo": {EntityRef: core.EntityRef{Name: "Foo"}}, + }, + expectedResult: handleNetworkResult{ + desugaredConnections: []src.Connection{ + { + Normal: &src.NormalConnection{ + Senders: []src.ConnectionSender{{ + PortAddr: &src.PortAddr{ + Node: "__union__1", + Port: "res", + }, + }}, + Receivers: []src.ConnectionReceiver{ + {PortAddr: &src.PortAddr{Node: "foo", Port: "bar"}}, + }, + }, + }, + { + Normal: &src.NormalConnection{ + Senders: []src.ConnectionSender{{ + Const: &src.Const{ + Value: src.ConstValue{ + Message: &src.MsgLiteral{ + Str: compiler.Pointer("Int"), + }, + }, + }, + }}, + Receivers: []src.ConnectionReceiver{{ + PortAddr: &src.PortAddr{ + Node: "__union__1", + Port: "tag", + }, + }}, + }, + }, + { + Normal: &src.NormalConnection{ + Senders: []src.ConnectionSender{{ + Const: &src.Const{ + TypeExpr: ts.Expr{ + Inst: &ts.InstExpr{ + Ref: core.EntityRef{Name: "int"}, + }, + }, + Value: src.ConstValue{ + Message: &src.MsgLiteral{Int: compiler.Pointer(42)}, + }, + }, + }}, + Receivers: []src.ConnectionReceiver{{ + PortAddr: &src.PortAddr{ + Node: "__union__1", + Port: "data", + }, + }}, + }, + }, + }, + nodesToInsert: map[string]src.Node{ + "__union__1": { + EntityRef: core.EntityRef{ + Pkg: "builtin", + Name: "UnionWrap", + }, + }, + }, + constsToInsert: map[string]src.Const{ + "__union_tag__1": { + Value: src.ConstValue{ + Message: &src.MsgLiteral{ + Str: compiler.Pointer("Int"), + }, + }, + }, + }, + }, + }, } for _, tt := range tests { diff --git a/internal/compiler/desugarer/union.go b/internal/compiler/desugarer/union.go index 5ffd8cc9..82880b21 100644 --- a/internal/compiler/desugarer/union.go +++ b/internal/compiler/desugarer/union.go @@ -1,213 +1,213 @@ package desugarer -// import ( -// "fmt" - -// "github.com/nevalang/neva/internal/compiler" -// src "github.com/nevalang/neva/internal/compiler/sourcecode" -// "github.com/nevalang/neva/internal/compiler/sourcecode/core" -// ) - -// type handleUnionSenderResult struct { -// replace src.Connection -// insert []src.Connection -// } - -// // desugarUnionSender handles the four cases of union senders: -// // 1. Input::Int -> (non-chained, tag only) -// // 2. -> Input::Int -> (chained, tag only) -// // 3. Input::Int(foo) -> (non-chained, with value) -// // 4. -> Input::Int(foo) -> (chained, with value) -// func (d *Desugarer) desugarUnionSender( -// union src.UnionSender, -// normConn src.NormalConnection, -// nodesToInsert map[string]src.Node, -// constsToInsert map[string]src.Const, -// ) (handleUnionSenderResult, error) { -// if union.Data == nil { -// // cases 1 & 2: tag only -// return d.handleTagOnlyUnionSender(union, normConn, nodesToInsert, constsToInsert) -// } -// // cases 3 & 4: with value -// return d.handleValueUnionSender(union, normConn, nodesToInsert, constsToInsert) -// } - -// // handleTagOnlyUnionSender handles cases 1 & 2 (tag-only union senders) -// func (d *Desugarer) handleTagOnlyUnionSender( -// union src.UnionSender, -// normConn src.NormalConnection, -// nodesToInsert map[string]src.Node, -// constsToInsert map[string]src.Const, -// ) (handleUnionSenderResult, error) { -// // create virtual const of type union with specified tag -// d.virtualConstCount++ -// constName := fmt.Sprintf("__union_const__%d", d.virtualConstCount) - -// // create const with union type and tag -// constsToInsert[constName] = src.Const{ -// Value: src.ConstValue{ -// Message: &src.MsgLiteral{ -// Union: &src.UnionLiteral{ -// EntityRef: union.EntityRef, -// Tag: union.Tag, -// }, -// }, -// }, -// Meta: union.Meta, -// } - -// // create new node and bind const to it -// constNodeName := fmt.Sprintf("__new__%d", d.virtualConstCount) -// nodesToInsert[constNodeName] = src.Node{ -// EntityRef: core.EntityRef{ -// Pkg: "builtin", -// Name: "New", -// }, -// Directives: map[src.Directive][]string{ -// compiler.BindDirective: {constName}, -// }, -// Meta: union.Meta, -// } - -// // create connection from new node to original receiver -// replace := src.Connection{ -// Normal: &src.NormalConnection{ -// Senders: []src.ConnectionSender{{ -// PortAddr: &src.PortAddr{ -// Node: constNodeName, -// Port: "res", -// }, -// Meta: union.Meta, -// }}, -// Receivers: normConn.Receivers, -// Meta: union.Meta, -// }, -// Meta: union.Meta, -// } - -// insert := []src.Connection{ -// { -// Normal: &src.NormalConnection{ -// Senders: []src.ConnectionSender{{ -// Const: &src.Const{ -// Value: src.ConstValue{ -// Message: &src.MsgLiteral{ -// Union: &src.UnionLiteral{ -// EntityRef: union.EntityRef, -// Tag: union.Tag, -// }, -// }, -// }, -// }, -// Meta: union.Meta, -// }}, -// Receivers: []src.ConnectionReceiver{{ -// PortAddr: &src.PortAddr{ -// Node: constNodeName, -// Port: "data", -// }, -// Meta: union.Meta, -// }}, -// Meta: union.Meta, -// }, -// Meta: union.Meta, -// }, -// } - -// return handleUnionSenderResult{ -// replace: replace, -// insert: insert, -// }, nil -// } - -// // handleValueUnionSender handles cases 3 & 4 (union senders with wrapped values) -// func (d *Desugarer) handleValueUnionSender( -// union src.UnionSender, -// normConn src.NormalConnection, -// nodesToInsert map[string]src.Node, -// constsToInsert map[string]src.Const, -// ) (handleUnionSenderResult, error) { -// // create virtual const for tag -// d.virtualConstCount++ -// constName := fmt.Sprintf("__union_tag__%d", d.virtualConstCount) - -// constsToInsert[constName] = src.Const{ -// Value: src.ConstValue{ -// Message: &src.MsgLiteral{ -// Str: &union.Tag, -// }, -// }, -// Meta: union.Meta, -// } - -// // create union wrapper node -// nodeName := fmt.Sprintf("__union__%d", d.virtualConstCount) -// nodesToInsert[nodeName] = src.Node{ -// EntityRef: core.EntityRef{ -// Pkg: "builtin", -// Name: "UnionWrap", -// }, -// Meta: union.Meta, -// } - -// // create connections for the union wrapper -// replace := src.Connection{ -// Normal: &src.NormalConnection{ -// Senders: []src.ConnectionSender{{ -// PortAddr: &src.PortAddr{ -// Node: nodeName, -// Port: "res", -// }, -// Meta: union.Meta, -// }}, -// Receivers: normConn.Receivers, -// Meta: union.Meta, -// }, -// Meta: union.Meta, -// } - -// insert := []src.Connection{ -// { -// Normal: &src.NormalConnection{ -// Senders: []src.ConnectionSender{{ -// Const: &src.Const{ -// Value: src.ConstValue{ -// Message: &src.MsgLiteral{ -// Str: &union.Tag, -// }, -// }, -// }, -// Meta: union.Meta, -// }}, -// Receivers: []src.ConnectionReceiver{{ -// PortAddr: &src.PortAddr{ -// Node: nodeName, -// Port: "tag", -// }, -// Meta: union.Meta, -// }}, -// Meta: union.Meta, -// }, -// Meta: union.Meta, -// }, -// { -// Normal: &src.NormalConnection{ -// Senders: []src.ConnectionSender{*union.Data}, -// Receivers: []src.ConnectionReceiver{{ -// PortAddr: &src.PortAddr{ -// Node: nodeName, -// Port: "data", -// }, -// Meta: union.Meta, -// }}, -// Meta: union.Meta, -// }, -// Meta: union.Meta, -// }, -// } - -// return handleUnionSenderResult{ -// replace: replace, -// insert: insert, -// }, nil -// } +import ( + "fmt" + + "github.com/nevalang/neva/internal/compiler" + src "github.com/nevalang/neva/internal/compiler/sourcecode" + "github.com/nevalang/neva/internal/compiler/sourcecode/core" +) + +type handleUnionSenderResult struct { + replace src.Connection + insert []src.Connection +} + +// desugarUnionSender handles the four cases of union senders: +// 1. Input::Int -> (non-chained, tag only) +// 2. -> Input::Int -> (chained, tag only) +// 3. Input::Int(foo) -> (non-chained, with value) +// 4. -> Input::Int(foo) -> (chained, with value) +func (d *Desugarer) desugarUnionSender( + union src.UnionSender, + normConn src.NormalConnection, + nodesToInsert map[string]src.Node, + constsToInsert map[string]src.Const, +) (handleUnionSenderResult, error) { + if union.Data == nil { + // cases 1 & 2: tag only + return d.handleTagOnlyUnionSender(union, normConn, nodesToInsert, constsToInsert) + } + // cases 3 & 4: with value + return d.handleValueUnionSender(union, normConn, nodesToInsert, constsToInsert) +} + +// handleTagOnlyUnionSender handles cases 1 & 2 (tag-only union senders) +func (d *Desugarer) handleTagOnlyUnionSender( + union src.UnionSender, + normConn src.NormalConnection, + nodesToInsert map[string]src.Node, + constsToInsert map[string]src.Const, +) (handleUnionSenderResult, error) { + // create virtual const of type union with specified tag + d.virtualConstCount++ + constName := fmt.Sprintf("__union_const__%d", d.virtualConstCount) + + // create const with union type and tag + constsToInsert[constName] = src.Const{ + Value: src.ConstValue{ + Message: &src.MsgLiteral{ + Union: &src.UnionLiteral{ + EntityRef: union.EntityRef, + Tag: union.Tag, + }, + }, + }, + Meta: union.Meta, + } + + // create new node and bind const to it + constNodeName := fmt.Sprintf("__new__%d", d.virtualConstCount) + nodesToInsert[constNodeName] = src.Node{ + EntityRef: core.EntityRef{ + Pkg: "builtin", + Name: "New", + }, + Directives: map[src.Directive][]string{ + compiler.BindDirective: {constName}, + }, + Meta: union.Meta, + } + + // create connection from new node to original receiver + replace := src.Connection{ + Normal: &src.NormalConnection{ + Senders: []src.ConnectionSender{{ + PortAddr: &src.PortAddr{ + Node: constNodeName, + Port: "res", + }, + Meta: union.Meta, + }}, + Receivers: normConn.Receivers, + Meta: union.Meta, + }, + Meta: union.Meta, + } + + insert := []src.Connection{ + { + Normal: &src.NormalConnection{ + Senders: []src.ConnectionSender{{ + Const: &src.Const{ + Value: src.ConstValue{ + Message: &src.MsgLiteral{ + Union: &src.UnionLiteral{ + EntityRef: union.EntityRef, + Tag: union.Tag, + }, + }, + }, + }, + Meta: union.Meta, + }}, + Receivers: []src.ConnectionReceiver{{ + PortAddr: &src.PortAddr{ + Node: constNodeName, + Port: "data", + }, + Meta: union.Meta, + }}, + Meta: union.Meta, + }, + Meta: union.Meta, + }, + } + + return handleUnionSenderResult{ + replace: replace, + insert: insert, + }, nil +} + +// handleValueUnionSender handles cases 3 & 4 (union senders with wrapped values) +func (d *Desugarer) handleValueUnionSender( + union src.UnionSender, + normConn src.NormalConnection, + nodesToInsert map[string]src.Node, + constsToInsert map[string]src.Const, +) (handleUnionSenderResult, error) { + // create virtual const for tag + d.virtualConstCount++ + constName := fmt.Sprintf("__union_tag__%d", d.virtualConstCount) + + constsToInsert[constName] = src.Const{ + Value: src.ConstValue{ + Message: &src.MsgLiteral{ + Str: &union.Tag, + }, + }, + Meta: union.Meta, + } + + // create union wrapper node + nodeName := fmt.Sprintf("__union__%d", d.virtualConstCount) + nodesToInsert[nodeName] = src.Node{ + EntityRef: core.EntityRef{ + Pkg: "builtin", + Name: "UnionWrap", + }, + Meta: union.Meta, + } + + // create connections for the union wrapper + replace := src.Connection{ + Normal: &src.NormalConnection{ + Senders: []src.ConnectionSender{{ + PortAddr: &src.PortAddr{ + Node: nodeName, + Port: "res", + }, + Meta: union.Meta, + }}, + Receivers: normConn.Receivers, + Meta: union.Meta, + }, + Meta: union.Meta, + } + + insert := []src.Connection{ + { + Normal: &src.NormalConnection{ + Senders: []src.ConnectionSender{{ + Const: &src.Const{ + Value: src.ConstValue{ + Message: &src.MsgLiteral{ + Str: &union.Tag, + }, + }, + }, + Meta: union.Meta, + }}, + Receivers: []src.ConnectionReceiver{{ + PortAddr: &src.PortAddr{ + Node: nodeName, + Port: "tag", + }, + Meta: union.Meta, + }}, + Meta: union.Meta, + }, + Meta: union.Meta, + }, + { + Normal: &src.NormalConnection{ + Senders: []src.ConnectionSender{*union.Data}, + Receivers: []src.ConnectionReceiver{{ + PortAddr: &src.PortAddr{ + Node: nodeName, + Port: "data", + }, + Meta: union.Meta, + }}, + Meta: union.Meta, + }, + Meta: union.Meta, + }, + } + + return handleUnionSenderResult{ + replace: replace, + insert: insert, + }, nil +}