e7991adfaa
The base graph implementation has been moved into its own package, `graph`, and been made fully generic, ie the value on each vertex/edge is a parameterized type. This will allow us to use the graph for both syntax parsing (gg) and runtime evaluation (vm), with each use-case being able to use slightly different Value types.
112 lines
2.8 KiB
Go
112 lines
2.8 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(graph.OpenEdge[gg.Value], Scope) (Value, error)
|
|
}
|
|
|
|
func preEvalValOp(fn func(Value) (Value, error)) Operation {
|
|
|
|
return OperationFunc(func(edge graph.OpenEdge[gg.Value], 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 graph.Graph[gg.Value] 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(graph.OpenEdge[gg.Value]) (Value, error)) Operation {
|
|
|
|
return preEvalValOp(func(val Value) (Value, error) {
|
|
|
|
var edge graph.OpenEdge[gg.Value]
|
|
|
|
if len(val.Tuple) > 0 {
|
|
|
|
tupEdges := make([]graph.OpenEdge[gg.Value], len(val.Tuple))
|
|
|
|
for i := range val.Tuple {
|
|
tupEdges[i] = graph.ValueOut[gg.Value](val.Tuple[i].Value, gg.ZeroValue)
|
|
}
|
|
|
|
edge = graph.TupleOut[gg.Value](tupEdges, gg.ZeroValue)
|
|
|
|
} else {
|
|
|
|
edge = graph.ValueOut[gg.Value](val.Value, gg.ZeroValue)
|
|
|
|
}
|
|
|
|
return fn(edge)
|
|
})
|
|
|
|
}
|
|
|
|
type graphOp struct {
|
|
*graph.Graph[gg.Value]
|
|
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 *graph.Graph[gg.Value], scope Scope) Operation {
|
|
return &graphOp{
|
|
Graph: g,
|
|
scope: scope,
|
|
}
|
|
}
|
|
|
|
func (g *graphOp) Perform(edge graph.OpenEdge[gg.Value], scope Scope) (Value, error) {
|
|
|
|
return preEvalEdgeOp(func(edge graph.OpenEdge[gg.Value]) (Value, error) {
|
|
|
|
scope = ScopeFromGraph(
|
|
g.Graph.AddValueIn(edge, inVal.Value),
|
|
g.scope,
|
|
)
|
|
|
|
return scope.Evaluate(outVal)
|
|
|
|
}).Perform(edge, scope)
|
|
|
|
}
|
|
|
|
// OperationFunc is a function which implements the Operation interface.
|
|
type OperationFunc func(graph.OpenEdge[gg.Value], Scope) (Value, error)
|
|
|
|
// Perform calls the underlying OperationFunc directly.
|
|
func (f OperationFunc) Perform(edge graph.OpenEdge[gg.Value], scope Scope) (Value, error) {
|
|
return f(edge, scope)
|
|
}
|