From d715d38e9e8d71ecd6fc169e1bb26afe70ff0b27 Mon Sep 17 00:00:00 2001 From: "M.V. Hutz" Date: Sun, 18 Jan 2026 15:11:04 -0500 Subject: [PATCH] refactor: rename interpreter to runtime and use receiver methods - Rename pkg/interpreter to pkg/runtime - Move ReduceOnce to new pkg/normalorder package - Convert standalone functions (Substitute, Rename, GetFree, IsFree) to receiver methods on concrete expression types - Change Set from pointer receivers to value receivers - Update all references from interpreter to runtime terminology Co-Authored-By: Claude Opus 4.5 --- .gitea/ISSUE_TEMPLATE/bug.md | 2 +- .gitea/ISSUE_TEMPLATE/feature.md | 2 +- LICENSE | 2 +- Makefile | 2 +- README.md | 2 +- cmd/lambda/lambda.go | 16 ++--- cmd/lambda/lambda_test.go | 6 +- internal/plugins/debug.go | 8 +-- internal/plugins/explanation.go | 10 +-- internal/plugins/performance.go | 8 +-- internal/plugins/statistics.go | 10 +-- pkg/convert/saccharine_to_lambda.go | 4 +- pkg/emitter/emitter.go | 4 +- pkg/expr/expr.go | 2 +- pkg/lambda/expression.go | 18 +++++ pkg/lambda/generate_name.go | 2 +- pkg/lambda/get_free_variables.go | 32 ++++----- pkg/lambda/is_free_variable.go | 22 +++--- pkg/lambda/reduce.go | 32 --------- pkg/lambda/rename.go | 49 +++++++------ pkg/lambda/substitute.go | 68 +++++++++---------- pkg/normalorder/reduce_once.go | 34 ++++++++++ .../interpreter.go => normalorder/runtime.go} | 29 ++++---- pkg/{interpreter => runtime}/events.go | 2 +- .../interpreter.go => runtime/runtime.go} | 14 ++-- pkg/set/set.go | 16 ++--- 26 files changed, 200 insertions(+), 196 deletions(-) delete mode 100644 pkg/lambda/reduce.go create mode 100644 pkg/normalorder/reduce_once.go rename pkg/{lambda/interpreter.go => normalorder/runtime.go} (55%) rename pkg/{interpreter => runtime}/events.go (93%) rename pkg/{interpreter/interpreter.go => runtime/runtime.go} (53%) diff --git a/.gitea/ISSUE_TEMPLATE/bug.md b/.gitea/ISSUE_TEMPLATE/bug.md index 2a143f4..00ddd39 100644 --- a/.gitea/ISSUE_TEMPLATE/bug.md +++ b/.gitea/ISSUE_TEMPLATE/bug.md @@ -1,6 +1,6 @@ --- name: "Bug Report" -about: "Report a bug or unexpected behavior in the lambda interpreter." +about: "Report a bug or unexpected behavior in the lambda runtime." title: "fix: " ref: "main" assignees: [] diff --git a/.gitea/ISSUE_TEMPLATE/feature.md b/.gitea/ISSUE_TEMPLATE/feature.md index cedccb8..d827e34 100644 --- a/.gitea/ISSUE_TEMPLATE/feature.md +++ b/.gitea/ISSUE_TEMPLATE/feature.md @@ -1,6 +1,6 @@ --- name: "Feature Request" -about: "Suggest a new feature or enhancement for the lambda interpreter." +about: "Suggest a new feature or enhancement for the lambda runtime." title: "feat: " ref: "main" assignees: [] diff --git a/LICENSE b/LICENSE index d3bfbc7..e2abf19 100644 --- a/LICENSE +++ b/LICENSE @@ -48,7 +48,7 @@ The "source code" for a work means the preferred form of the work for making mod A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. -The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. +The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code runtime used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. diff --git a/Makefile b/Makefile index 1d52d28..2ce49e2 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ TEST=simple help: echo "Available targets:" echo " build - Build the lambda executable" - echo " run - Build and run the lambda interpreter (use TEST= to specify sample)" + echo " run - Build and run the lambda runtime (use TEST= to specify sample)" echo " profile - Build and run with CPU profiling enabled" echo " explain - Build and run with explanation mode and profiling" echo " graph - Generate and open CPU profile visualization" diff --git a/README.md b/README.md index cbb727f..1dd7537 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # lambda -Making a lambda calculus interpreter in Go. +Making a lambda calculus runtime in Go. ## Things to talk about diff --git a/cmd/lambda/lambda.go b/cmd/lambda/lambda.go index 1aa3d16..7722471 100644 --- a/cmd/lambda/lambda.go +++ b/cmd/lambda/lambda.go @@ -7,7 +7,7 @@ import ( "git.maximhutz.com/max/lambda/internal/config" "git.maximhutz.com/max/lambda/internal/plugins" "git.maximhutz.com/max/lambda/pkg/convert" - "git.maximhutz.com/max/lambda/pkg/lambda" + "git.maximhutz.com/max/lambda/pkg/normalorder" "git.maximhutz.com/max/lambda/pkg/saccharine" ) @@ -34,34 +34,34 @@ func main() { logger.Info("compiled λ expression", "tree", compiled.String()) // Create reducer with the compiled expression. - interpreter := lambda.NewInterpreter(compiled) + runtime := normalorder.NewRuntime(compiled) // If the user selected to track CPU performance, attach a profiler. if options.Profile != "" { - plugins.NewPerformance(options.Profile, interpreter) + plugins.NewPerformance(options.Profile, runtime) } // If the user selected to produce a step-by-step explanation, attach an // observer. if options.Explanation { - plugins.NewExplanation(interpreter) + plugins.NewExplanation(runtime) } // If the user opted to track statistics, attach a tracker. if options.Statistics { - plugins.NewStatistics(interpreter) + plugins.NewStatistics(runtime) } // If the user selected for verbose debug logs, attach a reduction tracker. if options.Verbose { - plugins.NewLogs(logger, interpreter) + plugins.NewLogs(logger, runtime) } // Run reduction. - interpreter.Run() + runtime.Run() // Return the final reduced result. - result := interpreter.Expression().String() + result := runtime.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 96e5ec0..cbca46b 100644 --- a/cmd/lambda/lambda_test.go +++ b/cmd/lambda/lambda_test.go @@ -7,12 +7,12 @@ import ( "testing" "git.maximhutz.com/max/lambda/pkg/convert" - "git.maximhutz.com/max/lambda/pkg/lambda" + "git.maximhutz.com/max/lambda/pkg/normalorder" "git.maximhutz.com/max/lambda/pkg/saccharine" "github.com/stretchr/testify/assert" ) -// Helper function to run a single sample through the lambda interpreter. +// Helper function to run a single sample through the lambda runtime. func runSample(samplePath string) (string, error) { // Read the sample file. input, err := os.ReadFile(samplePath) @@ -30,7 +30,7 @@ func runSample(samplePath string) (string, error) { compiled := convert.SaccharineToLambda(ast) // Create and run the reducer. - reducer := lambda.NewInterpreter(compiled) + reducer := normalorder.NewRuntime(compiled) reducer.Run() return reducer.Expression().String() + "\n", nil diff --git a/internal/plugins/debug.go b/internal/plugins/debug.go index 6816cea..82cd1b8 100644 --- a/internal/plugins/debug.go +++ b/internal/plugins/debug.go @@ -3,17 +3,17 @@ package plugins import ( "log/slog" - "git.maximhutz.com/max/lambda/pkg/interpreter" + "git.maximhutz.com/max/lambda/pkg/runtime" ) type Logs struct { logger *slog.Logger - reducer interpreter.Interpreter + reducer runtime.Runtime } -func NewLogs(logger *slog.Logger, r interpreter.Interpreter) *Logs { +func NewLogs(logger *slog.Logger, r runtime.Runtime) *Logs { plugin := &Logs{logger, r} - r.On(interpreter.StepEvent, plugin.Step) + r.On(runtime.StepEvent, plugin.Step) return plugin } diff --git a/internal/plugins/explanation.go b/internal/plugins/explanation.go index b7bd698..ba92a99 100644 --- a/internal/plugins/explanation.go +++ b/internal/plugins/explanation.go @@ -5,19 +5,19 @@ package plugins import ( "fmt" - "git.maximhutz.com/max/lambda/pkg/interpreter" + "git.maximhutz.com/max/lambda/pkg/runtime" ) // Track the reductions made by a reduction process. type Explanation struct { - reducer interpreter.Interpreter + reducer runtime.Runtime } // Attaches a new explanation tracker to a reducer. -func NewExplanation(r interpreter.Interpreter) *Explanation { +func NewExplanation(r runtime.Runtime) *Explanation { plugin := &Explanation{reducer: r} - r.On(interpreter.StartEvent, plugin.Start) - r.On(interpreter.StepEvent, plugin.Step) + r.On(runtime.StartEvent, plugin.Start) + r.On(runtime.StepEvent, plugin.Step) return plugin } diff --git a/internal/plugins/performance.go b/internal/plugins/performance.go index 42be69a..eac3f20 100644 --- a/internal/plugins/performance.go +++ b/internal/plugins/performance.go @@ -7,7 +7,7 @@ import ( "path/filepath" "runtime/pprof" - "git.maximhutz.com/max/lambda/pkg/interpreter" + "git.maximhutz.com/max/lambda/pkg/runtime" ) // 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 interpreter.Interpreter) *Performance { +func NewPerformance(file string, process runtime.Runtime) *Performance { plugin := &Performance{File: file} - process.On(interpreter.StartEvent, plugin.Start) - process.On(interpreter.StopEvent, plugin.Stop) + process.On(runtime.StartEvent, plugin.Start) + process.On(runtime.StopEvent, plugin.Stop) return plugin } diff --git a/internal/plugins/statistics.go b/internal/plugins/statistics.go index a5a7be2..2845738 100644 --- a/internal/plugins/statistics.go +++ b/internal/plugins/statistics.go @@ -6,7 +6,7 @@ import ( "time" "git.maximhutz.com/max/lambda/internal/statistics" - "git.maximhutz.com/max/lambda/pkg/interpreter" + "git.maximhutz.com/max/lambda/pkg/runtime" ) // An observer, to track reduction performance. @@ -16,11 +16,11 @@ type Statistics struct { } // Create a new reduction performance Statistics. -func NewStatistics(r interpreter.Interpreter) *Statistics { +func NewStatistics(r runtime.Runtime) *Statistics { plugin := &Statistics{} - r.On(interpreter.StartEvent, plugin.Start) - r.On(interpreter.StepEvent, plugin.Step) - r.On(interpreter.StopEvent, plugin.Stop) + r.On(runtime.StartEvent, plugin.Start) + r.On(runtime.StepEvent, plugin.Step) + r.On(runtime.StopEvent, plugin.Stop) return plugin } diff --git a/pkg/convert/saccharine_to_lambda.go b/pkg/convert/saccharine_to_lambda.go index f0810f7..8bab6b8 100644 --- a/pkg/convert/saccharine_to_lambda.go +++ b/pkg/convert/saccharine_to_lambda.go @@ -19,7 +19,7 @@ func convertAbstraction(n *saccharine.Abstraction) lambda.Expression { // If the function has no parameters, it is a thunk. Lambda calculus still // requires _some_ parameter exists, so generate one. if len(parameters) == 0 { - freeVars := lambda.GetFreeVariables(result) + freeVars := result.GetFree() freshName := lambda.GenerateFreshName(freeVars) parameters = append(parameters, freshName) } @@ -63,7 +63,7 @@ func reduceLet(s *saccharine.LetStatement, e lambda.Expression) lambda.Expressio } func reduceDeclare(s *saccharine.DeclareStatement, e lambda.Expression) lambda.Expression { - freshVar := lambda.GenerateFreshName(lambda.GetFreeVariables(e)) + freshVar := lambda.GenerateFreshName(e.GetFree()) return lambda.NewApplication( lambda.NewAbstraction(freshVar, e), diff --git a/pkg/emitter/emitter.go b/pkg/emitter/emitter.go index d387fba..2c34b55 100644 --- a/pkg/emitter/emitter.go +++ b/pkg/emitter/emitter.go @@ -9,7 +9,7 @@ type Emitter[E comparable] interface { } type BaseEmitter[E comparable] struct { - listeners map[E]*set.Set[Listener[E]] + listeners map[E]set.Set[Listener[E]] } func (e *BaseEmitter[E]) On(kind E, fn func()) Listener[E] { @@ -41,6 +41,6 @@ func (e *BaseEmitter[E]) Emit(event E) { func New[E comparable]() *BaseEmitter[E] { return &BaseEmitter[E]{ - listeners: map[E]*set.Set[Listener[E]]{}, + listeners: map[E]set.Set[Listener[E]]{}, } } diff --git a/pkg/expr/expr.go b/pkg/expr/expr.go index a041966..b45a515 100644 --- a/pkg/expr/expr.go +++ b/pkg/expr/expr.go @@ -1,5 +1,5 @@ // Package expr provides the abstract Expression interface for all evaluatable -// expression types in the lambda interpreter. +// expression types in the lambda runtime. package expr import ( diff --git a/pkg/lambda/expression.go b/pkg/lambda/expression.go index e20396c..dd28fe6 100644 --- a/pkg/lambda/expression.go +++ b/pkg/lambda/expression.go @@ -2,12 +2,30 @@ package lambda import ( "git.maximhutz.com/max/lambda/pkg/expr" + "git.maximhutz.com/max/lambda/pkg/set" ) // Expression is the interface for all lambda calculus expression types. // It embeds the general expr.Expression interface for cross-mode compatibility. type Expression interface { expr.Expression + + // Substitute replaces all free occurrences of the target variable with the + // replacement expression. Alpha-renaming is performed automatically to + // avoid variable capture. + Substitute(target string, replacement Expression) Expression + + // GetFree returns the set of all free variable names in the expression. + // This function does not mutate the input expression. + // The returned set is newly allocated and can be modified by the caller. + GetFree() set.Set[string] + + // Rename replaces all occurrences of the target variable name with the new name. + Rename(target string, newName string) Expression + + // IsFree returns true if the variable name n occurs free in the expression. + // This function does not mutate the input expression. + IsFree(n string) bool } /** ------------------------------------------------------------------------- */ diff --git a/pkg/lambda/generate_name.go b/pkg/lambda/generate_name.go index b436861..fbfa898 100644 --- a/pkg/lambda/generate_name.go +++ b/pkg/lambda/generate_name.go @@ -10,7 +10,7 @@ var ticker uint64 = 0 // GenerateFreshName generates a variable name that is not in the used set. // This function does not mutate the used set. -func GenerateFreshName(used *set.Set[string]) string { +func GenerateFreshName(used set.Set[string]) string { for i := uint64(0); ; i++ { attempt := "_" + string(strconv.AppendUint(nil, ticker, 10)) diff --git a/pkg/lambda/get_free_variables.go b/pkg/lambda/get_free_variables.go index aab0169..1c2bf91 100644 --- a/pkg/lambda/get_free_variables.go +++ b/pkg/lambda/get_free_variables.go @@ -2,22 +2,18 @@ package lambda import "git.maximhutz.com/max/lambda/pkg/set" -// GetFreeVariables returns the set of all free variable names in the expression. -// This function does not mutate the input expression. -// The returned set is newly allocated and can be modified by the caller. -func GetFreeVariables(e Expression) *set.Set[string] { - switch e := e.(type) { - case Variable: - return set.New(e.Name()) - case Abstraction: - vars := GetFreeVariables(e.Body()) - vars.Remove(e.Parameter()) - return vars - case Application: - vars := GetFreeVariables(e.Abstraction()) - vars.Merge(GetFreeVariables(e.Argument())) - return vars - default: - return set.New[string]() - } +func (e Variable) GetFree() set.Set[string] { + return set.New(e.Name()) +} + +func (e Abstraction) GetFree() set.Set[string] { + vars := e.Body().GetFree() + vars.Remove(e.Parameter()) + return vars +} + +func (e Application) GetFree() set.Set[string] { + vars := e.Abstraction().GetFree() + vars.Merge(e.Argument().GetFree()) + return vars } diff --git a/pkg/lambda/is_free_variable.go b/pkg/lambda/is_free_variable.go index 08e35dd..09d77a6 100644 --- a/pkg/lambda/is_free_variable.go +++ b/pkg/lambda/is_free_variable.go @@ -1,16 +1,12 @@ package lambda -// IsFreeVariable returns true if the variable name n occurs free in the expression. -// This function does not mutate the input expression. -func IsFreeVariable(n string, e Expression) bool { - switch e := e.(type) { - case Variable: - return e.Name() == n - case Abstraction: - return e.Parameter() != n && IsFreeVariable(n, e.Body()) - case Application: - return IsFreeVariable(n, e.Abstraction()) || IsFreeVariable(n, e.Argument()) - default: - return false - } +func (e Variable) IsFree(n string) bool { + return e.Name() == n +} + +func (e Abstraction) IsFree(n string) bool { + return e.Parameter() != n && e.Body().IsFree(n) +} +func (e Application) IsFree(n string) bool { + return e.Abstraction().IsFree(n) || e.Argument().IsFree(n) } diff --git a/pkg/lambda/reduce.go b/pkg/lambda/reduce.go deleted file mode 100644 index 148c2d1..0000000 --- a/pkg/lambda/reduce.go +++ /dev/null @@ -1,32 +0,0 @@ -package lambda - -func ReduceOnce(e Expression) (Expression, bool) { - switch e := e.(type) { - case Abstraction: - body, reduced := ReduceOnce(e.Body()) - if reduced { - return NewAbstraction(e.Parameter(), body), true - } - return e, false - - case Application: - if fn, fnOk := e.Abstraction().(Abstraction); fnOk { - return Substitute(fn.Body(), fn.Parameter(), e.Argument()), true - } - - abs, reduced := ReduceOnce(e.Abstraction()) - if reduced { - return NewApplication(abs, e.Argument()), true - } - - arg, reduced := ReduceOnce(e.Argument()) - if reduced { - return NewApplication(e.Abstraction(), arg), true - } - - return e, false - - default: - return e, false - } -} diff --git a/pkg/lambda/rename.go b/pkg/lambda/rename.go index 3229709..2c39237 100644 --- a/pkg/lambda/rename.go +++ b/pkg/lambda/rename.go @@ -1,31 +1,28 @@ package lambda // Rename replaces all occurrences of the target variable name with the new name. -func Rename(expr Expression, target string, newName string) Expression { - switch e := expr.(type) { - case Variable: - if e.Name() == target { - return NewVariable(newName) - } - return e - - case Abstraction: - newParam := e.Parameter() - if e.Parameter() == target { - newParam = newName - } - - newBody := Rename(e.Body(), target, newName) - - return NewAbstraction(newParam, newBody) - - case Application: - newAbs := Rename(e.Abstraction(), target, newName) - newArg := Rename(e.Argument(), target, newName) - - return NewApplication(newAbs, newArg) - - default: - return expr +func (e Variable) Rename(target string, newName string) Expression { + if e.Name() == target { + return NewVariable(newName) } + + return e +} + +func (e Abstraction) Rename(target string, newName string) Expression { + newParam := e.Parameter() + if e.Parameter() == target { + newParam = newName + } + + newBody := e.Body().Rename(target, newName) + + return NewAbstraction(newParam, newBody) +} + +func (e Application) Rename(target string, newName string) Expression { + newAbs := e.Abstraction().Rename(target, newName) + newArg := e.Argument().Rename(target, newName) + + return NewApplication(newAbs, newArg) } diff --git a/pkg/lambda/substitute.go b/pkg/lambda/substitute.go index 28b3abc..1f6b38e 100644 --- a/pkg/lambda/substitute.go +++ b/pkg/lambda/substitute.go @@ -1,41 +1,35 @@ package lambda -// Substitute replaces all free occurrences of the target variable with the replacement expression. -// Alpha-renaming is performed automatically to avoid variable capture. -func Substitute(expr Expression, target string, replacement Expression) Expression { - switch e := expr.(type) { - case Variable: - if e.Name() == target { - return replacement - } - return e - - case Abstraction: - if e.Parameter() == target { - return e - } - - body := e.Body() - param := e.Parameter() - if IsFreeVariable(param, replacement) { - freeVars := GetFreeVariables(replacement) - freeVars.Merge(GetFreeVariables(body)) - freshVar := GenerateFreshName(freeVars) - body = Rename(body, param, freshVar) - param = freshVar - } - - newBody := Substitute(body, target, replacement) - - return NewAbstraction(param, newBody) - - case Application: - newAbs := Substitute(e.Abstraction(), target, replacement) - newArg := Substitute(e.Argument(), target, replacement) - - return NewApplication(newAbs, newArg) - - default: - return expr +func (e Variable) Substitute(target string, replacement Expression) Expression { + if e.Name() == target { + return replacement } + + return e +} + +func (e Abstraction) Substitute(target string, replacement Expression) Expression { + if e.Parameter() == target { + return e + } + + body := e.Body() + param := e.Parameter() + if replacement.IsFree(param) { + freeVars := replacement.GetFree() + freeVars.Merge(body.GetFree()) + freshVar := GenerateFreshName(freeVars) + body = body.Rename(param, freshVar) + param = freshVar + } + + newBody := body.Substitute(target, replacement) + return NewAbstraction(param, newBody) +} + +func (e Application) Substitute(target string, replacement Expression) Expression { + abs := e.Abstraction().Substitute(target, replacement) + arg := e.Argument().Substitute(target, replacement) + + return NewApplication(abs, arg) } diff --git a/pkg/normalorder/reduce_once.go b/pkg/normalorder/reduce_once.go new file mode 100644 index 0000000..e1a8aff --- /dev/null +++ b/pkg/normalorder/reduce_once.go @@ -0,0 +1,34 @@ +package normalorder + +import "git.maximhutz.com/max/lambda/pkg/lambda" + +func ReduceOnce(e lambda.Expression) (lambda.Expression, bool) { + switch e := e.(type) { + case lambda.Abstraction: + body, reduced := ReduceOnce(e.Body()) + if reduced { + return lambda.NewAbstraction(e.Parameter(), body), true + } + return e, false + + case lambda.Application: + if fn, fnOk := e.Abstraction().(lambda.Abstraction); fnOk { + return fn.Body().Substitute(fn.Parameter(), e.Argument()), true + } + + abs, reduced := ReduceOnce(e.Abstraction()) + if reduced { + return lambda.NewApplication(abs, e.Argument()), true + } + + arg, reduced := ReduceOnce(e.Argument()) + if reduced { + return lambda.NewApplication(e.Abstraction(), arg), true + } + + return e, false + + default: + return e, false + } +} diff --git a/pkg/lambda/interpreter.go b/pkg/normalorder/runtime.go similarity index 55% rename from pkg/lambda/interpreter.go rename to pkg/normalorder/runtime.go index e858ca0..37d38e4 100644 --- a/pkg/lambda/interpreter.go +++ b/pkg/normalorder/runtime.go @@ -1,32 +1,33 @@ -package lambda +package normalorder import ( "git.maximhutz.com/max/lambda/pkg/emitter" "git.maximhutz.com/max/lambda/pkg/expr" - "git.maximhutz.com/max/lambda/pkg/interpreter" + "git.maximhutz.com/max/lambda/pkg/lambda" + "git.maximhutz.com/max/lambda/pkg/runtime" ) // NormalOrderReducer implements normal order (leftmost-outermost) reduction // for lambda calculus expressions. -type Interpreter struct { - emitter.BaseEmitter[interpreter.Event] - expression Expression +type Runtime struct { + emitter.BaseEmitter[runtime.Event] + expression lambda.Expression } // NewNormalOrderReducer creates a new normal order reducer. -func NewInterpreter(expression Expression) *Interpreter { - return &Interpreter{ - BaseEmitter: *emitter.New[interpreter.Event](), +func NewRuntime(expression lambda.Expression) *Runtime { + return &Runtime{ + BaseEmitter: *emitter.New[runtime.Event](), expression: expression, } } // Expression returns the current expression state. -func (r *Interpreter) Expression() expr.Expression { +func (r *Runtime) Expression() expr.Expression { return r.expression } -func (r *Interpreter) Step() bool { +func (r *Runtime) Step() bool { result, done := ReduceOnce(r.expression) r.expression = result return !done @@ -34,12 +35,12 @@ func (r *Interpreter) Step() bool { // Reduce performs normal order reduction on a lambda expression. // The expression must be a lambda.Expression; other types are returned unchanged. -func (r *Interpreter) Run() { - r.Emit(interpreter.StartEvent) +func (r *Runtime) Run() { + r.Emit(runtime.StartEvent) for !r.Step() { - r.Emit(interpreter.StepEvent) + r.Emit(runtime.StepEvent) } - r.Emit(interpreter.StopEvent) + r.Emit(runtime.StopEvent) } diff --git a/pkg/interpreter/events.go b/pkg/runtime/events.go similarity index 93% rename from pkg/interpreter/events.go rename to pkg/runtime/events.go index e64a320..6e884c8 100644 --- a/pkg/interpreter/events.go +++ b/pkg/runtime/events.go @@ -1,4 +1,4 @@ -package interpreter +package runtime // Event represents lifecycle events during interpretation. type Event int diff --git a/pkg/interpreter/interpreter.go b/pkg/runtime/runtime.go similarity index 53% rename from pkg/interpreter/interpreter.go rename to pkg/runtime/runtime.go index c6b82b8..c2aafad 100644 --- a/pkg/interpreter/interpreter.go +++ b/pkg/runtime/runtime.go @@ -1,27 +1,27 @@ -// Package interpreter provides the abstract Reducer interface for all expression +// Package runtime provides the abstract Reducer interface for all expression // reduction strategies. -package interpreter +package runtime import ( "git.maximhutz.com/max/lambda/pkg/emitter" "git.maximhutz.com/max/lambda/pkg/expr" ) -// Reducer defines the interface for expression reduction strategies. +// Runtime 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 +// Runtimes also implement the Emitter interface to allow plugins to observe // reduction lifecycle events (Start, Step, Stop). -type Interpreter interface { +type Runtime interface { emitter.Emitter[Event] - // Run a single step. Returns whether the interpreter is complete or not. + // Run a single step. Returns whether the runtime is complete or not. Step() bool // Run until completion. Run() - // Expression returns the current expression state. + // Copy the state of the runtime. Expression() expr.Expression } diff --git a/pkg/set/set.go b/pkg/set/set.go index f5a70f0..c66cf71 100644 --- a/pkg/set/set.go +++ b/pkg/set/set.go @@ -4,9 +4,9 @@ import "iter" type Set[T comparable] map[T]bool -func (s *Set[T]) Add(items ...T) { +func (s Set[T]) Add(items ...T) { for _, item := range items { - (*s)[item] = true + s[item] = true } } @@ -14,14 +14,14 @@ func (s Set[T]) Has(item T) bool { return s[item] } -func (s *Set[T]) Remove(items ...T) { +func (s Set[T]) Remove(items ...T) { for _, item := range items { - delete(*s, item) + delete(s, item) } } -func (s *Set[T]) Merge(o *Set[T]) { - for item := range *o { +func (s Set[T]) Merge(o Set[T]) { + for item := range o { s.Add(item) } } @@ -46,8 +46,8 @@ func (s Set[T]) Items() iter.Seq[T] { } } -func New[T comparable](items ...T) *Set[T] { - result := &Set[T]{} +func New[T comparable](items ...T) Set[T] { + result := Set[T]{} for _, item := range items { result.Add(item) -- 2.49.1