Compare commits
3 Commits
588f4cd521
...
5ccc41b104
| Author | SHA1 | Date | |
|---|---|---|---|
|
5ccc41b104
|
|||
|
e17a85e0a3
|
|||
|
4a5c424e54
|
6
Makefile
6
Makefile
@@ -1,7 +1,7 @@
|
|||||||
BINARY_NAME=lambda
|
BINARY_NAME=lambda
|
||||||
TEST=simple
|
TEST=simple
|
||||||
|
|
||||||
.PHONY: help build run profile explain graph docs bench clean
|
.PHONY: help build run profile explain graph docs test bench clean
|
||||||
.DEFAULT_GOAL := help
|
.DEFAULT_GOAL := help
|
||||||
.SILENT:
|
.SILENT:
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@ help:
|
|||||||
echo " explain - Build and run with explanation mode and profiling"
|
echo " explain - Build and run with explanation mode and profiling"
|
||||||
echo " graph - Generate and open CPU profile visualization"
|
echo " graph - Generate and open CPU profile visualization"
|
||||||
echo " docs - Start local godoc server on port 6060"
|
echo " docs - Start local godoc server on port 6060"
|
||||||
|
echo " test - Run tests for all samples"
|
||||||
echo " bench - Run benchmarks for all samples"
|
echo " bench - Run benchmarks for all samples"
|
||||||
echo " clean - Remove all build artifacts"
|
echo " clean - Remove all build artifacts"
|
||||||
|
|
||||||
@@ -38,6 +39,9 @@ docs:
|
|||||||
echo ">>> View at 'http://localhost:6060/pkg/git.maximhutz.com/max/lambda/'"
|
echo ">>> View at 'http://localhost:6060/pkg/git.maximhutz.com/max/lambda/'"
|
||||||
go run golang.org/x/tools/cmd/godoc@latest -http=:6060
|
go run golang.org/x/tools/cmd/godoc@latest -http=:6060
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test -v ./cmd/lambda
|
||||||
|
|
||||||
bench:
|
bench:
|
||||||
go test -bench=. -benchtime=10x -cpu=4 ./cmd/lambda
|
go test -bench=. -benchtime=10x -cpu=4 ./cmd/lambda
|
||||||
|
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"git.maximhutz.com/max/lambda/internal/config"
|
|
||||||
"git.maximhutz.com/max/lambda/internal/engine"
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/convert"
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/lambda"
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/saccharine"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Helper function to run a single sample through the lambda interpreter.
|
|
||||||
func runSample(samplePath string) error {
|
|
||||||
// Read the sample file.
|
|
||||||
input, err := os.ReadFile(samplePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse code into syntax tree.
|
|
||||||
ast, err := saccharine.Parse(string(input))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile expression to lambda calculus.
|
|
||||||
compiled := convert.SaccharineToLambda(ast)
|
|
||||||
|
|
||||||
// Create minimal config for benchmarking.
|
|
||||||
cfg := &config.Config{
|
|
||||||
Source: config.StringSource{Data: ""},
|
|
||||||
Destination: config.StdoutDestination{},
|
|
||||||
Profile: "",
|
|
||||||
Explanation: false,
|
|
||||||
Statistics: false,
|
|
||||||
Verbose: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and run the engine.
|
|
||||||
process := engine.New(cfg, &compiled)
|
|
||||||
process.Run()
|
|
||||||
|
|
||||||
// Get final result (to ensure it's not optimized away).
|
|
||||||
_ = lambda.Stringify(compiled)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Benchmark all samples using sub-benchmarks.
|
|
||||||
func BenchmarkSamples(b *testing.B) {
|
|
||||||
samples := map[string]string{
|
|
||||||
"Church": "../../tests/church.test",
|
|
||||||
"Fast": "../../tests/fast.test",
|
|
||||||
"Saccharine": "../../tests/saccharine.test",
|
|
||||||
"Simple": "../../tests/simple.test",
|
|
||||||
"Thunk": "../../tests/thunk.test",
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, path := range samples {
|
|
||||||
b.Run(name, func(b *testing.B) {
|
|
||||||
for b.Loop() {
|
|
||||||
if err := runSample(path); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
97
cmd/lambda/lambda_test.go
Normal file
97
cmd/lambda/lambda_test.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.maximhutz.com/max/lambda/internal/config"
|
||||||
|
"git.maximhutz.com/max/lambda/internal/engine"
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/convert"
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/lambda"
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/saccharine"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Helper function to run a single sample through the lambda interpreter.
|
||||||
|
func runSample(samplePath string) (string, error) {
|
||||||
|
// Read the sample file.
|
||||||
|
input, err := os.ReadFile(samplePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse code into syntax tree.
|
||||||
|
ast, err := saccharine.Parse(string(input))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile expression to lambda calculus.
|
||||||
|
compiled := convert.SaccharineToLambda(ast)
|
||||||
|
|
||||||
|
// Create minimal config for benchmarking.
|
||||||
|
cfg := &config.Config{
|
||||||
|
Source: config.StringSource{Data: ""},
|
||||||
|
Destination: config.StdoutDestination{},
|
||||||
|
Profile: "",
|
||||||
|
Explanation: false,
|
||||||
|
Statistics: false,
|
||||||
|
Verbose: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and run the engine.
|
||||||
|
process := engine.New(cfg, &compiled)
|
||||||
|
process.Run()
|
||||||
|
|
||||||
|
return lambda.Stringify(compiled) + "\n", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that all samples produce expected output.
|
||||||
|
func TestSamplesValidity(t *testing.T) {
|
||||||
|
// Discover all .test files in the tests directory.
|
||||||
|
testFiles, err := filepath.Glob("../../tests/*.test")
|
||||||
|
assert.NoError(t, err, "Failed to read tests directory.")
|
||||||
|
assert.NotEmpty(t, testFiles, "No '*.test' files found in directory.")
|
||||||
|
|
||||||
|
for _, testPath := range testFiles {
|
||||||
|
// Build expected file path.
|
||||||
|
expectedPath := strings.TrimSuffix(testPath, filepath.Ext(testPath)) + ".expected"
|
||||||
|
|
||||||
|
name := strings.TrimSuffix(filepath.Base(testPath), filepath.Ext(testPath))
|
||||||
|
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
// Run the sample and capture output.
|
||||||
|
actual, err := runSample(testPath)
|
||||||
|
assert.NoError(t, err, "Failed to run sample.")
|
||||||
|
|
||||||
|
// Read expected output.
|
||||||
|
expectedBytes, err := os.ReadFile(expectedPath)
|
||||||
|
assert.NoError(t, err, "Failed to read expected output.")
|
||||||
|
expected := string(expectedBytes)
|
||||||
|
|
||||||
|
// Compare outputs.
|
||||||
|
assert.Equal(t, expected, actual, "Output does not match expected.")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark all samples using sub-benchmarks.
|
||||||
|
func BenchmarkSamples(b *testing.B) {
|
||||||
|
// Discover all .test files in the tests directory.
|
||||||
|
testFiles, err := filepath.Glob("../../tests/*.test")
|
||||||
|
assert.NoError(b, err, "Failed to read tests directory.")
|
||||||
|
assert.NotEmpty(b, testFiles, "No '*.test' files found in directory.")
|
||||||
|
|
||||||
|
for _, path := range testFiles {
|
||||||
|
name := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
|
||||||
|
|
||||||
|
b.Run(name, func(b *testing.B) {
|
||||||
|
for b.Loop() {
|
||||||
|
_, err := runSample(path)
|
||||||
|
assert.NoError(b, err, "Failed to run sample.")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
8
go.mod
8
go.mod
@@ -1,3 +1,11 @@
|
|||||||
module git.maximhutz.com/max/lambda
|
module git.maximhutz.com/max/lambda
|
||||||
|
|
||||||
go 1.25.5
|
go 1.25.5
|
||||||
|
|
||||||
|
require github.com/stretchr/testify v1.11.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
|
|||||||
9
go.sum
Normal file
9
go.sum
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
1
tests/church_5^5.expected
Normal file
1
tests/church_5^5.expected
Normal file
File diff suppressed because one or more lines are too long
@@ -1,7 +1,8 @@
|
|||||||
0 := \f.\x.x
|
0 := \f.\x.x
|
||||||
inc n := \f x.(f (n f x))
|
inc n := \f x.(f (n f x))
|
||||||
exp n m := (m n)
|
exp n m := (m n)
|
||||||
|
print n := (n F X)
|
||||||
|
|
||||||
N := (inc (inc (inc (inc (inc 0)))))
|
N := (inc (inc (inc (inc (inc 0)))))
|
||||||
|
|
||||||
(exp N N)
|
(print (exp N N))
|
||||||
1
tests/church_6^6.expected
Normal file
1
tests/church_6^6.expected
Normal file
File diff suppressed because one or more lines are too long
8
tests/church_6^6.test
Normal file
8
tests/church_6^6.test
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
0 := \f.\x.x
|
||||||
|
inc n := \f x.(f (n f x))
|
||||||
|
exp n m := (m n)
|
||||||
|
print n := (n F X)
|
||||||
|
|
||||||
|
N := (inc (inc (inc (inc (inc (inc 0))))))
|
||||||
|
|
||||||
|
(print (exp N N))
|
||||||
1
tests/fast_list_2^30.expected
Normal file
1
tests/fast_list_2^30.expected
Normal file
@@ -0,0 +1 @@
|
|||||||
|
(0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (1 END)))))))))))))))))))))))))))))))
|
||||||
1
tests/list_2^30.expected
Normal file
1
tests/list_2^30.expected
Normal file
@@ -0,0 +1 @@
|
|||||||
|
(0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (0 (1 END)))))))))))))))))))))))))))))))
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
(\0.
|
|
||||||
(\inc.
|
|
||||||
(\add.
|
|
||||||
(\mult.
|
|
||||||
(\exp.
|
|
||||||
(exp (inc (inc (inc (inc 0)))) (inc (inc (inc (inc (inc 0))))))
|
|
||||||
\n m.(m n)
|
|
||||||
)
|
|
||||||
\m n f.(m (n f))
|
|
||||||
)
|
|
||||||
\n m.(m inc n)
|
|
||||||
)
|
|
||||||
\n f x.(f (n f x))
|
|
||||||
)
|
|
||||||
\f x.x
|
|
||||||
)
|
|
||||||
1
tests/thunk.expected
Normal file
1
tests/thunk.expected
Normal file
@@ -0,0 +1 @@
|
|||||||
|
VALUE
|
||||||
Reference in New Issue
Block a user