From c112223611ab637f4a1a7b8b1328888753845a3f Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Sat, 25 Apr 2020 12:50:07 +0100 Subject: [PATCH] pkg/eval: Simplify API of "src". Remove the Type and Root fields, and add a new IsFile field. --- pkg/eval/builtin_fn_debug.go | 53 ++++++++++----------------- pkg/eval/builtin_special.go | 2 +- pkg/eval/source.go | 70 ++++++++++-------------------------- pkg/eval/source_test.go | 22 ++++++------ 4 files changed, 49 insertions(+), 98 deletions(-) diff --git a/pkg/eval/builtin_fn_debug.go b/pkg/eval/builtin_fn_debug.go index 62cb667b..dd15f35f 100644 --- a/pkg/eval/builtin_fn_debug.go +++ b/pkg/eval/builtin_fn_debug.go @@ -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 diff --git a/pkg/eval/builtin_special.go b/pkg/eval/builtin_special.go index a0067cf9..26801215 100644 --- a/pkg/eval/builtin_special.go +++ b/pkg/eval/builtin_special.go @@ -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 diff --git a/pkg/eval/source.go b/pkg/eval/source.go index de48f776..0349d4c3 100644 --- a/pkg/eval/source.go +++ b/pkg/eval/source.go @@ -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, parse.Quote(src.Name), src.Root) + "", 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") } diff --git a/pkg/eval/source_test.go b/pkg/eval/source_test.go index 179e537c..450f9463 100644 --- a/pkg/eval/source_test.go +++ b/pkg/eval/source_test.go @@ -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(""). - AllKeys("type", "name", "root", "code"). - Index("type", "interactive"). + Repr(""). + 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) }