| // 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 must be |
| // initialized with Init(). It maintains a list of errors which can |
| // be retrieved with GetErrorList and GetError. |
| // |
| // 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; |
| } |
| |
| |
| // Init initializes an ErrorVector. |
| func (h *ErrorVector) Init() { |
| h.errors.Init(0); |
| } |
| |
| |
| // NewErrorVector creates a new ErrorVector. |
| func NewErrorVector() *ErrorVector { |
| h := new(ErrorVector); |
| h.Init(); |
| return h; |
| } |
| |
| |
| // 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 SortInterface. |
| 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); |
| } |
| } |