snapshot of https://github.com/sqlc-dev/sqlc.git (main, 2026-09-16; blobs > 131072B omitted)
Some checks failed
kotlin / test (push) Has been skipped
python / test (push) Has been skipped
typescript / test (push) Has been skipped
go / build darwin/amd64 (push) Has been cancelled
go / build linux/amd64 (push) Has been cancelled
go / build windows/amd64 (push) Has been cancelled
go / build darwin/arm64 (push) Has been cancelled
go / build linux/arm64 (push) Has been cancelled
go / build windows/arm64 (push) Has been cancelled
go / test (push) Has been cancelled
go / vuln_check (push) Has been cancelled
Some checks failed
kotlin / test (push) Has been skipped
python / test (push) Has been skipped
typescript / test (push) Has been skipped
go / build darwin/amd64 (push) Has been cancelled
go / build linux/amd64 (push) Has been cancelled
go / build windows/amd64 (push) Has been cancelled
go / build darwin/arm64 (push) Has been cancelled
go / build linux/arm64 (push) Has been cancelled
go / build windows/arm64 (push) Has been cancelled
go / test (push) Has been cancelled
go / vuln_check (push) Has been cancelled
This commit is contained in:
162
internal/engine/dolphin/parse.go
Normal file
162
internal/engine/dolphin/parse.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package dolphin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/sqlc-dev/marino/parser"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/source"
|
||||
"github.com/sqlc-dev/sqlc/internal/sql/ast"
|
||||
"github.com/sqlc-dev/sqlc/internal/sql/sqlerr"
|
||||
)
|
||||
|
||||
func NewParser() *Parser {
|
||||
return &Parser{pingcap: parser.New()}
|
||||
}
|
||||
|
||||
// NewFormatParser returns the parser sqlc fmt uses. It differs from the
|
||||
// compiler's parser in one way: identifiers keep the case the author wrote.
|
||||
// The compiler lowercases them so catalog lookups are case-insensitive, but
|
||||
// a formatter that prints `Event` as `event` renames a table on the many
|
||||
// servers where table names are case-sensitive.
|
||||
func NewFormatParser() *Parser {
|
||||
return &Parser{pingcap: parser.New(), preserveCase: true}
|
||||
}
|
||||
|
||||
type Parser struct {
|
||||
pingcap *parser.Parser
|
||||
preserveCase bool
|
||||
}
|
||||
|
||||
var lineColumn = regexp.MustCompile(`^line (\d+) column (\d+) (.*)`)
|
||||
|
||||
func normalizeErr(err error) error {
|
||||
if err == nil {
|
||||
return err
|
||||
}
|
||||
parts := strings.Split(err.Error(), "\n")
|
||||
msg := strings.TrimSpace(parts[0] + "\"")
|
||||
out := lineColumn.FindStringSubmatch(msg)
|
||||
if len(out) == 4 {
|
||||
line, lineErr := strconv.Atoi(out[1])
|
||||
col, colErr := strconv.Atoi(out[2])
|
||||
if lineErr != nil || colErr != nil {
|
||||
return errors.New(msg)
|
||||
}
|
||||
return &sqlerr.Error{
|
||||
Message: "syntax error",
|
||||
Err: errors.New(out[3]),
|
||||
Line: line,
|
||||
Column: col,
|
||||
}
|
||||
}
|
||||
return errors.New(msg)
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(r io.Reader) ([]ast.Statement, error) {
|
||||
f, err := p.ParseFile(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The compiler skips statements sqlc has no node for; the formatter
|
||||
// must see them, so the filter lives here, not in ParseFile.
|
||||
var stmts []ast.Statement
|
||||
for _, stmt := range f.Stmts {
|
||||
if _, ok := stmt.Raw.Stmt.(*ast.TODO); ok {
|
||||
continue
|
||||
}
|
||||
stmts = append(stmts, stmt)
|
||||
}
|
||||
return stmts, nil
|
||||
}
|
||||
|
||||
// ParseFile parses like Parse and also carries the file's comments, which
|
||||
// marino's lexer records as it scans.
|
||||
func (p *Parser) ParseFile(r io.Reader) (*ast.File, error) {
|
||||
blob, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
src := string(blob)
|
||||
stmtNodes, _, err := p.pingcap.Parse(src, "", "")
|
||||
if err != nil {
|
||||
return nil, normalizeErr(err)
|
||||
}
|
||||
var stmts []ast.Statement
|
||||
// A statement's text spans from the end of the previous statement
|
||||
// through its terminator, so it carries the comments written above it
|
||||
// (that's where the "-- name:" annotation lives). Each text is a
|
||||
// contiguous slice of src laid down after the one before it, so
|
||||
// searching from the previous statement's end pins every text to its
|
||||
// own occurrence even when two statements read the same.
|
||||
searchFrom := 0
|
||||
for i := range stmtNodes {
|
||||
converter := &cc{preserveCase: p.preserveCase}
|
||||
// A statement sqlc has no node for converts to a TODO and stays in
|
||||
// the list: the formatter needs its extent to keep it as written,
|
||||
// and Parse filters it out for the compiler.
|
||||
out := converter.convert(stmtNodes[i])
|
||||
|
||||
text := stmtNodes[i].Text()
|
||||
idx := strings.Index(src[searchFrom:], text)
|
||||
if idx < 0 {
|
||||
return nil, fmt.Errorf("could not locate statement %d in source", i)
|
||||
}
|
||||
loc := searchFrom + idx
|
||||
searchFrom = loc + len(text)
|
||||
|
||||
stmtLen := len(text)
|
||||
if stmtLen > 0 && text[stmtLen-1] == ';' {
|
||||
stmtLen -= 1 // Subtract one to remove semicolon
|
||||
}
|
||||
|
||||
stmts = append(stmts, ast.Statement{
|
||||
Raw: &ast.RawStmt{
|
||||
Stmt: out,
|
||||
StmtLocation: loc,
|
||||
StmtLen: stmtLen,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
var comments []ast.Comment
|
||||
for _, c := range p.pingcap.Comments() {
|
||||
comments = append(comments, ast.Comment{
|
||||
Text: strings.TrimRight(src[c.Begin:c.End], " \t\r\n"),
|
||||
Start: c.Begin,
|
||||
End: c.End,
|
||||
OwnLine: ownLine(src, c.Begin),
|
||||
})
|
||||
}
|
||||
return &ast.File{Stmts: stmts, Comments: comments}, nil
|
||||
}
|
||||
|
||||
// ownLine reports that only blank space sits between the preceding line
|
||||
// break and pos.
|
||||
func ownLine(src string, pos int) bool {
|
||||
for j := pos - 1; j >= 0; j-- {
|
||||
switch src[j] {
|
||||
case '\n':
|
||||
return true
|
||||
case ' ', '\t', '\r':
|
||||
continue
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// https://dev.mysql.com/doc/refman/8.0/en/comments.html
|
||||
func (p *Parser) CommentSyntax() source.CommentSyntax {
|
||||
return source.CommentSyntax{
|
||||
Dash: true,
|
||||
SlashStar: true,
|
||||
Hash: true,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user