-
Notifications
You must be signed in to change notification settings - Fork 1
/
errors.go
172 lines (152 loc) · 4.28 KB
/
errors.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*
* File: src/github.com/Ken1JF/ah/errors.go
* Project: abst-hier
*
* Created by Ken Friedenbach on 12/08/09.
* Copyright 2009-2014, all rights reserved.
* Much of this logic is based on the scanner for Go,
* whiich may be found in:
* ${GOROOT}/src/pkg/go/scanner/
*
* Copyright 2009 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package ah
import (
"fmt"
"io"
"sort"
)
// Token source positions are represented by a Position value.
// A Position is valid if the line number is > 0.
type Position struct {
Filename string // filename, if any
Offset int // byte offset, starting at 0
Line int // line number, starting at 1
Column int // column number, starting at 1 (character count)
}
// Pos is an accessor method for anonymous Position fields.
// It returns its receiver.
func (pos *Position) Pos() Position { return *pos }
// IsValid returns true if the position is valid.
func (pos *Position) IsValid() bool { return pos.Line > 0 }
// String() string function for fmt.Print
func (pos *Position) String() string {
s := pos.Filename
if pos.IsValid() {
if s != "" {
s += ":"
}
s += fmt.Sprintf("%d:%d", pos.Line, pos.Column)
}
if s == "" {
s = "???"
}
return s
}
// NoPos is used when there is no corresponding source position for a token.
var NoPos Position
// In an ErrorList, an error is represented by a *ErrorWithPosition.
// The position Pos, if valid, points to the beginning
// of the offending token, and the error condition is described
// by Msg.
type ErrorWithPosition struct {
Pos Position
Msg string
}
// ErrorWithPosition implements the error interface
func (e ErrorWithPosition) Error() string {
if e.Pos.Filename != "" || e.Pos.IsValid() {
// don't print "<unknown position>"
return e.Pos.String() + ": " + e.Msg
}
return e.Msg
}
// ErrorList is a list of *Errors.
// The zero value for an ErrorList is an empty ErrorList ready to use.
type ErrorList []*ErrorWithPosition
// Add adds an ErrorWithPosition with given position and error message to an ErrorList.
func (p *ErrorList) Add(pos Position, msg string) {
*p = append(*p, &ErrorWithPosition{pos, msg})
}
// Reset resets an ErrorList to no errors.
func (p *ErrorList) Reset() { *p = (*p)[0:0] }
// ErrorList implements the sort Interface.
func (p ErrorList) Len() int { return len(p) }
func (p ErrorList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p ErrorList) Less(i, j int) bool {
e := &p[i].Pos
f := &p[j].Pos
// Note that it is not sufficient to simply compare file offsets because
// the offsets do not reflect modified line information (through //line
// comments).
if e.Filename < f.Filename {
return true
}
if e.Filename == f.Filename {
if e.Line < f.Line {
return true
}
if e.Line == f.Line {
return e.Column < f.Column
}
}
return false
}
// Sort sorts an ErrorList.
// *ErrorWithPosition entries are sorted by position
// other errors are sorted by error message,
// and before any *ErrorWithPosition entry.
func (p ErrorList) Sort() {
sort.Sort(p)
}
// RemoveMultiples sorts an ErrorList
// and removes all but the first error per line.
func (p *ErrorList) RemoveMultiples() {
sort.Sort(p)
var last Position // initial last.Line is != any legal error line
i := 0
for _, e := range *p {
if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line {
last = e.Pos
(*p)[i] = e
i++
}
}
(*p) = (*p)[0:i]
}
// Return the number of errors
func (p ErrorList) ErrorCount() int {
return len(p)
}
// An ErrorList implements the error interface.
func (p ErrorList) Error() string {
switch len(p) {
case 0:
return "no errors"
case 1:
return p[0].Error()
}
return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1)
}
// Err returns an error equivalent to this error list.
// If the list is empty, Err returns nil.
func (p ErrorList) Err() error {
if len(p) == 0 {
return nil
}
return p
}
// PrintError is a utility function that prints a list of errors to w,
// one error per line, if the err parameter is an ErrorList. Otherwise
// it prints the err string.
func PrintError(w io.Writer, err error) {
if list, ok := err.(ErrorList); ok {
for _, e := range list {
fmt.Fprintf(w, "%s\n", e)
}
} else {
fmt.Fprintf(w, "%s\n", err)
}
}