Implement all builtins required to get fib working

Getting `recur` to work required adding an Operation argument to a bunch
of places in a really hacky way. I don't like it at all, but I'm also
kind of out of mental energy to figure it out properly. The fibonacci
demo in the README _works_, at least, though I don't think it's actually
tail recursive.
This commit is contained in:
Brian Picciano 2021-12-30 15:10:25 -07:00
parent 3a2423a937
commit 6257495fe4
5 changed files with 86 additions and 24 deletions

View File

@ -4,17 +4,23 @@ Fibonacci function in ginger:
```
fib = {
decr = { out = add < (in; -1;); };
out = {
n = 0 < in;
a = 1 < in;
b = 2 < in;
out < if < (
n = tupEl < (in; 0;);
a = tupEl < (in; 1;);
b = tupEl < (in; 2;);
out = if < (
zero? < n;
a;
recur < (decr < n; b; add < (a;b;); );
recur < (
decr < n;
b;
add < (a;b;);
);
);
} < (in; 0; 1;);

View File

@ -46,15 +46,18 @@ func evalThunks(args []Thunk) Thunk {
// Operation is an entity which can accept one or more arguments (each not
// having been evaluated yet) and return a Thunk which will perform some internal processing on those
// arguments and return a resultant Value.
// having been evaluated yet) and return a Thunk which will perform some
// internal processing on those arguments and return a resultant Value.
//
// The Operation passed into Perform is the Operation which is calling the
// Perform. It may be nil.
type Operation interface {
Perform([]Thunk) (Thunk, error)
Perform([]Thunk, Operation) (Thunk, error)
}
func preEvalValOp(fn func(Value) (Value, error)) Operation {
return OperationFunc(func(args []Thunk) (Thunk, error) {
return OperationFunc(func(args []Thunk, _ Operation) (Thunk, error) {
return func() (Value, error) {
@ -89,18 +92,19 @@ func OperationFromGraph(g *gg.Graph, scope Scope) Operation {
}
}
func (g *graphOp) Perform(args []Thunk) (Thunk, error) {
func (g *graphOp) Perform(args []Thunk, _ Operation) (Thunk, error) {
return ScopeFromGraph(
g.Graph,
evalThunks(args),
g.scope,
g,
).Evaluate(outVal)
}
// OperationFunc is a function which implements the Operation interface.
type OperationFunc func([]Thunk) (Thunk, error)
type OperationFunc func([]Thunk, Operation) (Thunk, error)
// Perform calls the underlying OperationFunc directly.
func (f OperationFunc) Perform(args []Thunk) (Thunk, error) {
return f(args)
func (f OperationFunc) Perform(args []Thunk, op Operation) (Thunk, error) {
return f(args, op)
}

View File

@ -24,7 +24,9 @@ type Scope interface {
// EvaluateEdge will use the given Scope to evaluate the edge's ultimate Value,
// after passing all leaf vertices up the tree through all Operations found on
// edge values.
func EvaluateEdge(edge *gg.OpenEdge, scope Scope) (Value, error) {
//
// The Operation is the Operation which is evaluating the edge, if any.
func EvaluateEdge(edge *gg.OpenEdge, scope Scope, op Operation) (Value, error) {
thunk, err := graph.MapReduce[gg.Value, gg.Value, Thunk](
edge,
@ -73,7 +75,7 @@ func EvaluateEdge(edge *gg.OpenEdge, scope Scope) (Value, error) {
return nil, fmt.Errorf("edge value must be an operation")
}
return edgeVal.Operation.Perform(args)
return edgeVal.Operation.Perform(args, op)
},
)
@ -115,6 +117,7 @@ type graphScope struct {
*gg.Graph
in Thunk
parent Scope
op Operation
}
// ScopeFromGraph returns a Scope which will use the given Graph for evaluation.
@ -133,11 +136,12 @@ type graphScope struct {
//
// NewScope will return the parent scope, if one is given, or an empty ScopeMap
// if not.
func ScopeFromGraph(g *gg.Graph, in Thunk, parent Scope) Scope {
func ScopeFromGraph(g *gg.Graph, in Thunk, parent Scope, op Operation) Scope {
return &graphScope{
Graph: g,
in: in,
parent: parent,
op: op,
}
}
@ -165,7 +169,9 @@ func (g *graphScope) Evaluate(nameVal Value) (Thunk, error) {
)
}
return func() (Value, error) { return EvaluateEdge(edgesIn[0], g) }, nil
return func() (Value, error) {
return EvaluateEdge(edgesIn[0], g, g.op)
}, nil
}
func (g *graphScope) NewScope() Scope {

View File

@ -12,10 +12,6 @@ var GlobalScope = ScopeMap{
"add": Value{Operation: preEvalValOp(func(val Value) (Value, error) {
if len(val.Tuple) == 0 {
return Value{}, fmt.Errorf("add requires a non-zero tuple of numbers as an argument")
}
var sum int64
for _, tupVal := range val.Tuple {
@ -30,4 +26,53 @@ var GlobalScope = ScopeMap{
return Value{Value: gg.Value{Number: &sum}}, nil
})},
"tupEl": Value{Operation: preEvalValOp(func(val Value) (Value, error) {
tup, i := val.Tuple[0], val.Tuple[1]
return tup.Tuple[int(*i.Number)], nil
})},
"isZero": Value{Operation: preEvalValOp(func(val Value) (Value, error) {
if *val.Number == 0 {
one := int64(1)
return Value{Value: gg.Value{Number: &one}}, nil
}
zero := int64(0)
return Value{Value: gg.Value{Number: &zero}}, nil
})},
"if": Value{Operation: OperationFunc(func(args []Thunk, _ Operation) (Thunk, error) {
b := args[0]
onTrue := args[1]
onFalse := args[2]
return func() (Value, error) {
bVal, err := b()
if err != nil {
return ZeroValue, err
}
if *bVal.Number == 0 {
return onFalse()
}
return onTrue()
}, nil
})},
"recur": Value{Operation: OperationFunc(func(args []Thunk, op Operation) (Thunk, error) {
return op.Perform(args, op)
})},
}

View File

@ -115,9 +115,10 @@ func EvaluateSource(opSrc io.Reader, input gg.Value, scope Scope) (Value, error)
op := OperationFromGraph(g, scope.NewScope())
thunk, err := op.Perform([]Thunk{
func() (Value, error) { return Value{Value: input}, nil },
})
thunk, err := op.Perform(
[]Thunk{valThunk(Value{Value: input})},
nil,
)
if err != nil {
return ZeroValue, err