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.
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
|||||||
|
|
||||||
"git.maximhutz.com/max/lambda/internal/cli"
|
"git.maximhutz.com/max/lambda/internal/cli"
|
||||||
"git.maximhutz.com/max/lambda/internal/config"
|
"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/internal/plugins"
|
||||||
"git.maximhutz.com/max/lambda/pkg/convert"
|
"git.maximhutz.com/max/lambda/pkg/convert"
|
||||||
"git.maximhutz.com/max/lambda/pkg/lambda"
|
"git.maximhutz.com/max/lambda/pkg/lambda"
|
||||||
@@ -34,36 +33,35 @@ func main() {
|
|||||||
compiled := convert.SaccharineToLambda(ast)
|
compiled := convert.SaccharineToLambda(ast)
|
||||||
logger.Info("compiled λ expression", "tree", compiled.String())
|
logger.Info("compiled λ expression", "tree", compiled.String())
|
||||||
|
|
||||||
// Create reduction engine with normal order reducer.
|
// Create reducer.
|
||||||
reducer := lambda.NewNormalOrderReducer()
|
reducer := lambda.NewNormalOrderReducer()
|
||||||
process := engine.New(options, compiled, reducer)
|
|
||||||
|
|
||||||
// If the user selected to track CPU performance, attach a profiler to the
|
// If the user selected to track CPU performance, attach a profiler.
|
||||||
// process.
|
|
||||||
if options.Profile != "" {
|
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
|
// If the user selected to produce a step-by-step explanation, attach an
|
||||||
// observer here.
|
// observer.
|
||||||
if options.Explanation {
|
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 {
|
if options.Statistics {
|
||||||
plugins.NewStatistics(process)
|
plugins.NewStatistics(reducer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user selected for verbose debug logs, attach a reduction tracker.
|
// If the user selected for verbose debug logs, attach a reduction tracker.
|
||||||
if options.Verbose {
|
if options.Verbose {
|
||||||
plugins.NewLogs(logger, process)
|
plugins.NewLogs(logger, reducer)
|
||||||
}
|
}
|
||||||
|
|
||||||
process.Run()
|
// Run reduction.
|
||||||
|
reducer.Reduce(compiled)
|
||||||
|
|
||||||
// Return the final reduced result.
|
// Return the final reduced result.
|
||||||
result := process.Expression.String()
|
result := reducer.Expression().String()
|
||||||
err = options.Destination.Write(result)
|
err = options.Destination.Write(result)
|
||||||
cli.HandleError(err)
|
cli.HandleError(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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/convert"
|
||||||
"git.maximhutz.com/max/lambda/pkg/lambda"
|
"git.maximhutz.com/max/lambda/pkg/lambda"
|
||||||
"git.maximhutz.com/max/lambda/pkg/saccharine"
|
"git.maximhutz.com/max/lambda/pkg/saccharine"
|
||||||
@@ -31,22 +29,11 @@ func runSample(samplePath string) (string, error) {
|
|||||||
// Compile expression to lambda calculus.
|
// Compile expression to lambda calculus.
|
||||||
compiled := convert.SaccharineToLambda(ast)
|
compiled := convert.SaccharineToLambda(ast)
|
||||||
|
|
||||||
// Create minimal config for benchmarking.
|
// Create and run the reducer.
|
||||||
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.
|
|
||||||
reducer := lambda.NewNormalOrderReducer()
|
reducer := lambda.NewNormalOrderReducer()
|
||||||
process := engine.New(cfg, compiled, reducer)
|
reducer.Reduce(compiled)
|
||||||
process.Run()
|
|
||||||
|
|
||||||
return process.Expression.String() + "\n", nil
|
return reducer.Expression().String() + "\n", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that all samples produce expected output.
|
// Test that all samples produce expected output.
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package engine
|
|
||||||
|
|
||||||
type Event int
|
|
||||||
|
|
||||||
const (
|
|
||||||
StartEvent Event = iota
|
|
||||||
StepEvent
|
|
||||||
StopEvent
|
|
||||||
)
|
|
||||||
@@ -3,21 +3,21 @@ package plugins
|
|||||||
import (
|
import (
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
||||||
"git.maximhutz.com/max/lambda/internal/engine"
|
"git.maximhutz.com/max/lambda/pkg/reducer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Logs struct {
|
type Logs struct {
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
process *engine.Engine
|
reducer reducer.Reducer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLogs(logger *slog.Logger, process *engine.Engine) *Logs {
|
func NewLogs(logger *slog.Logger, r reducer.Reducer) *Logs {
|
||||||
plugin := &Logs{logger, process}
|
plugin := &Logs{logger, r}
|
||||||
process.On(engine.StepEvent, plugin.Step)
|
r.On(reducer.StepEvent, plugin.Step)
|
||||||
|
|
||||||
return plugin
|
return plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Logs) Step() {
|
func (t *Logs) Step() {
|
||||||
t.logger.Info("reduction", "tree", t.process.Expression.String())
|
t.logger.Info("reduction", "tree", t.reducer.Expression().String())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,27 +5,27 @@ package plugins
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.maximhutz.com/max/lambda/internal/engine"
|
"git.maximhutz.com/max/lambda/pkg/reducer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Track the reductions made by a reduction process.
|
// Track the reductions made by a reduction process.
|
||||||
type Explanation struct {
|
type Explanation struct {
|
||||||
process *engine.Engine
|
reducer reducer.Reducer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attaches a new explanation tracker to a process.
|
// Attaches a new explanation tracker to a reducer.
|
||||||
func NewExplanation(process *engine.Engine) *Explanation {
|
func NewExplanation(r reducer.Reducer) *Explanation {
|
||||||
plugin := &Explanation{process: process}
|
plugin := &Explanation{reducer: r}
|
||||||
process.On(engine.StartEvent, plugin.Start)
|
r.On(reducer.StartEvent, plugin.Start)
|
||||||
process.On(engine.StepEvent, plugin.Step)
|
r.On(reducer.StepEvent, plugin.Step)
|
||||||
|
|
||||||
return plugin
|
return plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Explanation) Start() {
|
func (t *Explanation) Start() {
|
||||||
fmt.Println(t.process.Expression.String())
|
fmt.Println(t.reducer.Expression().String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Explanation) Step() {
|
func (t *Explanation) Step() {
|
||||||
fmt.Println(" =", t.process.Expression.String())
|
fmt.Println(" =", t.reducer.Expression().String())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
// execution.
|
||||||
package plugins
|
package plugins
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime/pprof"
|
"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
|
// 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".
|
// 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}
|
plugin := &Performance{File: file}
|
||||||
process.On(engine.StartEvent, plugin.Start)
|
r.On(reducer.StartEvent, plugin.Start)
|
||||||
process.On(engine.StopEvent, plugin.Stop)
|
r.On(reducer.StopEvent, plugin.Stop)
|
||||||
|
|
||||||
return plugin
|
return plugin
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.maximhutz.com/max/lambda/internal/engine"
|
|
||||||
"git.maximhutz.com/max/lambda/internal/statistics"
|
"git.maximhutz.com/max/lambda/internal/statistics"
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/reducer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An observer, to track reduction performance.
|
// An observer, to track reduction performance.
|
||||||
@@ -16,11 +16,11 @@ type Statistics struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a new reduction performance Statistics.
|
// Create a new reduction performance Statistics.
|
||||||
func NewStatistics(process *engine.Engine) *Statistics {
|
func NewStatistics(r reducer.Reducer) *Statistics {
|
||||||
plugin := &Statistics{}
|
plugin := &Statistics{}
|
||||||
process.On(engine.StartEvent, plugin.Start)
|
r.On(reducer.StartEvent, plugin.Start)
|
||||||
process.On(engine.StepEvent, plugin.Step)
|
r.On(reducer.StepEvent, plugin.Step)
|
||||||
process.On(engine.StopEvent, plugin.Stop)
|
r.On(reducer.StopEvent, plugin.Stop)
|
||||||
|
|
||||||
return plugin
|
return plugin
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package emitter
|
|||||||
import "git.maximhutz.com/max/lambda/pkg/set"
|
import "git.maximhutz.com/max/lambda/pkg/set"
|
||||||
|
|
||||||
type Emitter[E comparable] interface {
|
type Emitter[E comparable] interface {
|
||||||
On(string, func()) Listener[E]
|
On(E, func()) Listener[E]
|
||||||
Off(Listener[E])
|
Off(Listener[E])
|
||||||
Emit(E)
|
Emit(E)
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,13 @@ func (e *BaseEmitter[E]) On(kind E, fn func()) Listener[E] {
|
|||||||
return listener
|
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) {
|
func (e *BaseEmitter[E]) Emit(event E) {
|
||||||
if e.listeners[event] == nil {
|
if e.listeners[event] == nil {
|
||||||
e.listeners[event] = set.New[Listener[E]]()
|
e.listeners[event] = set.New[Listener[E]]()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package lambda
|
package lambda
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/emitter"
|
||||||
"git.maximhutz.com/max/lambda/pkg/expr"
|
"git.maximhutz.com/max/lambda/pkg/expr"
|
||||||
"git.maximhutz.com/max/lambda/pkg/reducer"
|
"git.maximhutz.com/max/lambda/pkg/reducer"
|
||||||
)
|
)
|
||||||
@@ -10,24 +11,42 @@ var _ reducer.Reducer = (*NormalOrderReducer)(nil)
|
|||||||
|
|
||||||
// NormalOrderReducer implements normal order (leftmost-outermost) reduction
|
// NormalOrderReducer implements normal order (leftmost-outermost) reduction
|
||||||
// for lambda calculus expressions.
|
// for lambda calculus expressions.
|
||||||
type NormalOrderReducer struct{}
|
type NormalOrderReducer struct {
|
||||||
|
emitter.BaseEmitter[reducer.Event]
|
||||||
|
expression expr.Expression
|
||||||
|
}
|
||||||
|
|
||||||
// NewNormalOrderReducer creates a new normal order reducer.
|
// NewNormalOrderReducer creates a new normal order reducer.
|
||||||
func NewNormalOrderReducer() *NormalOrderReducer {
|
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.
|
// Reduce performs normal order reduction on a lambda expression.
|
||||||
// The expression must be a lambda.Expression; other types are returned unchanged.
|
// 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)
|
lambdaExpr, ok := e.(Expression)
|
||||||
if !ok {
|
if !ok {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.Emit(reducer.StartEvent)
|
||||||
|
|
||||||
ReduceAll(&lambdaExpr, func() {
|
ReduceAll(&lambdaExpr, func() {
|
||||||
onStep(lambdaExpr)
|
r.expression = lambdaExpr
|
||||||
|
r.Emit(reducer.StepEvent)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
r.expression = lambdaExpr
|
||||||
|
r.Emit(reducer.StopEvent)
|
||||||
|
|
||||||
return lambdaExpr
|
return lambdaExpr
|
||||||
}
|
}
|
||||||
|
|||||||
13
pkg/reducer/events.go
Normal file
13
pkg/reducer/events.go
Normal file
@@ -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
|
||||||
|
)
|
||||||
@@ -2,14 +2,26 @@
|
|||||||
// reduction strategies.
|
// reduction strategies.
|
||||||
package reducer
|
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.
|
// Reducer defines the interface for expression reduction strategies.
|
||||||
// Different evaluation modes (normal order, applicative order, SKI combinators,
|
// Different evaluation modes (normal order, applicative order, SKI combinators,
|
||||||
// etc.) implement this interface with their own reduction logic.
|
// 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 {
|
type Reducer interface {
|
||||||
// Reduce performs all reduction steps on the expression, calling onStep
|
emitter.Emitter[Event]
|
||||||
// after each reduction.
|
|
||||||
|
// 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.
|
// 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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user