perf: implement structural sharing for expression trees #10
@@ -2,18 +2,21 @@ package lambda
|
||||
|
||||
type Expression interface {
|
||||
Accept(Visitor)
|
||||
Copy() Expression
|
||||
}
|
||||
|
||||
/** ------------------------------------------------------------------------- */
|
||||
|
||||
type Abstraction struct {
|
||||
Parameter string
|
||||
Body Expression
|
||||
parameter string
|
||||
body Expression
|
||||
}
|
||||
|
||||
func (a *Abstraction) Copy() Expression {
|
||||
return NewAbstraction(a.Parameter, a.Body.Copy())
|
||||
func (a *Abstraction) Parameter() string {
|
||||
return a.parameter
|
||||
}
|
||||
|
||||
func (a *Abstraction) Body() Expression {
|
||||
return a.body
|
||||
}
|
||||
|
||||
func (a *Abstraction) Accept(v Visitor) {
|
||||
@@ -21,36 +24,40 @@ func (a *Abstraction) Accept(v Visitor) {
|
||||
}
|
||||
|
||||
func NewAbstraction(parameter string, body Expression) *Abstraction {
|
||||
return &Abstraction{Parameter: parameter, Body: body}
|
||||
return &Abstraction{parameter: parameter, body: body}
|
||||
}
|
||||
|
||||
/** ------------------------------------------------------------------------- */
|
||||
|
||||
type Application struct {
|
||||
Abstraction Expression
|
||||
Argument Expression
|
||||
abstraction Expression
|
||||
argument Expression
|
||||
}
|
||||
|
||||
func (a *Application) Copy() Expression {
|
||||
return NewApplication(a.Abstraction.Copy(), a.Argument.Copy())
|
||||
func (a *Application) Abstraction() Expression {
|
||||
return a.abstraction
|
||||
}
|
||||
|
||||
func (a *Application) Argument() Expression {
|
||||
return a.argument
|
||||
}
|
||||
|
||||
func (a *Application) Accept(v Visitor) {
|
||||
v.VisitApplication(a)
|
||||
}
|
||||
|
||||
func NewApplication(function Expression, argument Expression) *Application {
|
||||
return &Application{Abstraction: function, Argument: argument}
|
||||
func NewApplication(abstraction Expression, argument Expression) *Application {
|
||||
return &Application{abstraction: abstraction, argument: argument}
|
||||
}
|
||||
|
||||
/** ------------------------------------------------------------------------- */
|
||||
|
||||
type Variable struct {
|
||||
Value string
|
||||
value string
|
||||
}
|
||||
|
||||
func (v *Variable) Copy() Expression {
|
||||
return NewVariable(v.Value)
|
||||
func (v *Variable) Value() string {
|
||||
return v.value
|
||||
}
|
||||
|
||||
func (v *Variable) Accept(visitor Visitor) {
|
||||
@@ -58,7 +65,7 @@ func (v *Variable) Accept(visitor Visitor) {
|
||||
}
|
||||
|
||||
func NewVariable(name string) *Variable {
|
||||
return &Variable{Value: name}
|
||||
return &Variable{value: name}
|
||||
}
|
||||
|
||||
/** ------------------------------------------------------------------------- */
|
||||
|
||||
@@ -5,14 +5,14 @@ import "git.maximhutz.com/max/lambda/pkg/set"
|
||||
func GetFreeVariables(e Expression) *set.Set[string] {
|
||||
switch e := e.(type) {
|
||||
case *Variable:
|
||||
return set.New(e.Value)
|
||||
return set.New(e.value)
|
||||
case *Abstraction:
|
||||
vars := GetFreeVariables(e.Body)
|
||||
vars.Remove(e.Parameter)
|
||||
vars := GetFreeVariables(e.body)
|
||||
vars.Remove(e.parameter)
|
||||
return vars
|
||||
case *Application:
|
||||
vars := GetFreeVariables(e.Abstraction)
|
||||
vars.Merge(GetFreeVariables(e.Argument))
|
||||
vars := GetFreeVariables(e.abstraction)
|
||||
vars.Merge(GetFreeVariables(e.argument))
|
||||
return vars
|
||||
default:
|
||||
return nil
|
||||
|
||||
@@ -3,11 +3,11 @@ package lambda
|
||||
func IsFreeVariable(n string, e Expression) bool {
|
||||
switch e := e.(type) {
|
||||
case *Variable:
|
||||
return e.Value == n
|
||||
return e.value == n
|
||||
case *Abstraction:
|
||||
return e.Parameter != n && IsFreeVariable(n, e.Body)
|
||||
return e.parameter != n && IsFreeVariable(n, e.body)
|
||||
case *Application:
|
||||
return IsFreeVariable(n, e.Abstraction) || IsFreeVariable(n, e.Argument)
|
||||
return IsFreeVariable(n, e.abstraction) || IsFreeVariable(n, e.argument)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -10,16 +10,16 @@ func ReduceOnce(e *Expression) bool {
|
||||
|
||||
switch typed := (*top).(type) {
|
||||
case *Abstraction:
|
||||
stack.Push(&typed.Body)
|
||||
stack.Push(&typed.body)
|
||||
case *Application:
|
||||
if fn, fnOk := typed.Abstraction.(*Abstraction); fnOk {
|
||||
Substitute(&fn.Body, fn.Parameter, typed.Argument)
|
||||
*top = fn.Body
|
||||
if fn, fnOk := typed.abstraction.(*Abstraction); fnOk {
|
||||
reduced := Substitute(fn.body, fn.parameter, typed.argument)
|
||||
*top = reduced
|
||||
return true
|
||||
}
|
||||
|
||||
stack.Push(&typed.Argument)
|
||||
stack.Push(&typed.Abstraction)
|
||||
stack.Push(&typed.argument)
|
||||
stack.Push(&typed.abstraction)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,38 @@
|
||||
package lambda
|
||||
|
||||
func Rename(e Expression, target string, substitute string) {
|
||||
switch e := e.(type) {
|
||||
func Rename(expr Expression, target string, newName string) Expression {
|
||||
switch e := expr.(type) {
|
||||
case *Variable:
|
||||
if e.Value == target {
|
||||
e.Value = substitute
|
||||
if e.value == target {
|
||||
return NewVariable(newName)
|
||||
}
|
||||
return e
|
||||
|
||||
case *Abstraction:
|
||||
if e.Parameter == target {
|
||||
e.Parameter = substitute
|
||||
newParam := e.parameter
|
||||
if e.parameter == target {
|
||||
newParam = newName
|
||||
}
|
||||
|
||||
Rename(e.Body, target, substitute)
|
||||
newBody := Rename(e.body, target, newName)
|
||||
|
||||
if newParam == e.parameter && newBody == e.body {
|
||||
return e
|
||||
}
|
||||
|
||||
return NewAbstraction(newParam, newBody)
|
||||
|
||||
case *Application:
|
||||
Rename(e.Abstraction, target, substitute)
|
||||
Rename(e.Argument, target, substitute)
|
||||
newAbs := Rename(e.abstraction, target, newName)
|
||||
newArg := Rename(e.argument, target, newName)
|
||||
|
||||
if newAbs == e.abstraction && newArg == e.argument {
|
||||
return e
|
||||
}
|
||||
|
||||
return NewApplication(newAbs, newArg)
|
||||
|
||||
default:
|
||||
return expr
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,21 +7,21 @@ type stringifyVisitor struct {
|
||||
}
|
||||
|
||||
func (v *stringifyVisitor) VisitVariable(a *Variable) {
|
||||
v.builder.WriteString(a.Value)
|
||||
v.builder.WriteString(a.value)
|
||||
}
|
||||
|
||||
func (v *stringifyVisitor) VisitAbstraction(f *Abstraction) {
|
||||
v.builder.WriteRune('\\')
|
||||
v.builder.WriteString(f.Parameter)
|
||||
v.builder.WriteString(f.parameter)
|
||||
v.builder.WriteRune('.')
|
||||
f.Body.Accept(v)
|
||||
f.body.Accept(v)
|
||||
}
|
||||
|
||||
func (v *stringifyVisitor) VisitApplication(c *Application) {
|
||||
v.builder.WriteRune('(')
|
||||
c.Abstraction.Accept(v)
|
||||
c.abstraction.Accept(v)
|
||||
v.builder.WriteRune(' ')
|
||||
c.Argument.Accept(v)
|
||||
c.argument.Accept(v)
|
||||
v.builder.WriteRune(')')
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +1,46 @@
|
||||
package lambda
|
||||
|
||||
func Substitute(e *Expression, target string, replacement Expression) {
|
||||
switch typed := (*e).(type) {
|
||||
func Substitute(expr Expression, target string, replacement Expression) Expression {
|
||||
switch e := expr.(type) {
|
||||
case *Variable:
|
||||
if typed.Value == target {
|
||||
*e = replacement.Copy()
|
||||
if e.value == target {
|
||||
return replacement
|
||||
}
|
||||
return e
|
||||
|
||||
case *Abstraction:
|
||||
if typed.Parameter == target {
|
||||
return
|
||||
if e.parameter == target {
|
||||
return e
|
||||
}
|
||||
|
||||
if IsFreeVariable(typed.Parameter, replacement) {
|
||||
replacementFreeVars := GetFreeVariables(replacement)
|
||||
used := GetFreeVariables(typed.Body)
|
||||
used.Merge(replacementFreeVars)
|
||||
freshVar := GenerateFreshName(used)
|
||||
Rename(typed, typed.Parameter, freshVar)
|
||||
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
|
||||
}
|
||||
|
||||
Substitute(&typed.Body, target, replacement)
|
||||
newBody := Substitute(body, target, replacement)
|
||||
if newBody == body && param == e.parameter {
|
||||
return e
|
||||
}
|
||||
|
||||
return NewAbstraction(param, newBody)
|
||||
|
||||
case *Application:
|
||||
Substitute(&typed.Abstraction, target, replacement)
|
||||
Substitute(&typed.Argument, target, replacement)
|
||||
newAbs := Substitute(e.abstraction, target, replacement)
|
||||
newArg := Substitute(e.argument, target, replacement)
|
||||
|
||||
if newAbs == e.abstraction && newArg == e.argument {
|
||||
return e
|
||||
}
|
||||
|
||||
return NewApplication(newAbs, newArg)
|
||||
|
||||
default:
|
||||
return expr
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user