feat: add De Bruijn indexed reduction engine

Add a new interpreter option (-i debruijn) that uses De Bruijn indices
for variable representation, eliminating the need for variable renaming
during substitution.

- Add -i flag to select interpreter (lambda or debruijn)
- Create debruijn package with Expression types (Variable with index,
  Abstraction without parameter, Application)
- Implement shift and substitute operations for De Bruijn indices
- Add conversion functions between lambda and De Bruijn representations
- Update CLI to support switching between interpreters
- Add De Bruijn tests to verify all samples pass

Closes #26
This commit is contained in:
2026-01-16 19:36:05 -05:00
parent 1974ad582f
commit 528956b033
12 changed files with 621 additions and 9 deletions

119
pkg/debruijn/expression.go Normal file
View File

@@ -0,0 +1,119 @@
// Package debruijn provides De Bruijn indexed lambda calculus expressions.
// De Bruijn indices eliminate the need for variable names by using numeric
// indices to refer to bound variables, avoiding capture issues during substitution.
package debruijn
import "git.maximhutz.com/max/lambda/pkg/expr"
// Expression is the interface for all De Bruijn indexed expression types.
// It embeds the general expr.Expression interface for cross-mode compatibility.
type Expression interface {
expr.Expression
Accept(Visitor)
}
/** ------------------------------------------------------------------------- */
// Abstraction represents a lambda abstraction without a named parameter.
// In De Bruijn notation, the parameter is implicit and referenced by index 0
// within the body.
type Abstraction struct {
body Expression
}
// Body returns the body of the abstraction.
func (a *Abstraction) Body() Expression {
return a.body
}
// Accept implements the Visitor pattern.
func (a *Abstraction) Accept(v Visitor) {
v.VisitAbstraction(a)
}
// String returns the De Bruijn notation string representation.
func (a *Abstraction) String() string {
return Stringify(a)
}
// NewAbstraction creates a new De Bruijn abstraction with the given body.
func NewAbstraction(body Expression) *Abstraction {
return &Abstraction{body: body}
}
/** ------------------------------------------------------------------------- */
// Application represents the application of one expression to another.
type Application struct {
abstraction Expression
argument Expression
}
// Abstraction returns the function expression being applied.
func (a *Application) Abstraction() Expression {
return a.abstraction
}
// Argument returns the argument expression.
func (a *Application) Argument() Expression {
return a.argument
}
// Accept implements the Visitor pattern.
func (a *Application) Accept(v Visitor) {
v.VisitApplication(a)
}
// String returns the De Bruijn notation string representation.
func (a *Application) String() string {
return Stringify(a)
}
// NewApplication creates a new application expression.
func NewApplication(abstraction Expression, argument Expression) *Application {
return &Application{abstraction: abstraction, argument: argument}
}
/** ------------------------------------------------------------------------- */
// Variable represents a De Bruijn indexed variable.
// The index indicates how many binders to skip to find the binding abstraction.
// The label is an optional hint for display purposes.
type Variable struct {
index int
label string
}
// Index returns the De Bruijn index.
func (v *Variable) Index() int {
return v.index
}
// Label returns the optional variable label.
func (v *Variable) Label() string {
return v.label
}
// Accept implements the Visitor pattern.
func (v *Variable) Accept(visitor Visitor) {
visitor.VisitVariable(v)
}
// String returns the De Bruijn notation string representation.
func (v *Variable) String() string {
return Stringify(v)
}
// NewVariable creates a new De Bruijn variable with the given index and label.
func NewVariable(index int, label string) *Variable {
return &Variable{index: index, label: label}
}
/** ------------------------------------------------------------------------- */
// Visitor interface for traversing De Bruijn expressions.
type Visitor interface {
VisitAbstraction(*Abstraction)
VisitApplication(*Application)
VisitVariable(*Variable)
}