2be865181d
This commit is the result of many days of picking vm apart and putting it back together again. The result is an implementation which separates compile and runtime into separate steps, and which functions (more) correctly in the face of recursion. Pretty much all aspects of vm have been modified or deleted, so it's not even really worth it to describe specific changes. Just pretend this is the original implementaiton and the old one was never done.
130 lines
2.7 KiB
Go
130 lines
2.7 KiB
Go
// Package vm implements the execution of gg.Graphs as programs.
|
|
package vm
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"github.com/mediocregopher/ginger/gg"
|
|
"github.com/mediocregopher/ginger/graph"
|
|
)
|
|
|
|
// ZeroValue is a Value with no fields set. It is equivalent to the 0-tuple.
|
|
var ZeroValue Value
|
|
|
|
// Value extends a gg.Value to include Operations and Tuples as a possible
|
|
// types.
|
|
type Value struct {
|
|
gg.Value
|
|
|
|
Operation
|
|
Tuple []Value
|
|
}
|
|
|
|
// Tuple returns a tuple Value comprising the given Values. Calling Tuple with
|
|
// no arguments returns ZeroValue.
|
|
func Tuple(vals ...Value) Value {
|
|
return Value{Tuple: vals}
|
|
}
|
|
|
|
// IsZero returns true if the Value is the zero value (aka the 0-tuple).
|
|
// LexerToken (within the gg.Value) is ignored for this check.
|
|
func (v Value) IsZero() bool {
|
|
return v.Equal(ZeroValue)
|
|
}
|
|
|
|
// Equal returns true if the passed in Value is equivalent, ignoring the
|
|
// LexerToken on either Value.
|
|
//
|
|
// Will panic if the passed in v2 is not a Value from this package.
|
|
func (v Value) Equal(v2g graph.Value) bool {
|
|
|
|
v2 := v2g.(Value)
|
|
|
|
switch {
|
|
|
|
case !v.Value.IsZero() || !v2.Value.IsZero():
|
|
return v.Value.Equal(v2.Value)
|
|
|
|
case v.Operation != nil || v2.Operation != nil:
|
|
// for now we say that Operations can't be compared. This will probably
|
|
// get revisted later.
|
|
return false
|
|
|
|
case len(v.Tuple) == len(v2.Tuple):
|
|
|
|
for i := range v.Tuple {
|
|
if !v.Tuple[i].Equal(v2.Tuple[i]) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
// if both were the zero value then both tuples would have the same
|
|
// length (0), which is covered by the previous check. So anything left
|
|
// over must be tuples with differing lengths.
|
|
return false
|
|
}
|
|
|
|
}
|
|
|
|
func (v Value) String() string {
|
|
|
|
switch {
|
|
|
|
case v.Operation != nil:
|
|
|
|
// We can try to get better strings for ops later
|
|
return "<op>"
|
|
|
|
case !v.Value.IsZero():
|
|
return v.Value.String()
|
|
|
|
default:
|
|
|
|
// we consider zero value to be the 0-tuple
|
|
|
|
strs := make([]string, len(v.Tuple))
|
|
|
|
for i := range v.Tuple {
|
|
strs[i] = v.Tuple[i].String()
|
|
}
|
|
|
|
return fmt.Sprintf("(%s)", strings.Join(strs, ", "))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
// as the argument to the operation, and the resultant value is returned.
|
|
//
|
|
// scope contains pre-defined operations and values which are available during
|
|
// the evaluation.
|
|
func EvaluateSource(opSrc io.Reader, input Value, scope Scope) (Value, error) {
|
|
lexer := gg.NewLexer(opSrc)
|
|
|
|
g, err := gg.DecodeLexer(lexer)
|
|
if err != nil {
|
|
return Value{}, err
|
|
}
|
|
|
|
op, err := OperationFromGraph(g, scope.NewScope())
|
|
|
|
if err != nil {
|
|
return Value{}, err
|
|
}
|
|
|
|
return op.Perform(input), nil
|
|
}
|