3 Commits

Author SHA1 Message Date
35224ee4d7 style: renamed samples to tests 2026-01-11 20:31:36 -05:00
da9ee0bcb0 fix: correct gitignore pattern to only exclude root binary
Changed 'lambda' to '/lambda' in .gitignore to prevent accidentally
hiding files in pkg/lambda/ directory. The pattern now correctly
excludes only the compiled binary at the repository root.

Also adds previously hidden pkg/lambda/iterator.go to the repository.
2026-01-11 20:21:15 -05:00
d4f33c1658 feat: progress 2026-01-11 20:18:38 -05:00
14 changed files with 11658 additions and 135 deletions

2
.gitignore vendored
View File

@@ -3,7 +3,7 @@
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
# #
# Binaries for programs and plugins # Binaries for programs and plugins
lambda /lambda
*.exe *.exe
*.exe~ *.exe~
*.dll *.dll

View File

@@ -21,7 +21,7 @@ build:
chmod +x ${BINARY_NAME} chmod +x ${BINARY_NAME}
run: build run: build
./${BINARY_NAME} -f ./samples/$(TEST).txt -o program.out ./${BINARY_NAME} -s -f ./samples/$(TEST).txt -o program.out
profile: build profile: build
./${BINARY_NAME} -p profile/cpu.prof -f ./samples/$(TEST).txt -o program.out ./${BINARY_NAME} -p profile/cpu.prof -f ./samples/$(TEST).txt -o program.out

View File

