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
|
@ go build -o ${BINARY_NAME} ./cmd/lambda
|
||||||
@ chmod +x ${BINARY_NAME}
|
@ chmod +x ${BINARY_NAME}
|
||||||
|
|
||||||
ex1: it
|
ex: it
|
||||||
@ ./lambda.exe "(\n.\f.\x.(f ((n f) x)) \f.\x.x)"
|
@ ./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)
|
expression, err := parser.GetTree(tokens)
|
||||||
cli.HandleError(err)
|
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)
|
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
|
package lambda
|
||||||
|
|
||||||
type Expression interface {
|
type Expression interface {
|
||||||
Accept(ExpressionVisitor)
|
Accept(Visitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ------------------------------------------------------------------------- */
|
/** ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
type Function struct {
|
type Abstraction struct {
|
||||||
Parameter string
|
Parameter string
|
||||||
Body Expression
|
Body Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFunction(parameter string, body Expression) *Function {
|
func NewAbstraction(parameter string, body Expression) *Abstraction {
|
||||||
return &Function{
|
return &Abstraction{Parameter: parameter, Body: body}
|
||||||
Parameter: parameter,
|
|
||||||
Body: body,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Function) Accept(v ExpressionVisitor) {
|
func (this *Abstraction) Accept(v Visitor) {
|
||||||
v.VisitFunction(f)
|
v.VisitAbstraction(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ------------------------------------------------------------------------- */
|
/** ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
type Call struct {
|
type Application struct {
|
||||||
Function Expression
|
Abstraction Expression
|
||||||
Argument Expression
|
Argument Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCall(function Expression, argument Expression) *Call {
|
func NewApplication(function Expression, argument Expression) *Application {
|
||||||
return &Call{
|
return &Application{Abstraction: function, Argument: argument}
|
||||||
Function: function,
|
|
||||||
Argument: argument,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Call) Accept(v ExpressionVisitor) {
|
func (this *Application) Accept(v Visitor) {
|
||||||
v.VisitCall(c)
|
v.VisitApplication(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ------------------------------------------------------------------------- */
|
/** ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
type Atom struct {
|
type Variable struct {
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAtom(name string) *Atom {
|
func NewVariable(name string) *Variable {
|
||||||
return &Atom{
|
return &Variable{Value: name}
|
||||||
Value: name,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Atom) Accept(v ExpressionVisitor) {
|
func (this *Variable) Accept(v Visitor) {
|
||||||
v.VisitAtom(a)
|
v.VisitVariable(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ------------------------------------------------------------------------- */
|
/** ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
type ExpressionVisitor interface {
|
type Visitor interface {
|
||||||
VisitFunction(*Function)
|
VisitAbstraction(*Abstraction)
|
||||||
VisitCall(*Call)
|
VisitApplication(*Application)
|
||||||
VisitAtom(*Atom)
|
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 {
|
switch token.Type {
|
||||||
case tokenizer.TokenAtom:
|
case tokenizer.TokenVariable:
|
||||||
return lambda.NewAtom(token.Value), nil
|
return lambda.NewVariable(token.Value), nil
|
||||||
case tokenizer.TokenDot:
|
case tokenizer.TokenDot:
|
||||||
return nil, fmt.Errorf("Token '.' found without a corresponding slash (column %d).", token.Index)
|
return nil, fmt.Errorf("Token '.' found without a corresponding slash (column %d).", token.Index)
|
||||||
case tokenizer.TokenSlash:
|
case tokenizer.TokenSlash:
|
||||||
atom, atom_err := i.Next()
|
atom, atom_err := i.Next()
|
||||||
if atom_err != nil {
|
if atom_err != nil {
|
||||||
return nil, fmt.Errorf("Could not find parameter of function: %w", atom_err)
|
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)
|
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 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:
|
case tokenizer.TokenOpenParen:
|
||||||
fn, fn_err := ParseExpression(i)
|
fn, fn_err := ParseExpression(i)
|
||||||
if fn_err != nil {
|
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 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:
|
case tokenizer.TokenCloseParen:
|
||||||
return nil, fmt.Errorf("Token ')' found without a corresponding openning parenthesis (column %d).", token.Index)
|
return nil, fmt.Errorf("Token ')' found without a corresponding openning parenthesis (column %d).", token.Index)
|
||||||
default:
|
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 (
|
const (
|
||||||
TokenOpenParen TokenType = iota
|
TokenOpenParen TokenType = iota
|
||||||
TokenCloseParen
|
TokenCloseParen
|
||||||
TokenAtom
|
TokenVariable
|
||||||
TokenSlash
|
TokenSlash
|
||||||
TokenDot
|
TokenDot
|
||||||
)
|
)
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
Index int
|
Index int
|
||||||
Type TokenType
|
Type TokenType
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func getToken(i *iterator.Iterator[rune]) (*Token, error) {
|
|||||||
if err != nil || unicode.IsSpace(pop) || unicode.IsPunct(pop) {
|
if err != nil || unicode.IsSpace(pop) || unicode.IsPunct(pop) {
|
||||||
return &Token{
|
return &Token{
|
||||||
Index: index,
|
Index: index,
|
||||||
Type: TokenAtom,
|
Type: TokenVariable,
|
||||||
Value: atom,
|
Value: atom,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user