Skip to content

Commit a941ad3

Browse files
committed
Merge remote-tracking branch 'origin/main' into return-error-from-println
2 parents 52db95f + f8ccd72 commit a941ad3

File tree

17 files changed

+174
-92
lines changed

17 files changed

+174
-92
lines changed

.github/workflows/lint.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ jobs:
1717
- uses: actions/checkout@v4
1818
- uses: actions/setup-go@v5
1919
with:
20-
go-version: '1.23'
20+
go-version: '1.24'
2121
cache: false
2222
- name: golangci-lint
2323
uses: golangci/golangci-lint-action@v6
2424
with:
25-
version: v1.60
25+
version: v1.64
2626
only-new-issues: true

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ cmd/tmp
2828
/neva-lsp-windows-arm64.exe
2929
dist
3030
trace.log
31+
ir.yml
3132
output
3233
node_modules
3334
__debug*

.vscode/launch.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"request": "launch",
88
"mode": "auto",
99
"program": "${workspaceFolder}/cmd/neva",
10-
"cwd": "${workspaceFolder}/e2e/for_with_range_and_if",
10+
"cwd": "${workspaceFolder}/e2e/errors_must",
1111
"args": ["run", "--trace", "main"]
1212
},
1313
{

benchmarks/message_passing/bench_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func BenchmarkMessagePassing(b *testing.B) {
3030
// Reset timer after setup
3131
b.ResetTimer()
3232

33-
for i := 0; i < b.N; i++ {
33+
for b.Loop() {
3434
cmd := exec.Command("neva", "run", "message_passing")
3535
out, err := cmd.CombinedOutput()
3636
require.NoError(b, err, string(out))

e2e/cli/run_with_ir/e2e_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package test
2+
3+
import (
4+
"os"
5+
"os/exec"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
"gopkg.in/yaml.v3"
10+
)
11+
12+
func Test(t *testing.T) {
13+
defer func() {
14+
require.NoError(t, os.RemoveAll("src"))
15+
require.NoError(t, os.Remove("ir.yml"))
16+
}()
17+
18+
// Create new project
19+
cmd := exec.Command("neva", "new")
20+
require.NoError(t, cmd.Run())
21+
22+
// Run with IR emission
23+
cmd = exec.Command("neva", "run", "--emit-ir", "src")
24+
out, err := cmd.CombinedOutput()
25+
require.NoError(t, err)
26+
require.Equal(t, "Hello, World!\n", string(out))
27+
require.Equal(t, 0, cmd.ProcessState.ExitCode())
28+
29+
// Verify IR file exists and is valid YAML
30+
irBytes, err := os.ReadFile("ir.yml")
31+
require.NoError(t, err)
32+
33+
var ir struct {
34+
Connections map[string]string `yaml:"connections"`
35+
Funcs []any `yaml:"funcs"`
36+
}
37+
require.NoError(t, yaml.Unmarshal(irBytes, &ir))
38+
require.NotEmpty(t, ir.Funcs)
39+
}

e2e/cli/run_with_ir/neva.yml

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
neva: 0.31.0

e2e/errors_must/e2e_test.go

+12-9
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@ import (
88
)
99

1010
func Test(t *testing.T) {
11-
cmd := exec.Command("neva", "run", "main")
11+
// we run N times to make sure https://github.com/nevalang/neva/issues/872 is fixed
12+
for range 10 {
13+
cmd := exec.Command("neva", "run", "main")
1214

13-
out, err := cmd.CombinedOutput()
14-
require.NoError(t, err)
15-
require.Equal(
16-
t,
17-
"success!\n",
18-
string(out),
19-
)
15+
out, err := cmd.CombinedOutput()
16+
require.NoError(t, err)
17+
require.Equal(
18+
t,
19+
"success!\n",
20+
string(out),
21+
)
2022

21-
require.Equal(t, 0, cmd.ProcessState.ExitCode())
23+
require.Equal(t, 0, cmd.ProcessState.ExitCode())
24+
}
2225
}

examples/delayed_echo/e2e_test.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
)
1212

1313
func Test(t *testing.T) {
14+
// for i := 0; i < 10; i++ {
1415
err := os.Chdir("..")
1516
require.NoError(t, err)
1617

@@ -23,18 +24,18 @@ func Test(t *testing.T) {
2324
start := time.Now()
2425
out, err := cmd.CombinedOutput()
2526
elapsed := time.Since(start)
26-
require.NoError(t, err)
27+
require.NoError(t, err, string(out))
2728

2829
// Check execution time is between 1-5 seconds
2930
require.GreaterOrEqual(t, elapsed.Seconds(), 1.0)
3031
require.LessOrEqual(t, elapsed.Seconds(), 5.0)
3132

3233
// Split output into lines and verify contents
3334
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
34-
require.Equal(t, 7, len(lines)) // Hello + World + 5 numbers
35+
require.Equal(t, 7, len(lines), string(out)) // Hello + World + 5 numbers
3536

3637
// First line must be Hello
37-
require.Equal(t, "Hello", lines[0])
38+
require.Equal(t, "Hello", lines[0], string(out))
3839

3940
// Create set of expected remaining values
4041
expected := map[string]bool{
@@ -59,4 +60,5 @@ func Test(t *testing.T) {
5960
}
6061

6162
require.Equal(t, 0, cmd.ProcessState.ExitCode())
63+
// }
6264
}

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/nevalang/neva
22

3-
go 1.23
3+
go 1.24
44

55
require (
66
github.com/Masterminds/semver/v3 v3.2.1

internal/cli/run.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ func newRunCmd(workdir string, nativec compiler.Compiler) *cli.Command {
2222
Name: "trace",
2323
Usage: "Write trace information to file",
2424
},
25+
&cli.BoolFlag{
26+
Name: "emit-ir",
27+
Usage: "Emit intermediate representation to ir.yml file",
28+
},
2529
},
2630
ArgsUsage: "Provide path to main package",
2731
Action: func(cliCtx *cli.Context) error {
@@ -30,10 +34,8 @@ func newRunCmd(workdir string, nativec compiler.Compiler) *cli.Command {
3034
return err
3135
}
3236

33-
var trace bool
34-
if cliCtx.IsSet("trace") {
35-
trace = true
36-
}
37+
trace := cliCtx.IsSet("trace")
38+
emitIR := cliCtx.IsSet("emit-ir")
3739

3840
// we need to always set GOOS for compiler backend
3941
prevGOOS := os.Getenv("GOOS")
@@ -55,6 +57,7 @@ func newRunCmd(workdir string, nativec compiler.Compiler) *cli.Command {
5557
Main: mainPkg,
5658
Output: workdir,
5759
Trace: trace,
60+
EmitIR: emitIR,
5861
}
5962

6063
if err := nativec.Compile(cliCtx.Context, input); err != nil {

internal/compiler/compiler.go

+23
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,13 @@ package compiler
33
import (
44
"context"
55
"errors"
6+
"fmt"
7+
"os"
8+
"path/filepath"
69
"strings"
710

11+
"gopkg.in/yaml.v3"
12+
813
"github.com/nevalang/neva/internal/compiler/ir"
914
"github.com/nevalang/neva/internal/compiler/sourcecode"
1015
"github.com/nevalang/neva/internal/compiler/sourcecode/core"
@@ -20,6 +25,7 @@ type CompilerInput struct {
2025
Main string
2126
Output string
2227
Trace bool
28+
EmitIR bool
2329
}
2430

2531
func (c Compiler) Compile(ctx context.Context, input CompilerInput) error {
@@ -33,9 +39,26 @@ func (c Compiler) Compile(ctx context.Context, input CompilerInput) error {
3339
return err
3440
}
3541

42+
if input.EmitIR {
43+
if err := c.emitIR(input.Output, meResult.IR); err != nil {
44+
return fmt.Errorf("emit IR: %w", err)
45+
}
46+
}
47+
3648
return c.be.Emit(input.Output, meResult.IR, input.Trace)
3749
}
3850

51+
func (c Compiler) emitIR(dst string, prog *ir.Program) error {
52+
path := filepath.Join(dst, "ir.yml")
53+
f, err := os.Create(path)
54+
if err != nil {
55+
return err
56+
}
57+
defer f.Close()
58+
// fmt.Println(dst, path, prog)
59+
return yaml.NewEncoder(f).Encode(prog)
60+
}
61+
3962
type Frontend struct {
4063
builder Builder
4164
parser Parser

internal/compiler/desugarer/desugarer.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@ type Scope interface {
120120
Entity(ref core.EntityRef) (src.Entity, core.Location, error)
121121
Relocate(location core.Location) src.Scope
122122
Location() *core.Location
123-
GetFirstInportName(nodes map[string]src.Node, portAddr src.PortAddr) (string, error)
124-
GetFirstOutportName(nodes map[string]src.Node, portAddr src.PortAddr) (string, error)
123+
GetNodeIOByPortAddr(nodes map[string]src.Node, portAddr src.PortAddr) (src.IO, error)
125124
}
126125

127126
func (d *Desugarer) desugarPkg(pkg src.Package, scope Scope) (src.Package, error) {

internal/compiler/desugarer/mocks_test.go

+7-22
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/compiler/desugarer/network.go

+40-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package desugarer
22

33
import (
4+
"errors"
45
"fmt"
56

67
"github.com/nevalang/neva/internal/compiler"
@@ -238,7 +239,7 @@ func (d *Desugarer) desugarSingleReceiver(
238239
}, nil
239240
}
240241

241-
firstInportName, err := scope.GetFirstInportName(nodes, *receiver.PortAddr)
242+
firstInportName, err := d.getFirstInportName(scope, nodes, *receiver.PortAddr)
242243
if err != nil {
243244
return desugarReceiverResult{}, fmt.Errorf("get first inport name: %w", err)
244245
}
@@ -441,7 +442,7 @@ func (d *Desugarer) desugarChainedConnection(
441442
chainHeadPort = chainHead.PortAddr.Port
442443
if chainHeadPort == "" {
443444
var err error
444-
chainHeadPort, err = scope.GetFirstInportName(nodes, *chainHead.PortAddr)
445+
chainHeadPort, err = d.getFirstInportName(scope, nodes, *chainHead.PortAddr)
445446
if err != nil {
446447
return desugarConnectionResult{}, fmt.Errorf("get first inport name: %w", err)
447448
}
@@ -688,7 +689,7 @@ func (d *Desugarer) desugarSingleSender(
688689
if sender.PortAddr != nil {
689690
portName := sender.PortAddr.Port
690691
if sender.PortAddr.Port == "" {
691-
firstOutportName, err := scope.GetFirstOutportName(nodes, *sender.PortAddr)
692+
firstOutportName, err := d.getFirstOutportName(scope, nodes, *sender.PortAddr)
692693
if err != nil {
693694
return desugarSenderResult{}, fmt.Errorf("get first outport name: %w", err)
694695
}
@@ -842,6 +843,42 @@ func (d *Desugarer) desugarSingleSender(
842843
}, nil
843844
}
844845

846+
func (d *Desugarer) getFirstInportName(
847+
scope Scope,
848+
nodes map[string]src.Node,
849+
portAddr src.PortAddr,
850+
) (string, error) {
851+
io, err := scope.GetNodeIOByPortAddr(nodes, portAddr)
852+
if err != nil {
853+
return "", err
854+
}
855+
for inport := range io.In {
856+
return inport, nil
857+
}
858+
return "", errors.New("first inport not found")
859+
}
860+
861+
func (d *Desugarer) getFirstOutportName(
862+
scope Scope,
863+
nodes map[string]src.Node,
864+
portAddr src.PortAddr,
865+
) (string, error) {
866+
io, err := scope.GetNodeIOByPortAddr(nodes, portAddr)
867+
if err != nil {
868+
return "", err
869+
}
870+
871+
// important: skip `err` outport if node has err guard
872+
for outport := range io.Out {
873+
if outport == "err" && nodes[portAddr.Node].ErrGuard {
874+
continue
875+
}
876+
return outport, nil
877+
}
878+
879+
return "", errors.New("first outport not found")
880+
}
881+
845882
var newComponentRef = core.EntityRef{
846883
Pkg: "builtin",
847884
Name: "New",

0 commit comments

Comments
 (0)