2a96e9a593
The order of these methods now matches the order of edge/value in every other context.
112 lines
2.7 KiB
Go
112 lines
2.7 KiB
Go
package vm
|
|
|
|
import (
|
|
"github.com/mediocregopher/ginger/gg"
|
|
"github.com/mediocregopher/ginger/graph"
|
|
)
|
|
|
|
var (
|
|
inVal = nameVal("in")
|
|
outVal = nameVal("out")
|
|
)
|
|
|
|
// Operation is an entity which can accept a single argument (the OpenEdge),
|
|
// perform some internal processing on that argument, and return a resultant
|
|
// Value.
|
|
//
|
|
// The Scope passed into Perform can be used to Evaluate the OpenEdge, as
|
|
// needed.
|
|
type Operation interface {
|
|
Perform(*gg.OpenEdge, Scope) (Value, error)
|
|
}
|
|
|
|
func preEvalValOp(fn func(Value) (Value, error)) Operation {
|
|
|
|
return OperationFunc(func(edge *gg.OpenEdge, scope Scope) (Value, error) {
|
|
|
|
edgeVal, err := EvaluateEdge(edge, scope)
|
|
|
|
if err != nil {
|
|
return Value{}, err
|
|
}
|
|
|
|
return fn(edgeVal)
|
|
})
|
|
}
|
|
|
|
// NOTE this is a giant hack to get around the fact that we're not yet
|
|
// using a genericized Graph implementation, so when we do AddValueIn
|
|
// on a gg.Graph we can't use a Tuple value (because gg has no Tuple
|
|
// value), we have to use a Tuple vertex instead.
|
|
//
|
|
// This also doesn't yet support passing an operation as a value to another
|
|
// operation.
|
|
func preEvalEdgeOp(fn func(*gg.OpenEdge) (Value, error)) Operation {
|
|
|
|
return preEvalValOp(func(val Value) (Value, error) {
|
|
|
|
var edge *gg.OpenEdge
|
|
|
|
if len(val.Tuple) > 0 {
|
|
|
|
tupEdges := make([]*gg.OpenEdge, len(val.Tuple))
|
|
|
|
for i := range val.Tuple {
|
|
tupEdges[i] = graph.ValueOut[gg.Value](gg.ZeroValue, val.Tuple[i].Value)
|
|
}
|
|
|
|
edge = graph.TupleOut[gg.Value](gg.ZeroValue, tupEdges...)
|
|
|
|
} else {
|
|
|
|
edge = graph.ValueOut[gg.Value](gg.ZeroValue, val.Value)
|
|
|
|
}
|
|
|
|
return fn(edge)
|
|
})
|
|
|
|
}
|
|
|
|
type graphOp struct {
|
|
*gg.Graph
|
|
scope Scope
|
|
}
|
|
|
|
// OperationFromGraph wraps the given Graph such that it can be used as an
|
|
// operation.
|
|
//
|
|
// When Perform is called the passed in OpenEdge is set to the "in" name value
|
|
// of the given Graph, then that resultant graph and the given parent Scope are
|
|
// used to construct a new Scope. The "out" name value is Evaluated on that
|
|
// Scope to obtain a resultant Value.
|
|
func OperationFromGraph(g *gg.Graph, scope Scope) Operation {
|
|
return &graphOp{
|
|
Graph: g,
|
|
scope: scope,
|
|
}
|
|
}
|
|
|
|
func (g *graphOp) Perform(edge *gg.OpenEdge, scope Scope) (Value, error) {
|
|
|
|
return preEvalEdgeOp(func(edge *gg.OpenEdge) (Value, error) {
|
|
|
|
scope = ScopeFromGraph(
|
|
g.Graph.AddValueIn(inVal.Value, edge),
|
|
g.scope,
|
|
)
|
|
|
|
return scope.Evaluate(outVal)
|
|
|
|
}).Perform(edge, scope)
|
|
|
|
}
|
|
|
|
// OperationFunc is a function which implements the Operation interface.
|
|
type OperationFunc func(*gg.OpenEdge, Scope) (Value, error)
|
|
|
|
// Perform calls the underlying OperationFunc directly.
|
|
func (f OperationFunc) Perform(edge *gg.OpenEdge, scope Scope) (Value, error) {
|
|
return f(edge, scope)
|
|
}
|