## 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>
62 lines
1.6 KiB
Go
62 lines
1.6 KiB
Go
package lambda
|
|
|
|
import (
|
|
"git.maximhutz.com/max/lambda/pkg/emitter"
|
|
"git.maximhutz.com/max/lambda/pkg/expr"
|
|
"git.maximhutz.com/max/lambda/pkg/reducer"
|
|
)
|
|
|
|
// NormalOrderReducer implements normal order (leftmost-outermost) reduction
|
|
// for lambda calculus expressions.
|
|
type NormalOrderReducer struct {
|
|
emitter.BaseEmitter[reducer.Event]
|
|
expression *Expression
|
|
}
|
|
|
|
// NewNormalOrderReducer creates a new normal order reducer.
|
|
func NewNormalOrderReducer(expression *Expression) *NormalOrderReducer {
|
|
return &NormalOrderReducer{
|
|
BaseEmitter: *emitter.New[reducer.Event](),
|
|
expression: expression,
|
|
}
|
|
}
|
|
|
|
// Expression returns the current expression state.
|
|
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() {
|
|
r.Emit(reducer.StartEvent)
|
|
it := NewIterator(r.expression)
|
|
|
|
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.StopEvent)
|
|
}
|