refactor: make lambda expression types immutable #38
@@ -34,34 +34,34 @@ func main() {
|
||||
logger.Info("compiled λ expression", "tree", compiled.String())
|
||||
|
||||
// Create reducer with the compiled expression.
|
||||
reducer := lambda.NewNormalOrderReducer(&compiled)
|
||||
interpreter := lambda.NewInterpreter(compiled)
|
||||
|
||||
// If the user selected to track CPU performance, attach a profiler.
|
||||
if options.Profile != "" {
|
||||
plugins.NewPerformance(options.Profile, reducer)
|
||||
plugins.NewPerformance(options.Profile, interpreter)
|
||||
}
|
||||
|
||||
// If the user selected to produce a step-by-step explanation, attach an
|
||||
// observer.
|
||||
if options.Explanation {
|
||||
plugins.NewExplanation(reducer)
|
||||
plugins.NewExplanation(interpreter)
|
||||
}
|
||||
|
||||
// If the user opted to track statistics, attach a tracker.
|
||||
if options.Statistics {
|
||||
plugins.NewStatistics(reducer)
|
||||
plugins.NewStatistics(interpreter)
|
||||
}
|
||||
|
||||
// If the user selected for verbose debug logs, attach a reduction tracker.
|
||||
if options.Verbose {
|
||||
plugins.NewLogs(logger, reducer)
|
||||
plugins.NewLogs(logger, interpreter)
|
||||
}
|
||||
|
||||
// Run reduction.
|
||||
reducer.Reduce()
|
||||
interpreter.Run()
|
||||
|
||||
// Return the final reduced result.
|
||||
result := reducer.Expression().String()
|
||||
result := interpreter.Expression().String()
|
||||
err = options.Destination.Write(result)
|
||||
cli.HandleError(err)
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ func runSample(samplePath string) (string, error) {
|
||||
compiled := convert.SaccharineToLambda(ast)
|
||||
|
||||
// Create and run the reducer.
|
||||
reducer := lambda.NewNormalOrderReducer(&compiled)
|
||||
reducer.Reduce()
|
||||
reducer := lambda.NewInterpreter(compiled)
|
||||
reducer.Run()
|
||||
|
||||
return reducer.Expression().String() + "\n", nil
|
||||
}
|
||||
|
||||
@@ -3,17 +3,17 @@ package plugins
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"git.maximhutz.com/max/lambda/pkg/reducer"
|
||||
"git.maximhutz.com/max/lambda/pkg/interpreter"
|
||||
)
|
||||
|
||||
type Logs struct {
|
||||
logger *slog.Logger
|
||||
reducer reducer.Reducer
|
||||
reducer interpreter.Interpreter
|
||||
}
|
||||
|
||||
func NewLogs(logger *slog.Logger, r reducer.Reducer) *Logs {
|
||||
func NewLogs(logger *slog.Logger, r interpreter.Interpreter) *Logs {
|
||||
plugin := &Logs{logger, r}
|
||||
r.On(reducer.StepEvent, plugin.Step)
|
||||
r.On(interpreter.StepEvent, plugin.Step)
|
||||
|
||||
return plugin
|
||||
}
|
||||
|
||||
@@ -5,19 +5,19 @@ package plugins
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.maximhutz.com/max/lambda/pkg/reducer"
|
||||
"git.maximhutz.com/max/lambda/pkg/interpreter"
|
||||
)
|
||||
|
||||
// Track the reductions made by a reduction process.
|
||||
type Explanation struct {
|
||||
reducer reducer.Reducer
|
||||
reducer interpreter.Interpreter
|
||||
}
|
||||
|
||||
// Attaches a new explanation tracker to a reducer.
|
||||
func NewExplanation(r reducer.Reducer) *Explanation {
|
||||
func NewExplanation(r interpreter.Interpreter) *Explanation {
|
||||
plugin := &Explanation{reducer: r}
|
||||
r.On(reducer.StartEvent, plugin.Start)
|
||||
r.On(reducer.StepEvent, plugin.Step)
|
||||
r.On(interpreter.StartEvent, plugin.Start)
|
||||
r.On(interpreter.StepEvent, plugin.Step)
|
||||
|
||||
return plugin
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"path/filepath"
|
||||
"runtime/pprof"
|
||||
|
||||
"git.maximhutz.com/max/lambda/pkg/reducer"
|
||||
"git.maximhutz.com/max/lambda/pkg/interpreter"
|
||||
)
|
||||
|
||||
// Observes a reduction process, and publishes a CPU performance profile on
|
||||
@@ -19,10 +19,10 @@ type Performance struct {
|
||||
}
|
||||
|
||||
// Create a performance tracker that outputs a profile to "file".
|
||||
func NewPerformance(file string, process reducer.Reducer) *Performance {
|
||||
func NewPerformance(file string, process interpreter.Interpreter) *Performance {
|
||||
plugin := &Performance{File: file}
|
||||
process.On(reducer.StartEvent, plugin.Start)
|
||||
process.On(reducer.StopEvent, plugin.Stop)
|
||||
process.On(interpreter.StartEvent, plugin.Start)
|
||||
process.On(interpreter.StopEvent, plugin.Stop)
|
||||
|
||||
return plugin
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"git.maximhutz.com/max/lambda/internal/statistics"
|
||||
"git.maximhutz.com/max/lambda/pkg/reducer"
|
||||
"git.maximhutz.com/max/lambda/pkg/interpreter"
|
||||
)
|
||||
|
||||
// An observer, to track reduction performance.
|
||||
@@ -16,11 +16,11 @@ type Statistics struct {
|
||||
}
|
||||
|
||||
// Create a new reduction performance Statistics.
|
||||
func NewStatistics(r reducer.Reducer) *Statistics {
|
||||
func NewStatistics(r interpreter.Interpreter) *Statistics {
|
||||
plugin := &Statistics{}
|
||||
r.On(reducer.StartEvent, plugin.Start)
|
||||
r.On(reducer.StepEvent, plugin.Step)
|
||||
r.On(reducer.StopEvent, plugin.Stop)
|
||||
r.On(interpreter.StartEvent, plugin.Start)
|
||||
r.On(interpreter.StepEvent, plugin.Step)
|
||||
r.On(interpreter.StopEvent, plugin.Stop)
|
||||
|
||||
return plugin
|
||||
}
|
||||
|
||||
13
pkg/interpreter/events.go
Normal file
13
pkg/interpreter/events.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package interpreter
|
||||
|
||||
// Event represents lifecycle events during interpretation.
|
||||
type Event int
|
||||
|
||||
const (
|
||||
// StartEvent is emitted before interpretation begins.
|
||||
StartEvent Event = iota
|
||||
// StepEvent is emitted after each interpretation step.
|
||||
StepEvent
|
||||
// StopEvent is emitted after interpretation completes.
|
||||
StopEvent
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
// Package reducer provides the abstract Reducer interface for all expression
|
||||
// Package interpreter provides the abstract Reducer interface for all expression
|
||||
// reduction strategies.
|
||||
package reducer
|
||||
package interpreter
|
||||
|
||||
import (
|
||||
"git.maximhutz.com/max/lambda/pkg/emitter"
|
||||
@@ -13,14 +13,14 @@ import (
|
||||
//
|
||||
// Reducers also implement the Emitter interface to allow plugins to observe
|
||||
// reduction lifecycle events (Start, Step, Stop).
|
||||
type Reducer interface {
|
||||
type Interpreter interface {
|
||||
emitter.Emitter[Event]
|
||||
|
||||
// Reduce performs all reduction steps on the expression.
|
||||
// Emits StartEvent before reduction, StepEvent after each step, and
|
||||
// StopEvent after completion.
|
||||
// Returns the final reduced expression.
|
||||
Reduce()
|
||||
// Run a single step. Returns whether the interpreter is complete or not.
|
||||
Step() bool
|
||||
|
||||
// Run until completion.
|
||||
Run()
|
||||
|
||||
// Expression returns the current expression state.
|
||||
Expression() expr.Expression
|
||||
@@ -17,22 +17,22 @@ type Abstraction struct {
|
||||
body Expression
|
||||
}
|
||||
|
||||
var _ Expression = (*Abstraction)(nil)
|
||||
var _ Expression = Abstraction{}
|
||||
|
||||
func (a *Abstraction) Parameter() string {
|
||||
func (a Abstraction) Parameter() string {
|
||||
return a.parameter
|
||||
}
|
||||
|
||||
func (a *Abstraction) Body() Expression {
|
||||
func (a Abstraction) Body() Expression {
|
||||
return a.body
|
||||
}
|
||||
|
||||
func (a *Abstraction) String() string {
|
||||
func (a Abstraction) String() string {
|
||||
return "\\" + a.parameter + "." + a.body.String()
|
||||
}
|
||||
|
||||
func NewAbstraction(parameter string, body Expression) *Abstraction {
|
||||
return &Abstraction{parameter, body}
|
||||
func NewAbstraction(parameter string, body Expression) Abstraction {
|
||||
return Abstraction{parameter, body}
|
||||
}
|
||||
|
||||
/** ------------------------------------------------------------------------- */
|
||||
@@ -42,40 +42,40 @@ type Application struct {
|
||||
argument Expression
|
||||
}
|
||||
|
||||
var _ Expression = (*Application)(nil)
|
||||
var _ Expression = Application{}
|
||||
|
||||
func (a *Application) Abstraction() Expression {
|
||||
func (a Application) Abstraction() Expression {
|
||||
return a.abstraction
|
||||
}
|
||||
|
||||
func (a *Application) Argument() Expression {
|
||||
func (a Application) Argument() Expression {
|
||||
return a.argument
|
||||
}
|
||||
|
||||
func (a *Application) String() string {
|
||||
func (a Application) String() string {
|
||||
return "(" + a.abstraction.String() + " " + a.argument.String() + ")"
|
||||
}
|
||||
|
||||
func NewApplication(abstraction Expression, argument Expression) *Application {
|
||||
return &Application{abstraction, argument}
|
||||
func NewApplication(abstraction Expression, argument Expression) Application {
|
||||
return Application{abstraction, argument}
|
||||
}
|
||||
|
||||
/** ------------------------------------------------------------------------- */
|
||||
|
||||
type Variable struct {
|
||||
value string
|
||||
name string
|
||||
}
|
||||
|
||||
var _ Expression = (*Variable)(nil)
|
||||
var _ Expression = Variable{}
|
||||
|
||||
func (v *Variable) Value() string {
|
||||
return v.value
|
||||
func (v Variable) Name() string {
|
||||
return v.name
|
||||
}
|
||||
|
||||
func (v *Variable) String() string {
|
||||
return v.value
|
||||
func (v Variable) String() string {
|
||||
return v.name
|
||||
}
|
||||
|
||||
func NewVariable(name string) *Variable {
|
||||
return &Variable{name}
|
||||
func NewVariable(name string) Variable {
|
||||
return Variable{name}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,13 @@ import (
|
||||
"git.maximhutz.com/max/lambda/pkg/set"
|
||||
)
|
||||
|
||||
var ticker uint64 = 0
|
||||
|
||||
// GenerateFreshName generates a variable name that is not in the used set.
|
||||
// This function does not mutate the used set.
|
||||
func GenerateFreshName(used *set.Set[string]) string {
|
||||
for i := uint64(0); ; i++ {
|
||||
attempt := "_" + string(strconv.AppendUint(nil, i, 10))
|
||||
attempt := "_" + string(strconv.AppendUint(nil, ticker, 10))
|
||||
|
||||
if !used.Has(attempt) {
|
||||
return attempt
|
||||
|
||||
@@ -2,19 +2,22 @@ package lambda
|
||||
|
||||
import "git.maximhutz.com/max/lambda/pkg/set"
|
||||
|
||||
// GetFreeVariables 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 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)
|
||||
case Variable:
|
||||
return set.New(e.Name())
|
||||
case Abstraction:
|
||||
vars := GetFreeVariables(e.Body())
|
||||
vars.Remove(e.Parameter())
|
||||
return vars
|
||||
case *Application:
|
||||
vars := GetFreeVariables(e.abstraction)
|
||||
vars.Merge(GetFreeVariables(e.argument))
|
||||
case Application:
|
||||
vars := GetFreeVariables(e.Abstraction())
|
||||
vars.Merge(GetFreeVariables(e.Argument()))
|
||||
return vars
|
||||
default:
|
||||
return nil
|
||||
return set.New[string]()
|
||||
}
|
||||
}
|
||||
|
||||
45
pkg/lambda/interpreter.go
Normal file
45
pkg/lambda/interpreter.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package lambda
|
||||
|
||||
import (
|
||||
"git.maximhutz.com/max/lambda/pkg/emitter"
|
||||
"git.maximhutz.com/max/lambda/pkg/expr"
|
||||
"git.maximhutz.com/max/lambda/pkg/interpreter"
|
||||
)
|
||||
|
||||
// NormalOrderReducer implements normal order (leftmost-outermost) reduction
|
||||
// for lambda calculus expressions.
|
||||
type Interpreter struct {
|
||||
emitter.BaseEmitter[interpreter.Event]
|
||||
expression Expression
|
||||
}
|
||||
|
||||
// NewNormalOrderReducer creates a new normal order reducer.
|
||||
func NewInterpreter(expression Expression) *Interpreter {
|
||||
return &Interpreter{
|
||||
BaseEmitter: *emitter.New[interpreter.Event](),
|
||||
expression: expression,
|
||||
}
|
||||
}
|
||||
|
||||
// Expression returns the current expression state.
|
||||
func (r *Interpreter) Expression() expr.Expression {
|
||||
return r.expression
|
||||
}
|
||||
|
||||
func (r *Interpreter) Step() bool {
|
||||
result, done := ReduceOnce(r.expression)
|
||||
r.expression = result
|
||||
return !done
|
||||
}
|
||||
|
||||
// Reduce performs normal order reduction on a lambda expression.
|
||||
// The expression must be a lambda.Expression; other types are returned unchanged.
|
||||
func (r *Interpreter) Run() {
|
||||
r.Emit(interpreter.StartEvent)
|
||||
|
||||
for !r.Step() {
|
||||
r.Emit(interpreter.StepEvent)
|
||||
}
|
||||
|
||||
r.Emit(interpreter.StopEvent)
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
package lambda
|
||||
|
||||
// IsFreeVariable returns true if the variable name n occurs free in the expression.
|
||||
// This function does not mutate the input expression.
|
||||
func IsFreeVariable(n string, e Expression) bool {
|
||||
switch e := e.(type) {
|
||||
case *Variable:
|
||||
return e.value == n
|
||||
case *Abstraction:
|
||||
return e.parameter != n && IsFreeVariable(n, e.body)
|
||||
case *Application:
|
||||
return IsFreeVariable(n, e.abstraction) || IsFreeVariable(n, e.argument)
|
||||
case Variable:
|
||||
return e.Name() == n
|
||||
case Abstraction:
|
||||
return e.Parameter() != n && IsFreeVariable(n, e.Body())
|
||||
case Application:
|
||||
return IsFreeVariable(n, e.Abstraction()) || IsFreeVariable(n, e.Argument())
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package lambda
|
||||
|
||||
type Iterator struct {
|
||||
trace []*Expression
|
||||
}
|
||||
|
||||
func NewIterator(expr *Expression) *Iterator {
|
||||
return &Iterator{[]*Expression{expr}}
|
||||
}
|
||||
|
||||
func (i *Iterator) Done() bool {
|
||||
return len(i.trace) == 0
|
||||
}
|
||||
|
||||
func (i *Iterator) Current() *Expression {
|
||||
if i.Done() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return i.trace[len(i.trace)-1]
|
||||
}
|
||||
|
||||
func (i *Iterator) Parent() *Expression {
|
||||
if len(i.trace) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return i.trace[len(i.trace)-2]
|
||||
}
|
||||
|
||||
func (i *Iterator) Swap(with Expression) {
|
||||
current := i.Current()
|
||||
if current != nil {
|
||||
*current = with
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Iterator) Back() bool {
|
||||
if i.Done() {
|
||||
return false
|
||||
}
|
||||
|
||||
i.trace = i.trace[:len(i.trace)-1]
|
||||
return true
|
||||
}
|
||||
|
||||
func (i *Iterator) Next() {
|
||||
switch typed := (*i.Current()).(type) {
|
||||
case *Abstraction:
|
||||
i.trace = append(i.trace, &typed.body)
|
||||
case *Application:
|
||||
i.trace = append(i.trace, &typed.abstraction)
|
||||
case *Variable:
|
||||
for len(i.trace) > 1 {
|
||||
if app, ok := (*i.Parent()).(*Application); ok {
|
||||
if app.abstraction == *i.Current() {
|
||||
i.Back()
|
||||
i.trace = append(i.trace, &app.argument)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
i.Back()
|
||||
}
|
||||
|
||||
i.trace = []*Expression{}
|
||||
}
|
||||
}
|
||||
32
pkg/lambda/reduce.go
Normal file
32
pkg/lambda/reduce.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package lambda
|
||||
|
||||
func ReduceOnce(e Expression) (Expression, bool) {
|
||||
switch e := e.(type) {
|
||||
case Abstraction:
|
||||
body, reduced := ReduceOnce(e.Body())
|
||||
if reduced {
|
||||
return NewAbstraction(e.Parameter(), body), true
|
||||
}
|
||||
return e, false
|
||||
|
||||
case Application:
|
||||
if fn, fnOk := e.Abstraction().(Abstraction); fnOk {
|
||||
return Substitute(fn.Body(), fn.Parameter(), e.Argument()), true
|
||||
}
|
||||
|
||||
abs, reduced := ReduceOnce(e.Abstraction())
|
||||
if reduced {
|
||||
return NewApplication(abs, e.Argument()), true
|
||||
}
|
||||
|
||||
arg, reduced := ReduceOnce(e.Argument())
|
||||
if reduced {
|
||||
return NewApplication(e.Abstraction(), arg), true
|
||||
}
|
||||
|
||||
return e, false
|
||||
|
||||
default:
|
||||
return e, false
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package lambda
|
||||
|
||||
import (
|
||||
"git.maximhutz.com/max/lambda/pkg/emitter"
|
||||
"git.maximhutz.com/max/lambda/pkg/expr"
|
||||
"git.maximhutz.com/max/lambda/pkg/reducer"
|
||||
)
|
||||
|
||||
// NormalOrderReducer implements normal order (leftmost-outermost) reduction
|
||||
// for lambda calculus expressions.
|
||||
type NormalOrderReducer struct {
|
||||
emitter.BaseEmitter[reducer.Event]
|
||||
expression *Expression
|
||||
}
|
||||
|
||||
// NewNormalOrderReducer creates a new normal order reducer.
|
||||
func NewNormalOrderReducer(expression *Expression) *NormalOrderReducer {
|
||||
return &NormalOrderReducer{
|
||||
BaseEmitter: *emitter.New[reducer.Event](),
|
||||
expression: expression,
|
||||
}
|
||||
}
|
||||
|
||||
// Expression returns the current expression state.
|
||||
func (r *NormalOrderReducer) Expression() expr.Expression {
|
||||
return *r.expression
|
||||
}
|
||||
|
||||
func isViable(e *Expression) (*Abstraction, Expression, bool) {
|
||||
if e == nil {
|
||||
return nil, nil, false
|
||||
} else if app, appOk := (*e).(*Application); !appOk {
|
||||
return nil, nil, false
|
||||
} else if fn, fnOk := app.abstraction.(*Abstraction); !fnOk {
|
||||
return nil, nil, false
|
||||
} else {
|
||||
return fn, app.argument, true
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce performs normal order reduction on a lambda expression.
|
||||
// The expression must be a lambda.Expression; other types are returned unchanged.
|
||||
func (r *NormalOrderReducer) Reduce() {
|
||||
r.Emit(reducer.StartEvent)
|
||||
it := NewIterator(r.expression)
|
||||
|
||||
for !it.Done() {
|
||||
if fn, arg, ok := isViable(it.Current()); !ok {
|
||||
it.Next()
|
||||
} else {
|
||||
it.Swap(Substitute(fn.body, fn.parameter, arg))
|
||||
r.Emit(reducer.StepEvent)
|
||||
|
||||
if _, _, ok := isViable(it.Parent()); ok {
|
||||
it.Back()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.Emit(reducer.StopEvent)
|
||||
}
|
||||
@@ -1,34 +1,27 @@
|
||||
package lambda
|
||||
|
||||
// Rename replaces all occurrences of the target variable name with the new name.
|
||||
func Rename(expr Expression, target string, newName string) Expression {
|
||||
switch e := expr.(type) {
|
||||
case *Variable:
|
||||
if e.value == target {
|
||||
case Variable:
|
||||
if e.Name() == target {
|
||||
return NewVariable(newName)
|
||||
}
|
||||
return e
|
||||
|
||||
case *Abstraction:
|
||||
newParam := e.parameter
|
||||
if e.parameter == target {
|
||||
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
|
||||
}
|
||||
newBody := Rename(e.Body(), target, newName)
|
||||
|
||||
return NewAbstraction(newParam, newBody)
|
||||
|
||||
case *Application:
|
||||
newAbs := Rename(e.abstraction, target, newName)
|
||||
newArg := Rename(e.argument, target, newName)
|
||||
|
||||
if newAbs == e.abstraction && newArg == e.argument {
|
||||
return e
|
||||
}
|
||||
case Application:
|
||||
newAbs := Rename(e.Abstraction(), target, newName)
|
||||
newArg := Rename(e.Argument(), target, newName)
|
||||
|
||||
return NewApplication(newAbs, newArg)
|
||||
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
package lambda
|
||||
|
||||
// Substitute replaces all free occurrences of the target variable with the replacement expression.
|
||||
// Alpha-renaming is performed automatically to avoid variable capture.
|
||||
func Substitute(expr Expression, target string, replacement Expression) Expression {
|
||||
switch e := expr.(type) {
|
||||
case *Variable:
|
||||
if e.value == target {
|
||||
case Variable:
|
||||
if e.Name() == target {
|
||||
return replacement
|
||||
}
|
||||
return e
|
||||
|
||||
case *Abstraction:
|
||||
if e.parameter == target {
|
||||
case Abstraction:
|
||||
if e.Parameter() == target {
|
||||
return e
|
||||
}
|
||||
|
||||
body := e.body
|
||||
param := e.parameter
|
||||
body := e.Body()
|
||||
param := e.Parameter()
|
||||
if IsFreeVariable(param, replacement) {
|
||||
freeVars := GetFreeVariables(replacement)
|
||||
freeVars.Merge(GetFreeVariables(body))
|
||||
@@ -24,19 +26,12 @@ func Substitute(expr Expression, target string, replacement Expression) Expressi
|
||||
}
|
||||
|
||||
newBody := Substitute(body, target, replacement)
|
||||
if newBody == body && param == e.parameter {
|
||||
return e
|
||||
}
|
||||
|
||||
return NewAbstraction(param, newBody)
|
||||
|
||||
case *Application:
|
||||
newAbs := Substitute(e.abstraction, target, replacement)
|
||||
newArg := Substitute(e.argument, target, replacement)
|
||||
|
||||
if newAbs == e.abstraction && newArg == e.argument {
|
||||
return e
|
||||
}
|
||||
case Application:
|
||||
newAbs := Substitute(e.Abstraction(), target, replacement)
|
||||
newArg := Substitute(e.Argument(), target, replacement)
|
||||
|
||||
return NewApplication(newAbs, newArg)
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package reducer
|
||||
|
||||
// Event represents lifecycle events during reduction.
|
||||
type Event int
|
||||
|
||||
const (
|
||||
// StartEvent is emitted before reduction begins.
|
||||
StartEvent Event = iota
|
||||
// StepEvent is emitted after each reduction step.
|
||||
StepEvent
|
||||
// StopEvent is emitted after reduction completes.
|
||||
StopEvent
|
||||
)
|
||||
File diff suppressed because one or more lines are too long
@@ -1,8 +0,0 @@
|
||||
0 := \f.\x.x
|
||||
inc n := \f x.(f (n f x))
|
||||
exp n m := (m n)
|
||||
print n := (n F X)
|
||||
|
||||
N := (inc (inc (inc (inc (inc (inc 0))))))
|
||||
|
||||
(print (exp N N))
|
||||
Reference in New Issue
Block a user