diff options
Diffstat (limited to 'parser/errors.go')
| -rw-r--r-- | parser/errors.go | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/parser/errors.go b/parser/errors.go new file mode 100644 index 0000000..c2ed049 --- /dev/null +++ b/parser/errors.go @@ -0,0 +1,107 @@ +package parser + +import ( + "bytes" + "errors" + "fmt" + "strings" +) + +type ErrorLister interface { + Errors() []error +} + +func (e errList) Errors() []error { + return e +} + +// ParserError is the public interface to errors of type parserError +type ParserError interface { + Error() string + InnerError() error + Pos() (int, int, int) + Expected() []string +} + +func (p *parserError) InnerError() error { + return p.Inner +} + +func (p *parserError) Pos() (line, col, offset int) { + return p.pos.line, p.pos.col, p.pos.offset +} + +func (p *parserError) Expected() []string { + return p.expected +} + +func CaretErrors(err error, input string) error { + if el, ok := err.(ErrorLister); ok { + var buffer bytes.Buffer + for _, e := range el.Errors() { + err1, shouldReturn := caretError(e, input, buffer, err) + if shouldReturn { + return err1 + } + } + return errors.New(buffer.String()) + } + return err +} + +func caretError(e error, input string, buffer bytes.Buffer, err error) (error, bool) { + if parserErr, ok := e.(ParserError); ok { + _, col, off := parserErr.Pos() + line := extractLine(input, off) + if col >= len(line) { + col = len(line) - 1 + } else { + if col > 0 { + col-- + } + } + if col < 0 { + col = 0 + } + pos := col + for _, chr := range line[:col] { + if chr == '\t' { + pos += 7 + } + } + fmt.Fprintf(&buffer, "%s\n%s\n%s\n", line, strings.Repeat(" ", pos)+"^", err.Error()) + + return err, true + } else { + return err, true + } + return nil, false +} + +func extractLine(input string, initPos int) string { + if initPos < 0 { + initPos = 0 + } + if initPos >= len(input) && len(input) > 0 { + initPos = len(input) - 1 + } + startPos := initPos + endPos := initPos + for ; startPos > 0; startPos-- { + if input[startPos] == '\n' { + if startPos != initPos { + startPos++ + break + } + } + } + for ; endPos < len(input); endPos++ { + if input[endPos] == '\n' { + if endPos == initPos { + endPos++ + } + break + } + } + return input[startPos:endPos] +} |
