diff --git a/eval/builtin_fn.go b/eval/builtin_fn.go index 10146b2b..0b6d169e 100644 --- a/eval/builtin_fn.go +++ b/eval/builtin_fn.go @@ -11,7 +11,13 @@ import ( ) var ( - ErrArgs = errors.New("args error") + // ErrArgs is thrown when a builtin function gets erroneous arguments. + // + // TODO(xiaq): Replace this single error type with multiple types that carry + // richer error information. + ErrArgs = errors.New("args error") + // ErrNoOptAccepted is thrown when a builtin function that does not accept + // any options gets passed options. ErrNoOptAccepted = errors.New("function does not accept any options") ) @@ -73,6 +79,9 @@ type optionsPtr interface { SetDefaultOptions() } +// Inputs is the type that the last parameter of a builtin function can take. +// When that is the case, it is a callback to get inputs. See the doc of +// BuiltinFn for details. type Inputs func(func(interface{})) var ( diff --git a/eval/builtin_fn_cmd.go b/eval/builtin_fn_cmd.go index 4a78c0d3..d1932d62 100644 --- a/eval/builtin_fn_cmd.go +++ b/eval/builtin_fn_cmd.go @@ -9,8 +9,6 @@ import ( // Command and process control. -var ErrNotInSameGroup = errors.New("not in the same process group") - func init() { addBuiltinFns(map[string]interface{}{ // Command resolution diff --git a/eval/builtin_fn_cmd_unix.go b/eval/builtin_fn_cmd_unix.go index 043d2f74..858f503d 100644 --- a/eval/builtin_fn_cmd_unix.go +++ b/eval/builtin_fn_cmd_unix.go @@ -3,6 +3,7 @@ package eval import ( + "errors" "os" "os/exec" "strconv" @@ -12,6 +13,10 @@ import ( "github.com/elves/elvish/sys" ) +// ErrNotInSameProcessGroup is thrown when the process IDs passed to fg are not +// in the same process group. +var ErrNotInSameProcessGroup = errors.New("not in the same process group") + func execFn(fm *Frame, args ...interface{}) error { var argstrings []string if len(args) == 0 { @@ -47,7 +52,7 @@ func fg(pids ...int) error { if i == 0 { thepgid = pgid } else if pgid != thepgid { - return ErrNotInSameGroup + return ErrNotInSameProcessGroup } } diff --git a/eval/builtin_fn_container.go b/eval/builtin_fn_container.go index d134b083..259c4ee2 100644 --- a/eval/builtin_fn_container.go +++ b/eval/builtin_fn_container.go @@ -177,17 +177,16 @@ func hasKey(container, key interface{}) (bool, error) { // XXX(xiaq): Not all types that implement Lener have numerical indices _, err := vals.ConvertListIndex(key, len) return err == nil, nil - } else { - var found bool - err := vals.IterateKeys(container, func(k interface{}) bool { - if key == k { - found = true - } - return !found - }) - if err == nil { - return found, nil + } + var found bool + err := vals.IterateKeys(container, func(k interface{}) bool { + if key == k { + found = true } + return !found + }) + if err == nil { + return found, nil } return false, fmt.Errorf("couldn't get key or index of type '%s'", vals.Kind(container)) } diff --git a/eval/builtin_fn_fs.go b/eval/builtin_fn_fs.go index 8bd848dd..4c036b61 100644 --- a/eval/builtin_fn_fs.go +++ b/eval/builtin_fn_fs.go @@ -12,6 +12,7 @@ import ( // Filesystem. +// ErrStoreNotConnected is thrown by dir-history when the store is not connected. var ErrStoreNotConnected = errors.New("store not connected") func init() { diff --git a/eval/builtin_fn_io.go b/eval/builtin_fn_io.go index a9acbfac..df2ac9b6 100644 --- a/eval/builtin_fn_io.go +++ b/eval/builtin_fn_io.go @@ -208,7 +208,7 @@ func toJSON(fm *Frame, inputs Inputs) error { func fopen(fm *Frame, name string) (vals.File, error) { // TODO support opening files for writing etc as well. f, err := os.Open(name) - return vals.File{f}, err + return vals.NewFile(f), err } func fclose(f vals.File) error { @@ -217,7 +217,7 @@ func fclose(f vals.File) error { func pipe() (vals.Pipe, error) { r, w, err := os.Pipe() - return vals.Pipe{r, w}, err + return vals.NewPipe(r, w), err } func prclose(p vals.Pipe) error { diff --git a/eval/builtin_fn_misc.go b/eval/builtin_fn_misc.go index deabebff..f14884cc 100644 --- a/eval/builtin_fn_misc.go +++ b/eval/builtin_fn_misc.go @@ -69,14 +69,12 @@ func resolve(fm *Frame, head string) string { _, special := builtinSpecials[head] if special { return "special" - } else { - explode, ns, name := ParseVariableRef(head) - if !explode && fm.ResolveVar(ns, name+FnSuffix) != nil { - return "$" + head + FnSuffix - } else { - return "(external " + parse.Quote(head) + ")" - } } + explode, ns, name := ParseVariableRef(head) + if !explode && fm.ResolveVar(ns, name+FnSuffix) != nil { + return "$" + head + FnSuffix + } + return "(external " + parse.Quote(head) + ")" } func source(fm *Frame, fname string) error { diff --git a/eval/builtin_fn_str.go b/eval/builtin_fn_str.go index 9ddb76a9..c3dd7bdf 100644 --- a/eval/builtin_fn_str.go +++ b/eval/builtin_fn_str.go @@ -15,7 +15,8 @@ import ( // String operations. -var ErrInput = errors.New("input error") +// ErrInputOfEawkMustBeString is thrown when eawk gets a non-string input. +var ErrInputOfEawkMustBeString = errors.New("input of eawk must be string") func init() { addBuiltinFns(map[string]interface{}{ @@ -145,7 +146,7 @@ func eawk(fm *Frame, f Callable, inputs Inputs) error { line, ok := v.(string) if !ok { broken = true - err = ErrInput + err = ErrInputOfEawkMustBeString return } args := []interface{}{line} diff --git a/eval/builtin_special.go b/eval/builtin_special.go index 40ae0cde..22e6d691 100644 --- a/eval/builtin_special.go +++ b/eval/builtin_special.go @@ -427,7 +427,7 @@ func (op *ifOp) invoke(fm *Frame) error { for i, bodyOp := range op.bodyOps { bodies[i] = bodyOp.execlambdaOp(fm) } - else_ := op.elseOp.execlambdaOp(fm) + elseFn := op.elseOp.execlambdaOp(fm) for i, condOp := range op.condOps { condValues, err := condOp.exec(fm.fork("if cond")) if err != nil { @@ -438,7 +438,7 @@ func (op *ifOp) invoke(fm *Frame) error { } } if op.elseOp.body != nil { - return else_.Call(fm.fork("if else"), NoArgs, NoOpts) + return elseFn.Call(fm.fork("if else"), NoArgs, NoOpts) } return nil } @@ -624,7 +624,7 @@ func (op *tryOp) invoke(fm *Frame) error { return err } except := op.exceptOp.execlambdaOp(fm) - else_ := op.elseOp.execlambdaOp(fm) + elseFn := op.elseOp.execlambdaOp(fm) finally := op.finallyOp.execlambdaOp(fm) err = fm.fork("try body").Call(body, NoArgs, NoOpts) @@ -639,8 +639,8 @@ func (op *tryOp) invoke(fm *Frame) error { err = fm.fork("try except").Call(except, NoArgs, NoOpts) } } else { - if else_ != nil { - err = fm.fork("try else").Call(else_, NoArgs, NoOpts) + if elseFn != nil { + err = fm.fork("try else").Call(elseFn, NoArgs, NoOpts) } } if finally != nil { diff --git a/eval/closure.go b/eval/closure.go index 1a248762..41b8cead 100644 --- a/eval/closure.go +++ b/eval/closure.go @@ -16,7 +16,8 @@ import ( // supplies does not match with what is required. var ErrArityMismatch = errors.New("arity mismatch") -// Closure is a closure defined in elvish script. +// Closure is a closure defined in Elvish script. Each closure has its unique +// identity. type Closure struct { ArgNames []string // The name for the rest argument. If empty, the function has fixed arity. @@ -37,11 +38,12 @@ func (*Closure) Kind() string { return "fn" } -// Equal compares by identity. +// Equal compares by address. func (c *Closure) Equal(rhs interface{}) bool { return c == rhs } +// Hash returns the hash of the address of the closure. func (c *Closure) Hash() uint32 { return hash.Pointer(unsafe.Pointer(c)) } @@ -51,6 +53,9 @@ func (c *Closure) Repr(int) string { return fmt.Sprintf("", c) } +// Index supports the introspection of the closure. Supported keys are +// "arg-names", "rest-arg", "opt-names", "opt-defaults", "body", "def" and +// "src". func (c *Closure) Index(k interface{}) (interface{}, bool) { switch k { case "arg-names": @@ -71,6 +76,7 @@ func (c *Closure) Index(k interface{}) (interface{}, bool) { return nil, false } +// IterateKeys calls f with all the valid keys that can be used for Index. func (c *Closure) IterateKeys(f func(interface{}) bool) { util.Feed(f, "arg-names", "rest-arg", "opt-names", "opt-defaults", "body", "def", "src") diff --git a/eval/compilation_error.go b/eval/compilation_error.go index 816c8965..2f1521c7 100644 --- a/eval/compilation_error.go +++ b/eval/compilation_error.go @@ -7,12 +7,13 @@ import ( "github.com/elves/elvish/diag" ) -// CompilationError represents a compilation error and can pretty print it. +// CompilationError represents a compilation error. It can be pretty-printed. type CompilationError struct { Message string Context diag.SourceRange } +// Error returns a plain text representation of the compilation error. func (ce *CompilationError) Error() string { return fmt.Sprintf("compilation error: %d-%d in %s: %s", ce.Context.Begin, ce.Context.End, ce.Context.Name, ce.Message) @@ -23,7 +24,7 @@ func (ce *CompilationError) Range() diag.Ranging { return ce.Context.Range() } -// PPrint pretty-prints a compilation error. +// PPrint pretty-prints the compilation error. func (ce *CompilationError) PPrint(indent string) string { var buf bytes.Buffer diff --git a/eval/compile_effect.go b/eval/compile_effect.go index 47f2553b..53750bf5 100644 --- a/eval/compile_effect.go +++ b/eval/compile_effect.go @@ -134,10 +134,9 @@ func (op *pipelineOp) invoke(fm *Frame) error { } }() return nil - } else { - wg.Wait() - return ComposeExceptionsFromPipeline(errors) } + wg.Wait() + return ComposeExceptionsFromPipeline(errors) } func (cp *compiler) form(n *parse.Form) effectOpBody { @@ -344,9 +343,8 @@ func (op *formOp) invoke(fm *Frame) (errRet error) { if headFn != nil { return headFn.Call(fm, args, convertedOpts) - } else { - return op.spaceyAssignOp.exec(fm) } + return op.spaceyAssignOp.exec(fm) } func allTrue(vs []interface{}) bool { diff --git a/eval/compile_lvalue.go b/eval/compile_lvalue.go index 39acb86a..9c86e0b4 100644 --- a/eval/compile_lvalue.go +++ b/eval/compile_lvalue.go @@ -175,9 +175,8 @@ func (op *elemOp) invoke(fm *Frame) ([]vars.Var, error) { level := vars.ElementErrorLevel(err) if level < 0 { return nil, fm.errorpf(op.begin, op.end, "%s", err) - } else { - return nil, fm.errorpf(op.begin, op.ends[level], "%s", err) } + return nil, fm.errorpf(op.begin, op.ends[level], "%s", err) } return []vars.Var{elemVar}, nil } diff --git a/eval/exception.go b/eval/exception.go index bff32855..ea1dacbd 100644 --- a/eval/exception.go +++ b/eval/exception.go @@ -35,10 +35,12 @@ func Cause(err error) error { // exception. var OK = &Exception{} +// Error returns the message of the cause of the exception. func (exc *Exception) Error() string { return exc.Cause.Error() } +// PPrint pretty-prints the exception. func (exc *Exception) PPrint(indent string) string { buf := new(bytes.Buffer) @@ -73,10 +75,13 @@ func (exc *Exception) PPrint(indent string) string { return buf.String() } +// Kind returns "exception". func (exc *Exception) Kind() string { return "exception" } +// Repr returns a representation of the exception. It is lossy in that it does +// not preserve the stacktrace. func (exc *Exception) Repr(indent int) string { if exc.Cause == nil { return "$ok" @@ -87,19 +92,23 @@ func (exc *Exception) Repr(indent int) string { return "?(fail " + parse.Quote(exc.Cause.Error()) + ")" } -// Equal compares by identity. +// Equal compares by address. func (exc *Exception) Equal(rhs interface{}) bool { return exc == rhs } +// Hash returns the hash of the address. func (exc *Exception) Hash() uint32 { return hash.Pointer(unsafe.Pointer(exc)) } +// Bool returns whether this exception has a nil cause; that is, it is $ok. func (exc *Exception) Bool() bool { return exc.Cause == nil } +// Index supports introspection of the exception. Currently the only supported +// key is "cause". func (exc *Exception) Index(k interface{}) (interface{}, bool) { // TODO: Access to Traceback switch k { @@ -110,6 +119,7 @@ func (exc *Exception) Index(k interface{}) (interface{}, bool) { } } +// IterateKeys calls f with all the valid keys that can be used in Index. func (exc *Exception) IterateKeys(f func(interface{}) bool) { util.Feed(f, "cause") } @@ -120,6 +130,7 @@ type PipelineError struct { Errors []*Exception } +// Repr returns a representation of the pipeline error, using the multi-error builtin. func (pe PipelineError) Repr(indent int) string { // TODO Make a more generalized ListReprBuilder and use it here. b := new(bytes.Buffer) @@ -137,6 +148,7 @@ func (pe PipelineError) Repr(indent int) string { return b.String() } +// Error returns a plain text representation of the pipeline error. func (pe PipelineError) Error() string { b := new(bytes.Buffer) b.WriteString("(") @@ -197,6 +209,7 @@ var flowNames = [...]string{ "return", "break", "continue", } +// Repr returns a representation of the flow "error". func (f Flow) Repr(int) string { return "?(" + f.Error() + ")" } @@ -208,6 +221,7 @@ func (f Flow) Error() string { return flowNames[f] } +// PPrint pretty-prints the flow "error". func (f Flow) PPrint(string) string { return "\033[33;1m" + f.Error() + "\033[m" } @@ -221,6 +235,8 @@ type ExternalCmdExit struct { Pid int } +// NewExternalCmdExit constructs an error for representing a non-zero exit from +// an external command. func NewExternalCmdExit(name string, ws syscall.WaitStatus, pid int) error { if ws.Exited() && ws.ExitStatus() == 0 { return nil diff --git a/eval/external_cmd.go b/eval/external_cmd.go index 90297d55..2f0f6f66 100644 --- a/eval/external_cmd.go +++ b/eval/external_cmd.go @@ -14,8 +14,13 @@ import ( ) var ( + // ErrExternalCmdOpts is thrown when an external command is passed Elvish + // options. + // + // TODO: Catch this kind of errors at compilation time. ErrExternalCmdOpts = errors.New("external commands don't accept elvish options") - ErrCdNoArg = errors.New("implicit cd accepts no arguments") + // ErrImplicitCdNoArg is thrown when an implicit cd form is passed arguments. + ErrImplicitCdNoArg = errors.New("implicit cd accepts no arguments") ) // ExternalCmd is an external command. @@ -49,7 +54,7 @@ func (e ExternalCmd) Call(fm *Frame, argVals []interface{}, opts map[string]inte if err == nil && stat.IsDir() { // implicit cd if len(argVals) > 0 { - return ErrCdNoArg + return ErrImplicitCdNoArg } return fm.Chdir(e.Name) } diff --git a/eval/interrupts.go b/eval/interrupts.go index 6462664c..e6ed8961 100644 --- a/eval/interrupts.go +++ b/eval/interrupts.go @@ -12,6 +12,7 @@ func (fm *Frame) Interrupts() <-chan struct{} { return fm.intCh } +// ErrInterrupted is thrown when the execution is interrupted by a signal. var ErrInterrupted = errors.New("interrupted") // IsInterrupted reports whether there has been an interrupt. diff --git a/eval/options.go b/eval/options.go index e014d3e2..916e8485 100644 --- a/eval/options.go +++ b/eval/options.go @@ -9,6 +9,8 @@ import ( "github.com/elves/elvish/util" ) +// RawOptions is the type of an argument a builtin function can take to declare +// that it wants to parse options itself. See the doc of BuiltinFn for details. type RawOptions map[string]interface{} // Takes a raw option map and a pointer to a struct, and populate the struct diff --git a/eval/port.go b/eval/port.go index b47d3edd..0592eb09 100644 --- a/eval/port.go +++ b/eval/port.go @@ -38,7 +38,7 @@ var ( BlackholeChan = make(chan interface{}) // DevNull is /dev/null. DevNull *os.File - // DevNullClosedInput is a port made up from DevNull and ClosedChan, + // DevNullClosedChan is a port made up from DevNull and ClosedChan, // suitable as placeholder input port. DevNullClosedChan *Port ) diff --git a/eval/process_windows.go b/eval/process_windows.go index e7758b54..42de0695 100644 --- a/eval/process_windows.go +++ b/eval/process_windows.go @@ -5,12 +5,13 @@ import "syscall" // Nop on Windows. func putSelfInFg() error { return nil } -const DETACHED_PROCESS = 0x00000008 +// The bitmask for CreationFlags in SysProcAttr to start a process in background. +const detachedProcess = 0x00000008 func makeSysProcAttr(bg bool) *syscall.SysProcAttr { flags := uint32(0) if bg { - flags |= DETACHED_PROCESS + flags |= detachedProcess } return &syscall.SysProcAttr{CreationFlags: flags} } diff --git a/eval/pwd.go b/eval/pwd.go index a92f00ed..e084ecc1 100644 --- a/eval/pwd.go +++ b/eval/pwd.go @@ -14,6 +14,8 @@ type PwdVariable struct { var _ vars.Var = PwdVariable{} +// Get returns the current working directory. It returns /unknown/pwd when it +// cannot be determined. func (PwdVariable) Get() interface{} { pwd, err := os.Getwd() // TODO: Deprecate the $pwd variable. @@ -23,6 +25,7 @@ func (PwdVariable) Get() interface{} { return pwd } +// Set changes the current working directory. func (pwd PwdVariable) Set(v interface{}) error { path, ok := v.(string) if !ok { diff --git a/eval/testutils.go b/eval/testutils.go index 4d7bd4e2..74fbae7b 100644 --- a/eval/testutils.go +++ b/eval/testutils.go @@ -69,8 +69,8 @@ func (t TestCase) Puts(vs ...interface{}) TestCase { return t } -// Puts returns an altered Test that requires the source code to produce the -// specified strings in the value channel when evaluated. +// PutsStrings returns an altered Test that requires the source code to produce +// the specified strings in the value channel when evaluated. func (t TestCase) PutsStrings(ss []string) TestCase { t.want.out = make([]interface{}, len(ss)) for i, s := range ss { @@ -105,8 +105,8 @@ func Test(t *testing.T, tests ...TestCase) { TestWithSetup(t, func(*Evaler) {}, tests...) } -// Test runs test cases. For each test case, a new Evaler is created with -// NewEvaler and passed to the setup function. +// TestWithSetup runs test cases. For each test case, a new Evaler is created +// with NewEvaler and passed to the setup function. func TestWithSetup(t *testing.T, setup func(*Evaler), tests ...TestCase) { for _, tt := range tests { ev := NewEvaler()