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:
@@ -9,4 +9,5 @@ type Config struct {
|
||||
Explanation bool // Whether or not to print an explanation of the reduction.
|
||||
Profile string // If not nil, print a CPU profile during execution.
|
||||
Statistics bool // Whether or not to print statistics.
|
||||
Interpreter string // The interpreter to use: "lambda" or "debruijn".
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ func FromArgs() (*Config, error) {
|
||||
profile := flag.String("p", "", "CPU profiling. If an output file is defined, the program will profile its execution and dump its results into it.")
|
||||
file := flag.String("f", "", "File. If set, read source from the specified file.")
|
||||
output := flag.String("o", "", "Output. If set, write result to the specified file. Use '-' for stdout (default).")
|
||||
interpreter := flag.String("i", "lambda", "Interpreter. Choose 'lambda' or 'debruijn' reduction engine (default: lambda).")
|
||||
flag.Parse()
|
||||
|
||||
// Parse source type.
|
||||
@@ -45,6 +46,11 @@ func FromArgs() (*Config, error) {
|
||||
destination = FileDestination{Path: *output}
|
||||
}
|
||||
|
||||
// Validate interpreter flag.
|
||||
if *interpreter != "lambda" && *interpreter != "debruijn" {
|
||||
return nil, fmt.Errorf("invalid interpreter: %s (must be 'lambda' or 'debruijn')", *interpreter)
|
||||
}
|
||||
|
||||
return &Config{
|
||||
Source: source,
|
||||
Destination: destination,
|
||||
@@ -52,5 +58,6 @@ func FromArgs() (*Config, error) {
|
||||
Explanation: *explanation,
|
||||
Profile: *profile,
|
||||
Statistics: *statistics,
|
||||
Interpreter: *interpreter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
36
internal/engine/debruijn_engine.go
Normal file
36
internal/engine/debruijn_engine.go
Normal 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)
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
24
internal/engine/interface.go
Normal file
24
internal/engine/interface.go
Normal 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)
|
||||
}
|
||||
36
internal/engine/lambda_engine.go
Normal file
36
internal/engine/lambda_engine.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user