refactor: move event system to reducer, remove engine package (#32)

## Description

This PR completes the MVC-inspired refactoring by moving the event system from the engine into the reducer.
The engine package is now removed entirely, as the reducer handles both reduction logic and lifecycle events.

- Add `pkg/reducer/events.go` with `StartEvent`, `StepEvent`, and `StopEvent`.
- Extend `Reducer` interface to embed `Emitter[Event]` and add `Expression()` method.
- Update `NormalOrderReducer` to embed `BaseEmitter` and emit lifecycle events during reduction.
- Update all plugins to attach to `Reducer` instead of `Engine`.
- Remove `internal/engine` package entirely.
- Add `Off()` method to `BaseEmitter` to complete the `Emitter` interface.
- Fix `Emitter.On` signature to use generic type `E` instead of `string`.

### Decisions

- The `Reducer` interface now combines reduction logic with event emission, making it the single orchestration point.
- Plugins attach directly to the reducer, simplifying the architecture.
- The `Expression()` method on `Reducer` provides access to current state for plugins.

## Benefits

- Simpler architecture with one fewer abstraction layer.
- Plugins are now mode-agnostic - they work with any `Reducer` implementation.
- Cleaner separation: reducers handle reduction, plugins observe via events.
- Easier to add new evaluation modes - just implement `Reducer` with embedded emitter.

## Checklist

- [x] Code follows conventional commit format.
- [x] Branch follows naming convention (`<type>/<description>`).
- [x] Tests pass (if applicable).
- [ ] Documentation updated (if applicable).

Reviewed-on: #32
Co-authored-by: M.V. Hutz <git@maximhutz.me>
Co-committed-by: M.V. Hutz <git@maximhutz.me>
This commit was merged in pull request #32.
This commit is contained in:
2026-01-17 00:27:36 +00:00
committed by Maxim Hutz
parent f8e1223463
commit 1974ad582f
14 changed files with 143 additions and 154 deletions

View File

@@ -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.
reducer := lambda.NewNormalOrderReducer()
process := engine.New(options, compiled, reducer)
// Create reducer with the compiled expression.
reducer := lambda.NewNormalOrderReducer(&compiled)
// 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()
// Return the final reduced result.
result := process.Expression.String()
result := reducer.Expression().String()
err = options.Destination.Write(result)
cli.HandleError(err)
}

View File

@@ -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 reducer.
reducer := lambda.NewNormalOrderReducer(&compiled)
reducer.Reduce()
// Create and run the engine with normal order reducer.
reducer := lambda.NewNormalOrderReducer()
process := engine.New(cfg, compiled, reducer)
process.Run()
return process.Expression.String() + "\n", nil
return reducer.Expression().String() + "\n", nil
}
// Test that all samples produce expected output.