• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stephenafamo / bob / 15056625630

15 May 2025 10:44PM UTC coverage: 31.947% (-0.4%) from 32.329%
15056625630

push

github

web-flow
Merge pull request #420 from stephenafamo/other-lang-gen

Better support for generating code in multiple languages

208 of 361 new or added lines in 12 files covered. (57.62%)

3 existing lines in 1 file now uncovered.

6485 of 20299 relevant lines covered (31.95%)

208.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

5.22
/gen/bobgen-sqlite/driver/parser/parse.go
1
package parser
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7
        "strings"
8

9
        "github.com/aarondl/opt/omit"
10
        "github.com/antlr4-go/antlr/v4"
11
        "github.com/stephenafamo/bob/gen/bobgen-helpers/parser"
12
        antlrhelpers "github.com/stephenafamo/bob/gen/bobgen-helpers/parser/antlrhelpers"
13
        "github.com/stephenafamo/bob/gen/drivers"
14
        "github.com/stephenafamo/bob/gen/language"
15
        "github.com/stephenafamo/bob/internal"
16
        sqliteparser "github.com/stephenafamo/sqlparser/sqlite"
17
)
18

19
func New(t tables) Parser {
26✔
20
        return Parser{db: t}
26✔
21
}
26✔
22

23
type Parser struct {
24
        db tables
25
}
26

27
func (p Parser) ParseFolders(ctx context.Context, paths ...string) ([]drivers.QueryFolder, error) {
26✔
28
        return parser.ParseFolders(ctx, p, paths...)
26✔
29
}
26✔
30

31
func (p Parser) ParseQueries(_ context.Context, s string) ([]drivers.Query, error) {
×
32
        v := NewVisitor(p.db)
×
33
        infos, err := p.parse(v, s)
×
34
        if err != nil {
×
35
                return nil, fmt.Errorf("parse: %w", err)
×
36
        }
×
37

38
        queries := make([]drivers.Query, len(infos))
×
39
        for i, info := range infos {
×
40
                stmtStart := info.Node.GetStart().GetStart()
×
41
                stmtStop := info.Node.GetStop().GetStop()
×
42
                formatted, err := internal.EditStringSegment(s, stmtStart, stmtStop, info.EditRules...)
×
43
                if err != nil {
×
44
                        return nil, fmt.Errorf("format: %w", err)
×
45
                }
×
46

47
                cols := make([]drivers.QueryCol, len(info.Columns))
×
48
                for i, col := range info.Columns {
×
49
                        cols[i] = drivers.QueryCol{
×
50
                                Name:     col.Name,
×
51
                                DBName:   col.Name,
×
52
                                Nullable: omit.From(col.Type.Nullable()),
×
53
                                TypeName: TranslateColumnType(col.Type.ConfirmedDBType()),
×
54
                        }.Merge(col.Config)
×
55
                }
×
56

57
                name, configStr, _ := strings.Cut(info.Comment, " ")
×
58
                queries[i] = drivers.Query{
×
59
                        Name: name,
×
60
                        SQL:  formatted,
×
61
                        Type: info.QueryType,
×
62

×
63
                        Config: drivers.QueryConfig{
×
64
                                RowName:      name + "Row",
×
65
                                RowSliceName: "",
×
66
                                GenerateRow:  true,
×
67
                        }.Merge(parser.ParseQueryConfig(configStr)),
×
68

×
69
                        Columns: cols,
×
70
                        Args:    v.GetArgs(stmtStart, stmtStop, TranslateColumnType),
×
71
                        Mods:    stmtToMod{info},
×
72
                }
×
73
        }
74

75
        return queries, nil
×
76
}
77

78
func (Parser) parse(v *visitor, input string) ([]StmtInfo, error) {
×
79
        el := &errorListener{}
×
80

×
81
        // Get all hidden tokens (usually comments) and add edit rules to remove them
×
82
        v.BaseRules = []internal.EditRule{}
×
83
        hiddenLexer := sqliteparser.NewSQLiteLexer(antlr.NewInputStream(input))
×
84
        hiddenStream := antlr.NewCommonTokenStream(hiddenLexer, 1)
×
85
        hiddenStream.Fill()
×
86
        for _, token := range hiddenStream.GetAllTokens() {
×
87
                switch token.GetTokenType() {
×
88
                case sqliteparser.SQLiteParserSINGLE_LINE_COMMENT,
89
                        sqliteparser.SQLiteParserMULTILINE_COMMENT:
×
90
                        v.BaseRules = append(
×
91
                                v.BaseRules,
×
92
                                internal.Delete(token.GetStart(), token.GetStop()),
×
93
                        )
×
94
                }
95
        }
96

97
        // Get the regular tokens (usually the SQL statement)
98
        lexer := sqliteparser.NewSQLiteLexer(antlr.NewInputStream(input))
×
99
        stream := antlr.NewCommonTokenStream(lexer, 0)
×
100
        sqlParser := sqliteparser.NewSQLiteParser(stream)
×
101
        sqlParser.AddErrorListener(el)
×
102

×
103
        tree := sqlParser.Parse()
×
104
        if el.err != "" {
×
105
                return nil, errors.New(el.err)
×
106
        }
×
107

108
        infos, ok := tree.Accept(v).([]StmtInfo)
×
109
        if v.Err != nil {
×
110
                return nil, fmt.Errorf("visitor: %w", v.Err)
×
111
        }
×
112

113
        if !ok {
×
114
                return nil, fmt.Errorf("visitor: expected stmtInfo, got %T", infos)
×
115
        }
×
116

117
        return infos, nil
×
118
}
119

