diff --git a/pkg/lambda/expression.go b/pkg/lambda/expression.go index 5055986..d9678d2 100644 --- a/pkg/lambda/expression.go +++ b/pkg/lambda/expression.go @@ -3,8 +3,6 @@ package lambda // Expression represents an immutable lambda calculus expression. type Expression interface { Accept(Visitor) - Substitute(target string, replacement Expression) Expression - Rename(target string, newName string) Expression } /** ------------------------------------------------------------------------- */ diff --git a/pkg/lambda/reduce.go b/pkg/lambda/reduce.go index 2df9a9d..74deba2 100644 --- a/pkg/lambda/reduce.go +++ b/pkg/lambda/reduce.go @@ -13,7 +13,7 @@ func ReduceOnce(e *Expression) bool { stack.Push(&typed.body) case *Application: if fn, fnOk := typed.function.(*Abstraction); fnOk { - reduced := fn.body.Substitute(fn.parameter, typed.argument) + reduced := Substitute(fn.body, fn.parameter, typed.argument) *top = reduced return true } diff --git a/pkg/lambda/rename.go b/pkg/lambda/rename.go index 2c9b329..3a1d687 100644 --- a/pkg/lambda/rename.go +++ b/pkg/lambda/rename.go @@ -1,37 +1,39 @@ package lambda // Rename replaces all occurrences of target variable with newName. -func (v *Variable) Rename(target string, newName string) Expression { - if v.value == target { - return NewVariable(newName) +func Rename(expr Expression, target string, newName string) Expression { + switch e := expr.(type) { + case *Variable: + if e.value == target { + return NewVariable(newName) + } + return e + + case *Abstraction: + newParam := e.parameter + if e.parameter == target { + newParam = newName + } + + newBody := Rename(e.body, target, newName) + + if newParam == e.parameter && newBody == e.body { + return e + } + + return NewAbstraction(newParam, newBody) + + case *Application: + newFunc := Rename(e.function, target, newName) + newArg := Rename(e.argument, target, newName) + + if newFunc == e.function && newArg == e.argument { + return e + } + + return NewApplication(newFunc, newArg) + + default: + return expr } - return v -} - -// Rename replaces all occurrences of target variable with newName. -func (a *Abstraction) Rename(target string, newName string) Expression { - newParam := a.parameter - if a.parameter == target { - newParam = newName - } - - newBody := a.body.Rename(target, newName) - - if newParam == a.parameter && newBody == a.body { - return a - } - - return NewAbstraction(newParam, newBody) -} - -// Rename replaces all occurrences of target variable with newName. -func (a *Application) Rename(target string, newName string) Expression { - newFunc := a.function.Rename(target, newName) - newArg := a.argument.Rename(target, newName) - - if newFunc == a.function && newArg == a.argument { - return a - } - - return NewApplication(newFunc, newArg) } diff --git a/pkg/lambda/substitute.go b/pkg/lambda/substitute.go index e0ef99d..d2bb3d9 100644 --- a/pkg/lambda/substitute.go +++ b/pkg/lambda/substitute.go @@ -1,45 +1,47 @@ package lambda // Substitute replaces all free occurrences of target with replacement. -func (v *Variable) Substitute(target string, replacement Expression) Expression { - if v.value == target { - return replacement +func Substitute(expr Expression, target string, replacement Expression) Expression { + switch e := expr.(type) { + case *Variable: + if e.value == target { + return replacement + } + return e + + case *Abstraction: + if e.parameter == target { + return e + } + + body := e.body + param := e.parameter + if IsFreeVariable(param, replacement) { + freeVars := GetFreeVariables(replacement) + freeVars.Merge(GetFreeVariables(body)) + freshVar := GenerateFreshName(freeVars) + body = Rename(body, param, freshVar) + param = freshVar + } + + newBody := Substitute(body, target, replacement) + if newBody == body && param == e.parameter { + return e + } + + return NewAbstraction(param, newBody) + + case *Application: + newFunc := Substitute(e.function, target, replacement) + newArg := Substitute(e.argument, target, replacement) + + if newFunc == e.function && newArg == e.argument { + return e + } + + return NewApplication(newFunc, newArg) + + default: + return expr } - return v -} - -// Substitute replaces all free occurrences of target with replacement. -func (a *Abstraction) Substitute(target string, replacement Expression) Expression { - if a.parameter == target { - return a - } - - body := a.body - param := a.parameter - if IsFreeVariable(param, replacement) { - freeVars := GetFreeVariables(replacement) - freeVars.Merge(GetFreeVariables(body)) - freshVar := GenerateFreshName(freeVars) - body = body.Rename(param, freshVar) - param = freshVar - } - - newBody := body.Substitute(target, replacement) - if newBody == body && param == a.parameter { - return a - } - - return NewAbstraction(param, newBody) -} - -// Substitute replaces all free occurrences of target with replacement. -func (a *Application) Substitute(target string, replacement Expression) Expression { - newFunc := a.function.Substitute(target, replacement) - newArg := a.argument.Substitute(target, replacement) - - if newFunc == a.function && newArg == a.argument { - return a - } - - return NewApplication(newFunc, newArg) }