ginger/vm/scope.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

128 lines
2.5 KiB
Go

package vm
import (
"fmt"
"github.com/mediocregopher/ginger/gg"
)
// Scope encapsulates a set of name->Value mappings.
type Scope interface {
// Resolve accepts a name and returns an Value.
Resolve(string) (Value, error)
// NewScope returns a new Scope which sub-operations within this Scope
// should use for themselves.
NewScope() Scope
}
// ScopeMap implements the Scope interface.
type ScopeMap map[string]Value
var _ Scope = ScopeMap{}
// Resolve uses the given name as a key into the ScopeMap map, and
// returns the Operation held there for the key, if any.
func (m ScopeMap) Resolve(name string) (Value, error) {
v, ok := m[name]
if !ok {
return Value{}, fmt.Errorf("%q not defined", name)
}
return v, nil
}
// NewScope returns the ScopeMap as-is.
func (m ScopeMap) NewScope() Scope {
return m
}
type scopeWith struct {
Scope // parent
name string
val Value
}
// ScopeWith returns a copy of the given Scope, except that evaluating the given
// name will always return the given Value.
func ScopeWith(scope Scope, name string, val Value) Scope {
return &scopeWith{
Scope: scope,
name: name,
val: val,
}
}
func (s *scopeWith) Resolve(name string) (Value, error) {
if name == s.name {
return s.val, nil
}
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
}
*/