feat: reducer, but doesn`t work
This commit is contained in:
4
Makefile
4
Makefile
@@ -4,5 +4,5 @@ it:
|
||||
@ go build -o ${BINARY_NAME} ./cmd/lambda
|
||||
@ chmod +x ${BINARY_NAME}
|
||||
|
||||
ex1: it
|
||||
@ ./lambda.exe "(\n.\f.\x.(f ((n f) x)) \f.\x.x)"
|
||||
ex: it
|
||||
@ ./lambda.exe -v "(\add.(add (add \f.\x.x)) \n.\f.\x.(f ((n f) x)))"
|
||||
@@ -28,11 +28,11 @@ func main() {
|
||||
|
||||
expression, err := parser.GetTree(tokens)
|
||||
cli.HandleError(err)
|
||||
logger.Info("Parsed syntax tree.", "tree", expression)
|
||||
logger.Info("Parsed syntax tree.", "tree", lambda.Stringify(expression))
|
||||
|
||||
evaluated := lambda.Evaluate(expression)
|
||||
lambda.Normalize(&expression)
|
||||
cli.HandleError(err)
|
||||
logger.Info("Evaluated expression.", "tree", evaluated)
|
||||
logger.Info("Evaluated expression.", "tree", expression)
|
||||
|
||||
fmt.Println(lambda.ToString(evaluated))
|
||||
fmt.Println(lambda.Stringify(expression))
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
package lambda
|
||||
|
||||
// func replaceUnbound(name string, replacement Expression, e Expression) Expression {
|
||||
// switch e := e.(type) {
|
||||
// case Atom:
|
||||
// if e.Value == name {
|
||||
// return replacement
|
||||
// } else {
|
||||
// return e
|
||||
// }
|
||||
// case Function:
|
||||
// if e.Parameter == name {
|
||||
// return e
|
||||
// } else {
|
||||
// return Function{
|
||||
// Parameter: e.Parameter,
|
||||
// Body: replaceUnbound(name, replacement, e.Body),
|
||||
// }
|
||||
// }
|
||||
// case Call:
|
||||
// return Call{
|
||||
// Function: replaceUnbound(name, replacement, e.Function),
|
||||
// Argument: replaceUnbound(name, replacement, e.Argument),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// func evaluateAtom(a Atom) Expression {
|
||||
// return a
|
||||
// }
|
||||
|
||||
// func evaluateFunction(f Function) Expression {
|
||||
// return Function{
|
||||
// Parameter: f.Parameter,
|
||||
// Body: Evaluate(f.Body),
|
||||
// }
|
||||
// }
|
||||
|
||||
// func evaluateCall(c Call) Expression {
|
||||
// fn := c.Function
|
||||
|
||||
// as_fn, as_fn_ok := fn.(Function)
|
||||
// if !as_fn_ok {
|
||||
// fn = Evaluate(fn)
|
||||
|
||||
// if as_fn, as_fn_ok = fn.(Function); !as_fn_ok {
|
||||
// return Call{
|
||||
// Function: fn,
|
||||
// Argument: Evaluate(c.Argument),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// return replaceUnboundAtoms(as_fn.Parameter, c.Argument, as_fn.Body)
|
||||
// }
|
||||
|
||||
func Evaluate(e Expression) Expression {
|
||||
// switch e := e.(type) {
|
||||
// case Atom:
|
||||
// return e
|
||||
// case Call:
|
||||
|
||||
// case Function:
|
||||
// return Function{
|
||||
// Parameter: e.Parameter,
|
||||
// Body: Evaluate(e.Body),
|
||||
// }
|
||||
// }
|
||||
return e
|
||||
}
|
||||
@@ -1,65 +1,57 @@
|
||||
package lambda
|
||||
|
||||
type Expression interface {
|
||||
Accept(ExpressionVisitor)
|
||||
Accept(Visitor)
|
||||
}
|
||||
|
||||
/** ------------------------------------------------------------------------- */
|
||||
|
||||
type Function struct {
|
||||
type Abstraction struct {
|
||||
Parameter string
|
||||
Body Expression
|
||||
}
|
||||
|
||||
func NewFunction(parameter string, body Expression) *Function {
|
||||
return &Function{
|
||||
Parameter: parameter,
|
||||
Body: body,
|
||||
}
|
||||
func NewAbstraction(parameter string, body Expression) *Abstraction {
|
||||
return &Abstraction{Parameter: parameter, Body: body}
|
||||
}
|
||||
|
||||
func (f *Function) Accept(v ExpressionVisitor) {
|
||||
v.VisitFunction(f)
|
||||
func (this *Abstraction) Accept(v Visitor) {
|
||||
v.VisitAbstraction(this)
|
||||
}
|
||||
|
||||
/** ------------------------------------------------------------------------- */
|
||||
|
||||
type Call struct {
|
||||
Function Expression
|
||||
Argument Expression
|
||||
type Application struct {
|
||||
Abstraction Expression
|
||||
Argument Expression
|
||||
}
|
||||
|
||||
func NewCall(function Expression, argument Expression) *Call {
|
||||
return &Call{
|
||||
Function: function,
|
||||
Argument: argument,
|
||||
}
|
||||
func NewApplication(function Expression, argument Expression) *Application {
|
||||
return &Application{Abstraction: function, Argument: argument}
|
||||
}
|
||||
|
||||
func (c *Call) Accept(v ExpressionVisitor) {
|
||||
v.VisitCall(c)
|
||||
func (this *Application) Accept(v Visitor) {
|
||||
v.VisitApplication(this)
|
||||
}
|
||||
|
||||
/** ------------------------------------------------------------------------- */
|
||||
|
||||
type Atom struct {
|
||||
type Variable struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
func NewAtom(name string) *Atom {
|
||||
return &Atom{
|
||||
Value: name,
|
||||
}
|
||||
func NewVariable(name string) *Variable {
|
||||
return &Variable{Value: name}
|
||||
}
|
||||
|
||||
func (a *Atom) Accept(v ExpressionVisitor) {
|
||||
v.VisitAtom(a)
|
||||
func (this *Variable) Accept(v Visitor) {
|
||||
v.VisitVariable(this)
|
||||
}
|
||||
|
||||
/** ------------------------------------------------------------------------- */
|
||||
|
||||
type ExpressionVisitor interface {
|
||||
VisitFunction(*Function)
|
||||
VisitCall(*Call)
|
||||
VisitAtom(*Atom)
|
||||
type Visitor interface {
|
||||
VisitAbstraction(*Abstraction)
|
||||
VisitApplication(*Application)
|
||||
VisitVariable(*Variable)
|
||||
}
|
||||
|
||||
19
pkg/lambda/generate_name.go
Normal file
19
pkg/lambda/generate_name.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package lambda
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"git.maximhutz.com/max/lambda/pkg/set"
|
||||
)
|
||||
|
||||
func GenerateFreshName(used set.Set[string]) string {
|
||||
var i uint64 = 0
|
||||
for {
|
||||
attempt := "_" + string(strconv.AppendUint(nil, i, 10))
|
||||
i++
|
||||
|
||||
if !used.Has(attempt) {
|
||||
return attempt
|
||||
}
|
||||
}
|
||||
}
|
||||
20
pkg/lambda/get_free_variables.go
Normal file
20
pkg/lambda/get_free_variables.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package lambda
|
||||
|
||||
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)
|
||||
case *Abstraction:
|
||||
vars := GetFreeVariables(e.Body)
|
||||
vars.Remove(e.Parameter)
|
||||
return vars
|
||||
case *Application:
|
||||
vars := GetFreeVariables(e.Abstraction)
|
||||
vars.Union(GetFreeVariables(e.Argument))
|
||||
return vars
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package lambda
|
||||
|
||||
import "strings"
|
||||
|
||||
type StringifyVisitor struct {
|
||||
builder strings.Builder
|
||||
}
|
||||
|
||||
func (v *StringifyVisitor) VisitAtom(a *Atom) {
|
||||
v.builder.WriteString(a.Value)
|
||||
}
|
||||
|
||||
func (v *StringifyVisitor) VisitFunction(f *Function) {
|
||||
v.builder.WriteRune('\\')
|
||||
v.builder.WriteString(f.Parameter)
|
||||
v.builder.WriteRune('.')
|
||||
f.Body.Accept(v)
|
||||
}
|
||||
|
||||
func (v *StringifyVisitor) VisitCall(c *Call) {
|
||||
v.builder.WriteRune('(')
|
||||
c.Function.Accept(v)
|
||||
v.builder.WriteRune(' ')
|
||||
c.Argument.Accept(v)
|
||||
v.builder.WriteRune(')')
|
||||
}
|
||||
|
||||
func ToString(e Expression) string {
|
||||
b := StringifyVisitor{
|
||||
builder: strings.Builder{},
|
||||
}
|
||||
|
||||
e.Accept(&b)
|
||||
|
||||
return b.builder.String()
|
||||
}
|
||||
28
pkg/lambda/reduce.go
Normal file
28
pkg/lambda/reduce.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package lambda
|
||||
|
||||
func ReduceOnce(e *Expression) bool {
|
||||
switch typed := (*e).(type) {
|
||||
case *Abstraction:
|
||||
return ReduceOnce(&typed.Body)
|
||||
case *Application:
|
||||
fn, fn_ok := typed.Abstraction.(*Abstraction)
|
||||
if fn_ok {
|
||||
Substitute(&fn.Body, fn.Parameter, typed.Argument)
|
||||
*e = fn.Body
|
||||
return true
|
||||
}
|
||||
good := ReduceOnce(&typed.Abstraction)
|
||||
if good {
|
||||
return true
|
||||
}
|
||||
|
||||
return ReduceOnce(&typed.Argument)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func Normalize(e *Expression) {
|
||||
for ReduceOnce(e) {
|
||||
}
|
||||
}
|
||||
19
pkg/lambda/rename.go
Normal file
19
pkg/lambda/rename.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package lambda
|
||||
|
||||
func Rename(e Expression, target string, substitute string) {
|
||||
switch e := e.(type) {
|
||||
case *Variable:
|
||||
if e.Value == target {
|
||||
e.Value = substitute
|
||||
}
|
||||
case *Abstraction:
|
||||
if e.Parameter == target {
|
||||
e.Parameter = substitute
|
||||
}
|
||||
|
||||
Rename(e.Body, target, substitute)
|
||||
case *Application:
|
||||
Rename(e.Abstraction, target, substitute)
|
||||
Rename(e.Argument, target, substitute)
|
||||
}
|
||||
}
|
||||
32
pkg/lambda/stringify.go
Normal file
32
pkg/lambda/stringify.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package lambda
|
||||
|
||||
import "strings"
|
||||
|
||||
type stringifyVisitor struct {
|
||||
builder strings.Builder
|
||||
}
|
||||
|
||||
func (this *stringifyVisitor) VisitVariable(a *Variable) {
|
||||
this.builder.WriteString(a.Value)
|
||||
}
|
||||
|
||||
func (this *stringifyVisitor) VisitAbstraction(f *Abstraction) {
|
||||
this.builder.WriteRune('\\')
|
||||
this.builder.WriteString(f.Parameter)
|
||||
this.builder.WriteRune('.')
|
||||
f.Body.Accept(this)
|
||||
}
|
||||
|
||||
func (this *stringifyVisitor) VisitApplication(c *Application) {
|
||||
this.builder.WriteRune('(')
|
||||
c.Abstraction.Accept(this)
|
||||
this.builder.WriteRune(' ')
|
||||
c.Argument.Accept(this)
|
||||
this.builder.WriteRune(')')
|
||||
}
|
||||
|
||||
func Stringify(e Expression) string {
|
||||
b := &stringifyVisitor{}
|
||||
e.Accept(b)
|
||||
return b.builder.String()
|
||||
}
|
||||
29
pkg/lambda/substitute.go
Normal file
29
pkg/lambda/substitute.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package lambda
|
||||
|
||||
func Substitute(e *Expression, target string, replacement Expression) {
|
||||
switch typed := (*e).(type) {
|
||||
case *Variable:
|
||||
if typed.Value == target {
|
||||
*e = replacement
|
||||
}
|
||||
case *Abstraction:
|
||||
if typed.Parameter == target {
|
||||
return
|
||||
}
|
||||
|
||||
replacement_free_vars := GetFreeVariables(replacement)
|
||||
if !replacement_free_vars.Has(typed.Parameter) {
|
||||
Substitute(&typed.Body, target, replacement)
|
||||
return
|
||||
}
|
||||
|
||||
used := GetFreeVariables(typed.Body)
|
||||
used.Union(replacement_free_vars)
|
||||
fresh_var := GenerateFreshName(used)
|
||||
Rename(typed.Body, typed.Parameter, fresh_var)
|
||||
Substitute(&typed.Body, target, replacement)
|
||||
case *Application:
|
||||
Substitute(&typed.Abstraction, target, replacement)
|
||||
Substitute(&typed.Argument, target, replacement)
|
||||
}
|
||||
}
|
||||
@@ -15,15 +15,15 @@ func ParseExpression(i *iterator.Iterator[tokenizer.Token]) (lambda.Expression,
|
||||
}
|
||||
|
||||
switch token.Type {
|
||||
case tokenizer.TokenAtom:
|
||||
return lambda.NewAtom(token.Value), nil
|
||||
case tokenizer.TokenVariable:
|
||||
return lambda.NewVariable(token.Value), nil
|
||||
case tokenizer.TokenDot:
|
||||
return nil, fmt.Errorf("Token '.' found without a corresponding slash (column %d).", token.Index)
|
||||
case tokenizer.TokenSlash:
|
||||
atom, atom_err := i.Next()
|
||||
if atom_err != nil {
|
||||
return nil, fmt.Errorf("Could not find parameter of function: %w", atom_err)
|
||||
} else if atom.Type != tokenizer.TokenAtom {
|
||||
} else if atom.Type != tokenizer.TokenVariable {
|
||||
return nil, fmt.Errorf("Expected function parameter, got '%v' (column %d).", atom.Value, atom.Index)
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func ParseExpression(i *iterator.Iterator[tokenizer.Token]) (lambda.Expression,
|
||||
return nil, fmt.Errorf("Could not parse function body: %w", body_err)
|
||||
}
|
||||
|
||||
return lambda.NewFunction(atom.Value, body), nil
|
||||
return lambda.NewAbstraction(atom.Value, body), nil
|
||||
case tokenizer.TokenOpenParen:
|
||||
fn, fn_err := ParseExpression(i)
|
||||
if fn_err != nil {
|
||||
@@ -58,7 +58,7 @@ func ParseExpression(i *iterator.Iterator[tokenizer.Token]) (lambda.Expression,
|
||||
return nil, fmt.Errorf("Expected call terminating parenthesis, got '%v' (column %v).", close.Value, close.Index)
|
||||
}
|
||||
|
||||
return lambda.NewCall(fn, arg), nil
|
||||
return lambda.NewApplication(fn, arg), nil
|
||||
case tokenizer.TokenCloseParen:
|
||||
return nil, fmt.Errorf("Token ')' found without a corresponding openning parenthesis (column %d).", token.Index)
|
||||
default:
|
||||
|
||||
45
pkg/set/set.go
Normal file
45
pkg/set/set.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package set
|
||||
|
||||
type Set[T comparable] map[T]bool
|
||||
|
||||
func (this *Set[T]) Add(items ...T) {
|
||||
for _, item := range items {
|
||||
(*this)[item] = true
|
||||
}
|
||||
}
|
||||
|
||||
func (this Set[T]) Has(item T) bool {
|
||||
return this[item] == true
|
||||
}
|
||||
|
||||
func (this *Set[T]) Remove(items ...T) {
|
||||
for _, item := range items {
|
||||
delete(*this, item)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Set[T]) Union(o Set[T]) {
|
||||
for item := range o {
|
||||
this.Add(item)
|
||||
}
|
||||
}
|
||||
|
||||
func (this Set[T]) ToList() []T {
|
||||
list := []T{}
|
||||
|
||||
for item := range this {
|
||||
list = append(list, item)
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
func New[T comparable](items ...T) Set[T] {
|
||||
result := Set[T]{}
|
||||
|
||||
for _, item := range items {
|
||||
result.Add(item)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -5,13 +5,13 @@ type TokenType int
|
||||
const (
|
||||
TokenOpenParen TokenType = iota
|
||||
TokenCloseParen
|
||||
TokenAtom
|
||||
TokenVariable
|
||||
TokenSlash
|
||||
TokenDot
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
Index int
|
||||
Type TokenType
|
||||
Type TokenType
|
||||
Value string
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func getToken(i *iterator.Iterator[rune]) (*Token, error) {
|
||||
if err != nil || unicode.IsSpace(pop) || unicode.IsPunct(pop) {
|
||||
return &Token{
|
||||
Index: index,
|
||||
Type: TokenAtom,
|
||||
Type: TokenVariable,
|
||||
Value: atom,
|
||||
}, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user