package eval

import (
	
	
	
	
	

	
	
	
	
)

// Exception represents exceptions. It is both a Value accessible to Elvish
// code, and can be returned by methods like like (*Evaler).Eval.
type Exception interface {
	error
	diag.Shower
	Reason() error
	StackTrace() *StackTrace
	// This is not strictly necessary, but it makes sure that there is only one
	// implementation of Exception, so that the compiler may de-virtualize this
	// interface.
	isException()
}

// NewException creates a new Exception.
func ( error,  *StackTrace) Exception {
	return &exception{, }
}

// Implementation of the Exception interface.
type exception struct {
	reason     error
	stackTrace *StackTrace
}

// StackTrace represents a stack trace as a linked list of diag.Context. The
// head is the innermost stack.
//
// Since pipelines can call multiple functions in parallel, all the StackTrace
// nodes form a DAG.
type StackTrace struct {
	Head *diag.Context
	Next *StackTrace
}

// MakeStackTrace creates a new StackTrace from the given Context entries, using
// the first entry as the head.
func ( ...*diag.Context) *StackTrace {
	var  *StackTrace
	for  := len() - 1;  >= 0; -- {
		 = &StackTrace{Head: [], Next: }
	}
	return 
}

// Reason returns the Reason field if err is an Exception. Otherwise it returns
// err itself.
func ( error) error {
	if ,  := .(*exception);  {
		return .reason
	}
	return 
}

// OK is a pointer to a special value of Exception that represents the absence
// of exception.
var OK = &exception{}

func ( *exception) () {}

func ( *exception) () error { return .reason }

func ( *exception) () *StackTrace { return .stackTrace }

// Error returns the message of the cause of the exception.
func ( *exception) () string { return .reason.Error() }

// Show shows the exception.
func ( *exception) ( string) string {
	 := new(bytes.Buffer)

	var  string
	if ,  := .reason.(diag.Shower);  {
		 = .Show()
	} else if .reason == nil {
		 = "ok"
	} else {
		 = "\033[31;1m" + .reason.Error() + "\033[m"
	}
	fmt.Fprintf(, "Exception: %s", )

	if .stackTrace != nil {
		.WriteString("\n")
		if .stackTrace.Next == nil {
			.WriteString(.stackTrace.Head.ShowCompact())
		} else {
			.WriteString( + "Traceback:")
			for  := .stackTrace;  != nil;  = .Next {
				.WriteString("\n" +  + "  ")
				.WriteString(.Head.Show( + "    "))
			}
		}
	}

	if ,  := .reason.(PipelineError);  {
		.WriteString("\n" +  + "Caused by:")
		for ,  := range .Errors {
			if  == OK {
				continue
			}
			.WriteString("\n" +  + "  " + .Show(+"  "))
		}
	}

	return .String()
}

// Kind returns "exception".
func ( *exception) () string {
	return "exception"
}

// Repr returns a representation of the exception. It is lossy in that it does
// not preserve the stacktrace.
func ( *exception) ( int) string {
	if .reason == nil {
		return "$ok"
	}
	return "[&reason=" + vals.Repr(.reason, +1) + "]"
}

// Equal compares by address.
func ( *exception) ( interface{}) bool {
	return  == 
}

// Hash returns the hash of the address.
func ( *exception) () uint32 {
	return hash.Pointer(unsafe.Pointer())
}

// Bool returns whether this exception has a nil cause; that is, it is $ok.
func ( *exception) () bool {
	return .reason == nil
}

func ( *exception) () vals.StructMap { return excFields{} }

type excFields struct{ e *exception }

func (excFields) ()    {}
func ( excFields) () error { return .e.reason }

// PipelineError represents the errors of pipelines, in which multiple commands
// may error.
type PipelineError struct {
	Errors []Exception
}

// Error returns a plain text representation of the pipeline error.
func ( PipelineError) () string {
	 := new(bytes.Buffer)
	.WriteString("(")
	for ,  := range .Errors {
		if  > 0 {
			.WriteString(" | ")
		}
		if  == nil || .Reason() == nil {
			.WriteString("<nil>")
		} else {
			.WriteString(.Error())
		}
	}
	.WriteString(")")
	return .String()
}

