feat: registry, normalorder engine, lambda codec and marshaler
This commit is contained in:
24
cmd/lambda/registry.go
Normal file
24
cmd/lambda/registry.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.maximhutz.com/max/lambda/internal/cli"
|
||||||
|
"git.maximhutz.com/max/lambda/internal/registry"
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/convert"
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/engine/normalorder"
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/lambda"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MakeRegistry() *registry.Registry {
|
||||||
|
r := registry.New()
|
||||||
|
|
||||||
|
// Codecs
|
||||||
|
r.AddCodec(cli.ConvertCodec(convert.Saccharine2Lambda{}, "saccharine", "lambda"))
|
||||||
|
|
||||||
|
// Engines
|
||||||
|
r.AddEngine(cli.ConvertEngine(normalorder.Engine{}, "normalorder", "lambda"))
|
||||||
|
|
||||||
|
// Marshalers
|
||||||
|
r.AddMarshaler(cli.ConvertMarshaler(lambda.Marshaler{}, "lambda"))
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
75
internal/registry/registry.go
Normal file
75
internal/registry/registry.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.maximhutz.com/max/lambda/internal/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Registry struct {
|
||||||
|
marshalers map[string]cli.Marshaler
|
||||||
|
codecs []cli.Codec
|
||||||
|
engines map[string]cli.Engine
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *Registry {
|
||||||
|
return &Registry{
|
||||||
|
marshalers: map[string]cli.Marshaler{},
|
||||||
|
codecs: []cli.Codec{},
|
||||||
|
engines: map[string]cli.Engine{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Registry) AddCodec(c cli.Codec) error {
|
||||||
|
r.codecs = append(r.codecs, c)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Registry) AddMarshaler(c cli.Marshaler) error {
|
||||||
|
if _, ok := r.marshalers[c.InType()]; ok {
|
||||||
|
return fmt.Errorf("marshaler for '%s' already registered", c.InType())
|
||||||
|
}
|
||||||
|
|
||||||
|
r.marshalers[c.InType()] = c
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Registry) AddEngine(e cli.Engine) error {
|
||||||
|
if _, ok := r.engines[e.Name()]; ok {
|
||||||
|
return fmt.Errorf("engine '%s' already registered", e.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
r.engines[e.Name()] = e
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Registry) GetEngine(name string) (cli.Engine, error) {
|
||||||
|
e, ok := r.engines[name]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("engine '%s' not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return e, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Registry) ConvertTo(repr cli.Repr, outType string) (cli.Repr, error) {
|
||||||
|
panic("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Registry) Marshal(repr cli.Repr) (string, error) {
|
||||||
|
m, ok := r.marshalers[repr.Id()]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("no marshaler for '%s'", repr.Id())
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.Encode(repr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Registry) Unmarshal(s string, outType string) (cli.Repr, error) {
|
||||||
|
m, ok := r.marshalers[outType]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("no marshaler for '%s'", outType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.Decode(s)
|
||||||
|
}
|
||||||
@@ -3,16 +3,17 @@ package convert
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/codec"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func convertAtom(n *saccharine.Atom) lambda.Expression {
|
func encodeAtom(n *saccharine.Atom) lambda.Expression {
|
||||||
return lambda.NewVariable(n.Name)
|
return lambda.NewVariable(n.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertAbstraction(n *saccharine.Abstraction) lambda.Expression {
|
func encodeAbstraction(n *saccharine.Abstraction) lambda.Expression {
|
||||||
result := SaccharineToLambda(n.Body)
|
result := encodeExpression(n.Body)
|
||||||
|
|
||||||
parameters := n.Parameters
|
parameters := n.Parameters
|
||||||
|
|
||||||
@@ -31,13 +32,13 @@ func convertAbstraction(n *saccharine.Abstraction) lambda.Expression {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertApplication(n *saccharine.Application) lambda.Expression {
|
func encodeApplication(n *saccharine.Application) lambda.Expression {
|
||||||
result := SaccharineToLambda(n.Abstraction)
|
result := encodeExpression(n.Abstraction)
|
||||||
|
|
||||||
arguments := []lambda.Expression{}
|
arguments := []lambda.Expression{}
|
||||||
for _, argument := range n.Arguments {
|
for _, argument := range n.Arguments {
|
||||||
convertedArgument := SaccharineToLambda(argument)
|
encodeedArgument := encodeExpression(argument)
|
||||||
arguments = append(arguments, convertedArgument)
|
arguments = append(arguments, encodeedArgument)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, argument := range arguments {
|
for _, argument := range arguments {
|
||||||
@@ -51,9 +52,9 @@ func reduceLet(s *saccharine.LetStatement, e lambda.Expression) lambda.Expressio
|
|||||||
var value lambda.Expression
|
var value lambda.Expression
|
||||||
|
|
||||||
if len(s.Parameters) == 0 {
|
if len(s.Parameters) == 0 {
|
||||||
value = SaccharineToLambda(s.Body)
|
value = encodeExpression(s.Body)
|
||||||
} else {
|
} else {
|
||||||
value = convertAbstraction(saccharine.NewAbstraction(s.Parameters, s.Body))
|
value = encodeAbstraction(saccharine.NewAbstraction(s.Parameters, s.Body))
|
||||||
}
|
}
|
||||||
|
|
||||||
return lambda.NewApplication(
|
return lambda.NewApplication(
|
||||||
@@ -67,7 +68,7 @@ func reduceDeclare(s *saccharine.DeclareStatement, e lambda.Expression) lambda.E
|
|||||||
|
|
||||||
return lambda.NewApplication(
|
return lambda.NewApplication(
|
||||||
lambda.NewAbstraction(freshVar, e),
|
lambda.NewAbstraction(freshVar, e),
|
||||||
SaccharineToLambda(s.Value),
|
encodeExpression(s.Value),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,8 +83,8 @@ func reduceStatement(s saccharine.Statement, e lambda.Expression) lambda.Express
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertClause(n *saccharine.Clause) lambda.Expression {
|
func encodeClause(n *saccharine.Clause) lambda.Expression {
|
||||||
result := SaccharineToLambda(n.Returns)
|
result := encodeExpression(n.Returns)
|
||||||
|
|
||||||
for i := len(n.Statements) - 1; i >= 0; i-- {
|
for i := len(n.Statements) - 1; i >= 0; i-- {
|
||||||
result = reduceStatement(n.Statements[i], result)
|
result = reduceStatement(n.Statements[i], result)
|
||||||
@@ -92,17 +93,46 @@ func convertClause(n *saccharine.Clause) lambda.Expression {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func SaccharineToLambda(n saccharine.Expression) lambda.Expression {
|
func encodeExpression(s saccharine.Expression) lambda.Expression {
|
||||||
switch n := n.(type) {
|
switch s := s.(type) {
|
||||||
case *saccharine.Atom:
|
case *saccharine.Atom:
|
||||||
return convertAtom(n)
|
return encodeAtom(s)
|
||||||
case *saccharine.Abstraction:
|
case *saccharine.Abstraction:
|
||||||
return convertAbstraction(n)
|
return encodeAbstraction(s)
|
||||||
case *saccharine.Application:
|
case *saccharine.Application:
|
||||||
return convertApplication(n)
|
return encodeApplication(s)
|
||||||
case *saccharine.Clause:
|
case *saccharine.Clause:
|
||||||
return convertClause(n)
|
return encodeClause(s)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("unknown expression type: %T", n))
|
panic(fmt.Errorf("unknown expression type: %T", s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeExression(l lambda.Expression) saccharine.Expression {
|
||||||
|
switch l := l.(type) {
|
||||||
|
case lambda.Variable:
|
||||||
|
return saccharine.NewAtom(l.Name())
|
||||||
|
case lambda.Abstraction:
|
||||||
|
return saccharine.NewAbstraction(
|
||||||
|
[]string{l.Parameter()},
|
||||||
|
decodeExression(l.Body()))
|
||||||
|
case lambda.Application:
|
||||||
|
return saccharine.NewApplication(
|
||||||
|
decodeExression(l.Abstraction()),
|
||||||
|
[]saccharine.Expression{decodeExression(l.Argument())})
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unknown expression type: %T", l))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Saccharine2Lambda struct{}
|
||||||
|
|
||||||
|
func (c Saccharine2Lambda) Decode(l lambda.Expression) (saccharine.Expression, error) {
|
||||||
|
return decodeExression(l), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Saccharine2Lambda) Encode(s saccharine.Expression) (lambda.Expression, error) {
|
||||||
|
return encodeExpression(s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ codec.Codec[saccharine.Expression, lambda.Expression] = (*Saccharine2Lambda)(nil)
|
||||||
|
|||||||
34
pkg/engine/normalorder/engine.go
Normal file
34
pkg/engine/normalorder/engine.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package normalorder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/engine"
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/lambda"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Engine struct {
|
||||||
|
expr lambda.Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Engine) Get() (lambda.Expression, error) {
|
||||||
|
return e.expr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Engine) Set(l lambda.Expression) error {
|
||||||
|
e.expr = l
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Engine) Step(i int) bool {
|
||||||
|
var reduced bool
|
||||||
|
|
||||||
|
for range i {
|
||||||
|
e.expr, reduced = ReduceOnce(e.expr)
|
||||||
|
if !reduced {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ engine.Engine[lambda.Expression] = (*Engine)(nil)
|
||||||
34
pkg/engine/normalorder/reduce_one.go
Normal file
34
pkg/engine/normalorder/reduce_one.go
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
19
pkg/lambda/marshaler.go
Normal file
19
pkg/lambda/marshaler.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package lambda
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.maximhutz.com/max/lambda/pkg/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Marshaler struct{}
|
||||||
|
|
||||||
|
func (m Marshaler) Decode(string) (Expression, error) {
|
||||||
|
return nil, fmt.Errorf("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Marshaler) Encode(e Expression) (string, error) {
|
||||||
|
return e.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ codec.Marshaler[Expression] = (*Marshaler)(nil)
|
||||||
Reference in New Issue
Block a user