pkg/eval: Simplify API of "src".

Remove the Type and Root fields, and add a new IsFile field.
This commit is contained in:
Qi Xiao 2020-04-25 12:50:07 +01:00
parent 905447eda5
commit c112223611
4 changed files with 49 additions and 98 deletions

View File

@ -15,49 +15,34 @@ import (
// Output a map-like value describing the current source being evaluated. The value
// contains the following fields:
//
// - `type`, which can be one of `interactive`, `script` or `module`;
// - `name`, a unique name of the current source. If the source originates from a
// file, it is the full path of the file.
//
// - `name`, which is set to the name under which a script is executed or a
// module is imported. It is an empty string when `type` = `interactive`;
// - `code`, the full body of the current source.
//
// - `path`, which is the path to the current source. It is an empty string when
// `type` = `interactive`;
//
// - `code`, which is the full body of the current source.
// - `is-file`, whether the source originates from a file.
//
// Examples:
//
// ```elvish-transcript
// ~> put (src)[type name path code]
// ▶ interactive
// ▶ ''
// ▶ ''
// ▶ 'put (src)[type name path code]'
// ~> echo 'put (src)[type name path code]' > foo.elv
// ~> elvish foo.elv
// ▶ script
// ▶ foo.elv
// ▶ /home/xiaq/foo.elv
// ▶ "put (src)[type name path code]\n"
// ~> echo 'put (src)[type name path code]' > ~/.elvish/lib/m.elv
// ~> use m
// ▶ module
// ▶ m
// ▶ /home/xiaq/.elvish/lib/m.elv
// ▶ "put (src)[type name path code]\n"
// ```
// ~> put (src)[name code is-file]
// ▶ '[tty]'
// ▶ 'put (src)[name code is-file]'
// ▶ $false
// ~> echo 'put (src)[name code is-file]' > show-src.elv
// ~> elvish show-src.elv
// ▶ /home/elf/show-src.elv
// ▶ "put (src)[name code is-file]\n"
// ▶ $true
//
// Note: this builtin always returns information of the source of the **calling
// function**. Example:
// Note: this builtin always returns information of the source of the function
// calling `src`. Consider the following example:
//
// ```elvish-transcript
// ~> echo 'fn f { put (src)[type name path code] }' > ~/.elvish/lib/n.elv
// ~> use n
// ~> n:f
// ▶ module
// ▶ n
// ▶ /home/xiaq/.elvish/lib/n.elv
// ▶ "fn f { put (src)[type name path code] }\n"
// ~> echo 'fn show { put (src)[name] }' > ~/.elvish/lib/src-util.elv
// ~> use src-util
// ~> src-util:show
// ▶ /home/elf/.elvish/lib/src-util.elv
// ```
//elvdoc:fn -gc

View File

@ -259,7 +259,7 @@ func (op useOp) invoke(fm *Frame) error {
func loadModule(fm *Frame, r diag.Ranger, spec string) (Ns, error) {
if strings.HasPrefix(spec, "./") || strings.HasPrefix(spec, "../") {
var dir string
if fm.srcMeta.Type == FileSource {
if fm.srcMeta.IsFile {
dir = filepath.Dir(fm.srcMeta.Name)
} else {
var err error

View File

@ -2,7 +2,6 @@ package eval
import (
"fmt"
"strconv"
"github.com/elves/elvish/pkg/parse"
"github.com/elves/elvish/pkg/util"
@ -11,36 +10,36 @@ import (
// Source describes a piece of source code.
type Source struct {
Type SourceType
Name string
Root bool
Code string
IsFile bool
}
// NewInteractiveSource returns a Source for a piece of code entered
// interactively.
func NewInteractiveSource(name, code string) *Source {
return &Source{InteractiveSource, "[tty]", true, code}
return &Source{Name: "[tty]", Code: code}
}
// NewScriptSource returns a Source for a piece of code used as a script.
func NewScriptSource(path, code string) *Source {
return &Source{FileSource, path, true, code}
return &Source{Name: path, Code: code, IsFile: true}
}
// NewModuleSource returns a Source for a piece of code used as a module.
func NewModuleSource(path, code string) *Source {
return &Source{FileSource, path, false, code}
return &Source{Name: path, Code: code, IsFile: true}
}
// NewInternalGoSource returns a Source for use as a placeholder when calling Elvish
// functions from Go code. It has no associated code.
func NewInternalGoSource(name string) *Source {
return &Source{InternalGoSource, name, true, ""}
return &Source{Name: name}
}
func NewInternalElvishSource(root bool, name, code string) *Source {
return &Source{InternalElvishSource, name, root, code}
return &Source{Name: name, Code: code}
}
func (src *Source) Kind() string {
@ -48,12 +47,15 @@ func (src *Source) Kind() string {
}
func (src *Source) Hash() uint32 {
var root uint32
if src.Root {
root = 1
return hash.DJB(
hash.String(src.Name), hash.String(src.Code), hashBool(src.IsFile))
}
func hashBool(b bool) uint32 {
if b {
return 1
}
return hash.DJB(uint32(src.Type),
hash.String(src.Name), root, hash.String(src.Code))
return 0
}
func (src *Source) Equal(other interface{}) bool {
@ -65,56 +67,22 @@ func (src *Source) Equal(other interface{}) bool {
func (src *Source) Repr(int) string {
return fmt.Sprintf(
"<src type:%s name:%s root:$%v code:...>",
src.Type, parse.Quote(src.Name), src.Root)
"<src name:%s code:... is-file:$%v>", parse.Quote(src.Name), src.IsFile)
}
func (src *Source) Index(k interface{}) (interface{}, bool) {
switch k {
case "type":
return src.Type.String(), true
case "name":
return src.Name, true
case "root":
return src.Root, true
case "code":
return src.Code, true
case "is-file":
return src.IsFile, true
default:
return nil, false
}
}
func (src *Source) IterateKeys(f func(interface{}) bool) {
util.Feed(f, "type", "name", "root", "code")
}
// SourceType records the type of a piece of source code.
type SourceType int
const (
InvalidSource SourceType = iota
// A special value used for the Frame when calling Elvish functions from Go.
// This is the only sourceType without associated code.
InternalGoSource
// Code from an internal buffer.
InternalElvishSource
// Code entered interactively.
InteractiveSource
// Code from a file.
FileSource
)
func (t SourceType) String() string {
switch t {
case InternalGoSource:
return "internal-go"
case InternalElvishSource:
return "internal-elvish"
case InteractiveSource:
return "interactive"
case FileSource:
return "file"
default:
return "bad type " + strconv.Itoa(int(t))
}
util.Feed(f, "name", "code", "is-file")
}

View File

@ -10,23 +10,21 @@ import (
func TestSourceAsValue(t *testing.T) {
vals.TestValue(t, NewInteractiveSource("[tty]", "echo")).
Kind("map").
Hash(hash.DJB(uint32(InteractiveSource),
hash.String("[tty]"), 1, hash.String("echo"))).
Hash(hash.DJB(hash.String("[tty]"), hash.String("echo"), 0)).
Equal(NewInteractiveSource("[tty]", "echo")).
NotEqual(NewInteractiveSource("[tty]", "put")).
Repr("<src type:interactive name:'[tty]' root:$true code:...>").
AllKeys("type", "name", "root", "code").
Index("type", "interactive").
Repr("<src name:'[tty]' code:... is-file:$false>").
AllKeys("name", "code", "is-file").
Index("name", "[tty]").
Index("root", true).
Index("code", "echo")
Index("code", "echo").
Index("is-file", false)
vals.TestValue(t, NewInternalGoSource("[test]")).
Index("type", "internal-go")
Index("is-file", false)
vals.TestValue(t, NewInternalElvishSource(true, "[test]", "echo")).
Index("type", "internal-elvish")
Index("is-file", false)
vals.TestValue(t, NewScriptSource("/fake/path", "echo")).
Index("type", "file")
vals.TestValue(t, &Source{Type: -1}).
Index("type", "bad type -1")
Index("is-file", true)
}