-
Notifications
You must be signed in to change notification settings - Fork 1k
Expand file tree
/
Copy pathparse.go
More file actions
118 lines (104 loc) · 3.3 KB
/
parse.go
File metadata and controls
118 lines (104 loc) · 3.3 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
package sqlite
import (
"errors"
"fmt"
"io"
"unicode/utf8"
"github.com/antlr4-go/antlr/v4"
"github.com/sqlc-dev/sqlc/internal/engine/sqlite/parser"
"github.com/sqlc-dev/sqlc/internal/source"
"github.com/sqlc-dev/sqlc/internal/sql/ast"
)
type errorListener struct {
*antlr.DefaultErrorListener
err string
}
func (el *errorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) {
el.err = msg
}
// func (el *errorListener) ReportAmbiguity(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex int, exact bool, ambigAlts *antlr.BitSet, configs antlr.ATNConfigSet) {
// }
//
// func (el *errorListener) ReportAttemptingFullContext(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex int, conflictingAlts *antlr.BitSet, configs antlr.ATNConfigSet) {
// }
//
// func (el *errorListener) ReportContextSensitivity(recognizer antlr.Parser, dfa *antlr.DFA, startIndex, stopIndex, prediction int, configs antlr.ATNConfigSet) {
// }
func NewParser() *Parser {
return &Parser{}
}
type Parser struct {
}
func (p *Parser) Parse(r io.Reader) ([]ast.Statement, error) {
blob, err := io.ReadAll(r)
if err != nil {
return nil, err
}
src := string(blob)
input := antlr.NewInputStream(src)
lexer := parser.NewSQLiteLexer(input)
stream := antlr.NewCommonTokenStream(lexer, 0)
pp := parser.NewSQLiteParser(stream)
el := &errorListener{}
pp.AddErrorListener(el)
// pp.BuildParseTrees = true
tree := pp.Parse()
if el.err != "" {
return nil, errors.New(el.err)
}
pctx, ok := tree.(*parser.ParseContext)
if !ok {
return nil, fmt.Errorf("expected ParserContext; got %T\n", tree)
}
// ANTLR's InputStream operates on characters (runes), so token
// positions are character indices. source.Pluck slices with byte
// offsets. Build a lookup table so we can translate correctly when
// the input contains multi-byte UTF-8 characters (e.g. em-dash).
runeToByteOffset := buildRuneToByteOffsets(src)
var stmts []ast.Statement
for _, istmt := range pctx.AllSql_stmt_list() {
list, ok := istmt.(*parser.Sql_stmt_listContext)
if !ok {
return nil, fmt.Errorf("expected Sql_stmt_listContext; got %T\n", istmt)
}
loc := 0
for _, stmt := range list.AllSql_stmt() {
converter := &cc{}
out := converter.convert(stmt)
if _, ok := out.(*ast.TODO); ok {
loc = stmt.GetStop().GetStop() + 2
continue
}
byteLoc := runeToByteOffset[loc]
byteEnd := runeToByteOffset[stmt.GetStop().GetStop()+1]
stmts = append(stmts, ast.Statement{
Raw: &ast.RawStmt{
Stmt: out,
StmtLocation: byteLoc,
StmtLen: byteEnd - byteLoc,
},
})
loc = stmt.GetStop().GetStop() + 2
}
}
return stmts, nil
}
// buildRuneToByteOffsets returns a slice mapping rune index to byte offset.
// Entry i holds the byte offset where rune i begins; the final entry holds
// len(s) so that an exclusive end position can be looked up safely.
func buildRuneToByteOffsets(s string) []int {
n := utf8.RuneCountInString(s)
offsets := make([]int, 0, n+1)
for bytePos := range s {
offsets = append(offsets, bytePos)
}
offsets = append(offsets, len(s))
return offsets
}
func (p *Parser) CommentSyntax() source.CommentSyntax {
return source.CommentSyntax{
Dash: true,
Hash: false,
SlashStar: true,
}
}