120
type stmtToMod struct {
121
        info StmtInfo
122
}
123

NEW
124
func (s stmtToMod) IncludeInTemplate(i language.Importer) string {
×
125
        for _, im := range s.info.Imports {
×
126
                i.Import(im...)
×
127
        }
×
128
        return s.info.Mods.String()
×
129
}
130

131
type errorListener struct {
132
        *antlr.DefaultErrorListener
133

134
        err string
135
}
136

137
func (el *errorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol any, line, column int, msg string, e antlr.RecognitionException) {
×
138
        el.err = msg
×
139
}
×
140

141
//nolint:gochecknoglobals
142
var defaultFunctions = Functions{
143
        "abs": {
144
                RequiredArgs: 1,
145
                Args:         []string{""},
146
                CalcReturnType: func(args ...string) string {
×
147
                        if args[0] == "INTEGER" {
×
148
                                return "INTEGER"
×
149
                        }
×
150
                        return "REAL"
×
151
                },
152
        },
153
        "changes": {
154
                ReturnType: "INTEGER",
155
        },
156
        "char": {
157
                RequiredArgs: 1,
158
                Variadic:     true,
159
                Args:         []string{"INTEGER"},
160
                ReturnType:   "TEXT",
161
        },
162
        "coalesce": {
163
                RequiredArgs:         1,
164
                Variadic:             true,
165
                Args:                 []string{""},
166
                ShouldArgsBeNullable: true,
167
                CalcReturnType: func(args ...string) string {
×
168
                        for _, arg := range args {
×
169
                                if arg != "" {
×
170
                                        return arg
×
171
                                }
×
172
                        }
173
                        return ""
×
174
                },
175
                CalcNullable: antlrhelpers.AllNullable,
176
        },
177
        "concat": {
178
                RequiredArgs: 1,
179
                Variadic:     true,
180
                Args:         []string{"TEXT"},
181
                ReturnType:   "TEXT",
182
                CalcNullable: antlrhelpers.NeverNullable,
183
        },
184
        "concat_ws": {
185
                RequiredArgs: 2,
186
                Variadic:     true,
187
                Args:         []string{"TEXT", "TEXT"},
188
                ReturnType:   "TEXT",
189
                CalcNullable: func(args ...func() bool) func() bool {
×
190
                        return args[0]
×
191
                },
×
192
        },
193
        "format": {
194
                RequiredArgs: 2,
195
                Variadic:     true,
196
                Args:         []string{"TEXT", ""},
197
                ReturnType:   "TEXT",
198
                CalcNullable: func(args ...func() bool) func() bool {
×
199
                        return args[0]
×
200
                },
×
201
        },
202
        "glob": {
203
                RequiredArgs: 2,
204
                Args:         []string{"TEXT", "TEXT"},
205
                ReturnType:   "BOOLEAN",
206
        },
207
        "hex": {
208
                RequiredArgs: 1,
209
                Args:         []string{""},
210
                ReturnType:   "TEXT",
211
        },
212
        "ifnull": {
213
                RequiredArgs: 2,
214
                Args:         []string{""},
215
                CalcReturnType: func(args ...string) string {
×
216
                        for _, arg := range args {
×
217
                                if arg != "" {
×
218
                                        return arg
×
219
                                }
×
220
                        }
221
                        return ""
×
222
                },
223
                CalcNullable: antlrhelpers.AllNullable,
224
        },
225
        "iif": {
226
                RequiredArgs: 3,
227
                Args:         []string{"BOOLEAN", "", ""},
228
                CalcReturnType: func(args ...string) string {
×
229
                        return args[1]
×
230
                },
×
231
                CalcNullable: func(args ...func() bool) func() bool {
×
232
                        return antlrhelpers.AnyNullable(args[1], args[2])
×
233
                },
×
234
        },
235
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc