refactor: move event system to reducer, remove engine package #32
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 (
|
||||
"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())
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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]]()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user