ginger/vm/vm.go
Brian Picciano 2be865181d Complete refactor vm to not be as stupid
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.
2022-03-31 09:27:52 -06:00

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
}