docs: document remaining packages and simplify AST types #45
@@ -21,7 +21,7 @@ func encodeAbstraction(n *saccharine.Abstraction) lambda.Expression {
|
|||||||
// If the function has no parameters, it is a thunk. Lambda calculus still
|
// If the function has no parameters, it is a thunk. Lambda calculus still
|
||||||
// requires _some_ parameter exists, so generate one.
|
// requires _some_ parameter exists, so generate one.
|
||||||
if len(parameters) == 0 {
|
if len(parameters) == 0 {
|
||||||
freeVars := result.GetFree()
|
freeVars := lambda.GetFree(result)
|
||||||
freshName := lambda.GenerateFreshName(freeVars)
|
freshName := lambda.GenerateFreshName(freeVars)
|
||||||
parameters = append(parameters, freshName)
|
parameters = append(parameters, freshName)
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ func reduceLet(s *saccharine.LetStatement, e lambda.Expression) lambda.Expressio
|
|||||||
}
|
}
|
||||||
|
|
||||||
func reduceDeclare(s *saccharine.DeclareStatement, e lambda.Expression) lambda.Expression {
|
func reduceDeclare(s *saccharine.DeclareStatement, e lambda.Expression) lambda.Expression {
|
||||||
freshVar := lambda.GenerateFreshName(e.GetFree())
|
freshVar := lambda.GenerateFreshName(lambda.GetFree(e))
|
||||||
|
|
||||||
return lambda.Application{
|
return lambda.Application{
|
||||||
Abstraction: lambda.Abstraction{Parameter: freshVar, Body: e},
|
Abstraction: lambda.Abstraction{Parameter: freshVar, Body: e},
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ func ReduceOnce(e lambda.Expression) (lambda.Expression, bool) {
|
|||||||
|
|
||||||
case lambda.Application:
|
case lambda.Application:
|
||||||
if fn, fnOk := e.Abstraction.(lambda.Abstraction); fnOk {
|
if fn, fnOk := e.Abstraction.(lambda.Abstraction); fnOk {
|
||||||
return fn.Body.Substitute(fn.Parameter, e.Argument), true
|
return lambda.Substitute(fn.Body, fn.Parameter, e.Argument), true
|
||||||
}
|
}
|
||||||
|
|
||||||
abs, reduced := ReduceOnce(e.Abstraction)
|
abs, reduced := ReduceOnce(e.Abstraction)
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import (
|
|||||||
"git.maximhutz.com/max/lambda/pkg/codec"
|
"git.maximhutz.com/max/lambda/pkg/codec"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Marshaler struct{}
|
type Codec struct{}
|
||||||
|
|
||||||
func (m Marshaler) Decode(string) (Expression, error) {
|
func (m Codec) Decode(string) (Expression, error) {
|
||||||
return nil, fmt.Errorf("unimplemented")
|
return nil, fmt.Errorf("unimplemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Marshaler) Encode(e Expression) (string, error) {
|
func (m Codec) Encode(e Expression) (string, error) {
|
||||||
return e.String(), nil
|
return Stringify(e), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ codec.Codec[Expression] = (*Marshaler)(nil)
|
var _ codec.Codec[Expression] = (*Codec)(nil)
|
||||||
|
|||||||
@@ -1,19 +1,27 @@
|
|||||||
package lambda
|
package lambda
|
||||||
|
|
||||||
import "git.maximhutz.com/max/lambda/pkg/set"
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
func (e Variable) GetFree() set.Set[string] {
|
"git.maximhutz.com/max/lambda/pkg/set"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetFree returns the set of all free variable names in the expression.
|
||||||
|
// This function does not mutate the input expression.
|
||||||
|
// The returned set is newly allocated and can be modified by the caller.
|
||||||
|
func GetFree(e Expression) set.Set[string] {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case Variable:
|
||||||
return set.New(e.Name)
|
return set.New(e.Name)
|
||||||
}
|
case Abstraction:
|
||||||
|
vars := GetFree(e.Body)
|
||||||
func (e Abstraction) GetFree() set.Set[string] {
|
|
||||||
vars := e.Body.GetFree()
|
|
||||||
vars.Remove(e.Parameter)
|
vars.Remove(e.Parameter)
|
||||||
return vars
|
return vars
|
||||||
}
|
case Application:
|
||||||
|
vars := GetFree(e.Abstraction)
|
||||||
func (e Application) GetFree() set.Set[string] {
|
vars.Merge(GetFree(e.Argument))
|
||||||
vars := e.Abstraction.GetFree()
|
|
||||||
vars.Merge(e.Argument.GetFree())
|
|
||||||
return vars
|
return vars
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unknown expression type: %v", e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
package lambda
|
package lambda
|
||||||
|
|
||||||
func (e Variable) IsFree(n string) bool {
|
import "fmt"
|
||||||
return e.Name == n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e Abstraction) IsFree(n string) bool {
|
// IsFree returns true if the variable name n occurs free in the expression.
|
||||||
return e.Parameter != n && e.Body.IsFree(n)
|
// This function does not mutate the input expression.
|
||||||
}
|
func IsFree(e Expression, n string) bool {
|
||||||
func (e Application) IsFree(n string) bool {
|
switch e := e.(type) {
|
||||||
return e.Abstraction.IsFree(n) || e.Argument.IsFree(n)
|
case Variable:
|
||||||
|
return e.Name == n
|
||||||
|
case Abstraction:
|
||||||
|
return e.Parameter != n && IsFree(e.Body, n)
|
||||||
|
case Application:
|
||||||
|
return IsFree(e.Abstraction, n) || IsFree(e.Argument, n)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unknown expression type: %v", e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +1,27 @@
|
|||||||
package lambda
|
package lambda
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/set"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Expression is the interface for all lambda calculus expression types.
|
// Expression is the interface for all lambda calculus expression types.
|
||||||
// It embeds the general expr.Expression interface for cross-mode compatibility.
|
// It embeds the general expr.Expression interface for cross-mode compatibility.
|
||||||
type Expression interface {
|
type Expression interface {
|
||||||
fmt.Stringer
|
expression()
|
||||||
|
|
||||||
// Substitute replaces all free occurrences of the target variable with the
|
|
||||||
// replacement expression. Alpha-renaming is performed automatically to
|
|
||||||
// avoid variable capture.
|
|
||||||
Substitute(target string, replacement Expression) Expression
|
|
||||||
|
|
||||||
// GetFree returns the set of all free variable names in the expression.
|
|
||||||
// This function does not mutate the input expression.
|
|
||||||
// The returned set is newly allocated and can be modified by the caller.
|
|
||||||
GetFree() set.Set[string]
|
|
||||||
|
|
||||||
// Rename replaces all occurrences of the target variable name with the new name.
|
|
||||||
Rename(target string, newName string) Expression
|
|
||||||
|
|
||||||
// IsFree returns true if the variable name n occurs free in the expression.
|
|
||||||
// This function does not mutate the input expression.
|
|
||||||
IsFree(n string) bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
type Abstraction struct {
|
type Abstraction struct {
|
||||||
Parameter string
|
Parameter string
|
||||||
Body Expression
|
Body Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Expression = Abstraction{}
|
func (a Abstraction) expression() {}
|
||||||
|
|
||||||
/** ------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
type Application struct {
|
type Application struct {
|
||||||
Abstraction Expression
|
Abstraction Expression
|
||||||
Argument Expression
|
Argument Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Expression = Application{}
|
func (a Application) expression() {}
|
||||||
|
|
||||||
/** ------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
type Variable struct {
|
type Variable struct {
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Expression = Variable{}
|
func (v Variable) expression() {}
|
||||||
|
|||||||
@@ -1,28 +1,31 @@
|
|||||||
package lambda
|
package lambda
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
// Rename replaces all occurrences of the target variable name with the new name.
|
// Rename replaces all occurrences of the target variable name with the new name.
|
||||||
func (e Variable) Rename(target string, newName string) Expression {
|
func Rename(e Expression, target string, newName string) Expression {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case Variable:
|
||||||
if e.Name == target {
|
if e.Name == target {
|
||||||
return Variable{Name: newName}
|
return Variable{Name: newName}
|
||||||
}
|
}
|
||||||
|
|
||||||
return e
|
return e
|
||||||
}
|
case Abstraction:
|
||||||
|
|
||||||
func (e Abstraction) Rename(target string, newName string) Expression {
|
|
||||||
newParam := e.Parameter
|
newParam := e.Parameter
|
||||||
if e.Parameter == target {
|
if e.Parameter == target {
|
||||||
newParam = newName
|
newParam = newName
|
||||||
}
|
}
|
||||||
|
|
||||||
newBody := e.Body.Rename(target, newName)
|
newBody := Rename(e.Body, target, newName)
|
||||||
|
|
||||||
return Abstraction{Parameter: newParam, Body: newBody}
|
return Abstraction{Parameter: newParam, Body: newBody}
|
||||||
}
|
case Application:
|
||||||
|
newAbs := Rename(e.Abstraction, target, newName)
|
||||||
func (e Application) Rename(target string, newName string) Expression {
|
newArg := Rename(e.Argument, target, newName)
|
||||||
newAbs := e.Abstraction.Rename(target, newName)
|
|
||||||
newArg := e.Argument.Rename(target, newName)
|
|
||||||
|
|
||||||
return Application{Abstraction: newAbs, Argument: newArg}
|
return Application{Abstraction: newAbs, Argument: newArg}
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unknown expression type: %v", e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
package lambda
|
package lambda
|
||||||
|
|
||||||
func (a Abstraction) String() string {
|
import "fmt"
|
||||||
return "\\" + a.Parameter + "." + a.Body.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a Application) String() string {
|
// Stringify turns an expression as a string.
|
||||||
return "(" + a.Abstraction.String() + " " + a.Argument.String() + ")"
|
func Stringify(e Expression) string {
|
||||||
}
|
switch e := e.(type) {
|
||||||
|
case Variable:
|
||||||
func (v Variable) String() string {
|
return e.Name
|
||||||
return v.Name
|
case Abstraction:
|
||||||
|
return "\\" + e.Parameter + "." + Stringify(e.Body)
|
||||||
|
case Application:
|
||||||
|
return "(" + Stringify(e.Abstraction) + " " + Stringify(e.Argument) + ")"
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unknown expression type: %v", e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,41 @@
|
|||||||
package lambda
|
package lambda
|
||||||
|
|
||||||
func (e Variable) Substitute(target string, replacement Expression) Expression {
|
import "fmt"
|
||||||
|
|
||||||
|
// Substitute replaces all free occurrences of the target variable with the
|
||||||
|
// replacement expression. Alpha-renaming is performed automatically to
|
||||||
|
// avoid variable capture.
|
||||||
|
func Substitute(e Expression, target string, replacement Expression) Expression {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case Variable:
|
||||||
if e.Name == target {
|
if e.Name == target {
|
||||||
return replacement
|
return replacement
|
||||||
}
|
}
|
||||||
|
|
||||||
return e
|
return e
|
||||||
}
|
case Abstraction:
|
||||||
|
|
||||||
func (e Abstraction) Substitute(target string, replacement Expression) Expression {
|
|
||||||
if e.Parameter == target {
|
if e.Parameter == target {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
body := e.Body
|
body := e.Body
|
||||||
param := e.Parameter
|
param := e.Parameter
|
||||||
if replacement.IsFree(param) {
|
if IsFree(replacement, param) {
|
||||||
freeVars := replacement.GetFree()
|
freeVars := GetFree(replacement)
|
||||||
freeVars.Merge(body.GetFree())
|
freeVars.Merge(GetFree(body))
|
||||||
freshVar := GenerateFreshName(freeVars)
|
freshVar := GenerateFreshName(freeVars)
|
||||||
body = body.Rename(param, freshVar)
|
body = Rename(body, param, freshVar)
|
||||||
param = freshVar
|
param = freshVar
|
||||||
}
|
}
|
||||||
|
|
||||||
newBody := body.Substitute(target, replacement)
|
newBody := Substitute(body, target, replacement)
|
||||||
return Abstraction{Parameter: param, Body: newBody}
|
return Abstraction{Parameter: param, Body: newBody}
|
||||||
}
|
case Application:
|
||||||
|
abs := Substitute(e.Abstraction, target, replacement)
|
||||||
func (e Application) Substitute(target string, replacement Expression) Expression {
|
arg := Substitute(e.Argument, target, replacement)
|
||||||
abs := e.Abstraction.Substitute(target, replacement)
|
|
||||||
arg := e.Argument.Substitute(target, replacement)
|
|
||||||
|
|
||||||
return Application{Abstraction: abs, Argument: arg}
|
return Application{Abstraction: abs, Argument: arg}
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unknown expression type: %v", e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user