eval: Move all variable types to eval/vartypes.

This commit is contained in:
Qi Xiao 2018-01-04 22:54:04 +00:00
parent b4a49732c3
commit 0472d61747
10 changed files with 111 additions and 90 deletions

View File

@ -11,7 +11,7 @@ import (
func makeBuiltinNs() Ns {
ns := Ns{
"_": BlackholeVariable{},
"_": vartypes.NewBlackhole(),
"pid": vartypes.NewRoVariable(types.String(strconv.Itoa(syscall.Getpid()))),
"ok": vartypes.NewRoVariable(OK),
"true": vartypes.NewRoVariable(types.Bool(true)),

View File

@ -193,6 +193,6 @@ func (cp *compiler) lvaluesOne(n *parse.Indexing, msg string) (bool, LValuesOpFu
assocers[i+1] = assocer
}
}
return []vartypes.Variable{&elemVariable{variable, assocers, indicies, nil}}
return []vartypes.Variable{vartypes.NewElem(variable, assocers, indicies)}
}
}

View File

@ -240,8 +240,8 @@ func (cp *compiler) form(n *parse.Form) OpFunc {
for i, v := range saveVars {
// XXX(xiaq): If the variable to save is a elemVariable, save
// the outermost variable instead.
if elemVar, ok := v.(*elemVariable); ok {
v = elemVar.variable
if u := vartypes.GetUnderlyingOfElem(v); u != nil {
v = u
saveVars[i] = v
}
val := v.Get()
@ -376,7 +376,7 @@ func makeAssignmentOpFunc(variablesOp, restOp LValuesOp, valuesOp ValuesOp) OpFu
func fixNilVariables(vs []vartypes.Variable) {
for _, v := range vs {
if _, isBlackhole := v.(BlackholeVariable); isBlackhole {
if vartypes.IsBlackhole(v) {
continue
}
if v.Get() == nil {

View File

@ -102,7 +102,7 @@ func (ec *Frame) ResolveVar(ns, name string) vartypes.Variable {
return vartypes.NewRoVariable(ExternalCmd{name[:len(name)-len(FnSuffix)]})
}
case "E":
return envVariable{name}
return vartypes.NewEnv(name)
case "shared":
if ec.DaemonClient == nil {
throw(ErrStoreUnconnected)

View File

@ -1,70 +0,0 @@
package eval
import (
"errors"
"os"
"github.com/elves/elvish/eval/types"
"github.com/elves/elvish/eval/vartypes"
)
// elemVariable is a (arbitrary nested) element.
// XXX(xiaq): This is an ephemeral "variable" and is a bad hack.
type elemVariable struct {
variable vartypes.Variable
assocers []types.Assocer
indices []types.Value
setValue types.Value
}
func (ev *elemVariable) Set(v0 types.Value) error {
v := v0
// Evaluate the actual new value from inside out. See comments in
// compile_lvalue.go for how assignment of indexed variables work.
for i := len(ev.assocers) - 1; i >= 0; i-- {
v = ev.assocers[i].Assoc(ev.indices[i], v)
}
err := ev.variable.Set(v)
// XXX(xiaq): Remember the set value for use in Get.
ev.setValue = v0
return err
}
func (ev *elemVariable) Get() types.Value {
// XXX(xiaq): This is only called from fixNilVariables. We don't want to
// waste time accessing the variable, so we simply return the value that was
// set.
return ev.setValue
}
// envVariable represents an environment variable.
type envVariable struct {
name string
}
func (ev envVariable) Set(val types.Value) error {
os.Setenv(ev.name, types.ToString(val))
return nil
}
func (ev envVariable) Get() types.Value {
return types.String(os.Getenv(ev.name))
}
// ErrGetBlackhole is raised when attempting to get the value of a blackhole
// variable.
var ErrGetBlackhole = errors.New("cannot get blackhole variable")
// BlackholeVariable represents a blackhole variable. Assignments to a blackhole
// variable will be discarded, and getting a blackhole variable always returns
// an empty string.
type BlackholeVariable struct{}
func (bv BlackholeVariable) Set(types.Value) error {
return nil
}
func (bv BlackholeVariable) Get() types.Value {
// TODO: Return a special placeholder value.
return types.String("")
}

View File

@ -0,0 +1,27 @@
package vartypes
import "github.com/elves/elvish/eval/types"
type blackhole struct{}
func (blackhole) Set(types.Value) error {
return nil
}
func (blackhole) Get() types.Value {
// TODO: Return a special placeholder value.
return types.String("")
}
// NewBlackhole returns a blackhole variable. Assignments to a blackhole
// variable will be discarded, and getting a blackhole variable always returns
// an empty string.
func NewBlackhole() Variable {
return blackhole{}
}
// IsBlackhole returns whether the variable is a blackhole variable.
func IsBlackhole(v Variable) bool {
_, ok := v.(blackhole)
return ok
}

47
eval/vartypes/elem.go Normal file
View File

@ -0,0 +1,47 @@
package vartypes
import (
"github.com/elves/elvish/eval/types"
)
type elem struct {
variable Variable
assocers []types.Assocer
indices []types.Value
setValue types.Value
}
func (ev *elem) Set(v0 types.Value) error {
v := v0
// Evaluate the actual new value from inside out. See comments in
// compile_lvalue.go for how assignment of indexed variables work.
for i := len(ev.assocers) - 1; i >= 0; i-- {
v = ev.assocers[i].Assoc(ev.indices[i], v)
}
err := ev.variable.Set(v)
// XXX(xiaq): Remember the set value for use in Get.
ev.setValue = v0
return err
}
func (ev *elem) Get() types.Value {
// XXX(xiaq): This is only called from fixNilVariables. We don't want to
// waste time accessing the variable, so we simply return the value that was
// set.
return ev.setValue
}
// NewEleme represents an ephemeral variable that represents arbitrary nested
// elements.
func NewElem(v Variable, a []types.Assocer, i []types.Value) Variable {
return &elem{v, a, i, types.String("")}
}
// GetUnderlyingOfEleme gets the underlying variable from an element variable,
// or nil if the argument is not an element variable.
func GetUnderlyingOfElem(v Variable) Variable {
if ev, ok := v.(*elem); ok {
return ev.variable
}
return nil
}

25
eval/vartypes/env.go Normal file
View File

@ -0,0 +1,25 @@
package vartypes
import (
"os"
"github.com/elves/elvish/eval/types"
)
// envVariable represents an environment variable.
type envVariable struct {
name string
}
func (ev envVariable) Set(val types.Value) error {
os.Setenv(ev.name, types.ToString(val))
return nil
}
func (ev envVariable) Get() types.Value {
return types.String(os.Getenv(ev.name))
}
func NewEnv(name string) Variable {
return envVariable{name}
}

View File

@ -1,15 +1,14 @@
package eval
package vartypes
import (
"os"
"testing"
"github.com/elves/elvish/eval/types"
"github.com/elves/elvish/eval/vartypes"
)
func TestPtrVariable(t *testing.T) {
v := vartypes.NewPtrVariable(types.Bool(true))
v := NewPtrVariable(types.Bool(true))
if v.Get() != types.Bool(true) {
t.Errorf("PtrVariable.Get doesn't return initial value")
}
@ -22,14 +21,14 @@ func TestPtrVariable(t *testing.T) {
}
func TestValidatedPtrVariable(t *testing.T) {
v := vartypes.NewValidatedPtrVariable(types.Bool(true), vartypes.ShouldBeBool)
v := NewValidatedPtrVariable(types.Bool(true), ShouldBeBool)
if v.Set(types.String("233")) == nil {
t.Errorf("ValidatedPtrVariable.Set doesn't error when setting incompatible value")
}
}
func TestRoVariable(t *testing.T) {
v := vartypes.NewRoVariable(types.String("haha"))
v := NewRoVariable(types.String("haha"))
if v.Get() != types.String("haha") {
t.Errorf("RoVariable.Get doesn't return initial value")
}
@ -50,7 +49,7 @@ func TestCbVariable(t *testing.T) {
return nil
}
v := vartypes.NewCallbackVariable(set, get)
v := NewCallbackVariable(set, get)
if v.Get() != types.String("cb") {
t.Errorf("cbVariable doesn't return value from callback")
}
@ -69,7 +68,7 @@ func TestRoCbVariable(t *testing.T) {
getCalled = true
return types.String("cb")
}
v := vartypes.NewRoCallbackVariable(get)
v := NewRoCallbackVariable(get)
if v.Get() != types.String("cb") {
t.Errorf("roCbVariable doesn't return value from callback")
}

View File

@ -1,7 +0,0 @@
package vartypes
import "testing"
func TestVariable(t *testing.T) {
// TODO
}