diff --git a/pkg/fifo/fifo.go b/pkg/fifo/fifo.go new file mode 100644 index 0000000..6e0657e --- /dev/null +++ b/pkg/fifo/fifo.go @@ -0,0 +1,35 @@ +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 +} diff --git a/pkg/lambda/reduce.go b/pkg/lambda/reduce.go index 3a812d9..b13d8ed 100644 --- a/pkg/lambda/reduce.go +++ b/pkg/lambda/reduce.go @@ -1,22 +1,27 @@ package lambda +import "git.maximhutz.com/max/lambda/pkg/fifo" + func ReduceOnce(e *Expression) bool { - switch typed := (*e).(type) { - case *Abstraction: - return ReduceOnce(&typed.Body) - case *Application: - if fn, fnOk := typed.Abstraction.(*Abstraction); fnOk { - Substitute(&fn.Body, fn.Parameter, typed.Argument) - *e = fn.Body - return true - } + stack := fifo.New(e) - if ReduceOnce(&typed.Abstraction) { - return true - } + for !stack.Empty() { + top := stack.MustPop() - return ReduceOnce(&typed.Argument) - default: - return false + switch typed := (*top).(type) { + case *Abstraction: + stack.Push(&typed.Body) + case *Application: + if fn, fnOk := typed.Abstraction.(*Abstraction); fnOk { + Substitute(&fn.Body, fn.Parameter, typed.Argument) + *top = fn.Body + return true + } + + stack.Push(&typed.Argument) + stack.Push(&typed.Abstraction) + } } + + return false } diff --git a/samples/simple.txt b/samples/simple.txt index 0b88432..192f069 100644 --- a/samples/simple.txt +++ b/samples/simple.txt @@ -12,5 +12,5 @@ ) \n f x.(f (n f x)) ) - \f x.((((((x)))))) -) \ No newline at end of file + \f x.x +)