mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-12 17:27:50 +08:00
Implement module file search mechanism for "use"
This resolves issue #44.
This commit is contained in:
parent
5fda0bc0a4
commit
1d59566aaf
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/elves/elvish/parse"
|
||||
)
|
||||
|
@ -313,8 +314,16 @@ func compileUse(cp *Compiler, fn *parse.FormNode) exitusOp {
|
|||
default:
|
||||
cp.errorf(fn.Args.Nodes[2].Pos, "superfluous argument")
|
||||
}
|
||||
// TODO(xiaq): File name should be relative to the current source when it
|
||||
// starts with . or .. and relative to ~/.elvish otherwise
|
||||
switch {
|
||||
case strings.HasPrefix(fname, "/"):
|
||||
// Absolute file name, do nothing
|
||||
case strings.HasPrefix(fname, "./") || strings.HasPrefix(fname, "../"):
|
||||
// File name relative to current source
|
||||
fname = path.Clean(path.Join(cp.dir, fname))
|
||||
default:
|
||||
// File name relative to data dir
|
||||
fname = path.Clean(path.Join(cp.dataDir, fname))
|
||||
}
|
||||
src, err := readFileUTF8(fname)
|
||||
if err != nil {
|
||||
cp.errorf(fnameNode.Pos, "cannot read module: %s", err.Error())
|
||||
|
@ -326,8 +335,8 @@ func compileUse(cp *Compiler, fn *parse.FormNode) exitusOp {
|
|||
cp.errorf(fnameNode.Pos, "cannot parse module: %s", err.Error())
|
||||
}
|
||||
|
||||
newCp := NewCompiler(cp.builtin)
|
||||
op, err := newCp.Compile(fname, src, cn)
|
||||
newCp := NewCompiler(cp.builtin, cp.dataDir)
|
||||
op, err := newCp.Compile(fname, src, path.Dir(fname), cn)
|
||||
if err != nil {
|
||||
// TODO(xiaq): Pretty print
|
||||
cp.errorf(fnameNode.Pos, "cannot compile module: %s", err.Error())
|
||||
|
@ -337,7 +346,7 @@ func compileUse(cp *Compiler, fn *parse.FormNode) exitusOp {
|
|||
|
||||
return func(ev *Evaluator) exitus {
|
||||
// XXX(xiaq): Should use some part of ev
|
||||
newEv := NewEvaluator(ev.store)
|
||||
newEv := NewEvaluator(ev.store, cp.dataDir)
|
||||
op(newEv)
|
||||
ev.mod[modname] = newEv.local
|
||||
return success
|
||||
|
|
|
@ -18,25 +18,26 @@ type Compiler struct {
|
|||
scopes []staticNS
|
||||
up staticNS
|
||||
mod map[string]staticNS
|
||||
dataDir string
|
||||
compilerEphemeral
|
||||
}
|
||||
|
||||
// compilerEphemeral wraps the ephemeral parts of a Compiler, namely the parts
|
||||
// only valid through one startCompile-stopCompile cycle.
|
||||
type compilerEphemeral struct {
|
||||
name, text string
|
||||
name, text, dir string
|
||||
}
|
||||
|
||||
// NewCompiler returns a new compiler.
|
||||
func NewCompiler(bi staticNS) *Compiler {
|
||||
func NewCompiler(bi staticNS, dataDir string) *Compiler {
|
||||
return &Compiler{
|
||||
bi, []staticNS{staticNS{}}, staticNS{}, map[string]staticNS{},
|
||||
bi, []staticNS{staticNS{}}, staticNS{}, map[string]staticNS{}, dataDir,
|
||||
compilerEphemeral{},
|
||||
}
|
||||
}
|
||||
|
||||
func (cp *Compiler) startCompile(name, text string) {
|
||||
cp.compilerEphemeral = compilerEphemeral{name, text}
|
||||
func (cp *Compiler) startCompile(name, text, dir string) {
|
||||
cp.compilerEphemeral = compilerEphemeral{name, text, dir}
|
||||
}
|
||||
|
||||
func (cp *Compiler) stopCompile() {
|
||||
|
@ -45,8 +46,8 @@ func (cp *Compiler) stopCompile() {
|
|||
|
||||
// Compile compiles a ChunkNode into an Op. The supplied name and text are used
|
||||
// in diagnostic messages.
|
||||
func (cp *Compiler) Compile(name, text string, n *parse.ChunkNode) (op Op, err error) {
|
||||
cp.startCompile(name, text)
|
||||
func (cp *Compiler) Compile(name, text, dir string, n *parse.ChunkNode) (op Op, err error) {
|
||||
cp.startCompile(name, text, dir)
|
||||
defer cp.stopCompile()
|
||||
defer errutil.Catch(&err)
|
||||
return cp.chunk(n), nil
|
||||
|
|
15
eval/eval.go
15
eval/eval.go
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
@ -55,7 +56,7 @@ func statusOk(vs []Value) bool {
|
|||
}
|
||||
|
||||
// NewEvaluator creates a new top-level Evaluator.
|
||||
func NewEvaluator(st *store.Store) *Evaluator {
|
||||
func NewEvaluator(st *store.Store, dataDir string) *Evaluator {
|
||||
pid := str(strconv.Itoa(syscall.Getpid()))
|
||||
bi := ns{
|
||||
"pid": newInternalVariableWithType(pid),
|
||||
|
@ -67,7 +68,7 @@ func NewEvaluator(st *store.Store) *Evaluator {
|
|||
bi["fn-"+b.Name] = newInternalVariableWithType(b)
|
||||
}
|
||||
ev := &Evaluator{
|
||||
Compiler: NewCompiler(makeCompilerScope(bi)),
|
||||
Compiler: NewCompiler(makeCompilerScope(bi), dataDir),
|
||||
local: ns{},
|
||||
up: ns{},
|
||||
builtin: bi,
|
||||
|
@ -151,8 +152,8 @@ func makeCompilerScope(s ns) staticNS {
|
|||
|
||||
// Eval evaluates a chunk node n. The supplied name and text are used in
|
||||
// diagnostic messages.
|
||||
func (ev *Evaluator) Eval(name, text string, n *parse.ChunkNode) error {
|
||||
op, err := ev.Compiler.Compile(name, text, n)
|
||||
func (ev *Evaluator) Eval(name, text, dir string, n *parse.ChunkNode) error {
|
||||
op, err := ev.Compiler.Compile(name, text, dir, n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -210,12 +211,12 @@ func (ev *Evaluator) applyPortOps(ports []portOp) {
|
|||
}
|
||||
}
|
||||
|
||||
func (ev *Evaluator) SourceText(name, src string) error {
|
||||
func (ev *Evaluator) SourceText(name, src, dir string) error {
|
||||
n, err := parse.Parse(name, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ev.Eval(name, src, n)
|
||||
return ev.Eval(name, src, dir, n)
|
||||
}
|
||||
|
||||
func readFileUTF8(fname string) (string, error) {
|
||||
|
@ -235,7 +236,7 @@ func (ev *Evaluator) Source(fname string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ev.SourceText(fname, src)
|
||||
return ev.SourceText(fname, src, path.Dir(fname))
|
||||
}
|
||||
|
||||
// ResolveVar resolves a variable. When the variable cannot be found, nil is
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func TestNewEvaluator(t *testing.T) {
|
||||
ev := NewEvaluator(nil)
|
||||
ev := NewEvaluator(nil, "")
|
||||
pid := strconv.Itoa(syscall.Getpid())
|
||||
if toString(ev.builtin["pid"].Get()) != pid {
|
||||
t.Errorf(`ev.builtin["pid"] = %v, want %v`, ev.builtin["pid"], pid)
|
||||
|
@ -132,7 +132,7 @@ func TestEval(t *testing.T) {
|
|||
for _, tt := range evalTests {
|
||||
n := mustParse(name, tt.text)
|
||||
|
||||
ev := NewEvaluator(nil)
|
||||
ev := NewEvaluator(nil, ".")
|
||||
out := make(chan Value, len(tt.wanted))
|
||||
outs := []Value{}
|
||||
exhausted := make(chan struct{})
|
||||
|
@ -145,7 +145,7 @@ func TestEval(t *testing.T) {
|
|||
|
||||
ev.ports[1].ch = out
|
||||
|
||||
e := ev.Eval(name, tt.text, n)
|
||||
e := ev.Eval(name, tt.text, ".", n)
|
||||
close(out)
|
||||
<-exhausted
|
||||
if tt.wantError {
|
||||
|
|
16
main.go
16
main.go
|
@ -30,12 +30,20 @@ func newEvaluator() *eval.Evaluator {
|
|||
}
|
||||
}()
|
||||
|
||||
st, err := store.NewStore()
|
||||
dataDir, err := store.EnsureDataDir()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Warning: cannot connect to store:", err)
|
||||
fmt.Fprintln(os.Stderr, "Warning: cannot create data dir ~/.elvish")
|
||||
}
|
||||
|
||||
ev := eval.NewEvaluator(st)
|
||||
var st *store.Store
|
||||
if err == nil {
|
||||
st, err = store.NewStore(dataDir)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Warning: cannot connect to store:", err)
|
||||
}
|
||||
}
|
||||
|
||||
ev := eval.NewEvaluator(st, dataDir)
|
||||
ev.SetChanOut(ch)
|
||||
return ev
|
||||
}
|
||||
|
@ -105,7 +113,7 @@ func interact() {
|
|||
printError(err)
|
||||
|
||||
if err == nil {
|
||||
err := ev.Eval(name, lr.Line, n)
|
||||
err := ev.Eval(name, lr.Line, ".", n)
|
||||
printError(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,19 +15,15 @@ type Store struct {
|
|||
var createTable = map[string]string{}
|
||||
|
||||
// DefaultDB returns the default database for storage.
|
||||
func DefaultDB() (*sql.DB, error) {
|
||||
ddir, err := EnsureDataDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
uri := "file:" + url.QueryEscape(ddir+"/db") +
|
||||
func DefaultDB(dataDir string) (*sql.DB, error) {
|
||||
uri := "file:" + url.QueryEscape(dataDir+"/db") +
|
||||
"?mode=rwc&cache=shared&vfs=unix-dotfile"
|
||||
return sql.Open("sqlite3", uri)
|
||||
}
|
||||
|
||||
// NewStore creates a new Store with the default database.
|
||||
func NewStore() (*Store, error) {
|
||||
db, err := DefaultDB()
|
||||
func NewStore(dataDir string) (*Store, error) {
|
||||
db, err := DefaultDB(dataDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -24,7 +24,13 @@ func init() {
|
|||
}
|
||||
|
||||
func TestNewStore(t *testing.T) {
|
||||
_, err := NewStore()
|
||||
// XXX(xiaq): Also tests EnsureDataDir
|
||||
dataDir, err := EnsureDataDir()
|
||||
if err != nil {
|
||||
t.Errorf("EnsureDataDir() -> (*, %v), want (*, <nil>)", err)
|
||||
}
|
||||
|
||||
_, err = NewStore(dataDir)
|
||||
if err != nil {
|
||||
t.Errorf("NewStore() -> (*, %v), want (*, <nil>)", err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user