From bcc03311494b6b405eaea6421926c3ad87a9bb00 Mon Sep 17 00:00:00 2001 From: "M.V. Hutz" Date: Fri, 16 Jan 2026 18:50:52 -0500 Subject: [PATCH 1/7] refactor: move event system to reducer, remove engine package Move the event emitter and lifecycle events from the engine into the reducer, making the reducer the single point of orchestration for reduction. This eliminates the engine package entirely. - Add events.go to pkg/reducer with Start, Step, and Stop events. - Extend Reducer interface to embed Emitter and add Expression() method. - Update NormalOrderReducer to embed BaseEmitter and emit lifecycle events. - Update all plugins to attach to Reducer instead of Engine. - Remove internal/engine package. - Add Off() method to BaseEmitter to complete Emitter interface. - Fix Emitter.On signature to use generic type E instead of string. --- cmd/lambda/lambda.go | 24 +++++++++----------- cmd/lambda/lambda_test.go | 19 +++------------- internal/engine/engine.go | 40 --------------------------------- internal/engine/events.go | 9 -------- internal/plugins/debug.go | 12 +++++----- internal/plugins/explanation.go | 18 +++++++-------- internal/plugins/performance.go | 10 ++++----- internal/plugins/statistics.go | 10 ++++----- pkg/emitter/emitter.go | 9 +++++++- pkg/lambda/reducer.go | 27 ++++++++++++++++++---- pkg/reducer/events.go | 13 +++++++++++ pkg/reducer/reducer.go | 20 +++++++++++++---- 12 files changed, 99 insertions(+), 112 deletions(-) delete mode 100644 internal/engine/engine.go delete mode 100644 internal/engine/events.go create mode 100644 pkg/reducer/events.go diff --git a/cmd/lambda/lambda.go b/cmd/lambda/lambda.go index 2679d27..3d8570f 100644 --- a/cmd/lambda/lambda.go +++ b/cmd/lambda/lambda.go @@ -5,7 +5,6 @@ import ( "git.maximhutz.com/max/lambda/internal/cli" "git.maximhutz.com/max/lambda/internal/config" - "git.maximhutz.com/max/lambda/internal/engine" "git.maximhutz.com/max/lambda/internal/plugins" "git.maximhutz.com/max/lambda/pkg/convert" "git.maximhutz.com/max/lambda/pkg/lambda" @@ -34,36 +33,35 @@ func main() { compiled := convert.SaccharineToLambda(ast) logger.Info("compiled λ expression", "tree", compiled.String()) - // Create reduction engine with normal order reducer. + // Create reducer. reducer := lambda.NewNormalOrderReducer() - process := engine.New(options, compiled, reducer) - // If the user selected to track CPU performance, attach a profiler to the - // process. + // If the user selected to track CPU performance, attach a profiler. if options.Profile != "" { - plugins.NewPerformance(options.Profile, process) + plugins.NewPerformance(options.Profile, reducer) } // If the user selected to produce a step-by-step explanation, attach an - // observer here. + // observer. if options.Explanation { - plugins.NewExplanation(process) + plugins.NewExplanation(reducer) } - // If the user opted to track statistics, attach a tracker here, too. + // If the user opted to track statistics, attach a tracker. if options.Statistics { - plugins.NewStatistics(process) + plugins.NewStatistics(reducer) } // If the user selected for verbose debug logs, attach a reduction tracker. if options.Verbose { - plugins.NewLogs(logger, process) + plugins.NewLogs(logger, reducer) } - process.Run() + // Run reduction. + reducer.Reduce(compiled) // Return the final reduced result. - result := process.Expression.String() + result := reducer.Expression().String() err = options.Destination.Write(result) cli.HandleError(err) } diff --git a/cmd/lambda/lambda_test.go b/cmd/lambda/lambda_test.go index 6c84fd6..3855fc5 100644 --- a/cmd/lambda/lambda_test.go +++ b/cmd/lambda/lambda_test.go @@ -6,8 +6,6 @@ import ( "strings" "testing" - "git.maximhutz.com/max/lambda/internal/config" - "git.maximhutz.com/max/lambda/internal/engine" "git.maximhutz.com/max/lambda/pkg/convert" "git.maximhutz.com/max/lambda/pkg/lambda" "git.maximhutz.com/max/lambda/pkg/saccharine" @@ -31,22 +29,11 @@ func runSample(samplePath string) (string, error) { // Compile expression to lambda calculus. compiled := convert.SaccharineToLambda(ast) - // Create minimal config for benchmarking. - cfg := &config.Config{ - Source: config.StringSource{Data: ""}, - Destination: config.StdoutDestination{}, - Profile: "", - Explanation: false, - Statistics: false, - Verbose: false, - } - - // Create and run the engine with normal order reducer. + // Create and run the reducer. reducer := lambda.NewNormalOrderReducer() - process := engine.New(cfg, compiled, reducer) - process.Run() + reducer.Reduce(compiled) - return process.Expression.String() + "\n", nil + return reducer.Expression().String() + "\n", nil } // Test that all samples produce expected output. diff --git a/internal/engine/engine.go b/internal/engine/engine.go deleted file mode 100644 index ea075b4..0000000 --- a/internal/engine/engine.go +++ /dev/null @@ -1,40 +0,0 @@ -// Package "engine" provides an extensible interface for users to interact with -// λ-calculus and other expression evaluation modes. -package engine - -import ( - "git.maximhutz.com/max/lambda/internal/config" - "git.maximhutz.com/max/lambda/pkg/emitter" - "git.maximhutz.com/max/lambda/pkg/expr" - "git.maximhutz.com/max/lambda/pkg/reducer" -) - -// Engine is a process for reducing one expression. -type Engine struct { - Config *config.Config - Expression expr.Expression - Reducer reducer.Reducer - emitter.BaseEmitter[Event] -} - -// New creates a new engine with the given expression and reducer. -func New(config *config.Config, expression expr.Expression, reducer reducer.Reducer) *Engine { - return &Engine{ - Config: config, - Expression: expression, - Reducer: reducer, - BaseEmitter: *emitter.New[Event](), - } -} - -// Run begins the reduction process. -func (e *Engine) Run() { - e.Emit(StartEvent) - - e.Expression = e.Reducer.Reduce(e.Expression, func(reduced expr.Expression) { - e.Expression = reduced - e.Emit(StepEvent) - }) - - e.Emit(StopEvent) -} diff --git a/internal/engine/events.go b/internal/engine/events.go deleted file mode 100644 index 995ebea..0000000 --- a/internal/engine/events.go +++ /dev/null @@ -1,9 +0,0 @@ -package engine - -type Event int - -const ( - StartEvent Event = iota - StepEvent - StopEvent -) diff --git a/internal/plugins/debug.go b/internal/plugins/debug.go index 1c1a95b..62fbffd 100644 --- a/internal/plugins/debug.go +++ b/internal/plugins/debug.go @@ -3,21 +3,21 @@ package plugins import ( "log/slog" - "git.maximhutz.com/max/lambda/internal/engine" + "git.maximhutz.com/max/lambda/pkg/reducer" ) type Logs struct { logger *slog.Logger - process *engine.Engine + reducer reducer.Reducer } -func NewLogs(logger *slog.Logger, process *engine.Engine) *Logs { - plugin := &Logs{logger, process} - process.On(engine.StepEvent, plugin.Step) +func NewLogs(logger *slog.Logger, r reducer.Reducer) *Logs { + plugin := &Logs{logger, r} + r.On(reducer.StepEvent, plugin.Step) return plugin } func (t *Logs) Step() { - t.logger.Info("reduction", "tree", t.process.Expression.String()) + t.logger.Info("reduction", "tree", t.reducer.Expression().String()) } diff --git a/internal/plugins/explanation.go b/internal/plugins/explanation.go index ef46bb9..08285c2 100644 --- a/internal/plugins/explanation.go +++ b/internal/plugins/explanation.go @@ -5,27 +5,27 @@ package plugins import ( "fmt" - "git.maximhutz.com/max/lambda/internal/engine" + "git.maximhutz.com/max/lambda/pkg/reducer" ) // Track the reductions made by a reduction process. type Explanation struct { - process *engine.Engine + reducer reducer.Reducer } -// Attaches a new explanation tracker to a process. -func NewExplanation(process *engine.Engine) *Explanation { - plugin := &Explanation{process: process} - process.On(engine.StartEvent, plugin.Start) - process.On(engine.StepEvent, plugin.Step) +// Attaches a new explanation tracker to a reducer. +func NewExplanation(r reducer.Reducer) *Explanation { + plugin := &Explanation{reducer: r} + r.On(reducer.StartEvent, plugin.Start) + r.On(reducer.StepEvent, plugin.Step) return plugin } func (t *Explanation) Start() { - fmt.Println(t.process.Expression.String()) + fmt.Println(t.reducer.Expression().String()) } func (t *Explanation) Step() { - fmt.Println(" =", t.process.Expression.String()) + fmt.Println(" =", t.reducer.Expression().String()) } diff --git a/internal/plugins/performance.go b/internal/plugins/performance.go index 0dc69d4..08cc46b 100644 --- a/internal/plugins/performance.go +++ b/internal/plugins/performance.go @@ -1,4 +1,4 @@ -// Package "performance" provides a tracker to observer CPU performance during +// Package "performance" provides a tracker to observe CPU performance during // execution. package plugins @@ -7,7 +7,7 @@ import ( "path/filepath" "runtime/pprof" - "git.maximhutz.com/max/lambda/internal/engine" + "git.maximhutz.com/max/lambda/pkg/reducer" ) // 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 *engine.Engine) *Performance { +func NewPerformance(file string, r reducer.Reducer) *Performance { plugin := &Performance{File: file} - process.On(engine.StartEvent, plugin.Start) - process.On(engine.StopEvent, plugin.Stop) + r.On(reducer.StartEvent, plugin.Start) + r.On(reducer.StopEvent, plugin.Stop) return plugin } diff --git a/internal/plugins/statistics.go b/internal/plugins/statistics.go index 62223e4..b54ab62 100644 --- a/internal/plugins/statistics.go +++ b/internal/plugins/statistics.go @@ -5,8 +5,8 @@ import ( "os" "time" - "git.maximhutz.com/max/lambda/internal/engine" "git.maximhutz.com/max/lambda/internal/statistics" + "git.maximhutz.com/max/lambda/pkg/reducer" ) // An observer, to track reduction performance. @@ -16,11 +16,11 @@ type Statistics struct { } // Create a new reduction performance Statistics. -func NewStatistics(process *engine.Engine) *Statistics { +func NewStatistics(r reducer.Reducer) *Statistics { plugin := &Statistics{} - process.On(engine.StartEvent, plugin.Start) - process.On(engine.StepEvent, plugin.Step) - process.On(engine.StopEvent, plugin.Stop) + r.On(reducer.StartEvent, plugin.Start) + r.On(reducer.StepEvent, plugin.Step) + r.On(reducer.StopEvent, plugin.Stop) return plugin } diff --git a/pkg/emitter/emitter.go b/pkg/emitter/emitter.go index 594a18a..d387fba 100644 --- a/pkg/emitter/emitter.go +++ b/pkg/emitter/emitter.go @@ -3,7 +3,7 @@ package emitter import "git.maximhutz.com/max/lambda/pkg/set" type Emitter[E comparable] interface { - On(string, func()) Listener[E] + On(E, func()) Listener[E] Off(Listener[E]) Emit(E) } @@ -22,6 +22,13 @@ func (e *BaseEmitter[E]) On(kind E, fn func()) Listener[E] { return listener } +func (e *BaseEmitter[E]) Off(listener Listener[E]) { + kind := listener.Kind() + if e.listeners[kind] != nil { + e.listeners[kind].Remove(listener) + } +} + func (e *BaseEmitter[E]) Emit(event E) { if e.listeners[event] == nil { e.listeners[event] = set.New[Listener[E]]() diff --git a/pkg/lambda/reducer.go b/pkg/lambda/reducer.go index b806c62..2fe0d80 100644 --- a/pkg/lambda/reducer.go +++ b/pkg/lambda/reducer.go @@ -1,6 +1,7 @@ package lambda import ( + "git.maximhutz.com/max/lambda/pkg/emitter" "git.maximhutz.com/max/lambda/pkg/expr" "git.maximhutz.com/max/lambda/pkg/reducer" ) @@ -10,24 +11,42 @@ var _ reducer.Reducer = (*NormalOrderReducer)(nil) // NormalOrderReducer implements normal order (leftmost-outermost) reduction // for lambda calculus expressions. -type NormalOrderReducer struct{} +type NormalOrderReducer struct { + emitter.BaseEmitter[reducer.Event] + expression expr.Expression +} // NewNormalOrderReducer creates a new normal order reducer. func NewNormalOrderReducer() *NormalOrderReducer { - return &NormalOrderReducer{} + return &NormalOrderReducer{ + BaseEmitter: *emitter.New[reducer.Event](), + } +} + +// Expression returns the current expression state. +func (r *NormalOrderReducer) Expression() expr.Expression { + return r.expression } // 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(e expr.Expression, onStep func(expr.Expression)) expr.Expression { +func (r *NormalOrderReducer) Reduce(e expr.Expression) expr.Expression { + r.expression = e + lambdaExpr, ok := e.(Expression) if !ok { return e } + r.Emit(reducer.StartEvent) + ReduceAll(&lambdaExpr, func() { - onStep(lambdaExpr) + r.expression = lambdaExpr + r.Emit(reducer.StepEvent) }) + r.expression = lambdaExpr + r.Emit(reducer.StopEvent) + return lambdaExpr } diff --git a/pkg/reducer/events.go b/pkg/reducer/events.go new file mode 100644 index 0000000..ad432fe --- /dev/null +++ b/pkg/reducer/events.go @@ -0,0 +1,13 @@ +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 +) diff --git a/pkg/reducer/reducer.go b/pkg/reducer/reducer.go index 75d2dd9..ec2d02a 100644 --- a/pkg/reducer/reducer.go +++ b/pkg/reducer/reducer.go @@ -2,14 +2,26 @@ // reduction strategies. package reducer -import "git.maximhutz.com/max/lambda/pkg/expr" +import ( + "git.maximhutz.com/max/lambda/pkg/emitter" + "git.maximhutz.com/max/lambda/pkg/expr" +) // Reducer defines the interface for expression reduction strategies. // Different evaluation modes (normal order, applicative order, SKI combinators, // etc.) implement this interface with their own reduction logic. +// +// Reducers also implement the Emitter interface to allow plugins to observe +// reduction lifecycle events (Start, Step, Stop). type Reducer interface { - // Reduce performs all reduction steps on the expression, calling onStep - // after each reduction. + 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(e expr.Expression, onStep func(expr.Expression)) expr.Expression + Reduce(e expr.Expression) expr.Expression + + // Expression returns the current expression state. + Expression() expr.Expression } -- 2.49.1 From aeffe6480486f1bc0efc417eeb294f0386250dbd Mon Sep 17 00:00:00 2001 From: "M.V. Hutz" Date: Fri, 16 Jan 2026 18:56:03 -0500 Subject: [PATCH 2/7] fix: update main and tests for new Reducer API Update call sites to match the new Reducer interface where the expression is passed to the constructor and Reduce() takes no arguments. --- cmd/lambda/lambda.go | 6 +++--- cmd/lambda/lambda_test.go | 4 ++-- pkg/lambda/reducer.go | 37 +++++++++++++++++-------------------- pkg/reducer/reducer.go | 2 +- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/cmd/lambda/lambda.go b/cmd/lambda/lambda.go index 3d8570f..999e722 100644 --- a/cmd/lambda/lambda.go +++ b/cmd/lambda/lambda.go @@ -33,8 +33,8 @@ func main() { compiled := convert.SaccharineToLambda(ast) logger.Info("compiled λ expression", "tree", compiled.String()) - // Create reducer. - reducer := lambda.NewNormalOrderReducer() + // Create reducer with the compiled expression. + reducer := lambda.NewNormalOrderReducer(compiled) // If the user selected to track CPU performance, attach a profiler. if options.Profile != "" { @@ -58,7 +58,7 @@ func main() { } // Run reduction. - reducer.Reduce(compiled) + reducer.Reduce() // Return the final reduced result. result := reducer.Expression().String() diff --git a/cmd/lambda/lambda_test.go b/cmd/lambda/lambda_test.go index 3855fc5..73bdb85 100644 --- a/cmd/lambda/lambda_test.go +++ b/cmd/lambda/lambda_test.go @@ -30,8 +30,8 @@ func runSample(samplePath string) (string, error) { compiled := convert.SaccharineToLambda(ast) // Create and run the reducer. - reducer := lambda.NewNormalOrderReducer() - reducer.Reduce(compiled) + reducer := lambda.NewNormalOrderReducer(compiled) + reducer.Reduce() return reducer.Expression().String() + "\n", nil } diff --git a/pkg/lambda/reducer.go b/pkg/lambda/reducer.go index 2fe0d80..4c5ea19 100644 --- a/pkg/lambda/reducer.go +++ b/pkg/lambda/reducer.go @@ -6,20 +6,18 @@ import ( "git.maximhutz.com/max/lambda/pkg/reducer" ) -// Ensure NormalOrderReducer implements reducer.Reducer. -var _ reducer.Reducer = (*NormalOrderReducer)(nil) - // NormalOrderReducer implements normal order (leftmost-outermost) reduction // for lambda calculus expressions. type NormalOrderReducer struct { emitter.BaseEmitter[reducer.Event] - expression expr.Expression + expression Expression } // NewNormalOrderReducer creates a new normal order reducer. -func NewNormalOrderReducer() *NormalOrderReducer { +func NewNormalOrderReducer(expression Expression) *NormalOrderReducer { return &NormalOrderReducer{ BaseEmitter: *emitter.New[reducer.Event](), + expression: expression, } } @@ -30,23 +28,22 @@ func (r *NormalOrderReducer) Expression() expr.Expression { // 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(e expr.Expression) expr.Expression { - r.expression = e +func (r *NormalOrderReducer) Reduce() { + r.Emit(reducer.StartEvent) + it := NewIterator(&r.expression) - lambdaExpr, ok := e.(Expression) - if !ok { - return e + 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.StartEvent) - - ReduceAll(&lambdaExpr, func() { - r.expression = lambdaExpr - r.Emit(reducer.StepEvent) - }) - - r.expression = lambdaExpr r.Emit(reducer.StopEvent) - - return lambdaExpr } diff --git a/pkg/reducer/reducer.go b/pkg/reducer/reducer.go index ec2d02a..114691c 100644 --- a/pkg/reducer/reducer.go +++ b/pkg/reducer/reducer.go @@ -20,7 +20,7 @@ type Reducer interface { // Emits StartEvent before reduction, StepEvent after each step, and // StopEvent after completion. // Returns the final reduced expression. - Reduce(e expr.Expression) expr.Expression + Reduce() // Expression returns the current expression state. Expression() expr.Expression -- 2.49.1 From b69898cf398544aa13023c635e9970cf44d7de47 Mon Sep 17 00:00:00 2001 From: "M.V. Hutz" Date: Fri, 16 Jan 2026 18:58:08 -0500 Subject: [PATCH 3/7] docs: add tea pr edit instructions to CLAUDE.md --- CLAUDE.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 88ec76f..69a4704 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -80,3 +80,30 @@ Use the `tea` CLI (Gitea command-line tool) for PR operations instead of `gh`. **Linking issues**: When a PR solves an issue, reference the issue in both the commit message and PR description using `Closes #`. This automatically links and closes the issue when the PR is merged. + +### Updating PR Descriptions + +Use the `tea` CLI to update pull request descriptions: + +```bash +tea pr edit -d "New description text" +``` + +#### Options + +- `-d, --description` - Set description directly +- `--desc-file` - Read description from a file +- `-r, --repo` - Specify repo (defaults to current directory) + +#### Examples + +```bash +# Update PR #42 with inline text +tea pr edit 42 -d "Updated implementation based on feedback" + +# Update using a file +tea pr edit 42 --desc-file PR_DESCRIPTION.md + +# Update PR in a different repo +tea pr edit 42 -r username/repo -d "New description" +``` \ No newline at end of file -- 2.49.1 From 475d79c3349fcd0f08b65c6c0923065aa88eba1c Mon Sep 17 00:00:00 2001 From: "M.V. Hutz" Date: Fri, 16 Jan 2026 19:02:29 -0500 Subject: [PATCH 4/7] docs: replace tea pr edit with tea comment in CLAUDE.md The tea CLI does not have a pr edit command. Updated documentation to use tea comment instead. Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 69a4704..56e0529 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -81,29 +81,22 @@ Use the `tea` CLI (Gitea command-line tool) for PR operations instead of `gh`. **Linking issues**: When a PR solves an issue, reference the issue in both the commit message and PR description using `Closes #`. This automatically links and closes the issue when the PR is merged. -### Updating PR Descriptions +### Adding Comments to PRs -Use the `tea` CLI to update pull request descriptions: +Use the `tea` CLI to add comments to pull requests: ```bash -tea pr edit -d "New description text" +tea comment "Comment text" ``` -#### Options - -- `-d, --description` - Set description directly -- `--desc-file` - Read description from a file -- `-r, --repo` - Specify repo (defaults to current directory) - #### Examples ```bash -# Update PR #42 with inline text -tea pr edit 42 -d "Updated implementation based on feedback" +# Add a comment to PR #42 +tea comment 42 "Updated implementation based on feedback" -# Update using a file -tea pr edit 42 --desc-file PR_DESCRIPTION.md - -# Update PR in a different repo -tea pr edit 42 -r username/repo -d "New description" -``` \ No newline at end of file +# Add a multi-line comment +tea comment 42 "Summary of changes: +- Fixed bug in reducer +- Added new tests" +``` -- 2.49.1 From cb4f42c91d97304ed71925d061e1f5aaaf9e9f38 Mon Sep 17 00:00:00 2001 From: "M.V. Hutz" Date: Fri, 16 Jan 2026 19:03:16 -0500 Subject: [PATCH 5/7] docs: add guidance for commenting on PR updates Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 56e0529..bacb503 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -81,7 +81,10 @@ Use the `tea` CLI (Gitea command-line tool) for PR operations instead of `gh`. **Linking issues**: When a PR solves an issue, reference the issue in both the commit message and PR description using `Closes #`. This automatically links and closes the issue when the PR is merged. -### Adding Comments to PRs +### Updating PRs + +When pushing additional changes to an existing PR, add a comment summarizing the new commits. +This keeps reviewers informed of what changed since the initial PR description. Use the `tea` CLI to add comments to pull requests: -- 2.49.1 From d9639ecc2b6b01ed11387e564bd16a641162ea9a Mon Sep 17 00:00:00 2001 From: "M.V. Hutz" Date: Fri, 16 Jan 2026 19:09:27 -0500 Subject: [PATCH 6/7] style: moved isViable to reducer --- pkg/lambda/reduce.go | 30 ------------------------------ pkg/lambda/reducer.go | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 32 deletions(-) delete mode 100644 pkg/lambda/reduce.go diff --git a/pkg/lambda/reduce.go b/pkg/lambda/reduce.go deleted file mode 100644 index 7bcf50c..0000000 --- a/pkg/lambda/reduce.go +++ /dev/null @@ -1,30 +0,0 @@ -package lambda - -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 - } -} - -func ReduceAll(e *Expression, step func()) { - it := NewIterator(e) - - for !it.Done() { - if fn, arg, ok := IsViable(it.Current()); !ok { - it.Next() - } else { - it.Swap(Substitute(fn.body, fn.parameter, arg)) - step() - - if _, _, ok := IsViable(it.Parent()); ok { - it.Back() - } - } - } -} diff --git a/pkg/lambda/reducer.go b/pkg/lambda/reducer.go index 4c5ea19..4117096 100644 --- a/pkg/lambda/reducer.go +++ b/pkg/lambda/reducer.go @@ -26,6 +26,18 @@ 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() { @@ -33,13 +45,13 @@ func (r *NormalOrderReducer) Reduce() { it := NewIterator(&r.expression) for !it.Done() { - if fn, arg, ok := IsViable(it.Current()); !ok { + 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 { + if _, _, ok := isViable(it.Parent()); ok { it.Back() } } -- 2.49.1 From 8dc5d986fd9df06427a009c174e74c89568be0ca Mon Sep 17 00:00:00 2001 From: "M.V. Hutz" Date: Fri, 16 Jan 2026 19:27:16 -0500 Subject: [PATCH 7/7] feat: stuff --- cmd/lambda/lambda.go | 2 +- cmd/lambda/lambda_test.go | 2 +- internal/plugins/performance.go | 8 ++++---- pkg/lambda/reducer.go | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/lambda/lambda.go b/cmd/lambda/lambda.go index 999e722..843897d 100644 --- a/cmd/lambda/lambda.go +++ b/cmd/lambda/lambda.go @@ -34,7 +34,7 @@ func main() { logger.Info("compiled λ expression", "tree", compiled.String()) // Create reducer with the compiled expression. - reducer := lambda.NewNormalOrderReducer(compiled) + reducer := lambda.NewNormalOrderReducer(&compiled) // If the user selected to track CPU performance, attach a profiler. if options.Profile != "" { diff --git a/cmd/lambda/lambda_test.go b/cmd/lambda/lambda_test.go index 73bdb85..8f671d9 100644 --- a/cmd/lambda/lambda_test.go +++ b/cmd/lambda/lambda_test.go @@ -30,7 +30,7 @@ func runSample(samplePath string) (string, error) { compiled := convert.SaccharineToLambda(ast) // Create and run the reducer. - reducer := lambda.NewNormalOrderReducer(compiled) + reducer := lambda.NewNormalOrderReducer(&compiled) reducer.Reduce() return reducer.Expression().String() + "\n", nil diff --git a/internal/plugins/performance.go b/internal/plugins/performance.go index 08cc46b..857eca7 100644 --- a/internal/plugins/performance.go +++ b/internal/plugins/performance.go @@ -1,4 +1,4 @@ -// Package "performance" provides a tracker to observe CPU performance during +// Package "performance" provides a tracker to observer CPU performance during // execution. package plugins @@ -19,10 +19,10 @@ type Performance struct { } // Create a performance tracker that outputs a profile to "file". -func NewPerformance(file string, r reducer.Reducer) *Performance { +func NewPerformance(file string, process reducer.Reducer) *Performance { plugin := &Performance{File: file} - r.On(reducer.StartEvent, plugin.Start) - r.On(reducer.StopEvent, plugin.Stop) + process.On(reducer.StartEvent, plugin.Start) + process.On(reducer.StopEvent, plugin.Stop) return plugin } diff --git a/pkg/lambda/reducer.go b/pkg/lambda/reducer.go index 4117096..b4df0e6 100644 --- a/pkg/lambda/reducer.go +++ b/pkg/lambda/reducer.go @@ -10,11 +10,11 @@ import ( // for lambda calculus expressions. type NormalOrderReducer struct { emitter.BaseEmitter[reducer.Event] - expression Expression + expression *Expression } // NewNormalOrderReducer creates a new normal order reducer. -func NewNormalOrderReducer(expression Expression) *NormalOrderReducer { +func NewNormalOrderReducer(expression *Expression) *NormalOrderReducer { return &NormalOrderReducer{ BaseEmitter: *emitter.New[reducer.Event](), expression: expression, @@ -23,7 +23,7 @@ func NewNormalOrderReducer(expression Expression) *NormalOrderReducer { // Expression returns the current expression state. func (r *NormalOrderReducer) Expression() expr.Expression { - return r.expression + return *r.expression } func isViable(e *Expression) (*Abstraction, Expression, bool) { @@ -42,7 +42,7 @@ func isViable(e *Expression) (*Abstraction, Expression, bool) { // The expression must be a lambda.Expression; other types are returned unchanged. func (r *NormalOrderReducer) Reduce() { r.Emit(reducer.StartEvent) - it := NewIterator(&r.expression) + it := NewIterator(r.expression) for !it.Done() { if fn, arg, ok := isViable(it.Current()); !ok { -- 2.49.1