eval: Make builtin namespace a field of the Evaler.

This commit is contained in:
Qi Xiao 2017-06-24 18:08:47 +02:00
parent 8efbde49b9
commit e06234efb0
8 changed files with 53 additions and 35 deletions

View File

@ -120,7 +120,7 @@ func hasProperPrefix(s, p string) bool {
func iterateVariables(ev *eval.Evaler, ns string, f func(string)) {
switch ns {
case "":
for varname := range eval.Builtin() {
for varname := range ev.Builtin {
f(varname)
}
for varname := range ev.Global {

View File

@ -24,11 +24,12 @@ func goodFormHead(head string, ed *Editor) bool {
// XXX don't stat twice
return util.IsExecutable(head) || isDir(head)
} else {
ev := ed.evaler
explode, ns, name := eval.ParseVariable(head)
if !explode {
switch ns {
case "":
if eval.Builtin()[eval.FnPrefix+name] != nil || ed.evaler.Global[eval.FnPrefix+name] != nil {
if ev.Builtin[eval.FnPrefix+name] != nil || ev.Global[eval.FnPrefix+name] != nil {
return true
}
case "e":
@ -36,7 +37,7 @@ func goodFormHead(head string, ed *Editor) bool {
return true
}
default:
if ed.evaler.Modules[ns] != nil && ed.evaler.Modules[ns][eval.FnPrefix+name] != nil {
if ev.Modules[ns] != nil && ev.Modules[ns][eval.FnPrefix+name] != nil {
return true
}
}

View File

@ -215,10 +215,6 @@ func init() {
{"-ifaddrs", _ifaddrs},
}
for _, b := range builtinFns {
builtinNamespace[FnPrefix+b.Name] = NewRoVariable(b)
}
// For rand and randint.
rand.Seed(time.Now().UTC().UnixNano())
}

View File

@ -3,13 +3,21 @@ package eval
import (
"strconv"
"syscall"
"github.com/elves/elvish/daemon/api"
)
var builtinNamespace = Namespace{
"pid": NewRoVariable(String(strconv.Itoa(syscall.Getpid()))),
"ok": NewRoVariable(OK),
"true": NewRoVariable(Bool(true)),
"false": NewRoVariable(Bool(false)),
"paths": &EnvPathList{envName: "PATH"},
"pwd": PwdVariable{},
func makeBuiltinNamespace(daemon *api.Client) Namespace {
ns := Namespace{
"pid": NewRoVariable(String(strconv.Itoa(syscall.Getpid()))),
"ok": NewRoVariable(OK),
"true": NewRoVariable(Bool(true)),
"false": NewRoVariable(Bool(false)),
"paths": &EnvPathList{envName: "PATH"},
"pwd": PwdVariable{daemon},
}
for _, b := range builtinFns {
ns[FnPrefix+b.Name] = NewRoVariable(b)
}
return ns
}

View File

@ -15,6 +15,8 @@ type scope map[string]bool
// compiler maintains the set of states needed when compiling a single source
// file.
type compiler struct {
// Builtin scope.
builtin scope
// Lexical scopes.
scopes []scope
// Variables captured from outer scopes.
@ -25,8 +27,8 @@ type compiler struct {
name, text string
}
func compile(sc scope, n *parse.Chunk, name, text string) (op Op, err error) {
cp := &compiler{[]scope{sc}, scope{}, 0, 0, name, text}
func compile(b, g scope, n *parse.Chunk, name, text string) (op Op, err error) {
cp := &compiler{b, []scope{g}, scope{}, 0, 0, name, text}
defer util.Catch(&err)
return cp.chunkOp(n), nil
}
@ -85,7 +87,7 @@ func (cp *compiler) registerVariableGet(qname string) bool {
}
// Find in builtin scope
if ns == "" || ns == "builtin" {
_, ok := builtinNamespace[name]
_, ok := cp.builtin[name]
if ok {
return true
}

View File

@ -38,6 +38,7 @@ type Namespace map[string]Variable
// Evaler is used to evaluate elvish sources. It maintains runtime context
// shared among all evalCtx instances.
type Evaler struct {
Builtin Namespace
Global Namespace
Modules map[string]Namespace
Daemon *api.Client
@ -70,11 +71,20 @@ func NewEvaler(daemon *api.Client, toSpawn *daemon.Daemon, dataDir string) *Eval
"daemon": makeDaemonNamespace(daemon),
}
return &Evaler{Namespace{}, modules, daemon, toSpawn, nil, dataDir, nil}
return &Evaler{
Builtin: makeBuiltinNamespace(daemon),
Global: Namespace{},
Modules: modules,
Daemon: daemon,
ToSpawn: toSpawn,
Editor: nil,
DataDir: dataDir,
intCh: nil,
}
}
func (ev *Evaler) searchPaths() []string {
return builtinNamespace["paths"].(*EnvPathList).get()
return ev.Builtin["paths"].(*EnvPathList).get()
}
const (
@ -256,7 +266,7 @@ func summarize(text string) string {
// Compile compiles elvish code in the global scope. If the error is not nil, it
// always has type CompilationError.
func (ev *Evaler) Compile(n *parse.Chunk, name, text string) (Op, error) {
return compile(makeScope(ev.Global), n, name, text)
return compile(makeScope(ev.Builtin), makeScope(ev.Global), n, name, text)
}
// PEval evaluates an op in a protected environment so that calls to errorf are
@ -350,11 +360,6 @@ func (ev *Evaler) Source(fname string) error {
return ev.SourceText(fname, src)
}
// Builtin returns the builtin namespace.
func Builtin() Namespace {
return builtinNamespace
}
// ErrStoreUnconnected is thrown by ResolveVar when a shared: variable needs to
// be resolved but the store is not connected.
var ErrStoreUnconnected = errors.New("store unconnected")
@ -368,7 +373,7 @@ func (ec *EvalCtx) ResolveVar(ns, name string) Variable {
case "up":
return ec.up[name]
case "builtin":
return builtinNamespace[name]
return ec.Builtin[name]
case "":
if v := ec.getLocal(name); v != nil {
return v
@ -376,7 +381,7 @@ func (ec *EvalCtx) ResolveVar(ns, name string) Variable {
if v, ok := ec.up[name]; ok {
return v
}
return builtinNamespace[name]
return ec.Builtin[name]
case "e":
if strings.HasPrefix(name, FnPrefix) {
return NewRoVariable(ExternalCmd{name[len(FnPrefix):]})

View File

@ -13,10 +13,11 @@ import (
"github.com/elves/elvish/util"
)
func TestBuiltinNamespace(t *testing.T) {
func TestBuiltinPid(t *testing.T) {
pid := strconv.Itoa(syscall.Getpid())
if ToString(builtinNamespace["pid"].Get()) != pid {
t.Errorf(`ev.builtin["pid"] = %v, want %v`, builtinNamespace["pid"], pid)
builtinPid := ToString(makeBuiltinNamespace(nil)["pid"].Get())
if builtinPid != pid {
t.Errorf(`ev.builtin["pid"] = %v, want %v`, builtinPid, pid)
}
}

View File

@ -1,10 +1,16 @@
package eval
import "os"
import (
"os"
"github.com/elves/elvish/daemon/api"
)
// PwdVariable is a variable whose value always reflects the current working
// directory. Setting it changes the current working directory.
type PwdVariable struct{}
type PwdVariable struct {
daemon *api.Client
}
var _ Variable = PwdVariable{}
@ -14,12 +20,11 @@ func (PwdVariable) Get() Value {
return String(pwd)
}
// TODO(xiaq): Setting $pwd should also record the new directory in the history.
func (PwdVariable) Set(v Value) {
func (pwd PwdVariable) Set(v Value) {
path, ok := v.(String)
if !ok {
throw(ErrPathMustBeString)
}
err := Chdir(string(path), nil)
err := Chdir(string(path), pwd.daemon)
maybeThrow(err)
}