feat: cli versions
This commit is contained in:
55
internal/cli/codec.go
Normal file
55
internal/cli/codec.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Codec interface {
|
||||||
|
codec.Codec[Repr, Repr]
|
||||||
|
|
||||||
|
InType() string
|
||||||
|
OutType() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type convertedCodec[T, U any] struct {
|
||||||
|
codec codec.Codec[T, U]
|
||||||
|
inType, outType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c convertedCodec[T, U]) Decode(r Repr) (Repr, error) {
|
||||||
|
u, ok := r.Data().(U)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("could not parse '%v' as '%s'", r, c.inType)
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := c.codec.Decode(u)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewRepr(c.outType, t), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c convertedCodec[T, U]) Encode(r Repr) (Repr, error) {
|
||||||
|
t, ok := r.Data().(T)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("could not parse '%v' as '%s'", t, c.outType)
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := c.codec.Encode(t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewRepr(c.inType, u), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c convertedCodec[T, U]) InType() string { return c.inType }
|
||||||
|
|
||||||
|
func (c convertedCodec[T, U]) OutType() string { return c.outType }
|
||||||
|
|
||||||
|
func ConvertCodec[T, U any](e codec.Codec[T, U], inType, outType string) Codec {
|
||||||
|
return convertedCodec[T, U]{e, inType, outType}
|
||||||
|
}
|
||||||
49
internal/cli/engine.go
Normal file
49
internal/cli/engine.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/engine"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Engine interface {
|
||||||
|
engine.Engine[Repr]
|
||||||
|
|
||||||
|
Name() string
|
||||||
|
InType() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type convertedEngine[T any] struct {
|
||||||
|
engine engine.Engine[T]
|
||||||
|
name string
|
||||||
|
inType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b convertedEngine[T]) InType() string { return b.inType }
|
||||||
|
|
||||||
|
func (b convertedEngine[T]) Name() string { return b.name }
|
||||||
|
|
||||||
|
func (b convertedEngine[T]) Get() (Repr, error) {
|
||||||
|
s, err := b.engine.Get()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewRepr(b.inType, s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b convertedEngine[T]) Set(r Repr) error {
|
||||||
|
if t, ok := r.Data().(T); ok {
|
||||||
|
return b.engine.Set(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Incorrent format '%s' for engine '%s'.", r.Id(), b.inType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b convertedEngine[T]) Step(i int) bool {
|
||||||
|
return b.engine.Step(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertEngine[T any](e engine.Engine[T], name string, inType string) Engine {
|
||||||
|
return convertedEngine[T]{e, name, inType}
|
||||||
|
}
|
||||||
42
internal/cli/marshaler.go
Normal file
42
internal/cli/marshaler.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Marshaler interface {
|
||||||
|
codec.Marshaler[Repr]
|
||||||
|
|
||||||
|
InType() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type convertedMarshaler[T any] struct {
|
||||||
|
codec codec.Marshaler[T]
|
||||||
|
inType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c convertedMarshaler[T]) Decode(s string) (Repr, error) {
|
||||||
|
t, err := c.codec.Decode(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewRepr(c.inType, t), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c convertedMarshaler[T]) Encode(r Repr) (string, error) {
|
||||||
|
t, ok := r.Data().(T)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("could not parse '%v' as 'string'", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.codec.Encode(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c convertedMarshaler[T]) InType() string { return c.inType }
|
||||||
|
|
||||||
|
func ConvertMarshaler[T any](e codec.Marshaler[T], inType string) Marshaler {
|
||||||
|
return convertedMarshaler[T]{e, inType}
|
||||||
|
}
|
||||||
21
internal/cli/repr.go
Normal file
21
internal/cli/repr.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
type Repr interface {
|
||||||
|
// Id returns to name of the objects underlying representation. If is
|
||||||
|
// assumed that if two Repr objects have the same Id(), they share the same
|
||||||
|
// representation.
|
||||||
|
Id() string
|
||||||
|
|
||||||
|
Data() any
|
||||||
|
}
|
||||||
|
|
||||||
|
type baseRepr struct {
|
||||||
|
id string
|
||||||
|
data any
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r baseRepr) Id() string { return r.id }
|
||||||
|
|
||||||
|
func (r baseRepr) Data() any { return r.data }
|
||||||
|
|
||||||
|
func NewRepr(id string, data any) Repr { return baseRepr{id, data} }
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package plugins
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log/slog"
|
|
||||||
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Logs struct {
|
|
||||||
logger *slog.Logger
|
|
||||||
reducer runtime.Runtime
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLogs(logger *slog.Logger, r runtime.Runtime) *Logs {
|
|
||||||
plugin := &Logs{logger, r}
|
|
||||||
r.On(runtime.StepEvent, plugin.Step)
|
|
||||||
|
|
||||||
return plugin
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Logs) Step() {
|
|
||||||
t.logger.Info("reduction", "tree", t.reducer.Expression().String())
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
// Package "explanation" provides an observer to gather the reasoning during the
|
|
||||||
// reduction, and present a thorough explanation to the user for each step.
|
|
||||||
package plugins
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Track the reductions made by a reduction process.
|
|
||||||
type Explanation struct {
|
|
||||||
reducer runtime.Runtime
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attaches a new explanation tracker to a reducer.
|
|
||||||
func NewExplanation(r runtime.Runtime) *Explanation {
|
|
||||||
plugin := &Explanation{reducer: r}
|
|
||||||
r.On(runtime.StartEvent, plugin.Start)
|
|
||||||
r.On(runtime.StepEvent, plugin.Step)
|
|
||||||
|
|
||||||
return plugin
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Explanation) Start() {
|
|
||||||
fmt.Println(t.reducer.Expression().String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Explanation) Step() {
|
|
||||||
fmt.Println(" =", t.reducer.Expression().String())
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
// Package "performance" provides a tracker to observer CPU performance during
|
|
||||||
// execution.
|
|
||||||
package plugins
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime/pprof"
|
|
||||||
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Observes a reduction process, and publishes a CPU performance profile on
|
|
||||||
// completion.
|
|
||||||
type Performance struct {
|
|
||||||
File string
|
|
||||||
filePointer *os.File
|
|
||||||
Error error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a performance tracker that outputs a profile to "file".
|
|
||||||
func NewPerformance(file string, process runtime.Runtime) *Performance {
|
|
||||||
plugin := &Performance{File: file}
|
|
||||||
process.On(runtime.StartEvent, plugin.Start)
|
|
||||||
process.On(runtime.StopEvent, plugin.Stop)
|
|
||||||
|
|
||||||
return plugin
|
|
||||||
}
|
|
||||||
|
|
||||||
// Begin profiling.
|
|
||||||
func (t *Performance) Start() {
|
|
||||||
var absPath string
|
|
||||||
|
|
||||||
absPath, t.Error = filepath.Abs(t.File)
|
|
||||||
if t.Error != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Error = os.MkdirAll(filepath.Dir(absPath), 0777)
|
|
||||||
if t.Error != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t.filePointer, t.Error = os.Create(absPath)
|
|
||||||
if t.Error != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Error = pprof.StartCPUProfile(t.filePointer)
|
|
||||||
if t.Error != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop profiling.
|
|
||||||
func (t *Performance) Stop() {
|
|
||||||
pprof.StopCPUProfile()
|
|
||||||
t.filePointer.Close()
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package plugins
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.maximhutz.com/max/lambda/internal/statistics"
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// An observer, to track reduction performance.
|
|
||||||
type Statistics struct {
|
|
||||||
start time.Time
|
|
||||||
steps uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new reduction performance Statistics.
|
|
||||||
func NewStatistics(r runtime.Runtime) *Statistics {
|
|
||||||
plugin := &Statistics{}
|
|
||||||
r.On(runtime.StartEvent, plugin.Start)
|
|
||||||
r.On(runtime.StepEvent, plugin.Step)
|
|
||||||
r.On(runtime.StopEvent, plugin.Stop)
|
|
||||||
|
|
||||||
return plugin
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Statistics) Start() {
|
|
||||||
t.start = time.Now()
|
|
||||||
t.steps = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Statistics) Step() {
|
|
||||||
t.steps++
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Statistics) Stop() {
|
|
||||||
results := statistics.Results{
|
|
||||||
StepsTaken: t.steps,
|
|
||||||
TimeElapsed: uint64(time.Since(t.start).Milliseconds()),
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprint(os.Stderr, results.String())
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
// Package "statistics" provides a way to observer reduction speed during
|
|
||||||
// execution.
|
|
||||||
package statistics
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Statistics for a specific reduction.
|
|
||||||
type Results struct {
|
|
||||||
StepsTaken uint64 // Number of steps taken during execution.
|
|
||||||
TimeElapsed uint64 // The time (ms) taken for execution to complete.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the average number of operations per second of the execution.
|
|
||||||
func (r Results) OpsPerSecond() float32 {
|
|
||||||
return float32(r.StepsTaken) / (float32(r.TimeElapsed) / 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format the results as a string.
|
|
||||||
func (r Results) String() string {
|
|
||||||
builder := strings.Builder{}
|
|
||||||
fmt.Fprintln(&builder, "Time Spent:", r.TimeElapsed, "ms")
|
|
||||||
fmt.Fprintln(&builder, "Steps:", r.StepsTaken)
|
|
||||||
fmt.Fprintln(&builder, "Speed:", r.OpsPerSecond(), "ops")
|
|
||||||
return builder.String()
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,8 @@
|
|||||||
package codec
|
package codec
|
||||||
|
|
||||||
import "git.maximhutz.com/max/lambda/pkg/repr"
|
type Codec[T, U any] interface {
|
||||||
|
|
||||||
type String string
|
|
||||||
|
|
||||||
func (s String) Id() string { return "string" }
|
|
||||||
|
|
||||||
type Codec[T, U repr.Repr] interface {
|
|
||||||
Encode(T) (U, error)
|
Encode(T) (U, error)
|
||||||
Decode(U) (T, error)
|
Decode(U) (T, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Marshaler[T any] = Codec[T, string]
|
||||||
|
|||||||
7
pkg/engine/engine.go
Normal file
7
pkg/engine/engine.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package engine
|
||||||
|
|
||||||
|
type Engine[T any] interface {
|
||||||
|
Get() (T, error)
|
||||||
|
Set(T) error
|
||||||
|
Step(int) bool
|
||||||
|
}
|
||||||
@@ -3,14 +3,12 @@ package lambda
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/repr"
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/set"
|
"git.maximhutz.com/max/lambda/pkg/set"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Expression is the interface for all lambda calculus expression types.
|
// Expression is the interface for all lambda calculus expression types.
|
||||||
// It embeds the general expr.Expression interface for cross-mode compatibility.
|
// It embeds the general expr.Expression interface for cross-mode compatibility.
|
||||||
type Expression interface {
|
type Expression interface {
|
||||||
repr.Repr
|
|
||||||
fmt.Stringer
|
fmt.Stringer
|
||||||
|
|
||||||
// Substitute replaces all free occurrences of the target variable with the
|
// Substitute replaces all free occurrences of the target variable with the
|
||||||
@@ -40,8 +38,6 @@ type Abstraction struct {
|
|||||||
|
|
||||||
var _ Expression = Abstraction{}
|
var _ Expression = Abstraction{}
|
||||||
|
|
||||||
func (a Abstraction) Id() string { return "lambda" }
|
|
||||||
|
|
||||||
func (a Abstraction) Parameter() string {
|
func (a Abstraction) Parameter() string {
|
||||||
return a.parameter
|
return a.parameter
|
||||||
}
|
}
|
||||||
@@ -67,8 +63,6 @@ type Application struct {
|
|||||||
|
|
||||||
var _ Expression = Application{}
|
var _ Expression = Application{}
|
||||||
|
|
||||||
func (a Application) Id() string { return "lambda" }
|
|
||||||
|
|
||||||
func (a Application) Abstraction() Expression {
|
func (a Application) Abstraction() Expression {
|
||||||
return a.abstraction
|
return a.abstraction
|
||||||
}
|
}
|
||||||
@@ -93,8 +87,6 @@ type Variable struct {
|
|||||||
|
|
||||||
var _ Expression = Variable{}
|
var _ Expression = Variable{}
|
||||||
|
|
||||||
func (a Variable) Id() string { return "lambda" }
|
|
||||||
|
|
||||||
func (v Variable) Name() string {
|
func (v Variable) Name() string {
|
||||||
return v.name
|
return v.name
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
package normalorder
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/emitter"
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/lambda"
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/repr"
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NormalOrderReducer implements normal order (leftmost-outermost) reduction
|
|
||||||
// for lambda calculus expressions.
|
|
||||||
type Runtime struct {
|
|
||||||
emitter.BaseEmitter[runtime.Event]
|
|
||||||
expression lambda.Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNormalOrderReducer creates a new normal order reducer.
|
|
||||||
func NewRuntime(expression lambda.Expression) *Runtime {
|
|
||||||
return &Runtime{
|
|
||||||
BaseEmitter: *emitter.New[runtime.Event](),
|
|
||||||
expression: expression,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expression returns the current expression state.
|
|
||||||
func (r *Runtime) Expression() repr.Repr {
|
|
||||||
return r.expression
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Runtime) Step() bool {
|
|
||||||
result, done := ReduceOnce(r.expression)
|
|
||||||
r.expression = result
|
|
||||||
return !done
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reduce performs normal order reduction on a lambda expression.
|
|
||||||
// The expression must be a lambda.Expression; other types are returned unchanged.
|
|
||||||
func (r *Runtime) Run() {
|
|
||||||
r.Emit(runtime.StartEvent)
|
|
||||||
|
|
||||||
for !r.Step() {
|
|
||||||
r.Emit(runtime.StepEvent)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Emit(runtime.StopEvent)
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
// Package repr defines a general definition of a representation of lambda
|
|
||||||
// calculus.
|
|
||||||
package repr
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// Repr is a representation of lambda calculus.
|
|
||||||
type Repr interface {
|
|
||||||
fmt.Stringer
|
|
||||||
|
|
||||||
// Id returns to name of the objects underlying representation. If is
|
|
||||||
// assumed that if two Repr objects have the same Id(), they share the same
|
|
||||||
// representation.
|
|
||||||
Id() string
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package runtime
|
|
||||||
|
|
||||||
// Event represents lifecycle events during interpretation.
|
|
||||||
type Event int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// StartEvent is emitted before interpretation begins.
|
|
||||||
StartEvent Event = iota
|
|
||||||
// StepEvent is emitted after each interpretation step.
|
|
||||||
StepEvent
|
|
||||||
// StopEvent is emitted after interpretation completes.
|
|
||||||
StopEvent
|
|
||||||
)
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
// Package runtime provides the abstract Reducer interface for all expression
|
|
||||||
// reduction strategies.
|
|
||||||
package runtime
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/emitter"
|
|
||||||
"git.maximhutz.com/max/lambda/pkg/repr"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// Runtimes also implement the Emitter interface to allow plugins to observe
|
|
||||||
// reduction lifecycle events (Start, Step, Stop).
|
|
||||||
type Runtime interface {
|
|
||||||
emitter.Emitter[Event]
|
|
||||||
|
|
||||||
// Run a single step. Returns whether the runtime is complete or not.
|
|
||||||
Step() bool
|
|
||||||
|
|
||||||
// Run until completion.
|
|
||||||
Run()
|
|
||||||
|
|
||||||
// Copy the state of the runtime.
|
|
||||||
Expression() repr.Repr
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user