docs: document remaining packages and simplify AST types #45

Merged
mvhutz merged 15 commits from docs/rest into main 2026-02-10 01:15:42 +00:00
6 changed files with 72 additions and 27 deletions
Showing only changes of commit 98b327103f - Show all commits

View File

@@ -7,18 +7,24 @@ import (
"git.maximhutz.com/max/lambda/pkg/codec"
)
// A Codec is a type-erased codec that serializes and deserializes expressions
// as Expr values, regardless of the underlying representation type.
type Codec interface {
codec.Codec[Expr]
// InType returns the name of the representation this codec handles.
InType() string
}
type convertedCodec[T any] struct {
// A registeredCodec adapts a typed codec.Codec[T] into the type-erased Codec
// interface. It wraps decoded values into Expr on decode, and extracts the
// underlying T from an Expr on encode.
type registeredCodec[T any] struct {
codec codec.Codec[T]
inType string
}
func (c convertedCodec[T]) Decode(s string) (Expr, error) {
func (c registeredCodec[T]) Decode(s string) (Expr, error) {
t, err := c.codec.Decode(s)
if err != nil {
return nil, err
@@ -27,7 +33,7 @@ func (c convertedCodec[T]) Decode(s string) (Expr, error) {
return NewExpr(c.inType, t), nil
}
func (c convertedCodec[T]) Encode(r Expr) (string, error) {
func (c registeredCodec[T]) Encode(r Expr) (string, error) {
t, ok := r.Data().(T)
if !ok {
dataType := reflect.TypeOf(r.Data())
@@ -38,13 +44,15 @@ func (c convertedCodec[T]) Encode(r Expr) (string, error) {
return c.codec.Encode(t)
}
func (c convertedCodec[T]) InType() string { return c.inType }
func (c registeredCodec[T]) InType() string { return c.inType }
// RegisterCodec registers a typed codec under the given representation name.
// Returns an error if a codec for that representation is already registered.
func RegisterCodec[T any](registry *Registry, m codec.Codec[T], inType string) error {
if _, ok := registry.codecs[inType]; ok {
return fmt.Errorf("Codec for '%s' already registered", inType)
}
registry.codecs[inType] = convertedCodec[T]{m, inType}
registry.codecs[inType] = registeredCodec[T]{m, inType}
return nil
}

View File

@@ -6,19 +6,29 @@ import (
"git.maximhutz.com/max/lambda/pkg/codec"
)
// A Conversion is a type-erased transformation from one representation to
// another. It operates on Expr values, hiding the underlying representation
// types.
type Conversion interface {
// InType returns the name of the source representation.
InType() string
// OutType returns the name of the target representation.
OutType() string
// Run applies the conversion to the given expression. Returns an error if
// the expression's data does not match the expected source type.
Run(Expr) (Expr, error)
}
type convertedConversion[T, U any] struct {
// A registeredConversion adapts a typed codec.Conversion[T, U] into the
// type-erased Conversion interface. It extracts the underlying T from an Expr,
// applies the conversion, and wraps the result as a new Expr.
type registeredConversion[T, U any] struct {
conversion codec.Conversion[T, U]
inType, outType string
}
func (c convertedConversion[T, U]) Run(expr Expr) (Expr, error) {
func (c registeredConversion[T, U]) Run(expr Expr) (Expr, error) {
t, ok := expr.Data().(T)
if !ok {
return nil, fmt.Errorf("could not parse '%v' as '%s'", t, c.inType)
@@ -32,12 +42,18 @@ func (c convertedConversion[T, U]) Run(expr Expr) (Expr, error) {
return NewExpr(c.outType, u), nil
}
func (c convertedConversion[T, U]) InType() string { return c.inType }
func (c registeredConversion[T, U]) InType() string { return c.inType }
func (c convertedConversion[T, U]) OutType() string { return c.outType }
func (c registeredConversion[T, U]) OutType() string { return c.outType }
func RegisterConversion[T, U any](registry *Registry, conversion func(T) (U, error), inType, outType string) error {
registry.converter.Add(convertedConversion[T, U]{conversion, inType, outType})
// RegisterConversion registers a typed conversion function between two
// representations.
func RegisterConversion[T, U any](
registry *Registry,
conversion codec.Conversion[T, U],
inType, outType string,
) error {
registry.converter.Add(registeredConversion[T, U]{conversion, inType, outType})
return nil
}

View File

@@ -1,13 +1,18 @@
package registry
// A Converter is a directed graph of conversions between representations. Each
// node is a representation name, and each edge is a Conversion.
type Converter struct {
data map[string][]Conversion
}
// NewConverter creates an empty Converter with no registered conversions.
func NewConverter() *Converter {
return &Converter{data: map[string][]Conversion{}}
}
// Add registers a conversion, adding an edge from its source representation
// to its target representation.
func (g *Converter) Add(c Conversion) {
conversionsFromIn, ok := g.data[c.InType()]
if !ok {
@@ -18,6 +23,8 @@ func (g *Converter) Add(c Conversion) {
g.data[c.InType()] = conversionsFromIn
}
// ConversionsFrom returns all conversions that have the given representation
// as their source type.
func (g *Converter) ConversionsFrom(t string) []Conversion {
return g.data[t]
}

View File

@@ -6,26 +6,36 @@ import (
"git.maximhutz.com/max/lambda/pkg/engine"
)
// An Engine is a type-erased evaluation engine that can load an expression
// into a runnable Process.
type Engine interface {
// Load prepares an expression for evaluation, returning a Process. Returns
// an error if the expression's data does not match the engine's expected
// representation type.
Load(Expr) (Process, error)
// Name returns the name of this engine.
Name() string
// InType returns the name of the representation this engine operates on.
InType() string
}
type convertedEngine[T any] struct {
// A registeredEngine adapts a typed engine.Engine[T] into the type-erased
// Engine interface. It extracts the underlying T from an Expr before passing it
// to the engine.
type registeredEngine[T any] struct {
engine engine.Engine[T]
name string
inType string
}
func (e convertedEngine[T]) InType() string { return e.inType }
func (e registeredEngine[T]) InType() string { return e.inType }
func (e convertedEngine[T]) Name() string { return e.name }
func (e registeredEngine[T]) Name() string { return e.name }
func (e convertedEngine[T]) Load(expr Expr) (Process, error) {
func (e registeredEngine[T]) Load(expr Expr) (Process, error) {
t, ok := expr.Data().(T)
if !ok {
return nil, fmt.Errorf("'ncorrent format '%s' for engine '%s'", expr.Repr(), e.inType)
return nil, fmt.Errorf("incorrect format '%s' for engine '%s'", expr.Repr(), e.inType)
}
process, err := e.engine(t)
@@ -33,14 +43,16 @@ func (e convertedEngine[T]) Load(expr Expr) (Process, error) {
return nil, err
}
return convertedProcess[T]{process, e.inType}, nil
return registeredProcess[T]{process, e.inType}, nil
}
// RegisterEngine registers a typed engine under the given name. Returns an
// error if an engine with that name is already registered.
func RegisterEngine[T any](registry *Registry, e engine.Engine[T], name, inType string) error {
if _, ok := registry.engines[name]; ok {
return fmt.Errorf("engine '%s' already registered", name)
}
registry.engines[name] = &convertedEngine[T]{e, name, inType}
registry.engines[name] = &registeredEngine[T]{e, name, inType}
return nil
}

View File

@@ -1,17 +1,18 @@
package registry
// A Expr is a lambda calculus expression. It can have any type of
// Expresentation, so long as that class is known to the registry it is handled
// An Expr is a type-erased lambda calculus expression. It can have any type of
// representation, so long as that type is known to the registry it is handled
// by.
type Expr interface {
// Repr returns the name of the underlying Expresentation. It is assumed if
// two expressions have the same Repr(), they have the same Expresentation.
// Repr returns the name of the underlying representation. Two expressions
// with the same Repr() are assumed to have the same representation type.
Repr() string
// The base expression data.
// Data returns the underlying expression data.
Data() any
}
// A baseExpr is the default implementation of Expr.
type baseExpr struct {
id string
data any
@@ -21,4 +22,5 @@ func (r baseExpr) Repr() string { return r.id }
func (r baseExpr) Data() any { return r.data }
// NewExpr creates an Expr with the given representation name and data.
func NewExpr(id string, data any) Expr { return baseExpr{id, data} }

View File

@@ -10,14 +10,14 @@ type Process interface {
InType() string
}
type convertedProcess[T any] struct {
type registeredProcess[T any] struct {
process engine.Process[T]
inType string
}
func (e convertedProcess[T]) InType() string { return e.inType }
func (e registeredProcess[T]) InType() string { return e.inType }
func (b convertedProcess[T]) Get() (Expr, error) {
func (b registeredProcess[T]) Get() (Expr, error) {
s, err := b.process.Get()
if err != nil {
return nil, err
@@ -26,6 +26,6 @@ func (b convertedProcess[T]) Get() (Expr, error) {
return NewExpr(b.inType, s), nil
}
func (b convertedProcess[T]) Step(i int) bool {
func (b registeredProcess[T]) Step(i int) bool {
return b.process.Step(i)
}