aboutsummaryrefslogtreecommitdiff
path: root/parser/errors.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--parser/errors.go107
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]
+}