diff --git a/README.md b/README.md index d1f6440..c8ee356 100644 --- a/README.md +++ b/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;); diff --git a/vm/op.go b/vm/op.go index cd366ff..660d295 100644 --- a/vm/op.go +++ b/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) } diff --git a/vm/scope.go b/vm/scope.go index dbd67bd..8a552bc 100644 --- a/vm/scope.go +++ b/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 { diff --git a/vm/scope_global.go b/vm/scope_global.go index a516113..75e2c52 100644 --- a/vm/scope_global.go +++ b/vm/scope_global.go @@ -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) + })}, + } diff --git a/vm/vm.go b/vm/vm.go index d6f8a35..0aa7f50 100644 --- a/vm/vm.go +++ b/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