package parse import "fmt" // Error is an error that contains positional information. type Error interface { error Position () Position } type errInternal struct { position Position message string } var _ Error = new(errInternal) func (err *errInternal) Error () string { return err.String() } func (err *errInternal) String () string { return err.message } func (err *errInternal) Position () Position { return err.position } // Errorf produces an error with a fmt'd message. func Errorf (position Position, format string, variables ...any) Error { return &errInternal { position: position, message: fmt.Sprintf(format, variables...), } } // Format returns a formatted string representing an error. This string may take // up multiple lines and contain ANSI escape codes. If err does not fulfill the // Error interface, err.Error() is returned instead. // // When the error fulfills the Error interface, the formatted output will // be of the general form: // FILE:ROW+1:START+1: MESSAGE // ROW+1 | LINE // ^^^^ // Where the position of the underline (^^^^) corresponds to the start and end // fields in the error's position. Note that the row and column numbers as // displayed are increased by one from their normal zero-indexed state, because // most editors display row and column numbers starting at 1:1. Additionally, // because normal error messages do not produce trailing line breaks, neither // does this function. func Format (err error) string { if e, ok := err.(Error); ok { return fmt.Sprintf ( "%v: %v\n%v", e.Position(), e.Error(), e.Position().Format()) } else { return err.Error() } }