// MakePipelineError builds an error from the execution results of multiple
// commands in a pipeline.
//
// If all elements are either nil or OK, it returns nil. If there is exactly
// non-nil non-OK Exception, it returns it. Otherwise, it return a PipelineError
// built from the slice, with nil items turned into OK's for easier access from
// Elvish code.
func ( []Exception) error {
	 := make([]Exception, len())
	,  := 0, 0
	for ,  := range  {
		if  == nil {
			[] = OK
		} else {
			[] = 
			if .Reason() != nil {
				++
				 = 
			}
		}
	}
	switch  {
	case 0:
		return nil
	case 1:
		return []
	default:
		return PipelineError{}
	}
}

func ( PipelineError) () vals.StructMap { return peFields{} }

type peFields struct{ pe PipelineError }

func (peFields) () {}

func ( peFields) () string { return "pipeline" }

func ( peFields) () vals.List {
	 := vals.EmptyList
	for ,  := range .pe.Errors {
		 = .Cons()
	}
	return 
}

// Flow is a special type of error used for control flows.
type Flow uint

// Control flows.
const (
	Return Flow = iota
	Break
	Continue
)

var flowNames = [...]string{
	"return", "break", "continue",
}

func ( Flow) () string {
	if  >= Flow(len(flowNames)) {
		return fmt.Sprintf("!(BAD FLOW: %d)", )
	}
	return flowNames[]
}

// Show shows the flow "error".
func ( Flow) (string) string {
	return "\033[33;1m" + .Error() + "\033[m"
}

func ( Flow) () vals.StructMap { return flowFields{} }

type flowFields struct{ f Flow }

func (flowFields) () {}

func ( flowFields) () string { return "flow" }
func ( flowFields) () string { return .f.Error() }

// ExternalCmdExit contains the exit status of external commands.
type ExternalCmdExit struct {
	syscall.WaitStatus
	CmdName string
	Pid     int
}

// NewExternalCmdExit constructs an error for representing a non-zero exit from
// an external command.
func ( string,  syscall.WaitStatus,  int) error {
	if .Exited() && .ExitStatus() == 0 {
		return nil
	}
	return ExternalCmdExit{, , }
}

func ( ExternalCmdExit) () string {
	 := .WaitStatus
	 := parse.Quote(.CmdName)
	switch {
	case .Exited():
		return  + " exited with " + strconv.Itoa(.ExitStatus())
	case .Signaled():
		 :=  + " killed by signal " + .Signal().String()
		if .CoreDump() {
			 += " (core dumped)"
		}
		return 
	case .Stopped():
		 :=  + " stopped by signal " + fmt.Sprintf("%s (pid=%d)", .StopSignal(), .Pid)
		 := .TrapCause()
		if  != -1 {
			 += fmt.Sprintf(" (trapped %v)", )
		}
		return 
	default:
		return fmt.Sprint(, " has unknown WaitStatus ", )
	}
}

func ( ExternalCmdExit) () vals.StructMap {
	 := .WaitStatus
	 := exitFieldsCommon{}
	switch {
	case .Exited():
		return exitFieldsExited{}
	case .Signaled():
		return exitFieldsSignaled{}
	case .Stopped():
		return exitFieldsStopped{}
	default:
		return exitFieldsUnknown{}
	}
}

type exitFieldsCommon struct{ e ExternalCmdExit }

func (exitFieldsCommon) ()      {}
func ( exitFieldsCommon) () string { return .e.CmdName }
func ( exitFieldsCommon) () string     { return strconv.Itoa(.e.Pid) }

type exitFieldsExited struct{ exitFieldsCommon }

func (exitFieldsExited) () string         { return "external-cmd/exited" }
func ( exitFieldsExited) () string { return strconv.Itoa(.e.ExitStatus()) }

type exitFieldsSignaled struct{ exitFieldsCommon }

func ( exitFieldsSignaled) () string         { return "external-cmd/signaled" }
func ( exitFieldsSignaled) () string   { return .e.Signal().String() }
func ( exitFieldsSignaled) () string { return strconv.Itoa(int(.e.Signal())) }
func ( exitFieldsSignaled) () bool     { return .e.CoreDump() }

type exitFieldsStopped struct{ exitFieldsCommon }

func ( exitFieldsStopped) () string         { return "external-cmd/stopped" }
func ( exitFieldsStopped) () string   { return .e.StopSignal().String() }
func ( exitFieldsStopped) () string { return strconv.Itoa(int(.e.StopSignal())) }
func ( exitFieldsStopped) () int       { return .e.TrapCause() }

type exitFieldsUnknown struct{ exitFieldsCommon }

func (exitFieldsUnknown) () string { return "external-cmd/unknown" }