-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy patherrors.go
More file actions
141 lines (122 loc) · 4.19 KB
/
Copy patherrors.go
File metadata and controls
141 lines (122 loc) · 4.19 KB
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
// errors.go defines structured error types and the ANTLR listener used during parsing.
package postgresparser
import (
"errors"
"fmt"
"strings"
"github.com/antlr4-go/antlr/v4"
)
// Sentinel errors returned by the SQL parsing functions.
var (
// ErrNoStatements is returned when the input SQL contains no parseable statements.
ErrNoStatements = errors.New("no statements found")
// ErrMultipleStatements is returned by ParseSQLStrict when input contains
// more than one statement.
ErrMultipleStatements = errors.New("multiple statements found")
// ErrNilContext is returned when a required parser context is nil.
ErrNilContext = errors.New("nil context")
)
// MultipleStatementsError indicates ParseSQLStrict received a multi-statement input.
type MultipleStatementsError struct {
StatementCount int
}
// Error formats the strict-mode multi-statement validation failure.
func (e *MultipleStatementsError) Error() string {
return fmt.Sprintf("%s: expected exactly 1 statement, got %d", ErrMultipleStatements, e.StatementCount)
}
// Unwrap returns the sentinel error for errors.Is compatibility.
func (e *MultipleStatementsError) Unwrap() error {
return ErrMultipleStatements
}
// SyntaxError describes a single parser syntax error with line/column context.
type SyntaxError struct {
Line int
Column int
Message string
// TokenIndex is the offending token index when available; -1 when unknown.
TokenIndex int
}
// ParseErrors aggregates syntax errors encountered while parsing a SQL string.
type ParseErrors struct {
SQL string
Errors []SyntaxError
}
// Error formats the aggregated syntax errors, rendering each as a source line
// with a caret pointing at the offending token. Multiple errors are separated
// by a blank line.
func (p *ParseErrors) Error() string {
if p == nil || len(p.Errors) == 0 {
return "parse error"
}
lines := strings.Split(p.SQL, "\n")
blocks := make([]string, len(p.Errors))
for i, err := range p.Errors {
blocks[i] = formatSyntaxError(err, lines)
}
return strings.Join(blocks, "\n\n")
}
// formatSyntaxError renders one syntax error. When the source line is
// available it shows that line with a caret underneath; otherwise it falls
// back to the header plus message only.
func formatSyntaxError(err SyntaxError, lines []string) string {
header := fmt.Sprintf("parse error at line %d:%d", err.Line, err.Column)
idx := err.Line - 1
if idx < 0 || idx >= len(lines) {
return fmt.Sprintf("%s\n %s", header, err.Message)
}
src := lines[idx]
gutter := fmt.Sprintf(" %d | ", err.Line)
caretPad := strings.Repeat(" ", len(gutter)) + caretIndent(src, err.Column)
return fmt.Sprintf("%s\n%s%s\n%s^\n%s%s",
header, gutter, src, caretPad, strings.Repeat(" ", len(gutter)), err.Message)
}
// caretIndent builds the whitespace that positions a caret under column col of
// src, preserving tabs so alignment holds in tab-indented input.
func caretIndent(src string, col int) string {
if col < 0 {
col = 0
}
runes := []rune(src)
var b strings.Builder
for i := 0; i < col; i++ {
if i < len(runes) && runes[i] == '\t' {
b.WriteByte('\t')
} else {
b.WriteByte(' ')
}
}
return b.String()
}
// replaceErrorListeners removes ANTLR's default console listener so parse
// failures stay inside library results instead of going to process stderr.
func replaceErrorListeners(recognizer antlr.Recognizer, listeners ...antlr.ErrorListener) {
if recognizer == nil {
return
}
recognizer.RemoveErrorListeners()
for _, listener := range listeners {
if listener == nil {
continue
}
recognizer.AddErrorListener(listener)
}
}
// parseErrorListener collects syntax errors emitted by ANTLR recognizers.
type parseErrorListener struct {
antlr.DefaultErrorListener
errs []SyntaxError
}
// SyntaxError records each ANTLR syntax error with position data for later consumption.
func (l *parseErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{},
line, column int, msg string, e antlr.RecognitionException) {
tokenIndex := -1
if tok, ok := offendingSymbol.(antlr.Token); ok && tok != nil {
tokenIndex = tok.GetTokenIndex()
}
l.errs = append(l.errs, SyntaxError{
Line: line,
Column: column,
Message: msg,
TokenIndex: tokenIndex,
})
}