@@ -51,11 +51,11 @@ func runSample(samplePath string) error {
// Benchmark all samples using sub-benchmarks. // Benchmark all samples using sub-benchmarks.
func BenchmarkSamples(b *testing.B) { func BenchmarkSamples(b *testing.B) {
samples := map[string]string{ samples := map[string]string{
"Church": "../../samples/church.txt", "Church": "../../samples/church.test",
"Fast": "../../samples/fast.txt", "Fast": "../../samples/fast.test",
"Saccharine": "../../samples/saccharine.txt", "Saccharine": "../../samples/saccharine.test",
"Simple": "../../samples/simple.txt", "Simple": "../../samples/simple.test",
"Thunk": "../../samples/thunk.txt", "Thunk": "../../samples/thunk.test",
} }
for name, path := range samples { for name, path := range samples {

View File

@@ -24,9 +24,9 @@ func New(config *config.Config, expression *lambda.Expression) *Engine {
func (e Engine) Run() { func (e Engine) Run() {
e.Emit("start") e.Emit("start")
for lambda.ReduceOnce(e.Expression) { lambda.ReduceAll(e.Expression, func() {
e.Emit("step") e.Emit("step")
} })
e.Emit("end") e.Emit("end")
} }

11518
out.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,35 +0,0 @@
package fifo
import "fmt"
type FIFO[T any] []T
func New[T any](items ...T) *FIFO[T] {
f := FIFO[T](items)
return &f
}
func (f *FIFO[T]) Push(item T) {
*f = append(*f, item)
}
func (f *FIFO[T]) Empty() bool {
return len(*f) == 0
}
func (f *FIFO[T]) MustPop() T {
var item T
*f, item = (*f)[:len(*f)-1], (*f)[len(*f)-1]
return item
}
func (f *FIFO[T]) Pop() (T, error) {
var item T
if f.Empty() {
return item, fmt.Errorf("stack is exhausted")
}
return f.MustPop(), nil
}

59
pkg/lambda/iterator.go Normal file
View File

@@ -0,0 +1,59 @@
package lambda
type Iterator struct {
trace []*Expression
}
func NewIterator(expr *Expression) *Iterator {
return &Iterator{
trace: []*Expression{expr},
}
}
func (i *Iterator) Current() *Expression {
if len(i.trace) < 1 {
return nil
}
return i.trace[len(i.trace)-1]
}
func (i *Iterator) Parent() *Expression {
if len(i.trace) < 2 {
return nil
}
return i.trace[len(i.trace)-2]
}
func (i *Iterator) Next() {
switch typed := (*i.Current()).(type) {
case *Abstraction:
i.trace = append(i.trace, &typed.body)
case *Application:
i.trace = append(i.trace, &typed.abstraction)
case *Variable:
for len(i.trace) > 1 {
if app, ok := (*i.Parent()).(*Application); ok {
if app.abstraction == *i.Current() {
i.Back()
i.trace = append(i.trace, &app.argument)
return
}
}
i.Back()
}
i.trace = []*Expression{}
}
}
func (i *Iterator) Back() bool {
if len(i.trace) == 0 {
return false
}
i.trace = i.trace[:len(i.trace)-1]
return true
}

View File

@@ -1,9 +1,11 @@
package lambda package lambda
import "git.maximhutz.com/max/lambda/pkg/fifo" import (
"git.maximhutz.com/max/lambda/pkg/lifo"
)
func ReduceOnce(e *Expression) bool { func ReduceOnce(e *Expression) bool {
stack := fifo.New(e) stack := lifo.New(e)
for !stack.Empty() { for !stack.Empty() {
top := stack.MustPop() top := stack.MustPop()
@@ -13,8 +15,7 @@ func ReduceOnce(e *Expression) bool {
stack.Push(&typed.body) stack.Push(&typed.body)
case *Application: case *Application:
if fn, fnOk := typed.abstraction.(*Abstraction); fnOk { if fn, fnOk := typed.abstraction.(*Abstraction); fnOk {
reduced := Substitute(fn.body, fn.parameter, typed.argument) *top = Substitute(fn.body, fn.parameter, typed.argument)
*top = reduced
return true return true
} }
@@ -25,3 +26,35 @@ func ReduceOnce(e *Expression) bool {
return false return false
} }
func IsViable(e *Expression) (*Abstraction, Expression, bool) {
if e == nil {
return nil, nil, false
} else if app, appOk := (*e).(*Application); !appOk {
return nil, nil, false
} else if fn, fnOk := app.abstraction.(*Abstraction); !fnOk {
return nil, nil, false
} else {
return fn, app.argument, true
}
}
func ReduceAll(e *Expression, step func()) {
it := NewIterator(e)
for it.Current() != nil {
current := it.Current()
if fn, arg, ok := IsViable(current); !ok {
it.Next()
} else {
*current = Substitute(fn.body, fn.parameter, arg)
step()
if _, _, ok := IsViable(it.Parent()); ok {
it.Back()
} else {
}
}
}
}

35
pkg/lifo/lifo.go Normal file
View File

@@ -0,0 +1,35 @@
package lifo
import "fmt"
type LIFO[T any] []T
func New[T any](items ...T) *LIFO[T] {
l := LIFO[T](items)
return &l
}
func (l *LIFO[T]) Push(item T) {
*l = append(*l, item)
}
func (l *LIFO[T]) Empty() bool {
return len(*l) == 0
}
func (l *LIFO[T]) MustPop() T {
var item T
*l, item = (*l)[:len(*l)-1], (*l)[len(*l)-1]
return item
}
func (l *LIFO[T]) Pop() (T, error) {
var item T
if l.Empty() {
return item, fmt.Errorf("stack is exhausted")
}
return l.MustPop(), nil
}

View File

@@ -1,7 +0,0 @@
0 := \f.\x.x
inc n := \f x.(f (n f x))
exp n m := (m n)
N := (inc (inc (inc (inc (inc 0)))))
(exp N N)

View File

@@ -1,11 +0,0 @@
fix f := (\x.(f (x x)) \x.(f (x x)))
inc := (fix \self.\l.(((l \x.\y.x) ((((l \x.\y.y) \x.\y.x) \c.((c \x.\y.x) \c.((c \x.\y.y) (self ((l \x.\y.y) \x.\y.y))))) \c.((c \x.\y.x) \c.((c \x.\y.x) ((l \x.\y.y) \x.\y.y))))) \c.((c \x.\y.x) \c.((c \x.\y.x) \VAR0.\x.\y.y))))
one := \c.((c \x.\y.x) \c.((c \x.\y.x) \VAR0.\x.\y.y))
double := \N.\c.((c \x.\y.x) \c.((c \x.\y.y) N))
print := (fix \self.\l.(((l \x.\y.x) (((((l \x.\y.y) \x.\y.x) 1) 0) (self ((l \x.\y.y) \x.\y.y)))) END))
N := \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.y) \c.((c \x.\y.x) \c.((c \x.\y.x) \VAR0.\x.\y.y))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
(print N)

View File

@@ -1,52 +0,0 @@
T x y := x
F x y := y
if b t e := (b t e)
pair a b := \c.(c a b)
left p := (p T)
right p := (p F)
print n := (n 0 1 end)
fix f := (\x.(f (x x)) \x.(f (x x)))
some x := (pair T x)
none := \.F
isfull := left
unwrap := right
nil := none
push i l := (some (pair i l))
peek l := (left (unwrap l))
pop l := (right (unwrap l))
inc := (fix \self l.{
(if (isfull l)
(if (peek l)
(push F (self (pop l)))
(push T (pop l))
)
(push T nil)
)
})
print := (fix \self l.{
(if (isfull l)
((if (peek l) 1 0) (self (pop l)))
END
)
})
one := (push T nil)
double N := (push F N)
N :=
(double (double (double (double (double
(double (double (double (double (double
(double (double (double (double (double
(double (double (double (double (double
(double (double (double (double (double
(double (double (double (double (double
one))))))))))))))))))))))))))))))
(print N)

View File

@@ -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
)

View File

@@ -1 +0,0 @@
(\.VALUE anything)