Rename Operation to Function, plus some cleanup
This commit is contained in:
parent
7d0fcbf28a
commit
21c91731e9
235
vm/function.go
Normal file
235
vm/function.go
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/mediocregopher/ginger/gg"
|
||||||
|
"github.com/mediocregopher/ginger/graph"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Function is an entity which accepts an argument Value and performs some
|
||||||
|
// internal processing on that argument to return a resultant Value.
|
||||||
|
type Function interface {
|
||||||
|
Perform(Value) Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// FunctionFunc is a function which implements the Function interface.
|
||||||
|
type FunctionFunc func(Value) Value
|
||||||
|
|
||||||
|
// Perform calls the underlying FunctionFunc directly.
|
||||||
|
func (f FunctionFunc) Perform(arg Value) Value {
|
||||||
|
return f(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identity returns an Function which always returns the given Value,
|
||||||
|
// regardless of the input argument.
|
||||||
|
//
|
||||||
|
// TODO this might not be the right name
|
||||||
|
func Identity(val Value) Function {
|
||||||
|
return FunctionFunc(func(Value) Value {
|
||||||
|
return val
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type graphFn struct {
|
||||||
|
*gg.Graph
|
||||||
|
scope Scope
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
valNameIn = Value{Value: gg.Name("in")}
|
||||||
|
valNameOut = Value{Value: gg.Name("out")}
|
||||||
|
valNameIf = Value{Value: gg.Name("if")}
|
||||||
|
valNameRecur = Value{Value: gg.Name("recur")}
|
||||||
|
valNumberZero = Value{Value: gg.Number(0)}
|
||||||
|
)
|
||||||
|
|
||||||
|
// FunctionFromGraph wraps the given Graph such that it can be used as an
|
||||||
|
// Function. The given Scope determines what values outside of the Graph are
|
||||||
|
// available for use within the Function.
|
||||||
|
func FunctionFromGraph(g *gg.Graph, scope Scope) (Function, error) {
|
||||||
|
|
||||||
|
// edgeFn is distinct from a generic Function in that the Value passed into
|
||||||
|
// Perform will _always_ be the value of "in" for the overall Function.
|
||||||
|
//
|
||||||
|
// edgeFns will wrap each other, passing "in" downwards to the leaf edgeFns.
|
||||||
|
type edgeFn Function
|
||||||
|
|
||||||
|
var compileEdge func(*gg.OpenEdge) (edgeFn, error)
|
||||||
|
|
||||||
|
// TODO memoize?
|
||||||
|
valToEdgeFn := func(val Value) (edgeFn, error) {
|
||||||
|
|
||||||
|
if val.Name == nil {
|
||||||
|
return edgeFn(Identity(val)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
name := *val.Name
|
||||||
|
|
||||||
|
if val.Equal(valNameIn) {
|
||||||
|
return edgeFn(FunctionFunc(func(inArg Value) Value {
|
||||||
|
return inArg
|
||||||
|
})), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO intercept if and recur?
|
||||||
|
|
||||||
|
edgesIn := g.ValueIns(val.Value)
|
||||||
|
|
||||||
|
if l := len(edgesIn); l == 0 {
|
||||||
|
|
||||||
|
val, err := scope.Resolve(name)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("resolving name %q from the outer scope: %w", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return edgeFn(Identity(val)), nil
|
||||||
|
|
||||||
|
} else if l != 1 {
|
||||||
|
return nil, fmt.Errorf("resolved name %q to %d input edges, rather than one", name, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
edge := edgesIn[0]
|
||||||
|
|
||||||
|
return compileEdge(edge)
|
||||||
|
}
|
||||||
|
|
||||||
|
// "out" resolves to more than a static value, treat the graph as a full
|
||||||
|
// operation.
|
||||||
|
|
||||||
|
// thisFn is used to support recur. It will get filled in with the Function
|
||||||
|
// which is returned by this function, once that Function is created.
|
||||||
|
thisFn := new(Function)
|
||||||
|
|
||||||
|
compileEdge = func(edge *gg.OpenEdge) (edgeFn, error) {
|
||||||
|
|
||||||
|
return graph.MapReduce[gg.Value, gg.Value, edgeFn](
|
||||||
|
edge,
|
||||||
|
func(ggVal gg.Value) (edgeFn, error) {
|
||||||
|
return valToEdgeFn(Value{Value: ggVal})
|
||||||
|
},
|
||||||
|
func(ggEdgeVal gg.Value, inEdgeFns []edgeFn) (edgeFn, error) {
|
||||||
|
|
||||||
|
if ggEdgeVal.Equal(valNameIf.Value) {
|
||||||
|
|
||||||
|
if len(inEdgeFns) != 3 {
|
||||||
|
return nil, fmt.Errorf("'if' requires a 3-tuple argument")
|
||||||
|
}
|
||||||
|
|
||||||
|
return edgeFn(FunctionFunc(func(inArg Value) Value {
|
||||||
|
|
||||||
|
if pred := inEdgeFns[0].Perform(inArg); pred.Equal(valNumberZero) {
|
||||||
|
return inEdgeFns[2].Perform(inArg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return inEdgeFns[1].Perform(inArg)
|
||||||
|
|
||||||
|
})), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// "if" statements (above) are the only case where we want the
|
||||||
|
// input edges to remain separated, otherwise they should always
|
||||||
|
// be combined into a single edge whose value is a tuple. Do
|
||||||
|
// that here.
|
||||||
|
|
||||||
|
inEdgeFn := inEdgeFns[0]
|
||||||
|
|
||||||
|
if len(inEdgeFns) > 1 {
|
||||||
|
inEdgeFn = edgeFn(FunctionFunc(func(inArg Value) Value {
|
||||||
|
tupVals := make([]Value, len(inEdgeFns))
|
||||||
|
|
||||||
|
for i := range inEdgeFns {
|
||||||
|
tupVals[i] = inEdgeFns[i].Perform(inArg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Tuple(tupVals...)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeVal := Value{Value: ggEdgeVal}
|
||||||
|
|
||||||
|
if edgeVal.IsZero() {
|
||||||
|
return inEdgeFn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if edgeVal.Equal(valNameRecur) {
|
||||||
|
return edgeFn(FunctionFunc(func(inArg Value) Value {
|
||||||
|
return (*thisFn).Perform(inEdgeFn.Perform(inArg))
|
||||||
|
})), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if edgeVal.Graph != nil {
|
||||||
|
|
||||||
|
opFromGraph, err := FunctionFromGraph(
|
||||||
|
edgeVal.Graph,
|
||||||
|
scope.NewScope(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("compiling graph to operation: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
edgeVal = Value{Function: opFromGraph}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Function is known at compile-time, so we can wrap it
|
||||||
|
// directly into an edgeVal using the existing inEdgeFn as the
|
||||||
|
// input.
|
||||||
|
if edgeVal.Function != nil {
|
||||||
|
return edgeFn(FunctionFunc(func(inArg Value) Value {
|
||||||
|
return edgeVal.Function.Perform(inEdgeFn.Perform(inArg))
|
||||||
|
})), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// the edgeVal is not an Function at compile time, and so
|
||||||
|
// it must become one at runtime. We must resolve edgeVal to an
|
||||||
|
// edgeFn as well (edgeEdgeFn), and then at runtime that is
|
||||||
|
// given the inArg and (hopefully) the resultant Function is
|
||||||
|
// called.
|
||||||
|
|
||||||
|
edgeEdgeFn, err := valToEdgeFn(edgeVal)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return edgeFn(FunctionFunc(func(inArg Value) Value {
|
||||||
|
|
||||||
|
runtimeEdgeVal := edgeEdgeFn.Perform(inArg)
|
||||||
|
|
||||||
|
if runtimeEdgeVal.Graph != nil {
|
||||||
|
|
||||||
|
runtimeFn, err := FunctionFromGraph(
|
||||||
|
runtimeEdgeVal.Graph,
|
||||||
|
scope.NewScope(),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("compiling graph to operation: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
runtimeEdgeVal = Value{Function: runtimeFn}
|
||||||
|
}
|
||||||
|
|
||||||
|
if runtimeEdgeVal.Function == nil {
|
||||||
|
panic("edge value must be an operation")
|
||||||
|
}
|
||||||
|
|
||||||
|
return runtimeEdgeVal.Function.Perform(inEdgeFn.Perform(inArg))
|
||||||
|
|
||||||
|
})), nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
graphFn, err := valToEdgeFn(valNameOut)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
*thisFn = Function(graphFn)
|
||||||
|
|
||||||
|
return Function(graphFn), nil
|
||||||
|
}
|
235
vm/op.go
235
vm/op.go
@ -1,235 +0,0 @@
|
|||||||
package vm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/mediocregopher/ginger/gg"
|
|
||||||
"github.com/mediocregopher/ginger/graph"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Operation is an entity which accepts an argument Value and performs some
|
|
||||||
// internal processing on that argument to return a resultant Value.
|
|
||||||
type Operation interface {
|
|
||||||
Perform(Value) Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// OperationFunc is a function which implements the Operation interface.
|
|
||||||
type OperationFunc func(Value) Value
|
|
||||||
|
|
||||||
// Perform calls the underlying OperationFunc directly.
|
|
||||||
func (f OperationFunc) Perform(arg Value) Value {
|
|
||||||
return f(arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identity returns an Operation which always returns the given Value,
|
|
||||||
// regardless of the input argument.
|
|
||||||
//
|
|
||||||
// TODO this might not be the right name
|
|
||||||
func Identity(val Value) Operation {
|
|
||||||
return OperationFunc(func(Value) Value {
|
|
||||||
return val
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type graphOp struct {
|
|
||||||
*gg.Graph
|
|
||||||
scope Scope
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
valNameIn = Value{Value: gg.Name("in")}
|
|
||||||
valNameOut = Value{Value: gg.Name("out")}
|
|
||||||
valNameIf = Value{Value: gg.Name("if")}
|
|
||||||
valNameRecur = Value{Value: gg.Name("recur")}
|
|
||||||
valNumberZero = Value{Value: gg.Number(0)}
|
|
||||||
)
|
|
||||||
|
|
||||||
// OperationFromGraph wraps the given Graph such that it can be used as an
|
|
||||||
// Operation. The given Scope determines what values outside of the Graph are
|
|
||||||
// available for use within the Operation.
|
|
||||||
func OperationFromGraph(g *gg.Graph, scope Scope) (Operation, error) {
|
|
||||||
|
|
||||||
// edgeOp is distinct from a generic Operation in that the Value passed into
|
|
||||||
// Perform will _always_ be the value of "in" for the overall Operation.
|
|
||||||
//
|
|
||||||
// edgeOps will wrap each other, passing "in" downwards to the leaf edgeOps.
|
|
||||||
type edgeOp Operation
|
|
||||||
|
|
||||||
var compileEdge func(*gg.OpenEdge) (edgeOp, error)
|
|
||||||
|
|
||||||
// TODO memoize?
|
|
||||||
valToEdgeOp := func(val Value) (edgeOp, error) {
|
|
||||||
|
|
||||||
if val.Name == nil {
|
|
||||||
return edgeOp(Identity(val)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
name := *val.Name
|
|
||||||
|
|
||||||
if val.Equal(valNameIn) {
|
|
||||||
return edgeOp(OperationFunc(func(inArg Value) Value {
|
|
||||||
return inArg
|
|
||||||
})), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO intercept if and recur?
|
|
||||||
|
|
||||||
edgesIn := g.ValueIns(val.Value)
|
|
||||||
|
|
||||||
if l := len(edgesIn); l == 0 {
|
|
||||||
|
|
||||||
val, err := scope.Resolve(name)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("resolving name %q from the outer scope: %w", name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return edgeOp(Identity(val)), nil
|
|
||||||
|
|
||||||
} else if l != 1 {
|
|
||||||
return nil, fmt.Errorf("resolved name %q to %d input edges, rather than one", name, l)
|
|
||||||
}
|
|
||||||
|
|
||||||
edge := edgesIn[0]
|
|
||||||
|
|
||||||
return compileEdge(edge)
|
|
||||||
}
|
|
||||||
|
|
||||||
// "out" resolves to more than a static value, treat the graph as a full
|
|
||||||
// operation.
|
|
||||||
|
|
||||||
// thisOp is used to support recur. It will get filled in with the Operation
|
|
||||||
// which is returned by this function, once that Operation is created.
|
|
||||||
thisOp := new(Operation)
|
|
||||||
|
|
||||||
compileEdge = func(edge *gg.OpenEdge) (edgeOp, error) {
|
|
||||||
|
|
||||||
return graph.MapReduce[gg.Value, gg.Value, edgeOp](
|
|
||||||
edge,
|
|
||||||
func(ggVal gg.Value) (edgeOp, error) {
|
|
||||||
return valToEdgeOp(Value{Value: ggVal})
|
|
||||||
},
|
|
||||||
func(ggEdgeVal gg.Value, inEdgeOps []edgeOp) (edgeOp, error) {
|
|
||||||
|
|
||||||
if ggEdgeVal.Equal(valNameIf.Value) {
|
|
||||||
|
|
||||||
if len(inEdgeOps) != 3 {
|
|
||||||
return nil, fmt.Errorf("'if' requires a 3-tuple argument")
|
|
||||||
}
|
|
||||||
|
|
||||||
return edgeOp(OperationFunc(func(inArg Value) Value {
|
|
||||||
|
|
||||||
if pred := inEdgeOps[0].Perform(inArg); pred.Equal(valNumberZero) {
|
|
||||||
return inEdgeOps[2].Perform(inArg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return inEdgeOps[1].Perform(inArg)
|
|
||||||
|
|
||||||
})), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// "if" statements (above) are the only case where we want the
|
|
||||||
// input edges to remain separated, otherwise they should always
|
|
||||||
// be combined into a single edge whose value is a tuple. Do
|
|
||||||
// that here.
|
|
||||||
|
|
||||||
inEdgeOp := inEdgeOps[0]
|
|
||||||
|
|
||||||
if len(inEdgeOps) > 1 {
|
|
||||||
inEdgeOp = edgeOp(OperationFunc(func(inArg Value) Value {
|
|
||||||
tupVals := make([]Value, len(inEdgeOps))
|
|
||||||
|
|
||||||
for i := range inEdgeOps {
|
|
||||||
tupVals[i] = inEdgeOps[i].Perform(inArg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Tuple(tupVals...)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
edgeVal := Value{Value: ggEdgeVal}
|
|
||||||
|
|
||||||
if edgeVal.IsZero() {
|
|
||||||
return inEdgeOp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if edgeVal.Equal(valNameRecur) {
|
|
||||||
return edgeOp(OperationFunc(func(inArg Value) Value {
|
|
||||||
return (*thisOp).Perform(inEdgeOp.Perform(inArg))
|
|
||||||
})), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if edgeVal.Graph != nil {
|
|
||||||
|
|
||||||
opFromGraph, err := OperationFromGraph(
|
|
||||||
edgeVal.Graph,
|
|
||||||
scope.NewScope(),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("compiling graph to operation: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
edgeVal = Value{Operation: opFromGraph}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Operation is known at compile-time, so we can wrap it
|
|
||||||
// directly into an edgeVal using the existing inEdgeOp as the
|
|
||||||
// input.
|
|
||||||
if edgeVal.Operation != nil {
|
|
||||||
return edgeOp(OperationFunc(func(inArg Value) Value {
|
|
||||||
return edgeVal.Operation.Perform(inEdgeOp.Perform(inArg))
|
|
||||||
})), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// the edgeVal is not an Operation at compile time, and so
|
|
||||||
// it must become one at runtime. We must resolve edgeVal to an
|
|
||||||
// edgeOp as well (edgeEdgeOp), and then at runtime that is
|
|
||||||
// given the inArg and (hopefully) the resultant Operation is
|
|
||||||
// called.
|
|
||||||
|
|
||||||
edgeEdgeOp, err := valToEdgeOp(edgeVal)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return edgeOp(OperationFunc(func(inArg Value) Value {
|
|
||||||
|
|
||||||
runtimeEdgeVal := edgeEdgeOp.Perform(inArg)
|
|
||||||
|
|
||||||
if runtimeEdgeVal.Graph != nil {
|
|
||||||
|
|
||||||
runtimeOp, err := OperationFromGraph(
|
|
||||||
runtimeEdgeVal.Graph,
|
|
||||||
scope.NewScope(),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("compiling graph to operation: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
runtimeEdgeVal = Value{Operation: runtimeOp}
|
|
||||||
}
|
|
||||||
|
|
||||||
if runtimeEdgeVal.Operation == nil {
|
|
||||||
panic("edge value must be an operation")
|
|
||||||
}
|
|
||||||
|
|
||||||
return runtimeEdgeVal.Operation.Perform(inEdgeOp.Perform(inArg))
|
|
||||||
|
|
||||||
})), nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
graphOp, err := valToEdgeOp(valNameOut)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
*thisOp = Operation(graphOp)
|
|
||||||
|
|
||||||
return Operation(graphOp), nil
|
|
||||||
}
|
|
65
vm/scope.go
65
vm/scope.go
@ -2,8 +2,6 @@ package vm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/mediocregopher/ginger/gg"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Scope encapsulates a set of name->Value mappings.
|
// Scope encapsulates a set of name->Value mappings.
|
||||||
@ -62,66 +60,3 @@ func (s *scopeWith) Resolve(name string) (Value, error) {
|
|||||||
}
|
}
|
||||||
return s.Scope.Resolve(name)
|
return s.Scope.Resolve(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
type graphScope struct {
|
|
||||||
*gg.Graph
|
|
||||||
parent Scope
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
TODO I don't think this is actually necessary
|
|
||||||
|
|
||||||
// ScopeFromGraph returns a Scope which will use the given Graph for name
|
|
||||||
// resolution.
|
|
||||||
//
|
|
||||||
// When a name is resolved, that name will be looked up in the Graph. The name's
|
|
||||||
// vertex must have only a single OpenEdge leading to it. That edge will be
|
|
||||||
// compiled into an Operation and returned.
|
|
||||||
//
|
|
||||||
// If a name does not appear in the Graph, then the given parent Scope will be
|
|
||||||
// used to resolve that name. If the parent Scope is nil then an error is
|
|
||||||
// returned.
|
|
||||||
//
|
|
||||||
// NewScope will return the parent scope, if one is given, or an empty ScopeMap
|
|
||||||
// if not.
|
|
||||||
func ScopeFromGraph(g *gg.Graph, parent Scope) Scope {
|
|
||||||
return &graphScope{
|
|
||||||
Graph: g,
|
|
||||||
parent: parent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *graphScope) Resolve(name string) (Value, error) {
|
|
||||||
|
|
||||||
var ggNameVal gg.Value
|
|
||||||
ggNameVal.Name = &name
|
|
||||||
|
|
||||||
log.Printf("resolving %q", name)
|
|
||||||
edgesIn := g.ValueIns(ggNameVal)
|
|
||||||
|
|
||||||
if l := len(edgesIn); l == 0 && g.parent != nil {
|
|
||||||
|
|
||||||
return g.parent.Resolve(name)
|
|
||||||
|
|
||||||
} else if l != 1 {
|
|
||||||
|
|
||||||
return nil, fmt.Errorf(
|
|
||||||
"%q must have exactly one input edge, found %d input edges",
|
|
||||||
name, l,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return CompileEdge(edgesIn[0], g)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *graphScope) NewScope() Scope {
|
|
||||||
|
|
||||||
if g.parent == nil {
|
|
||||||
return ScopeMap{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return g.parent
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
@ -6,9 +6,9 @@ import (
|
|||||||
"github.com/mediocregopher/ginger/gg"
|
"github.com/mediocregopher/ginger/gg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func globalOp(fn func(Value) (Value, error)) Value {
|
func globalFn(fn func(Value) (Value, error)) Value {
|
||||||
return Value{
|
return Value{
|
||||||
Operation: OperationFunc(func(in Value) Value {
|
Function: FunctionFunc(func(in Value) Value {
|
||||||
res, err := fn(in)
|
res, err := fn(in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -22,7 +22,7 @@ func globalOp(fn func(Value) (Value, error)) Value {
|
|||||||
// any operation in a ginger program.
|
// any operation in a ginger program.
|
||||||
var GlobalScope = ScopeMap{
|
var GlobalScope = ScopeMap{
|
||||||
|
|
||||||
"add": globalOp(func(val Value) (Value, error) {
|
"add": globalFn(func(val Value) (Value, error) {
|
||||||
|
|
||||||
var sum int64
|
var sum int64
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ var GlobalScope = ScopeMap{
|
|||||||
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
"tupEl": globalOp(func(val Value) (Value, error) {
|
"tupEl": globalFn(func(val Value) (Value, error) {
|
||||||
|
|
||||||
tup, i := val.Tuple[0], val.Tuple[1]
|
tup, i := val.Tuple[0], val.Tuple[1]
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ var GlobalScope = ScopeMap{
|
|||||||
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
"isZero": globalOp(func(val Value) (Value, error) {
|
"isZero": globalFn(func(val Value) (Value, error) {
|
||||||
|
|
||||||
if *val.Number == 0 {
|
if *val.Number == 0 {
|
||||||
one := int64(1)
|
one := int64(1)
|
||||||
|
22
vm/vm.go
22
vm/vm.go
@ -13,12 +13,12 @@ import (
|
|||||||
// ZeroValue is a Value with no fields set. It is equivalent to the 0-tuple.
|
// ZeroValue is a Value with no fields set. It is equivalent to the 0-tuple.
|
||||||
var ZeroValue Value
|
var ZeroValue Value
|
||||||
|
|
||||||
// Value extends a gg.Value to include Operations and Tuples as a possible
|
// Value extends a gg.Value to include Functions and Tuples as a possible
|
||||||
// types.
|
// types.
|
||||||
type Value struct {
|
type Value struct {
|
||||||
gg.Value
|
gg.Value
|
||||||
|
|
||||||
Operation
|
Function
|
||||||
Tuple []Value
|
Tuple []Value
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,8 +47,8 @@ func (v Value) Equal(v2g graph.Value) bool {
|
|||||||
case !v.Value.IsZero() || !v2.Value.IsZero():
|
case !v.Value.IsZero() || !v2.Value.IsZero():
|
||||||
return v.Value.Equal(v2.Value)
|
return v.Value.Equal(v2.Value)
|
||||||
|
|
||||||
case v.Operation != nil || v2.Operation != nil:
|
case v.Function != nil || v2.Function != nil:
|
||||||
// for now we say that Operations can't be compared. This will probably
|
// for now we say that Functions can't be compared. This will probably
|
||||||
// get revisted later.
|
// get revisted later.
|
||||||
return false
|
return false
|
||||||
|
|
||||||
@ -76,10 +76,10 @@ func (v Value) String() string {
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|
||||||
case v.Operation != nil:
|
case v.Function != nil:
|
||||||
|
|
||||||
// We can try to get better strings for ops later
|
// We can try to get better strings for ops later
|
||||||
return "<op>"
|
return "<fn>"
|
||||||
|
|
||||||
case !v.Value.IsZero():
|
case !v.Value.IsZero():
|
||||||
return v.Value.String()
|
return v.Value.String()
|
||||||
@ -100,12 +100,6 @@ func (v Value) String() string {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func nameVal(n string) Value {
|
|
||||||
var val Value
|
|
||||||
val.Name = &n
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
// EvaluateSource reads and parses the io.Reader as an operation, input is used
|
// EvaluateSource reads and parses the io.Reader as an operation, input is used
|
||||||
// as the argument to the operation, and the resultant value is returned.
|
// as the argument to the operation, and the resultant value is returned.
|
||||||
//
|
//
|
||||||
@ -119,11 +113,11 @@ func EvaluateSource(opSrc io.Reader, input Value, scope Scope) (Value, error) {
|
|||||||
return Value{}, err
|
return Value{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
op, err := OperationFromGraph(g, scope.NewScope())
|
fn, err := FunctionFromGraph(g, scope.NewScope())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Value{}, err
|
return Value{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return op.Perform(input), nil
|
return fn.Perform(input), nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user