summaryrefslogtreecommitdiff
path: root/libgo/go/go/scanner/errors.go
blob: 47e35a7107dece072825b7612a18f0bc3f6f8a60 (plain)
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// 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 scanner

import (
	"container/vector"
	"fmt"
	"go/token"
	"io"
	"os"
	"sort"
)


// An implementation of an ErrorHandler may be provided to the Scanner.
// If a syntax error is encountered and a handler was installed, Error
// is called with a position and an error message. The position points
// to the beginning of the offending token.
//
type ErrorHandler interface {
	Error(pos token.Position, msg string)
}


// ErrorVector implements the ErrorHandler interface. It maintains a list
// of errors which can be retrieved with GetErrorList and GetError. The
// zero value for an ErrorVector is an empty ErrorVector ready to use.
//
// A common usage pattern is to embed an ErrorVector alongside a
// scanner in a data structure that uses the scanner. By passing a
// reference to an ErrorVector to the scanner's Init call, default
// error handling is obtained.
//
type ErrorVector struct {
	errors vector.Vector
}


// Reset resets an ErrorVector to no errors.
func (h *ErrorVector) Reset() { h.errors.Resize(0, 0) }


// ErrorCount returns the number of errors collected.
func (h *ErrorVector) ErrorCount() int { return h.errors.Len() }


// Within ErrorVector, an error is represented by an Error node. The
// position Pos, if valid, points to the beginning of the offending
// token, and the error condition is described by Msg.
//
type Error struct {
	Pos token.Position
	Msg string
}


func (e *Error) String() string {
	if e.Pos.Filename != "" || e.Pos.IsValid() {
		// don't print "<unknown position>"
		// TODO(gri) reconsider the semantics of Position.IsValid
		return e.Pos.String() + ": " + e.Msg
	}
	return e.Msg
}


// An ErrorList is a (possibly sorted) list of Errors.
type ErrorList []*Error


// 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
}


func (p ErrorList) String() string {
	switch len(p) {
	case 0:
		return "unspecified error"
	case 1:
		return p[0].String()
	}
	return fmt.Sprintf("%s (and %d more errors)", p[0].String(), len(p)-1)
}


// These constants control the construction of the ErrorList
// returned by GetErrors.
//
const (
	Raw         = iota // leave error list unchanged
	Sorted             // sort error list by file, line, and column number
	NoMultiples        // sort error list and leave only the first error per line
)


// GetErrorList returns the list of errors collected by an ErrorVector.
// The construction of the ErrorList returned is controlled by the mode
// parameter. If there are no errors, the result is nil.
//
func (h *ErrorVector) GetErrorList(mode int) ErrorList {
	if h.errors.Len() == 0 {
		return nil
	}

	list := make(ErrorList, h.errors.Len())
	for i := 0; i < h.errors.Len(); i++ {
		list[i] = h.errors.At(i).(*Error)
	}

	if mode >= Sorted {
		sort.Sort(list)
	}

	if mode >= NoMultiples {
		var last token.Position // initial last.Line is != any legal error line
		i := 0
		for _, e := range list {
			if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line {
				last = e.Pos
				list[i] = e
				i++
			}
		}
		list = list[0:i]
	}

	return list
}


// GetError is like GetErrorList, but it returns an os.Error instead
// so that a nil result can be assigned to an os.Error variable and
// remains nil.
//
func (h *ErrorVector) GetError(mode int) os.Error {
	if h.errors.Len() == 0 {
		return nil
	}

	return h.GetErrorList(mode)
}


// ErrorVector implements the ErrorHandler interface.
func (h *ErrorVector) Error(pos token.Position, msg string) {
	h.errors.Push(&Error{pos, msg})
}


// 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 os.Error) {
	if list, ok := err.(ErrorList); ok {
		for _, e := range list {
			fmt.Fprintf(w, "%s\n", e)
		}
	} else {
		fmt.Fprintf(w, "%s\n", err)
	}
}