feat: add De Bruijn index reduction engine

Closes #26

- Added -i flag to select interpreter (lambda or debruijn)
- Created debruijn package with Expression interface
  - Variable contains index and optional label
  - Abstraction contains only body (no parameter)
  - Application structure remains similar
- Implemented De Bruijn reduction without variable renaming
  - Shift operation handles index adjustments
  - Substitute replaces by index instead of name
- Abstracted Engine into interface with two implementations
  - LambdaEngine: original named variable engine
  - DeBruijnEngine: new index-based engine
- Added conversion functions between representations
  - LambdaToDeBruijn: converts named to indexed
  - DeBruijnToLambda: converts indexed back to named
  - SaccharineToDeBruijn: direct saccharine to De Bruijn
- Updated main to switch engines based on -i flag
- All test samples pass with both engines

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-12 21:27:40 -05:00
parent 335ce95c50
commit f3b9137d75
16 changed files with 679 additions and 63 deletions

View File

@@ -0,0 +1,36 @@
package engine
import (
"git.maximhutz.com/max/lambda/internal/config"
"git.maximhutz.com/max/lambda/pkg/debruijn"
"git.maximhutz.com/max/lambda/pkg/emitter"
)
// A process for reducing one λ-expression using De Bruijn indices.
type DeBruijnEngine struct {
Config *config.Config
Expression *debruijn.Expression
emitter.Emitter
}
// NewDeBruijnEngine creates a new De Bruijn engine.
func NewDeBruijnEngine(config *config.Config, expression interface{}) *DeBruijnEngine {
expr := expression.(*debruijn.Expression)
return &DeBruijnEngine{Config: config, Expression: expr}
}
// Run begins the reduction process.
func (e *DeBruijnEngine) Run() {
e.Emit("start")
debruijn.ReduceAll(e.Expression, func() {
e.Emit("step")
})
e.Emit("end")
}
// GetResult returns the stringified result.
func (e *DeBruijnEngine) GetResult() string {
return debruijn.Stringify(*e.Expression)
}

View File

@@ -1,32 +0,0 @@
// Package "engine" provides an extensible interface for users to interfact with
// λ-calculus.
package engine
import (
"git.maximhutz.com/max/lambda/internal/config"
"git.maximhutz.com/max/lambda/pkg/emitter"
"git.maximhutz.com/max/lambda/pkg/lambda"
)
// A process for reducing one λ-expression.
type Engine struct {
Config *config.Config
Expression *lambda.Expression
emitter.Emitter
}
// Create a new engine, given an unreduced λ-expression.
func New(config *config.Config, expression *lambda.Expression) *Engine {
return &Engine{Config: config, Expression: expression}
}
// Begin the reduction process.
func (e Engine) Run() {
e.Emit("start")
lambda.ReduceAll(e.Expression, func() {
e.Emit("step")
})
e.Emit("end")
}

View File

@@ -0,0 +1,24 @@
// Package "engine" provides an extensible interface for users to interfact with
// λ-calculus.
package engine
import (
"git.maximhutz.com/max/lambda/internal/config"
"git.maximhutz.com/max/lambda/pkg/emitter"
)
// Engine is an interface for reduction engines.
type Engine interface {
Run()
GetResult() string
On(message string, fn func()) *emitter.Observer
Emit(message string)
}
// New creates the appropriate engine based on the config.
func New(cfg *config.Config, input interface{}) Engine {
if cfg.Interpreter == "debruijn" {
return NewDeBruijnEngine(cfg, input)
}
return NewLambdaEngine(cfg, input)
}

View File

@@ -0,0 +1,36 @@
package engine
import (
"git.maximhutz.com/max/lambda/internal/config"
"git.maximhutz.com/max/lambda/pkg/emitter"
"git.maximhutz.com/max/lambda/pkg/lambda"
)
// A process for reducing one λ-expression using named variables.
type LambdaEngine struct {
Config *config.Config
Expression *lambda.Expression
emitter.Emitter
}
// NewLambdaEngine creates a new lambda engine.
func NewLambdaEngine(config *config.Config, expression interface{}) *LambdaEngine {
expr := expression.(*lambda.Expression)
return &LambdaEngine{Config: config, Expression: expr}
}
// Run begins the reduction process.
func (e *LambdaEngine) Run() {
e.Emit("start")
lambda.ReduceAll(e.Expression, func() {
e.Emit("step")
})
e.Emit("end")
}
// GetResult returns the stringified result.
func (e *LambdaEngine) GetResult() string {
return lambda.Stringify(*e.Expression)
}