diff --git a/eval/eval.go b/eval/eval.go index d540a1b6..58528bea 100644 --- a/eval/eval.go +++ b/eval/eval.go @@ -54,22 +54,24 @@ func NewEvaler(st *store.Store) *Evaler { searchPaths = []string{"/bin"} } + ev := &Evaler{nil, map[string]Namespace{}, searchPaths, st} + // Construct initial global namespace pid := String(strconv.Itoa(syscall.Getpid())) paths := NewList() paths.appendStrings(searchPaths) - global := Namespace{ - "pid": NewPtrVariable(pid), - "ok": NewPtrVariable(OK), - "true": NewPtrVariable(Bool(true)), - "false": NewPtrVariable(Bool(false)), - "paths": NewPtrVariable(paths), + ev.global = Namespace{ + "pid": NewRoVariable(pid), + "ok": NewRoVariable(OK), + "true": NewRoVariable(Bool(true)), + "false": NewRoVariable(Bool(false)), + "paths": NewRoVariable(PathList{&ev.searchPaths}), } for _, b := range builtinFns { - global[FnPrefix+b.Name] = NewPtrVariable(b) + ev.global[FnPrefix+b.Name] = NewRoVariable(b) } - return &Evaler{global, map[string]Namespace{}, searchPaths, st} + return ev } func (e *Evaler) AddModule(name string, ns Namespace) { @@ -263,7 +265,11 @@ func (ev *Evaler) Global() map[string]Variable { // returned. func (ec *EvalCtx) ResolveVar(ns, name string) Variable { if ns == "env" { - return newEnvVariable(name) + ev := envVariable{name} + if name == "PATH" { + return pathEnvVariable{ev, &ec.searchPaths} + } + return ev } if mod, ok := ec.modules[ns]; ok { return mod[name] diff --git a/eval/pathList.go b/eval/pathList.go new file mode 100644 index 00000000..d17f46d5 --- /dev/null +++ b/eval/pathList.go @@ -0,0 +1,41 @@ +package eval + +import ( + "errors" + + "github.com/elves/elvish/parse" +) + +// PathList wraps a list of search paths into a readonly list-like Value. +type PathList struct { + inner *[]string +} + +func (l PathList) Kind() string { + return "list" +} + +func (l PathList) Repr() string { + var b ListReprBuilder + for _, v := range *l.inner { + b.WriteElem(parse.Quote(v)) + } + return b.String() +} + +func (l PathList) IndexOne(idx Value) Value { + // XXX copied from index.go + i := intIndex(idx) + + if i < 0 { + i += len(*l.inner) + } + if i < 0 || i >= len(*l.inner) { + throw(ErrIndexOutOfRange) + } + return String((*l.inner)[i]) +} + +func (l PathList) IndexSet(idx, v Value) { + throw(errors.New("assignment to $paths not implemented; assign to $env:PATH instead")) +} diff --git a/eval/value.go b/eval/value.go index 9d53dfb5..2c62bf43 100644 --- a/eval/value.go +++ b/eval/value.go @@ -190,16 +190,32 @@ func (l List) appendStrings(ss []string) { } func (l List) Repr() string { - buf := new(bytes.Buffer) - buf.WriteRune('[') - for i, v := range *l.inner { - if i > 0 { - buf.WriteByte(' ') - } - buf.WriteString(v.Repr()) + var b ListReprBuilder + for _, v := range *l.inner { + b.WriteElem(v.Repr()) } - buf.WriteRune(']') - return buf.String() + return b.String() +} + +type ListReprBuilder struct { + buf bytes.Buffer +} + +func (b *ListReprBuilder) WriteElem(v string) { + if b.buf.Len() == 0 { + b.buf.WriteByte('[') + } else { + b.buf.WriteByte(' ') + } + b.buf.WriteString(v) +} + +func (b *ListReprBuilder) String() string { + if b.buf.Len() == 0 { + return "[" + } + b.buf.WriteByte(']') + return b.buf.String() } // Map is a map from string to Value. diff --git a/eval/variable.go b/eval/variable.go index 08088e3b..5b3f7998 100644 --- a/eval/variable.go +++ b/eval/variable.go @@ -3,6 +3,7 @@ package eval import ( "errors" "os" + "strings" ) var ( @@ -76,3 +77,15 @@ func (ev envVariable) Set(val Value) { func (ev envVariable) Get() Value { return String(os.Getenv(ev.name)) } + +type pathEnvVariable struct { + envVariable + ppaths *[]string +} + +func (pev pathEnvVariable) Set(val Value) { + s := ToString(val) + os.Setenv(pev.name, s) + paths := strings.Split(s, ":") + *pev.ppaths = paths +}