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:
parent
3a2423a937
commit
6257495fe4
16
README.md
16
README.md
@ -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;);
|
||||
|
20
vm/op.go
20
vm/op.go
@ -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)
|
||||
}
|
||||
|
14
vm/scope.go
14
vm/scope.go
@ -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 {
|
||||
|
@ -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)
|
||||
})},
|
||||
|
||||
}
|
||||
|
7
vm/vm.go
7
vm/vm.go
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user